-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
components.tsx
109 lines (96 loc) · 3.3 KB
/
components.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import React, {Children, isValidElement} from 'react';
// Wraps `element` in `Component`, if it is not already an instance of
// `Component`. If `props` is passed, those will be added as props on the
// wrapped component. If `element` is null, the component is not wrapped.
export function wrapWithComponent<TProps extends React.PropsWithChildren>(
element: React.ReactNode | null | undefined,
Component: React.ComponentType<TProps>,
props: TProps & JSX.IntrinsicAttributes,
): React.ReactNode {
if (element == null) {
return null;
}
return isElementOfType(element, Component) ? (
element
) : (
<Component {...props}>{element}</Component>
);
}
// In development, we compare based on the name of the function because
// React Hot Loader proxies React components in order to make updates. In
// production we can simply compare the components for equality.
const isComponent =
process.env.NODE_ENV === 'development'
? hotReloadComponentCheck
: (
AComponent: React.ComponentType<any>,
AnotherComponent: React.ComponentType<any>,
) => AComponent === AnotherComponent;
// Checks whether `element` is a React element of type `Component` (or one of
// the passed components, if `Component` is an array of React components).
export function isElementOfType<TProps>(
element: React.ReactNode | null | undefined,
Component: React.ComponentType<TProps> | React.ComponentType<TProps>[],
): boolean {
if (
element == null ||
!isValidElement(element) ||
typeof element.type === 'string'
) {
return false;
}
const {type: defaultType} = element;
// Type override allows components to bypass default wrapping behavior. Ex: Stack, ResourceList...
// See https://github.com/Shopify/app-extension-libs/issues/996#issuecomment-710437088
const overrideType = element.props?.__type__;
const type = overrideType || defaultType;
const Components = Array.isArray(Component) ? Component : [Component];
return Components.some(
(AComponent) => typeof type !== 'string' && isComponent(AComponent, type),
);
}
// Returns all children that are valid elements as an array. Can optionally be
// filtered by passing `predicate`.
export function elementChildren<T extends React.ReactElement>(
children: React.ReactNode,
predicate: (element: T) => boolean = () => true,
): T[] {
return Children.toArray(children).filter(
(child) => isValidElement(child) && predicate(child as T),
) as T[];
}
interface ConditionalWrapperProps {
children: any;
condition: boolean;
wrapper: (children: any) => any;
}
export function ConditionalWrapper({
condition,
wrapper,
children,
}: ConditionalWrapperProps): JSX.Element {
return condition ? wrapper(children) : children;
}
interface ConditionalRenderProps {
condition: boolean;
children: any;
}
export function ConditionalRender({
condition,
children,
}: ConditionalRenderProps): JSX.Element {
return condition ? children : null;
}
function hotReloadComponentCheck(
AComponent: React.ComponentType<any>,
AnotherComponent: React.ComponentType<any>,
) {
const componentName = AComponent.name;
const anotherComponentName = (
AnotherComponent as React.FunctionComponent<any>
).displayName;
return (
AComponent === AnotherComponent ||
(Boolean(componentName) && componentName === anotherComponentName)
);
}