piral-core 0.15.0-alpha.4041 → 0.15.0-alpha.4231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/PiralContext.js.map +1 -1
- package/esm/components/ErrorBoundary.d.ts +4 -0
- package/esm/components/ErrorBoundary.js.map +1 -1
- package/esm/components/ExtensionSlot.js +12 -4
- package/esm/components/ExtensionSlot.js.map +1 -1
- package/esm/components/ForeignComponentContainer.d.ts +1 -1
- package/esm/components/ForeignComponentContainer.js.map +1 -1
- package/esm/components/PiralSuspense.d.ts +4 -1
- package/esm/components/PiralSuspense.js.map +1 -1
- package/esm/components/PiralView.d.ts +4 -0
- package/esm/components/PiralView.js.map +1 -1
- package/esm/components/ResponsiveLayout.d.ts +7 -0
- package/esm/components/ResponsiveLayout.js.map +1 -1
- package/esm/components/wrapComponent.d.ts +1 -1
- package/esm/defaults/DefaultRouter.d.ts +2 -1
- package/esm/defaults/DefaultRouter.js.map +1 -1
- package/esm/state/withApi.js.map +1 -1
- package/esm/types/api.d.ts +16 -3
- package/esm/types/components.d.ts +9 -1
- package/esm/types/extension.d.ts +19 -0
- package/esm/types/instance.d.ts +3 -1
- package/esm/types/state.d.ts +20 -2
- package/lib/PiralContext.js.map +1 -1
- package/lib/actions/index.js +6 -6
- package/lib/actions/index.js.map +1 -1
- package/lib/components/ErrorBoundary.d.ts +4 -0
- package/lib/components/ErrorBoundary.js +1 -1
- package/lib/components/ErrorBoundary.js.map +1 -1
- package/lib/components/ExtensionSlot.js +12 -4
- package/lib/components/ExtensionSlot.js.map +1 -1
- package/lib/components/ForeignComponentContainer.d.ts +1 -1
- package/lib/components/ForeignComponentContainer.js.map +1 -1
- package/lib/components/PiralRoutes.js +1 -1
- package/lib/components/PiralRoutes.js.map +1 -1
- package/lib/components/PiralSuspense.d.ts +4 -1
- package/lib/components/PiralSuspense.js.map +1 -1
- package/lib/components/PiralView.d.ts +4 -0
- package/lib/components/PiralView.js.map +1 -1
- package/lib/components/ResponsiveLayout.d.ts +7 -0
- package/lib/components/ResponsiveLayout.js.map +1 -1
- package/lib/components/index.js +12 -12
- package/lib/components/index.js.map +1 -1
- package/lib/components/wrapComponent.d.ts +1 -1
- package/lib/defaults/DefaultRouteSwitch.js +1 -1
- package/lib/defaults/DefaultRouteSwitch.js.map +1 -1
- package/lib/defaults/DefaultRouter.d.ts +2 -1
- package/lib/defaults/DefaultRouter.js.map +1 -1
- package/lib/defaults/index.js +5 -5
- package/lib/defaults/index.js.map +1 -1
- package/lib/hooks/actions.js +1 -1
- package/lib/hooks/actions.js.map +1 -1
- package/lib/hooks/index.js +6 -6
- package/lib/hooks/index.js.map +1 -1
- package/lib/index.js +9 -9
- package/lib/index.js.map +1 -1
- package/lib/modules/index.js +3 -3
- package/lib/modules/index.js.map +1 -1
- package/lib/setters/index.js +7 -7
- package/lib/setters/index.js.map +1 -1
- package/lib/state/index.js +4 -4
- package/lib/state/index.js.map +1 -1
- package/lib/state/withApi.js.map +1 -1
- package/lib/types/api.d.ts +16 -3
- package/lib/types/components.d.ts +9 -1
- package/lib/types/extension.d.ts +19 -0
- package/lib/types/index.js +12 -12
- package/lib/types/index.js.map +1 -1
- package/lib/types/instance.d.ts +3 -1
- package/lib/types/state.d.ts +20 -2
- package/lib/utils/helpers.js +1 -1
- package/lib/utils/helpers.js.map +1 -1
- package/lib/utils/index.js +10 -10
- package/lib/utils/index.js.map +1 -1
- package/package.json +21 -8
- package/src/PiralContext.tsx +5 -1
- package/src/RootListener.test.tsx +7 -5
- package/src/actions/app.test.ts +1 -1
- package/src/components/ErrorBoundary.tsx +4 -0
- package/src/components/ExtensionSlot.test.tsx +32 -1
- package/src/components/ExtensionSlot.tsx +33 -16
- package/src/components/ForeignComponentContainer.test.tsx +25 -16
- package/src/components/ForeignComponentContainer.tsx +1 -2
- package/src/components/PiralSuspense.tsx +5 -1
- package/src/components/PiralView-server.test.tsx +27 -27
- package/src/components/PiralView.tsx +4 -0
- package/src/components/ResponsiveLayout.tsx +7 -0
- package/src/components/wrapComponent.tsx +1 -1
- package/src/defaults/DefaultRouter.tsx +2 -1
- package/src/hooks/setter.test.ts +3 -2
- package/src/state/withApi.tsx +2 -3
- package/src/types/api.ts +21 -3
- package/src/types/components.ts +11 -2
- package/src/types/extension.ts +19 -0
- package/src/types/instance.ts +3 -1
- package/src/types/state.ts +20 -2
|
@@ -49,6 +49,11 @@ const state = {
|
|
|
49
49
|
component: StubComponent3,
|
|
50
50
|
},
|
|
51
51
|
],
|
|
52
|
+
lol: [
|
|
53
|
+
{
|
|
54
|
+
component: StubComponent1,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
52
57
|
},
|
|
53
58
|
},
|
|
54
59
|
};
|
|
@@ -118,7 +123,7 @@ describe('Extension Module', () => {
|
|
|
118
123
|
it('overrides the empty renderer on not available extension', () => {
|
|
119
124
|
const node = mount(<ExtensionSlot name="qxz" empty={() => <StubComponent1 key="empty" />} />);
|
|
120
125
|
expect(node.find(StubComponent1).length).toBe(1);
|
|
121
|
-
expect(node.find(
|
|
126
|
+
expect(node.find(StubComponent2).length).toBe(0);
|
|
122
127
|
});
|
|
123
128
|
|
|
124
129
|
it('overrides the empty renderer on an available extension', () => {
|
|
@@ -138,4 +143,30 @@ describe('Extension Module', () => {
|
|
|
138
143
|
expect(node.find(StubComponent1).length).toBe(1);
|
|
139
144
|
expect(node.find(StubComponent2).length).toBe(1);
|
|
140
145
|
});
|
|
146
|
+
|
|
147
|
+
it('does not use the render function with empty when emptySkipsRender is set', () => {
|
|
148
|
+
const node = mount(
|
|
149
|
+
<ExtensionSlot
|
|
150
|
+
name="foo"
|
|
151
|
+
emptySkipsRender
|
|
152
|
+
empty={() => <StubComponent2 key="empty" />}
|
|
153
|
+
render={(nodes) => <StubComponent1 children={nodes} />}
|
|
154
|
+
/>,
|
|
155
|
+
);
|
|
156
|
+
expect(node.find(StubComponent1).length).toBe(0);
|
|
157
|
+
expect(node.find(StubComponent2).length).toBe(1);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('does use the render function without empty independent if emptySkipsRender is set', () => {
|
|
161
|
+
const node = mount(
|
|
162
|
+
<ExtensionSlot
|
|
163
|
+
name="lol"
|
|
164
|
+
emptySkipsRender
|
|
165
|
+
empty={() => <StubComponent2 key="empty" />}
|
|
166
|
+
render={(nodes) => <StubComponent1 children={nodes} />}
|
|
167
|
+
/>,
|
|
168
|
+
);
|
|
169
|
+
expect(node.find(StubComponent1).length).toBe(2);
|
|
170
|
+
expect(node.find(StubComponent2).length).toBe(0);
|
|
171
|
+
});
|
|
141
172
|
});
|
|
@@ -20,33 +20,50 @@ const renderExtensions: [ExtensionRegistration] = [
|
|
|
20
20
|
defaults: {},
|
|
21
21
|
pilet: '',
|
|
22
22
|
reference: {
|
|
23
|
-
displayName: '
|
|
23
|
+
displayName: 'AnyComponent',
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
26
|
];
|
|
27
27
|
|
|
28
|
+
function defaultOrder(extensions: Array<ExtensionRegistration>) {
|
|
29
|
+
return extensions;
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
/**
|
|
29
33
|
* The extension slot component to be used when the available
|
|
30
34
|
* extensions of a given name should be rendered at a specific
|
|
31
35
|
* location.
|
|
32
36
|
*/
|
|
33
37
|
export function ExtensionSlot<T extends string>(props: ExtensionSlotProps<T>) {
|
|
34
|
-
const {
|
|
38
|
+
const {
|
|
39
|
+
name,
|
|
40
|
+
render = defaultRender,
|
|
41
|
+
empty,
|
|
42
|
+
params,
|
|
43
|
+
children,
|
|
44
|
+
emptySkipsRender = false,
|
|
45
|
+
order = defaultOrder,
|
|
46
|
+
} = props;
|
|
35
47
|
const extensions = useGlobalState((s) => (name ? s.registry.extensions[name] || none : renderExtensions));
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const isEmpty = extensions.length === 0 && isfunc(empty);
|
|
49
|
+
const content = isEmpty
|
|
50
|
+
? [defaultRender(empty(), 'empty')]
|
|
51
|
+
: order(extensions).map(({ component: Component, reference, defaults = {} }, i) => (
|
|
52
|
+
<Component
|
|
53
|
+
key={`${reference?.displayName || '_'}${i}`}
|
|
54
|
+
children={children}
|
|
55
|
+
params={{
|
|
56
|
+
...defaults,
|
|
57
|
+
...params,
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
));
|
|
61
|
+
|
|
62
|
+
if (isEmpty && emptySkipsRender) {
|
|
63
|
+
return content[0];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return render(content);
|
|
50
67
|
}
|
|
51
68
|
|
|
52
69
|
ExtensionSlot.displayName = `ExtensionSlot`;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { act } from 'react-dom/test-utils';
|
|
3
4
|
import { ForeignComponentContainer } from './ForeignComponentContainer';
|
|
4
5
|
|
|
6
|
+
async function render(element: any, container: Element) {
|
|
7
|
+
const root = createRoot(container);
|
|
8
|
+
root.render(element);
|
|
9
|
+
await act(() => Promise.resolve());
|
|
10
|
+
return root;
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
describe('ForeignComponentContainer component', () => {
|
|
6
|
-
it('mounts an HTML component', () => {
|
|
14
|
+
it('mounts an HTML component', async () => {
|
|
7
15
|
const container = document.body.appendChild(document.createElement('div'));
|
|
8
16
|
const mount = jest.fn();
|
|
9
17
|
const component = { mount };
|
|
10
|
-
render(
|
|
18
|
+
await render(
|
|
11
19
|
<ForeignComponentContainer $component={component} $context={undefined} $portalId="foo" innerProps={{}} />,
|
|
12
20
|
container,
|
|
13
21
|
);
|
|
@@ -15,28 +23,28 @@ describe('ForeignComponentContainer component', () => {
|
|
|
15
23
|
container.remove();
|
|
16
24
|
});
|
|
17
25
|
|
|
18
|
-
it('unmounts an HTML component', () => {
|
|
26
|
+
it('unmounts an HTML component', async () => {
|
|
19
27
|
const container = document.body.appendChild(document.createElement('div'));
|
|
20
28
|
const mount = jest.fn();
|
|
21
29
|
const unmount = jest.fn();
|
|
22
30
|
const component = { mount, unmount };
|
|
23
|
-
render(
|
|
31
|
+
const root = await render(
|
|
24
32
|
<ForeignComponentContainer $component={component} $context={undefined} $portalId="foo" innerProps={{}} />,
|
|
25
33
|
container,
|
|
26
34
|
);
|
|
27
35
|
expect(mount).toHaveBeenCalled();
|
|
28
36
|
expect(unmount).not.toHaveBeenCalled();
|
|
29
|
-
|
|
37
|
+
root.unmount();
|
|
30
38
|
expect(unmount).toHaveBeenCalled();
|
|
31
39
|
container.remove();
|
|
32
40
|
});
|
|
33
41
|
|
|
34
|
-
it('updates an HTML component', () => {
|
|
42
|
+
it('updates an HTML component', async () => {
|
|
35
43
|
const container = document.body.appendChild(document.createElement('div'));
|
|
36
44
|
const mount = jest.fn();
|
|
37
45
|
const update = jest.fn();
|
|
38
46
|
const component = { mount, update };
|
|
39
|
-
render(
|
|
47
|
+
const root = await render(
|
|
40
48
|
<ForeignComponentContainer
|
|
41
49
|
$component={component}
|
|
42
50
|
$context={undefined}
|
|
@@ -47,20 +55,20 @@ describe('ForeignComponentContainer component', () => {
|
|
|
47
55
|
);
|
|
48
56
|
expect(mount).toHaveBeenCalled();
|
|
49
57
|
expect(update).not.toHaveBeenCalled();
|
|
50
|
-
render(
|
|
58
|
+
root.render(
|
|
51
59
|
<ForeignComponentContainer
|
|
52
60
|
$component={component}
|
|
53
61
|
$context={undefined}
|
|
54
62
|
$portalId="foo"
|
|
55
63
|
innerProps={{ a: 'foo' }}
|
|
56
64
|
/>,
|
|
57
|
-
container,
|
|
58
65
|
);
|
|
66
|
+
await act(() => Promise.resolve());
|
|
59
67
|
expect(update).toHaveBeenCalled();
|
|
60
68
|
container.remove();
|
|
61
69
|
});
|
|
62
70
|
|
|
63
|
-
it('forces re-rendering of an HTML component', () => {
|
|
71
|
+
it('forces re-rendering of an HTML component', async () => {
|
|
64
72
|
const container = document.body.appendChild(document.createElement('div'));
|
|
65
73
|
const componentDidMount = ForeignComponentContainer.prototype.componentDidMount;
|
|
66
74
|
ForeignComponentContainer.prototype.componentDidMount = function () {
|
|
@@ -73,7 +81,7 @@ describe('ForeignComponentContainer component', () => {
|
|
|
73
81
|
const update = jest.fn();
|
|
74
82
|
const unmount = jest.fn();
|
|
75
83
|
const component = { mount, update, unmount };
|
|
76
|
-
render(
|
|
84
|
+
const root = await render(
|
|
77
85
|
<ForeignComponentContainer
|
|
78
86
|
$component={component}
|
|
79
87
|
$context={undefined}
|
|
@@ -85,27 +93,27 @@ describe('ForeignComponentContainer component', () => {
|
|
|
85
93
|
expect(mount).toHaveBeenCalled();
|
|
86
94
|
expect(unmount).not.toHaveBeenCalled();
|
|
87
95
|
expect(update).not.toHaveBeenCalled();
|
|
88
|
-
render(
|
|
96
|
+
root.render(
|
|
89
97
|
<ForeignComponentContainer
|
|
90
98
|
$component={component}
|
|
91
99
|
$context={undefined}
|
|
92
100
|
$portalId="foo"
|
|
93
101
|
innerProps={{ a: 'foo' }}
|
|
94
102
|
/>,
|
|
95
|
-
container,
|
|
96
103
|
);
|
|
104
|
+
await act(() => Promise.resolve());
|
|
97
105
|
expect(update).not.toHaveBeenCalled();
|
|
98
106
|
expect(unmount).toHaveBeenCalled();
|
|
99
107
|
container.remove();
|
|
100
108
|
});
|
|
101
109
|
|
|
102
|
-
it('listens to render-html', () => {
|
|
110
|
+
it('listens to render-html', async () => {
|
|
103
111
|
const container = document.body.appendChild(document.createElement('div'));
|
|
104
112
|
const mount = jest.fn();
|
|
105
113
|
const renderHtmlExtension = jest.fn();
|
|
106
114
|
const component = { mount };
|
|
107
115
|
const props = { piral: { renderHtmlExtension }, meta: {} };
|
|
108
|
-
render(
|
|
116
|
+
await render(
|
|
109
117
|
<ForeignComponentContainer $component={component} $context={undefined} $portalId="foo" innerProps={props} />,
|
|
110
118
|
container,
|
|
111
119
|
);
|
|
@@ -113,6 +121,7 @@ describe('ForeignComponentContainer component', () => {
|
|
|
113
121
|
const node = document.querySelector('[data-portal-id=foo]');
|
|
114
122
|
expect(renderHtmlExtension).not.toHaveBeenCalled();
|
|
115
123
|
node.dispatchEvent(new CustomEvent('render-html', { detail: {} }));
|
|
124
|
+
await act(() => Promise.resolve());
|
|
116
125
|
expect(renderHtmlExtension).toHaveBeenCalled();
|
|
117
126
|
container.remove();
|
|
118
127
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { isfunc } from 'piral-base';
|
|
3
|
-
import {
|
|
4
|
-
import { ForeignComponent, BaseComponentProps, ComponentContext } from '../types';
|
|
3
|
+
import type { ForeignComponent, BaseComponentProps, ComponentContext } from '../types';
|
|
5
4
|
|
|
6
5
|
interface ForeignComponentContainerProps<T> {
|
|
7
6
|
$portalId: string;
|
|
@@ -2,7 +2,11 @@ import * as React from 'react';
|
|
|
2
2
|
import { RegisteredErrorInfo, RegisteredLoadingIndicator } from './components';
|
|
3
3
|
import { useGlobalState } from '../hooks';
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export interface PiralSuspenseProps {
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const PiralSuspense: React.FC<PiralSuspenseProps> = ({ children }) => {
|
|
6
10
|
const { error, loading } = useGlobalState((m) => m.app);
|
|
7
11
|
|
|
8
12
|
return error ? (
|
|
@@ -17,45 +17,45 @@ StubErrorInfo.displayName = 'StubErrorInfo';
|
|
|
17
17
|
const StubLoader: React.FC = () => <div />;
|
|
18
18
|
StubLoader.displayName = 'StubLoader';
|
|
19
19
|
|
|
20
|
-
const StubRouter: React.FC = ({ children }) => <div>{children}</div>;
|
|
20
|
+
const StubRouter: React.FC<React.PropsWithChildren<{}>> = ({ children }) => <div>{children}</div>;
|
|
21
21
|
StubRouter.displayName = 'StubRouter';
|
|
22
22
|
|
|
23
|
-
const StubLayout: React.FC = ({ children }) => <div>{children}</div>;
|
|
23
|
+
const StubLayout: React.FC<React.PropsWithChildren<{}>> = ({ children }) => <div>{children}</div>;
|
|
24
24
|
StubLayout.displayName = 'StubLayout';
|
|
25
25
|
|
|
26
26
|
jest.mock('../hooks');
|
|
27
27
|
jest.mock('./PiralRoutes');
|
|
28
28
|
|
|
29
29
|
const state = {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
30
|
+
app: {
|
|
31
|
+
error: undefined,
|
|
32
|
+
loading: true,
|
|
33
|
+
},
|
|
34
|
+
components: {
|
|
35
|
+
ErrorInfo: StubErrorInfo,
|
|
36
|
+
LoadingIndicator: StubLoader,
|
|
37
|
+
Router: StubRouter,
|
|
38
|
+
Layout: StubLayout,
|
|
39
|
+
},
|
|
40
|
+
registry: {
|
|
41
|
+
pages: {},
|
|
42
|
+
extensions: {},
|
|
43
|
+
},
|
|
44
|
+
portals: {},
|
|
45
|
+
routes: {},
|
|
46
|
+
provider: undefined,
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
(hooks as any).useGlobalState = (select: any) => select(state);
|
|
50
50
|
|
|
51
|
-
(routes as any).PiralRoutes = ({
|
|
51
|
+
(routes as any).PiralRoutes = ({}) => <StubDashboard />;
|
|
52
52
|
|
|
53
53
|
describe('Portal Module', () => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
it('In this test window should be undefined', () => {
|
|
55
|
+
state.app.loading = false;
|
|
56
|
+
state.app.error = undefined;
|
|
57
|
+
const node = render(<PiralView children={undefined} />);
|
|
58
|
+
expect(typeof window).toBe('undefined');
|
|
59
|
+
expect(node.length).toBe(1);
|
|
60
|
+
});
|
|
61
61
|
});
|
|
@@ -11,7 +11,14 @@ export interface ResponsiveLayoutProps {
|
|
|
11
11
|
* The individual breakpoints to be used for the different layouts.
|
|
12
12
|
*/
|
|
13
13
|
breakpoints?: LayoutBreakpoints;
|
|
14
|
+
/**
|
|
15
|
+
* The actual layout component to render to transport.
|
|
16
|
+
*/
|
|
14
17
|
Layout: React.ComponentType<LayoutProps>;
|
|
18
|
+
/**
|
|
19
|
+
* The content to display.
|
|
20
|
+
*/
|
|
21
|
+
children: React.ReactNode;
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/**
|
|
@@ -4,7 +4,7 @@ import { PortalRenderer } from './PortalRenderer';
|
|
|
4
4
|
import { ForeignComponentContainer } from './ForeignComponentContainer';
|
|
5
5
|
import { useGlobalStateContext } from '../hooks';
|
|
6
6
|
import { convertComponent, none } from '../utils';
|
|
7
|
-
import { AnyComponent, ComponentConverters, ForeignComponent, PiletApi, BaseComponentProps } from '../types';
|
|
7
|
+
import type { AnyComponent, ComponentConverters, ForeignComponent, PiletApi, BaseComponentProps } from '../types';
|
|
8
8
|
|
|
9
9
|
// this is an arbitrary start number to have 6 digits
|
|
10
10
|
let portalIdBase = 123456;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { BrowserRouter } from 'react-router-dom';
|
|
3
3
|
import { useGlobalState } from '../hooks';
|
|
4
|
+
import { RouterProps } from '../types';
|
|
4
5
|
|
|
5
|
-
export const DefaultRouter: React.FC = ({ children }) => {
|
|
6
|
+
export const DefaultRouter: React.FC<RouterProps> = ({ children }) => {
|
|
6
7
|
const publicPath = useGlobalState((s) => s.app.publicPath);
|
|
7
8
|
return <BrowserRouter basename={publicPath}>{children}</BrowserRouter>;
|
|
8
9
|
};
|
package/src/hooks/setter.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { act } from 'react-dom/test-utils';
|
|
3
|
-
import {
|
|
3
|
+
import { createRoot } from 'react-dom/client';
|
|
4
4
|
import { useSetter } from './setter';
|
|
5
5
|
|
|
6
6
|
describe('UseSetter Hook Module', () => {
|
|
@@ -12,7 +12,8 @@ describe('UseSetter Hook Module', () => {
|
|
|
12
12
|
return null;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const root = createRoot(document.body.appendChild(document.createElement('div')));
|
|
16
|
+
root.render(React.createElement(MyComponent));
|
|
16
17
|
await act(() => Promise.resolve());
|
|
17
18
|
expect(cb).toHaveBeenCalled();
|
|
18
19
|
});
|
package/src/state/withApi.tsx
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { __RouterContext } from 'react-router';
|
|
3
2
|
import { ErrorBoundary, wrapComponent } from '../components';
|
|
4
3
|
import { defaultRender } from '../utils';
|
|
5
4
|
import { AnyComponent, Errors, PiletApi, BaseComponentProps, GlobalStateContext } from '../types';
|
|
6
5
|
|
|
7
|
-
const DefaultWrapper: React.FC = (props) => defaultRender(props.children);
|
|
6
|
+
const DefaultWrapper: React.FC<React.PropsWithChildren<{}>> = (props) => defaultRender(props.children);
|
|
8
7
|
|
|
9
8
|
function getWrapper(wrappers: Record<string, React.ComponentType<any>>, wrapperType: string) {
|
|
10
9
|
const WrapAll = wrappers['*'];
|
|
@@ -26,7 +25,7 @@ function makeWrapper<TProps>(
|
|
|
26
25
|
outerProps: any,
|
|
27
26
|
wrapperType: string,
|
|
28
27
|
errorType: keyof Errors,
|
|
29
|
-
): React.FC<TProps
|
|
28
|
+
): React.FC<React.PropsWithChildren<TProps>> {
|
|
30
29
|
const OuterWrapper = context.readState((m) => getWrapper(m.registry.wrappers, wrapperType));
|
|
31
30
|
|
|
32
31
|
return (props) => (
|
package/src/types/api.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReactElement } from 'react';
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
2
|
import type { RouteComponentProps } from 'react-router';
|
|
3
3
|
import type {
|
|
4
4
|
PiletApi,
|
|
@@ -46,6 +46,10 @@ export interface ExtensionComponentProps<T> extends BaseComponentProps {
|
|
|
46
46
|
* The provided parameters for showing the extension.
|
|
47
47
|
*/
|
|
48
48
|
params: T extends keyof PiralExtensionSlotMap ? PiralExtensionSlotMap[T] : T extends string ? any : T;
|
|
49
|
+
/**
|
|
50
|
+
* The optional children to receive, if any.
|
|
51
|
+
*/
|
|
52
|
+
children?: ReactNode;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
/**
|
|
@@ -70,6 +74,20 @@ export interface PageComponentProps<T = any, S = any> extends RouteBaseProps<T,
|
|
|
70
74
|
meta: PiralPageMeta;
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
/**
|
|
78
|
+
* The meta data registered for a page.
|
|
79
|
+
*/
|
|
80
|
+
export interface PiralPageMeta extends PiralCustomPageMeta {}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Shorthand for the definition of an extension component.
|
|
84
|
+
*/
|
|
85
|
+
export type AnyExtensionComponent<TName> = TName extends keyof PiralExtensionSlotMap
|
|
86
|
+
? AnyComponent<ExtensionComponentProps<TName>>
|
|
87
|
+
: TName extends string
|
|
88
|
+
? AnyComponent<ExtensionComponentProps<any>>
|
|
89
|
+
: AnyComponent<ExtensionComponentProps<TName>>;
|
|
90
|
+
|
|
73
91
|
/**
|
|
74
92
|
* Defines the Pilet API from piral-core.
|
|
75
93
|
* This interface will be consumed by pilet developers so that their pilet can interact with the piral instance.
|
|
@@ -112,7 +130,7 @@ export interface PiletCoreApi {
|
|
|
112
130
|
*/
|
|
113
131
|
registerExtension<TName>(
|
|
114
132
|
name: TName extends string ? TName : string,
|
|
115
|
-
Component:
|
|
133
|
+
Component: AnyExtensionComponent<TName>,
|
|
116
134
|
defaults?: Partial<ExtensionParams<TName>>,
|
|
117
135
|
): RegistrationDisposer;
|
|
118
136
|
/**
|
|
@@ -123,7 +141,7 @@ export interface PiletCoreApi {
|
|
|
123
141
|
*/
|
|
124
142
|
unregisterExtension<TName>(
|
|
125
143
|
name: TName extends string ? TName : string,
|
|
126
|
-
Component:
|
|
144
|
+
Component: AnyExtensionComponent<TName>,
|
|
127
145
|
): void;
|
|
128
146
|
/**
|
|
129
147
|
* React component for displaying extensions for a given name.
|
package/src/types/components.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentType } from 'react';
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import type { RouteComponentProps, SwitchProps } from 'react-router';
|
|
3
3
|
import type { FirstParametersOf, UnionOf } from './common';
|
|
4
4
|
import type { PiralCustomErrors, PiralCustomComponentConverters } from './custom';
|
|
@@ -191,12 +191,21 @@ export interface LayoutProps {
|
|
|
191
191
|
* The currently selected layout type.
|
|
192
192
|
*/
|
|
193
193
|
currentLayout: LayoutType;
|
|
194
|
+
/**
|
|
195
|
+
* The page's content.
|
|
196
|
+
*/
|
|
197
|
+
children: ReactNode;
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
/**
|
|
197
201
|
* The props of a Router component.
|
|
198
202
|
*/
|
|
199
|
-
export interface RouterProps {
|
|
203
|
+
export interface RouterProps {
|
|
204
|
+
/**
|
|
205
|
+
* The content to be rendered inside the router.
|
|
206
|
+
*/
|
|
207
|
+
children?: ReactNode;
|
|
208
|
+
}
|
|
200
209
|
|
|
201
210
|
/**
|
|
202
211
|
* The props of the RouteSwitch component.
|
package/src/types/extension.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode, ReactElement } from 'react';
|
|
2
2
|
import type { PiralCustomExtensionSlotMap } from './custom';
|
|
3
|
+
import type { ExtensionRegistration } from './state';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The mapping of the existing (known) extension slots.
|
|
@@ -19,9 +20,27 @@ export interface BaseExtensionSlotProps<TName, TParams> {
|
|
|
19
20
|
* for the specified extension.
|
|
20
21
|
*/
|
|
21
22
|
empty?(): ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Determines if the `render` function should be called in case no
|
|
25
|
+
* components are available for the specified extension.
|
|
26
|
+
*
|
|
27
|
+
* If true, `empty` will be called and returned from the slot.
|
|
28
|
+
* If false, `render` will be called with the result of calling `empty`.
|
|
29
|
+
* The result of calling `render` will then be returned from the slot.
|
|
30
|
+
*/
|
|
31
|
+
emptySkipsRender?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Defines the order of the components to render.
|
|
34
|
+
* May be more convient than using `render` w.r.t. ordering extensions
|
|
35
|
+
* by their supplied metadata.
|
|
36
|
+
* @param extensions The registered extensions.
|
|
37
|
+
* @returns The ordered extensions.
|
|
38
|
+
*/
|
|
39
|
+
order?(extensions: Array<ExtensionRegistration>): Array<ExtensionRegistration>;
|
|
22
40
|
/**
|
|
23
41
|
* Defines how the provided nodes should be rendered.
|
|
24
42
|
* @param nodes The rendered extension nodes.
|
|
43
|
+
* @returns The rendered nodes, i.e., an ReactElement.
|
|
25
44
|
*/
|
|
26
45
|
render?(nodes: Array<ReactNode>): ReactElement<any, any> | null;
|
|
27
46
|
/**
|
package/src/types/instance.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
1
2
|
import type { PiletApi, PiletApiCreator, LoadPiletsOptions, EventEmitter } from 'piral-base';
|
|
2
3
|
import type { GlobalStateContext } from './state';
|
|
3
4
|
import type { LayoutBreakpoints } from './layout';
|
|
4
|
-
import { PiralConfiguration } from './config';
|
|
5
|
+
import type { PiralConfiguration } from './config';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* The props of the Piral context.
|
|
@@ -11,6 +12,7 @@ export interface PiralContextProps {
|
|
|
11
12
|
* The specific Piral instance to be used.
|
|
12
13
|
*/
|
|
13
14
|
instance?: PiralInstance;
|
|
15
|
+
children?: ReactNode;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/**
|
package/src/types/state.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentType, ReactPortal } from 'react';
|
|
1
|
+
import type { ComponentType, ReactPortal, PropsWithChildren } from 'react';
|
|
2
2
|
import type { RouteComponentProps } from 'react-router';
|
|
3
3
|
import type { Atom } from '@dbeining/react-atom';
|
|
4
4
|
import type { LoadPiletsOptions } from 'piral-base';
|
|
@@ -41,12 +41,15 @@ declare module './components' {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export type WrappedComponent<TProps> = ComponentType<Without<TProps, keyof BaseComponentProps
|
|
44
|
+
export type WrappedComponent<TProps> = ComponentType<PropsWithChildren<Without<TProps, keyof BaseComponentProps>>>;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* The base type for pilet component registration in the global state context.
|
|
48
48
|
*/
|
|
49
49
|
export interface BaseRegistration {
|
|
50
|
+
/**
|
|
51
|
+
* The pilet registering the component.
|
|
52
|
+
*/
|
|
50
53
|
pilet: string;
|
|
51
54
|
}
|
|
52
55
|
|
|
@@ -54,7 +57,13 @@ export interface BaseRegistration {
|
|
|
54
57
|
* The interface modeling the registration of a pilet page component.
|
|
55
58
|
*/
|
|
56
59
|
export interface PageRegistration extends BaseRegistration {
|
|
60
|
+
/**
|
|
61
|
+
* The registered page component.
|
|
62
|
+
*/
|
|
57
63
|
component: WrappedComponent<PageComponentProps>;
|
|
64
|
+
/**
|
|
65
|
+
* The page's associated metadata.
|
|
66
|
+
*/
|
|
58
67
|
meta: PiralPageMeta;
|
|
59
68
|
}
|
|
60
69
|
|
|
@@ -62,8 +71,17 @@ export interface PageRegistration extends BaseRegistration {
|
|
|
62
71
|
* The interface modeling the registration of a pilet extension component.
|
|
63
72
|
*/
|
|
64
73
|
export interface ExtensionRegistration extends BaseRegistration {
|
|
74
|
+
/**
|
|
75
|
+
* The wrapped registered extension component.
|
|
76
|
+
*/
|
|
65
77
|
component: WrappedComponent<ExtensionComponentProps<string>>;
|
|
78
|
+
/**
|
|
79
|
+
* The original extension component that has been registered.
|
|
80
|
+
*/
|
|
66
81
|
reference: any;
|
|
82
|
+
/**
|
|
83
|
+
* The default params (i.e., meta) of the extension.
|
|
84
|
+
*/
|
|
67
85
|
defaults: any;
|
|
68
86
|
}
|
|
69
87
|
|