@openmrs/esm-react-utils 5.3.3-pre.1237 → 5.3.3-pre.1247
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/.turbo/turbo-build.log +2 -2
- package/__mocks__/openmrs-esm-state.mock.ts +8 -20
- package/dist/openmrs-esm-react-utils.js.map +1 -1
- package/jest.config.js +9 -11
- package/package.json +7 -7
- package/src/ComponentContext.ts +2 -2
- package/src/ConfigurableLink.test.tsx +19 -23
- package/src/ConfigurableLink.tsx +5 -19
- package/src/Extension.tsx +36 -76
- package/src/ExtensionSlot.tsx +15 -24
- package/src/UserHasAccess.tsx +3 -7
- package/src/extensions.test.tsx +100 -179
- package/src/getLifecycle.ts +8 -18
- package/src/index.ts +31 -31
- package/src/openmrsComponentDecorator.test.tsx +12 -12
- package/src/openmrsComponentDecorator.tsx +12 -26
- package/src/public.ts +27 -27
- package/src/setup-tests.js +4 -4
- package/src/useAbortController.test.tsx +8 -8
- package/src/useAbortController.ts +1 -1
- package/src/useAssignedExtensionIds.ts +4 -5
- package/src/useAssignedExtensions.ts +3 -7
- package/src/useBodyScrollLock.ts +2 -2
- package/src/useConfig.test.tsx +96 -112
- package/src/useConfig.ts +15 -43
- package/src/useConnectedExtensions.ts +8 -17
- package/src/useConnectivity.ts +3 -6
- package/src/useDebounce.ts +1 -1
- package/src/useExtensionInternalStore.ts +3 -8
- package/src/useExtensionSlot.ts +5 -7
- package/src/useExtensionSlotMeta.ts +5 -11
- package/src/useExtensionStore.ts +3 -5
- package/src/useFeatureFlag.ts +4 -4
- package/src/useForceUpdate.ts +1 -1
- package/src/useLayoutType.ts +12 -13
- package/src/useLocations.tsx +3 -3
- package/src/useOnClickOutside.test.tsx +10 -10
- package/src/useOnClickOutside.ts +2 -5
- package/src/useOpenmrsSWR.ts +9 -9
- package/src/usePagination.ts +6 -18
- package/src/usePatient.ts +9 -13
- package/src/useSession.test.tsx +35 -43
- package/src/useSession.ts +9 -13
- package/src/useStore.test.ts +11 -13
- package/src/useStore.ts +10 -31
- package/src/useVisit.ts +14 -37
- package/src/useVisitTypes.ts +3 -3
- package/webpack.config.js +14 -14
package/src/extensions.test.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import React, { useCallback, useReducer } from
|
|
2
|
-
import { act, render, screen, waitFor, within } from
|
|
1
|
+
import React, { useCallback, useReducer } from 'react';
|
|
2
|
+
import { act, render, screen, waitFor, within } from '@testing-library/react';
|
|
3
3
|
import {
|
|
4
4
|
attach,
|
|
5
5
|
ConnectedExtension,
|
|
6
6
|
getExtensionNameFromId,
|
|
7
7
|
registerExtension,
|
|
8
8
|
updateInternalExtensionStore,
|
|
9
|
-
} from
|
|
9
|
+
} from '@openmrs/esm-extensions';
|
|
10
10
|
import {
|
|
11
11
|
getSyncLifecycle,
|
|
12
12
|
Extension,
|
|
@@ -14,41 +14,32 @@ import {
|
|
|
14
14
|
openmrsComponentDecorator,
|
|
15
15
|
useExtensionSlotMeta,
|
|
16
16
|
ExtensionData,
|
|
17
|
-
} from
|
|
18
|
-
import userEvent from
|
|
19
|
-
import {
|
|
20
|
-
registerFeatureFlag,
|
|
21
|
-
setFeatureFlag,
|
|
22
|
-
} from "@openmrs/esm-feature-flags";
|
|
17
|
+
} from '.';
|
|
18
|
+
import userEvent from '@testing-library/user-event';
|
|
19
|
+
import { registerFeatureFlag, setFeatureFlag } from '@openmrs/esm-feature-flags';
|
|
23
20
|
|
|
24
21
|
// For some reason in the text context `isEqual` always returns true
|
|
25
22
|
// when using the import substitution in jest.config.js. Here's a custom
|
|
26
23
|
// mock.
|
|
27
|
-
jest.mock(
|
|
28
|
-
"lodash-es/isEqual",
|
|
29
|
-
() => (a, b) => JSON.stringify(a) == JSON.stringify(b)
|
|
30
|
-
);
|
|
24
|
+
jest.mock('lodash-es/isEqual', () => (a, b) => JSON.stringify(a) == JSON.stringify(b));
|
|
31
25
|
|
|
32
|
-
describe(
|
|
26
|
+
describe('ExtensionSlot, Extension, and useExtensionSlotMeta', () => {
|
|
33
27
|
beforeEach(() => {
|
|
34
28
|
updateInternalExtensionStore(() => ({ slots: {}, extensions: {} }));
|
|
35
29
|
});
|
|
36
30
|
|
|
37
|
-
test(
|
|
31
|
+
test('Extension receives state changes passed through (not using <Extension>)', async () => {
|
|
38
32
|
function EnglishExtension({ suffix }) {
|
|
39
33
|
return <div>English{suffix}</div>;
|
|
40
34
|
}
|
|
41
|
-
registerSimpleExtension(
|
|
42
|
-
attach(
|
|
35
|
+
registerSimpleExtension('English', 'esm-languages-app', EnglishExtension);
|
|
36
|
+
attach('Box', 'English');
|
|
43
37
|
const App = openmrsComponentDecorator({
|
|
44
|
-
moduleName:
|
|
45
|
-
featureName:
|
|
38
|
+
moduleName: 'esm-languages-app',
|
|
39
|
+
featureName: 'Languages',
|
|
46
40
|
disableTranslations: true,
|
|
47
41
|
})(() => {
|
|
48
|
-
const [suffix, toggleSuffix] = useReducer(
|
|
49
|
-
(suffix) => (suffix == "!" ? "?" : "!"),
|
|
50
|
-
"!"
|
|
51
|
-
);
|
|
42
|
+
const [suffix, toggleSuffix] = useReducer((suffix) => (suffix == '!' ? '?' : '!'), '!');
|
|
52
43
|
return (
|
|
53
44
|
<div>
|
|
54
45
|
<ExtensionSlot name="Box" state={{ suffix }} />
|
|
@@ -58,35 +49,24 @@ describe("ExtensionSlot, Extension, and useExtensionSlotMeta", () => {
|
|
|
58
49
|
});
|
|
59
50
|
render(<App />);
|
|
60
51
|
|
|
61
|
-
await waitFor(() =>
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
expect(screen.getByText(/English/)).toHaveTextContent(
|
|
65
|
-
userEvent.click(screen.getByText("Toggle suffix"));
|
|
66
|
-
await waitFor(() =>
|
|
67
|
-
expect(screen.getByText(/English/)).toHaveTextContent("English?")
|
|
68
|
-
);
|
|
52
|
+
await waitFor(() => expect(screen.getByText(/English/)).toBeInTheDocument());
|
|
53
|
+
expect(screen.getByText(/English/)).toHaveTextContent('English!');
|
|
54
|
+
userEvent.click(screen.getByText('Toggle suffix'));
|
|
55
|
+
await waitFor(() => expect(screen.getByText(/English/)).toHaveTextContent('English?'));
|
|
69
56
|
});
|
|
70
57
|
|
|
71
|
-
test(
|
|
58
|
+
test('Extension receives state changes (using <Extension>)', async () => {
|
|
72
59
|
function HaitianCreoleExtension({ suffix }) {
|
|
73
60
|
return <div>Haitian Creole{suffix}</div>;
|
|
74
61
|
}
|
|
75
|
-
registerSimpleExtension(
|
|
76
|
-
|
|
77
|
-
"esm-languages-app",
|
|
78
|
-
HaitianCreoleExtension
|
|
79
|
-
);
|
|
80
|
-
attach("Box", "Haitian");
|
|
62
|
+
registerSimpleExtension('Haitian', 'esm-languages-app', HaitianCreoleExtension);
|
|
63
|
+
attach('Box', 'Haitian');
|
|
81
64
|
const App = openmrsComponentDecorator({
|
|
82
|
-
moduleName:
|
|
83
|
-
featureName:
|
|
65
|
+
moduleName: 'esm-languages-app',
|
|
66
|
+
featureName: 'Languages',
|
|
84
67
|
disableTranslations: true,
|
|
85
68
|
})(() => {
|
|
86
|
-
const [suffix, toggleSuffix] = useReducer(
|
|
87
|
-
(suffix) => (suffix == "!" ? "?" : "!"),
|
|
88
|
-
"!"
|
|
89
|
-
);
|
|
69
|
+
const [suffix, toggleSuffix] = useReducer((suffix) => (suffix == '!' ? '?' : '!'), '!');
|
|
90
70
|
|
|
91
71
|
return (
|
|
92
72
|
<div>
|
|
@@ -100,52 +80,46 @@ describe("ExtensionSlot, Extension, and useExtensionSlotMeta", () => {
|
|
|
100
80
|
});
|
|
101
81
|
render(<App />);
|
|
102
82
|
|
|
103
|
-
await waitFor(() =>
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
expect(screen.getByText(/Haitian/)).toHaveTextContent(
|
|
107
|
-
userEvent.click(screen.getByText("Toggle suffix"));
|
|
108
|
-
await waitFor(() =>
|
|
109
|
-
expect(screen.getByText(/Haitian/)).toHaveTextContent("Haitian Creole?")
|
|
110
|
-
);
|
|
83
|
+
await waitFor(() => expect(screen.getByText(/Haitian/)).toBeInTheDocument());
|
|
84
|
+
expect(screen.getByText(/Haitian/)).toHaveTextContent('Haitian Creole!');
|
|
85
|
+
userEvent.click(screen.getByText('Toggle suffix'));
|
|
86
|
+
await waitFor(() => expect(screen.getByText(/Haitian/)).toHaveTextContent('Haitian Creole?'));
|
|
111
87
|
});
|
|
112
88
|
|
|
113
|
-
test(
|
|
89
|
+
test('ExtensionSlot throws error if both state and children provided', () => {
|
|
114
90
|
const App = () => (
|
|
115
|
-
<ExtensionSlot name="Box" state={{ color:
|
|
91
|
+
<ExtensionSlot name="Box" state={{ color: 'red' }}>
|
|
116
92
|
<Extension />
|
|
117
93
|
</ExtensionSlot>
|
|
118
94
|
);
|
|
119
95
|
expect(() => render(<App />)).toThrowError(
|
|
120
96
|
expect.objectContaining({
|
|
121
97
|
message: expect.stringMatching(/children.*state/),
|
|
122
|
-
})
|
|
98
|
+
}),
|
|
123
99
|
);
|
|
124
100
|
});
|
|
125
101
|
|
|
126
|
-
test(
|
|
127
|
-
registerSimpleExtension(
|
|
128
|
-
code:
|
|
102
|
+
test('Extension Slot receives meta', async () => {
|
|
103
|
+
registerSimpleExtension('Spanish', 'esm-languages-app', undefined, {
|
|
104
|
+
code: 'es',
|
|
129
105
|
});
|
|
130
|
-
attach(
|
|
106
|
+
attach('Box', 'Spanish');
|
|
131
107
|
const App = openmrsComponentDecorator({
|
|
132
|
-
moduleName:
|
|
133
|
-
featureName:
|
|
108
|
+
moduleName: 'esm-languages-app',
|
|
109
|
+
featureName: 'Languages',
|
|
134
110
|
disableTranslations: true,
|
|
135
111
|
})(() => {
|
|
136
|
-
const metas = useExtensionSlotMeta(
|
|
112
|
+
const metas = useExtensionSlotMeta('Box');
|
|
137
113
|
const wrapItem = useCallback(
|
|
138
114
|
(slot: React.ReactNode, extension: ExtensionData) => {
|
|
139
115
|
return (
|
|
140
116
|
<div>
|
|
141
|
-
<h1>
|
|
142
|
-
{metas[getExtensionNameFromId(extension.extensionId)].code}
|
|
143
|
-
</h1>
|
|
117
|
+
<h1>{metas[getExtensionNameFromId(extension.extensionId)].code}</h1>
|
|
144
118
|
{slot}
|
|
145
119
|
</div>
|
|
146
120
|
);
|
|
147
121
|
},
|
|
148
|
-
[metas]
|
|
122
|
+
[metas],
|
|
149
123
|
);
|
|
150
124
|
return (
|
|
151
125
|
<div>
|
|
@@ -157,48 +131,36 @@ describe("ExtensionSlot, Extension, and useExtensionSlotMeta", () => {
|
|
|
157
131
|
});
|
|
158
132
|
render(<App />);
|
|
159
133
|
|
|
160
|
-
await waitFor(() =>
|
|
161
|
-
|
|
162
|
-
);
|
|
163
|
-
expect(screen.getByRole("heading")).toHaveTextContent("es");
|
|
164
|
-
await waitFor(() =>
|
|
165
|
-
expect(screen.getByText("Spanish")).toBeInTheDocument()
|
|
166
|
-
);
|
|
134
|
+
await waitFor(() => expect(screen.getByRole('heading')).toBeInTheDocument());
|
|
135
|
+
expect(screen.getByRole('heading')).toHaveTextContent('es');
|
|
136
|
+
await waitFor(() => expect(screen.getByText('Spanish')).toBeInTheDocument());
|
|
167
137
|
});
|
|
168
138
|
|
|
169
|
-
test(
|
|
139
|
+
test('Both meta and state can be used at the same time', async () => {
|
|
170
140
|
function SwahiliExtension({ suffix }) {
|
|
171
141
|
return <div>Swahili{suffix}</div>;
|
|
172
142
|
}
|
|
173
|
-
registerSimpleExtension(
|
|
174
|
-
code:
|
|
143
|
+
registerSimpleExtension('Swahili', 'esm-languages-app', SwahiliExtension, {
|
|
144
|
+
code: 'sw',
|
|
175
145
|
});
|
|
176
|
-
attach(
|
|
146
|
+
attach('Box', 'Swahili');
|
|
177
147
|
const App = openmrsComponentDecorator({
|
|
178
|
-
moduleName:
|
|
179
|
-
featureName:
|
|
148
|
+
moduleName: 'esm-languages-app',
|
|
149
|
+
featureName: 'Languages',
|
|
180
150
|
disableTranslations: true,
|
|
181
151
|
})(() => {
|
|
182
|
-
const [suffix, toggleSuffix] = useReducer(
|
|
183
|
-
|
|
184
|
-
"!"
|
|
185
|
-
);
|
|
186
|
-
const metas = useExtensionSlotMeta("Box");
|
|
152
|
+
const [suffix, toggleSuffix] = useReducer((suffix) => (suffix == '!' ? '?' : '!'), '!');
|
|
153
|
+
const metas = useExtensionSlotMeta('Box');
|
|
187
154
|
const wrapItem = useCallback(
|
|
188
155
|
(slot: React.ReactNode, extension?: ExtensionData) => {
|
|
189
156
|
return (
|
|
190
157
|
<div>
|
|
191
|
-
<h1>
|
|
192
|
-
{
|
|
193
|
-
metas[getExtensionNameFromId(extension?.extensionId ?? "")]
|
|
194
|
-
.code
|
|
195
|
-
}
|
|
196
|
-
</h1>
|
|
158
|
+
<h1>{metas[getExtensionNameFromId(extension?.extensionId ?? '')].code}</h1>
|
|
197
159
|
{slot}
|
|
198
160
|
</div>
|
|
199
161
|
);
|
|
200
162
|
},
|
|
201
|
-
[metas]
|
|
163
|
+
[metas],
|
|
202
164
|
);
|
|
203
165
|
return (
|
|
204
166
|
<div>
|
|
@@ -211,31 +173,25 @@ describe("ExtensionSlot, Extension, and useExtensionSlotMeta", () => {
|
|
|
211
173
|
});
|
|
212
174
|
render(<App />);
|
|
213
175
|
|
|
214
|
-
await waitFor(() =>
|
|
215
|
-
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
await waitFor(() =>
|
|
219
|
-
expect(screen.getByText(/Swahili/)).toHaveTextContent("Swahili!")
|
|
220
|
-
);
|
|
221
|
-
userEvent.click(screen.getByText("Toggle suffix"));
|
|
222
|
-
await waitFor(() =>
|
|
223
|
-
expect(screen.getByText(/Swahili/)).toHaveTextContent("Swahili?")
|
|
224
|
-
);
|
|
176
|
+
await waitFor(() => expect(screen.getByRole('heading')).toBeInTheDocument());
|
|
177
|
+
expect(screen.getByRole('heading')).toHaveTextContent('sw');
|
|
178
|
+
await waitFor(() => expect(screen.getByText(/Swahili/)).toHaveTextContent('Swahili!'));
|
|
179
|
+
userEvent.click(screen.getByText('Toggle suffix'));
|
|
180
|
+
await waitFor(() => expect(screen.getByText(/Swahili/)).toHaveTextContent('Swahili?'));
|
|
225
181
|
});
|
|
226
182
|
|
|
227
|
-
test(
|
|
228
|
-
registerSimpleExtension(
|
|
229
|
-
code:
|
|
183
|
+
test('Extension Slot renders function children', async () => {
|
|
184
|
+
registerSimpleExtension('Urdu', 'esm-languages-app', undefined, {
|
|
185
|
+
code: 'urd',
|
|
230
186
|
});
|
|
231
|
-
registerSimpleExtension(
|
|
232
|
-
code:
|
|
187
|
+
registerSimpleExtension('Hindi', 'esm-languages-app', undefined, {
|
|
188
|
+
code: 'hi',
|
|
233
189
|
});
|
|
234
|
-
attach(
|
|
235
|
-
attach(
|
|
190
|
+
attach('Box', 'Urdu');
|
|
191
|
+
attach('Box', 'Hindi');
|
|
236
192
|
const App = openmrsComponentDecorator({
|
|
237
|
-
moduleName:
|
|
238
|
-
featureName:
|
|
193
|
+
moduleName: 'esm-languages-app',
|
|
194
|
+
featureName: 'Languages',
|
|
239
195
|
disableTranslations: true,
|
|
240
196
|
})(() => {
|
|
241
197
|
return (
|
|
@@ -253,33 +209,25 @@ describe("ExtensionSlot, Extension, and useExtensionSlotMeta", () => {
|
|
|
253
209
|
});
|
|
254
210
|
render(<App />);
|
|
255
211
|
|
|
256
|
-
await waitFor(() => expect(screen.getByTestId(
|
|
257
|
-
expect(
|
|
258
|
-
|
|
259
|
-
).toHaveTextContent("urd");
|
|
260
|
-
expect(
|
|
261
|
-
within(screen.getByTestId("Hindi")).getByRole("heading")
|
|
262
|
-
).toHaveTextContent("hi");
|
|
212
|
+
await waitFor(() => expect(screen.getByTestId('Urdu')).toBeInTheDocument());
|
|
213
|
+
expect(within(screen.getByTestId('Urdu')).getByRole('heading')).toHaveTextContent('urd');
|
|
214
|
+
expect(within(screen.getByTestId('Hindi')).getByRole('heading')).toHaveTextContent('hi');
|
|
263
215
|
});
|
|
264
216
|
|
|
265
|
-
test(
|
|
266
|
-
registerSimpleExtension(
|
|
267
|
-
code:
|
|
217
|
+
test('Extension renders with child function', async () => {
|
|
218
|
+
registerSimpleExtension('Hindi', 'esm-languages-app', undefined, {
|
|
219
|
+
code: 'hi',
|
|
268
220
|
});
|
|
269
|
-
attach(
|
|
221
|
+
attach('Box', 'Hindi');
|
|
270
222
|
const App = openmrsComponentDecorator({
|
|
271
|
-
moduleName:
|
|
272
|
-
featureName:
|
|
223
|
+
moduleName: 'esm-languages-app',
|
|
224
|
+
featureName: 'Languages',
|
|
273
225
|
disableTranslations: true,
|
|
274
226
|
})(() => {
|
|
275
227
|
return (
|
|
276
228
|
<div>
|
|
277
229
|
<ExtensionSlot name="Box">
|
|
278
|
-
{() => (
|
|
279
|
-
<Extension>
|
|
280
|
-
{(slot) => <div data-testid="custom-wrapper">{slot}</div>}
|
|
281
|
-
</Extension>
|
|
282
|
-
)}
|
|
230
|
+
{() => <Extension>{(slot) => <div data-testid="custom-wrapper">{slot}</div>}</Extension>}
|
|
283
231
|
</ExtensionSlot>
|
|
284
232
|
</div>
|
|
285
233
|
);
|
|
@@ -287,64 +235,37 @@ describe("ExtensionSlot, Extension, and useExtensionSlotMeta", () => {
|
|
|
287
235
|
|
|
288
236
|
render(<App />);
|
|
289
237
|
|
|
290
|
-
await waitFor(() =>
|
|
291
|
-
expect(screen.getByTestId("custom-wrapper")).toBeInTheDocument()
|
|
292
|
-
);
|
|
238
|
+
await waitFor(() => expect(screen.getByTestId('custom-wrapper')).toBeInTheDocument());
|
|
293
239
|
|
|
294
240
|
// essentially: is the first child of custom-wrapper the extension?
|
|
295
|
-
expect(screen.getByTestId(
|
|
296
|
-
"data-extension-id",
|
|
297
|
-
"Hindi"
|
|
298
|
-
);
|
|
241
|
+
expect(screen.getByTestId('custom-wrapper').children[0]).toHaveAttribute('data-extension-id', 'Hindi');
|
|
299
242
|
});
|
|
300
243
|
|
|
301
|
-
test(
|
|
302
|
-
registerSimpleExtension(
|
|
303
|
-
registerSimpleExtension(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
undefined,
|
|
314
|
-
undefined,
|
|
315
|
-
"turkic"
|
|
316
|
-
);
|
|
317
|
-
registerSimpleExtension(
|
|
318
|
-
"Kurmanji",
|
|
319
|
-
"esm-languages-app",
|
|
320
|
-
undefined,
|
|
321
|
-
undefined,
|
|
322
|
-
"kurdish"
|
|
323
|
-
);
|
|
324
|
-
attach("Box", "Arabic");
|
|
325
|
-
attach("Box", "Turkish");
|
|
326
|
-
attach("Box", "Turkmeni");
|
|
327
|
-
attach("Box", "Kurmanji");
|
|
328
|
-
registerFeatureFlag("turkic", "", "");
|
|
329
|
-
registerFeatureFlag("kurdish", "", "");
|
|
330
|
-
setFeatureFlag("turkic", true);
|
|
244
|
+
test('Extensions behind feature flags only render when their feature flag is enabled', async () => {
|
|
245
|
+
registerSimpleExtension('Arabic', 'esm-languages-app');
|
|
246
|
+
registerSimpleExtension('Turkish', 'esm-languages-app', undefined, undefined, 'turkic');
|
|
247
|
+
registerSimpleExtension('Turkmeni', 'esm-languages-app', undefined, undefined, 'turkic');
|
|
248
|
+
registerSimpleExtension('Kurmanji', 'esm-languages-app', undefined, undefined, 'kurdish');
|
|
249
|
+
attach('Box', 'Arabic');
|
|
250
|
+
attach('Box', 'Turkish');
|
|
251
|
+
attach('Box', 'Turkmeni');
|
|
252
|
+
attach('Box', 'Kurmanji');
|
|
253
|
+
registerFeatureFlag('turkic', '', '');
|
|
254
|
+
registerFeatureFlag('kurdish', '', '');
|
|
255
|
+
setFeatureFlag('turkic', true);
|
|
331
256
|
const App = openmrsComponentDecorator({
|
|
332
|
-
moduleName:
|
|
333
|
-
featureName:
|
|
257
|
+
moduleName: 'esm-languages-app',
|
|
258
|
+
featureName: 'Languages',
|
|
334
259
|
disableTranslations: true,
|
|
335
260
|
})(() => <ExtensionSlot name="Box" />);
|
|
336
261
|
render(<App />);
|
|
337
262
|
|
|
338
|
-
await waitFor(() =>
|
|
339
|
-
|
|
340
|
-
);
|
|
341
|
-
expect(screen.
|
|
342
|
-
|
|
343
|
-
expect(screen.
|
|
344
|
-
act(() => setFeatureFlag("kurdish", true));
|
|
345
|
-
await waitFor(() =>
|
|
346
|
-
expect(screen.getByText("Kurmanji")).toBeInTheDocument()
|
|
347
|
-
);
|
|
263
|
+
await waitFor(() => expect(screen.getByText(/Turkmeni/)).toBeInTheDocument());
|
|
264
|
+
expect(screen.getByText('Arabic')).toBeInTheDocument();
|
|
265
|
+
expect(screen.getByText('Turkish')).toBeInTheDocument();
|
|
266
|
+
expect(screen.queryByText('Kurmanji')).not.toBeInTheDocument();
|
|
267
|
+
act(() => setFeatureFlag('kurdish', true));
|
|
268
|
+
await waitFor(() => expect(screen.getByText('Kurmanji')).toBeInTheDocument());
|
|
348
269
|
});
|
|
349
270
|
});
|
|
350
271
|
|
|
@@ -353,7 +274,7 @@ function registerSimpleExtension(
|
|
|
353
274
|
moduleName: string,
|
|
354
275
|
Component?: React.ComponentType<any>,
|
|
355
276
|
meta: object = {},
|
|
356
|
-
featureFlag?: string
|
|
277
|
+
featureFlag?: string,
|
|
357
278
|
) {
|
|
358
279
|
const SimpleComponent = () => <div>{name}</div>;
|
|
359
280
|
registerExtension({
|
package/src/getLifecycle.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
/** @module @category Framework */
|
|
2
|
-
import React, { ComponentType } from
|
|
3
|
-
import ReactDOMClient from
|
|
4
|
-
import singleSpaReact from
|
|
5
|
-
import {
|
|
6
|
-
openmrsComponentDecorator,
|
|
7
|
-
ComponentDecoratorOptions,
|
|
8
|
-
} from "./openmrsComponentDecorator";
|
|
2
|
+
import React, { ComponentType } from 'react';
|
|
3
|
+
import ReactDOMClient from 'react-dom/client';
|
|
4
|
+
import singleSpaReact from 'single-spa-react';
|
|
5
|
+
import { openmrsComponentDecorator, ComponentDecoratorOptions } from './openmrsComponentDecorator';
|
|
9
6
|
|
|
10
|
-
export function getLifecycle<T>(
|
|
11
|
-
Component: ComponentType<T>,
|
|
12
|
-
options: ComponentDecoratorOptions
|
|
13
|
-
) {
|
|
7
|
+
export function getLifecycle<T>(Component: ComponentType<T>, options: ComponentDecoratorOptions) {
|
|
14
8
|
return singleSpaReact({
|
|
15
9
|
React,
|
|
16
10
|
ReactDOMClient,
|
|
@@ -20,16 +14,12 @@ export function getLifecycle<T>(
|
|
|
20
14
|
|
|
21
15
|
export function getAsyncLifecycle<T>(
|
|
22
16
|
lazy: () => Promise<{ default: ComponentType<T> }>,
|
|
23
|
-
options: ComponentDecoratorOptions
|
|
17
|
+
options: ComponentDecoratorOptions,
|
|
24
18
|
) {
|
|
25
|
-
return () =>
|
|
26
|
-
lazy().then(({ default: Component }) => getLifecycle(Component, options));
|
|
19
|
+
return () => lazy().then(({ default: Component }) => getLifecycle(Component, options));
|
|
27
20
|
}
|
|
28
21
|
|
|
29
|
-
export function getSyncLifecycle<T>(
|
|
30
|
-
Component: ComponentType<T>,
|
|
31
|
-
options: ComponentDecoratorOptions
|
|
32
|
-
) {
|
|
22
|
+
export function getSyncLifecycle<T>(Component: ComponentType<T>, options: ComponentDecoratorOptions) {
|
|
33
23
|
return () => Promise.resolve(getLifecycle(Component, options));
|
|
34
24
|
}
|
|
35
25
|
|
package/src/index.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
11
|
-
export * from
|
|
12
|
-
export * from
|
|
13
|
-
export * from
|
|
14
|
-
export * from
|
|
15
|
-
export * from
|
|
16
|
-
export * from
|
|
17
|
-
export * from
|
|
18
|
-
export * from
|
|
19
|
-
export * from
|
|
20
|
-
export * from
|
|
21
|
-
export * from
|
|
22
|
-
export * from
|
|
23
|
-
export * from
|
|
24
|
-
export * from
|
|
25
|
-
export * from
|
|
26
|
-
export * from
|
|
27
|
-
export { useSession } from
|
|
28
|
-
export * from
|
|
29
|
-
export * from
|
|
30
|
-
export * from
|
|
31
|
-
export * from
|
|
1
|
+
export * from './ComponentContext';
|
|
2
|
+
export * from './ConfigurableLink';
|
|
3
|
+
export * from './Extension';
|
|
4
|
+
export * from './ExtensionSlot';
|
|
5
|
+
export * from './UserHasAccess';
|
|
6
|
+
export * from './getLifecycle';
|
|
7
|
+
export * from './openmrsComponentDecorator';
|
|
8
|
+
export * from './useAbortController';
|
|
9
|
+
export * from './useAssignedExtensions';
|
|
10
|
+
export * from './useAssignedExtensionIds';
|
|
11
|
+
export * from './useBodyScrollLock';
|
|
12
|
+
export * from './useConfig';
|
|
13
|
+
export * from './useConnectedExtensions';
|
|
14
|
+
export * from './useConnectivity';
|
|
15
|
+
export * from './useDebounce';
|
|
16
|
+
export * from './useExtensionInternalStore';
|
|
17
|
+
export * from './useExtensionSlot';
|
|
18
|
+
export * from './useExtensionSlotMeta';
|
|
19
|
+
export * from './useExtensionStore';
|
|
20
|
+
export * from './useFeatureFlag';
|
|
21
|
+
export * from './useForceUpdate';
|
|
22
|
+
export * from './useLayoutType';
|
|
23
|
+
export * from './useLocations';
|
|
24
|
+
export * from './useOnClickOutside';
|
|
25
|
+
export * from './useOpenmrsSWR';
|
|
26
|
+
export * from './usePatient';
|
|
27
|
+
export { useSession } from './useSession';
|
|
28
|
+
export * from './useStore';
|
|
29
|
+
export * from './useVisit';
|
|
30
|
+
export * from './useVisitTypes';
|
|
31
|
+
export * from './usePagination';
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { render, screen, waitFor } from
|
|
3
|
-
import { openmrsComponentDecorator } from
|
|
4
|
-
import { ComponentContext } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import { openmrsComponentDecorator } from './openmrsComponentDecorator';
|
|
4
|
+
import { ComponentContext } from './ComponentContext';
|
|
5
5
|
|
|
6
|
-
describe(
|
|
6
|
+
describe('openmrs-component-decorator', () => {
|
|
7
7
|
const opts = {
|
|
8
|
-
featureName:
|
|
8
|
+
featureName: 'Test',
|
|
9
9
|
throwErrorsToConsole: false,
|
|
10
|
-
moduleName:
|
|
10
|
+
moduleName: 'test',
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
it(
|
|
13
|
+
it('renders a component', () => {
|
|
14
14
|
const DecoratedComp = openmrsComponentDecorator(opts)(CompThatWorks);
|
|
15
15
|
render(<DecoratedComp />);
|
|
16
16
|
waitFor(() => {
|
|
17
|
-
expect(screen.getByText(
|
|
17
|
+
expect(screen.getByText('The button')).toBeTruthy();
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
it(
|
|
21
|
+
it('catches any errors in the component tree and renders a ui explaining something bad happened', () => {
|
|
22
22
|
const DecoratedComp = openmrsComponentDecorator(opts)(CompThatThrows);
|
|
23
23
|
render(<DecoratedComp />);
|
|
24
24
|
// TO-DO assert the UX for broken react app is showing
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it(
|
|
27
|
+
it('provides ComponentContext', () => {
|
|
28
28
|
const DecoratedComp = openmrsComponentDecorator(opts)(CompWithConfig);
|
|
29
29
|
render(<DecoratedComp />);
|
|
30
30
|
});
|
|
@@ -35,7 +35,7 @@ function CompThatWorks() {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
let CompThatThrows = function () {
|
|
38
|
-
throw Error(
|
|
38
|
+
throw Error('ahahaa');
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
function CompWithConfig() {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import React, { type ComponentType, Suspense } from
|
|
2
|
-
import { I18nextProvider } from
|
|
3
|
-
import type {} from
|
|
4
|
-
import {
|
|
5
|
-
ComponentConfig,
|
|
6
|
-
ComponentContext,
|
|
7
|
-
ExtensionData,
|
|
8
|
-
} from "./ComponentContext";
|
|
1
|
+
import React, { type ComponentType, Suspense } from 'react';
|
|
2
|
+
import { I18nextProvider } from 'react-i18next';
|
|
3
|
+
import type {} from '@openmrs/esm-globals';
|
|
4
|
+
import { ComponentConfig, ComponentContext, ExtensionData } from './ComponentContext';
|
|
9
5
|
|
|
10
6
|
const defaultOpts = {
|
|
11
7
|
strictMode: true,
|
|
@@ -32,22 +28,17 @@ export interface OpenmrsReactComponentState {
|
|
|
32
28
|
|
|
33
29
|
export function openmrsComponentDecorator(userOpts: ComponentDecoratorOptions) {
|
|
34
30
|
if (
|
|
35
|
-
typeof userOpts !==
|
|
36
|
-
typeof userOpts.featureName !==
|
|
37
|
-
typeof userOpts.moduleName !==
|
|
31
|
+
typeof userOpts !== 'object' ||
|
|
32
|
+
typeof userOpts.featureName !== 'string' ||
|
|
33
|
+
typeof userOpts.moduleName !== 'string'
|
|
38
34
|
) {
|
|
39
|
-
throw new Error(
|
|
35
|
+
throw new Error('Invalid options');
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
const opts = Object.assign({}, defaultOpts, userOpts);
|
|
43
39
|
|
|
44
|
-
return function decorateComponent(
|
|
45
|
-
|
|
46
|
-
): ComponentType<any> {
|
|
47
|
-
return class OpenmrsReactComponent extends React.Component<
|
|
48
|
-
OpenmrsReactComponentProps,
|
|
49
|
-
OpenmrsReactComponentState
|
|
50
|
-
> {
|
|
40
|
+
return function decorateComponent(Comp: ComponentType<any>): ComponentType<any> {
|
|
41
|
+
return class OpenmrsReactComponent extends React.Component<OpenmrsReactComponentProps, OpenmrsReactComponentState> {
|
|
51
42
|
static displayName = `OpenmrsReactComponent(${opts.featureName})`;
|
|
52
43
|
|
|
53
44
|
constructor(props: OpenmrsReactComponentProps) {
|
|
@@ -84,9 +75,7 @@ export function openmrsComponentDecorator(userOpts: ComponentDecoratorOptions) {
|
|
|
84
75
|
render() {
|
|
85
76
|
if (this.state.caughtError) {
|
|
86
77
|
// TO-DO have a UX designed for when a catastrophic error occurs
|
|
87
|
-
return
|
|
88
|
-
<div>An error has occurred. Please try reloading the page.</div>
|
|
89
|
-
);
|
|
78
|
+
return <div>An error has occurred. Please try reloading the page.</div>;
|
|
90
79
|
} else {
|
|
91
80
|
const content = (
|
|
92
81
|
<Suspense fallback={null}>
|
|
@@ -94,10 +83,7 @@ export function openmrsComponentDecorator(userOpts: ComponentDecoratorOptions) {
|
|
|
94
83
|
{opts.disableTranslations ? (
|
|
95
84
|
<Comp {...this.props} />
|
|
96
85
|
) : (
|
|
97
|
-
<I18nextProvider
|
|
98
|
-
i18n={window.i18next}
|
|
99
|
-
defaultNS={opts.moduleName}
|
|
100
|
-
>
|
|
86
|
+
<I18nextProvider i18n={window.i18next} defaultNS={opts.moduleName}>
|
|
101
87
|
<Comp {...this.props} />
|
|
102
88
|
</I18nextProvider>
|
|
103
89
|
)}
|