@openmrs/esm-implementer-tools-app 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 +45 -45
- package/__mocks__/react-i18next.js +10 -15
- package/dist/426.js.map +1 -1
- package/dist/560.js +1 -1
- package/dist/560.js.map +1 -1
- package/dist/587.js.map +1 -1
- package/dist/727.js +1 -1
- package/dist/727.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-implementer-tools-app.js +1 -1
- package/dist/openmrs-esm-implementer-tools-app.js.buildmanifest.json +13 -13
- package/dist/routes.json +1 -1
- package/jest.config.js +7 -7
- package/package.json +3 -3
- package/src/backend-dependencies/backend-dependencies.component.tsx +19 -29
- package/src/backend-dependencies/backend-dependencies.styles.scss +2 -2
- package/src/backend-dependencies/openmrs-backend-dependencies.ts +22 -43
- package/src/backend-dependencies/useBackendDependencies.ts +5 -9
- package/src/config-edit-button/config-edit-button.component.tsx +5 -7
- package/src/configuration/configuration.component.tsx +30 -57
- package/src/configuration/configuration.styles.scss +4 -4
- package/src/configuration/configuration.test.tsx +92 -115
- package/src/configuration/interactive-editor/config-subtree.component.tsx +10 -18
- package/src/configuration/interactive-editor/config-tree-for-module.component.tsx +8 -17
- package/src/configuration/interactive-editor/config-tree.component.tsx +5 -9
- package/src/configuration/interactive-editor/description.component.tsx +12 -27
- package/src/configuration/interactive-editor/description.styles.scss +7 -7
- package/src/configuration/interactive-editor/display-value.scss +2 -2
- package/src/configuration/interactive-editor/display-value.tsx +18 -22
- package/src/configuration/interactive-editor/editable-value.component.tsx +19 -44
- package/src/configuration/interactive-editor/editable-value.styles.scss +2 -2
- package/src/configuration/interactive-editor/extension-configure-tree.tsx +7 -11
- package/src/configuration/interactive-editor/extension-slots-config-tree.tsx +24 -56
- package/src/configuration/interactive-editor/layout/subtree.component.tsx +8 -31
- package/src/configuration/interactive-editor/layout/tree-container.component.tsx +3 -3
- package/src/configuration/interactive-editor/value-editor.scss +2 -2
- package/src/configuration/interactive-editor/value-editor.tsx +19 -35
- package/src/configuration/interactive-editor/value-editors/array-editor.tsx +12 -19
- package/src/configuration/interactive-editor/value-editors/concept-search.resource.tsx +4 -7
- package/src/configuration/interactive-editor/value-editors/concept-search.styles.scss +7 -7
- package/src/configuration/interactive-editor/value-editors/concept-search.tsx +15 -32
- package/src/configuration/interactive-editor/value-editors/extension-slot-add.tsx +4 -9
- package/src/configuration/interactive-editor/value-editors/extension-slot-order.tsx +2 -7
- package/src/configuration/interactive-editor/value-editors/extension-slot-remove.tsx +4 -9
- package/src/configuration/interactive-editor/value-editors/object-editor.tsx +10 -20
- package/src/configuration/interactive-editor/value-editors/patient-identifier-type-search.tsx +21 -47
- package/src/configuration/interactive-editor/value-editors/patient-identifier-type.resource.tsx +8 -8
- package/src/configuration/interactive-editor/value-editors/person-attribute-search.resource.tsx +6 -11
- package/src/configuration/interactive-editor/value-editors/person-attribute-search.tsx +20 -47
- package/src/configuration/interactive-editor/value-editors/uuid-search.scss +6 -6
- package/src/configuration/interactive-editor/value-editors/value-editor-field.tsx +27 -54
- package/src/configuration/json-editor/json-editor.component.tsx +11 -17
- package/src/declarations.d.ts +3 -3
- package/src/feature-flags/feature-flags.component.tsx +16 -26
- package/src/feature-flags/frontend-modules.scss +3 -3
- package/src/frontend-modules/frontend-modules.component.tsx +14 -22
- package/src/frontend-modules/frontend-modules.scss +3 -3
- package/src/global-implementer-tools-button.test.tsx +7 -7
- package/src/global-implementer-tools.component.tsx +6 -9
- package/src/hooks.ts +2 -2
- package/src/implementer-tools.button.tsx +8 -8
- package/src/implementer-tools.component.tsx +16 -32
- package/src/implementer-tools.styles.scss +6 -6
- package/src/implementer-tools.test.tsx +3 -3
- package/src/index.ts +11 -25
- package/src/popup/popup.component.tsx +16 -30
- package/src/popup/popup.styles.scss +1 -1
- package/src/store.ts +19 -44
- package/src/types.ts +1 -1
- package/src/ui-editor/extension-overlay.component.tsx +6 -14
- package/src/ui-editor/portal.tsx +1 -1
- package/src/ui-editor/ui-editor.tsx +24 -34
- package/webpack.config.js +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
@use
|
|
2
|
-
@use
|
|
3
|
-
@import
|
|
4
|
-
@import
|
|
1
|
+
@use '@carbon/styles/scss/spacing';
|
|
2
|
+
@use '@carbon/styles/scss/type';
|
|
3
|
+
@import '~@openmrs/esm-styleguide/src/vars';
|
|
4
|
+
@import '../implementer-tools.styles.scss';
|
|
5
5
|
|
|
6
6
|
.tools {
|
|
7
7
|
width: 100%;
|
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import
|
|
3
|
-
import { render, screen, within } from
|
|
4
|
-
import userEvent from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Type,
|
|
9
|
-
} from "@openmrs/esm-framework/src/internal";
|
|
10
|
-
import { Configuration } from "./configuration.component";
|
|
11
|
-
import {
|
|
12
|
-
useConceptLookup,
|
|
13
|
-
useGetConceptByUuid,
|
|
14
|
-
} from "./interactive-editor/value-editors/concept-search.resource";
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import { render, screen, within } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { implementerToolsConfigStore, temporaryConfigStore, Type } from '@openmrs/esm-framework/src/internal';
|
|
6
|
+
import { Configuration } from './configuration.component';
|
|
7
|
+
import { useConceptLookup, useGetConceptByUuid } from './interactive-editor/value-editors/concept-search.resource';
|
|
15
8
|
|
|
16
9
|
const mockUseConceptLookup = useConceptLookup as jest.Mock;
|
|
17
10
|
const mockUseGetConceptByUuid = useGetConceptByUuid as jest.Mock;
|
|
18
11
|
|
|
19
|
-
jest.mock(
|
|
20
|
-
jest.mock(
|
|
12
|
+
jest.mock('lodash-es/debounce', () => jest.fn((fn) => fn));
|
|
13
|
+
jest.mock('./interactive-editor/value-editors/concept-search.resource', () => ({
|
|
21
14
|
useConceptLookup: jest.fn().mockImplementation(() => ({
|
|
22
15
|
concepts: [],
|
|
23
16
|
error: null,
|
|
@@ -33,18 +26,18 @@ jest.mock("./interactive-editor/value-editors/concept-search.resource", () => ({
|
|
|
33
26
|
window.URL.createObjectURL = jest.fn();
|
|
34
27
|
|
|
35
28
|
const mockImplToolsConfig = {
|
|
36
|
-
|
|
29
|
+
'@openmrs/mario': {
|
|
37
30
|
hasHat: {
|
|
38
31
|
_type: Type.Boolean,
|
|
39
32
|
_default: true,
|
|
40
33
|
_value: false,
|
|
41
|
-
_source:
|
|
34
|
+
_source: 'provided',
|
|
42
35
|
},
|
|
43
36
|
hatUuid: {
|
|
44
37
|
_type: Type.ConceptUuid,
|
|
45
|
-
_default:
|
|
46
|
-
_value:
|
|
47
|
-
_source:
|
|
38
|
+
_default: 'c64b8446-145e-49a3-98eb-ae37333bedf1',
|
|
39
|
+
_value: '38c650cf-85d5-41b4-b0b1-46709248acca',
|
|
40
|
+
_source: 'provided',
|
|
48
41
|
},
|
|
49
42
|
numberFingers: {
|
|
50
43
|
_type: Type.Number,
|
|
@@ -54,27 +47,27 @@ const mockImplToolsConfig = {
|
|
|
54
47
|
},
|
|
55
48
|
nemesisName: {
|
|
56
49
|
_type: Type.String,
|
|
57
|
-
_default:
|
|
58
|
-
_value:
|
|
59
|
-
_source:
|
|
50
|
+
_default: 'Wario',
|
|
51
|
+
_value: 'Waluigi',
|
|
52
|
+
_source: 'fool',
|
|
60
53
|
},
|
|
61
54
|
mustacheUuid: {
|
|
62
55
|
_type: Type.UUID,
|
|
63
|
-
_default:
|
|
64
|
-
_value:
|
|
65
|
-
_source:
|
|
56
|
+
_default: '7e5b9aa1-69fb-45c0-90f5-edc66e23c81d',
|
|
57
|
+
_value: '181aee4a-5664-42da-8699-c36d28083bd0',
|
|
58
|
+
_source: 'temporary config',
|
|
66
59
|
},
|
|
67
60
|
},
|
|
68
|
-
|
|
61
|
+
'@openmrs/luigi': {
|
|
69
62
|
favoriteNumbers: {
|
|
70
63
|
_type: Type.Array,
|
|
71
64
|
_elements: { _type: Type.Number },
|
|
72
65
|
_default: [0],
|
|
73
66
|
_value: [4, 12],
|
|
74
|
-
_source:
|
|
67
|
+
_source: 'provided',
|
|
75
68
|
},
|
|
76
69
|
},
|
|
77
|
-
|
|
70
|
+
'@openmrs/bowser': {
|
|
78
71
|
minions: {
|
|
79
72
|
_type: Type.Array,
|
|
80
73
|
_elements: {
|
|
@@ -83,13 +76,13 @@ const mockImplToolsConfig = {
|
|
|
83
76
|
},
|
|
84
77
|
_default: [],
|
|
85
78
|
_value: [
|
|
86
|
-
{ name:
|
|
87
|
-
{ name:
|
|
79
|
+
{ name: 'goomba', canJump: false },
|
|
80
|
+
{ name: 'koopa', canJump: true },
|
|
88
81
|
],
|
|
89
|
-
_source:
|
|
82
|
+
_source: 'provided',
|
|
90
83
|
},
|
|
91
84
|
},
|
|
92
|
-
|
|
85
|
+
'@openmrs/peach': {
|
|
93
86
|
weapons: {
|
|
94
87
|
_type: Type.Object,
|
|
95
88
|
_elements: { _type: Type.Number },
|
|
@@ -99,7 +92,7 @@ const mockImplToolsConfig = {
|
|
|
99
92
|
},
|
|
100
93
|
};
|
|
101
94
|
|
|
102
|
-
describe(
|
|
95
|
+
describe('Configuration', () => {
|
|
103
96
|
afterEach(() => {
|
|
104
97
|
implementerToolsConfigStore.setState({ config: {} });
|
|
105
98
|
temporaryConfigStore.setState({ config: {} });
|
|
@@ -109,95 +102,89 @@ describe("Configuration", () => {
|
|
|
109
102
|
render(<Configuration />);
|
|
110
103
|
}
|
|
111
104
|
|
|
112
|
-
it(
|
|
105
|
+
it('renders the configuration component inside the implementer tools panel', () => {
|
|
113
106
|
renderConfiguration();
|
|
114
|
-
screen.getByRole(
|
|
115
|
-
screen.getByRole(
|
|
116
|
-
screen.getByRole(
|
|
117
|
-
screen.getByRole(
|
|
118
|
-
screen.getByRole(
|
|
107
|
+
screen.getByRole('switch', { name: /json editor/i });
|
|
108
|
+
screen.getByRole('switch', { name: /ui editor/i });
|
|
109
|
+
screen.getByRole('button', { name: /clear local config/i });
|
|
110
|
+
screen.getByRole('button', { name: /download config/i });
|
|
111
|
+
screen.getByRole('textbox', { name: /search configuration/i });
|
|
119
112
|
});
|
|
120
113
|
|
|
121
|
-
it(
|
|
114
|
+
it('displays correct boolean value and editor', async () => {
|
|
122
115
|
const user = userEvent.setup();
|
|
123
116
|
|
|
124
117
|
implementerToolsConfigStore.setState({
|
|
125
118
|
config: {
|
|
126
|
-
|
|
119
|
+
'@openmrs/mario': mockImplToolsConfig['@openmrs/mario'],
|
|
127
120
|
},
|
|
128
121
|
});
|
|
129
122
|
|
|
130
123
|
renderConfiguration();
|
|
131
124
|
|
|
132
|
-
const rowElement = screen
|
|
133
|
-
.getByText("hasHat")
|
|
134
|
-
.closest(".cds--structured-list-row");
|
|
125
|
+
const rowElement = screen.getByText('hasHat').closest('.cds--structured-list-row');
|
|
135
126
|
expect(rowElement).toBeInTheDocument();
|
|
136
127
|
|
|
137
128
|
if (rowElement) {
|
|
138
129
|
const row = within(rowElement as HTMLElement);
|
|
139
|
-
const value = row.getByText(
|
|
140
|
-
const editButton = row.getByText(
|
|
130
|
+
const value = row.getByText('false');
|
|
131
|
+
const editButton = row.getByText('Edit').parentElement as any;
|
|
141
132
|
await user.click(editButton);
|
|
142
|
-
const editor = row.getByRole(
|
|
133
|
+
const editor = row.getByRole('button', { name: /edit/i });
|
|
143
134
|
|
|
144
135
|
await user.click(editor);
|
|
145
|
-
await user.click(row.getByText(
|
|
136
|
+
await user.click(row.getByText('Save'));
|
|
146
137
|
|
|
147
138
|
expect(temporaryConfigStore.setState).toHaveBeenCalledWith({
|
|
148
|
-
config: {
|
|
139
|
+
config: { '@openmrs/mario': { hasHat: false } },
|
|
149
140
|
});
|
|
150
141
|
}
|
|
151
142
|
});
|
|
152
143
|
|
|
153
|
-
it(
|
|
144
|
+
it('displays correct concept UUID value and editor', async () => {
|
|
154
145
|
const user = userEvent.setup();
|
|
155
146
|
|
|
156
147
|
mockUseConceptLookup.mockImplementation(() => ({
|
|
157
|
-
concepts: [
|
|
158
|
-
{ uuid: "61523693-72e2-456d-8c64-8c5293febeb6", display: "Fedora" },
|
|
159
|
-
],
|
|
148
|
+
concepts: [{ uuid: '61523693-72e2-456d-8c64-8c5293febeb6', display: 'Fedora' }],
|
|
160
149
|
error: null,
|
|
161
150
|
isSearchingConcepts: false,
|
|
162
151
|
}));
|
|
163
152
|
|
|
164
153
|
mockUseGetConceptByUuid.mockImplementation(() => ({
|
|
165
|
-
concept: { name: { display:
|
|
154
|
+
concept: { name: { display: 'Fedora' } },
|
|
166
155
|
error: null,
|
|
167
156
|
isLoadingConcept: false,
|
|
168
157
|
}));
|
|
169
158
|
|
|
170
159
|
implementerToolsConfigStore.setState({
|
|
171
160
|
config: {
|
|
172
|
-
|
|
161
|
+
'@openmrs/mario': mockImplToolsConfig['@openmrs/mario'],
|
|
173
162
|
},
|
|
174
163
|
});
|
|
175
164
|
|
|
176
165
|
renderConfiguration();
|
|
177
166
|
|
|
178
|
-
const rowElement = (await screen.findByText(
|
|
179
|
-
".cds--structured-list-row"
|
|
180
|
-
);
|
|
167
|
+
const rowElement = (await screen.findByText('hatUuid')).closest('.cds--structured-list-row');
|
|
181
168
|
expect(rowElement).toBeInTheDocument();
|
|
182
169
|
|
|
183
170
|
if (rowElement) {
|
|
184
171
|
const row = within(rowElement as HTMLElement);
|
|
185
|
-
const editButton = row.getByRole(
|
|
172
|
+
const editButton = row.getByRole('button', { name: /edit/i });
|
|
186
173
|
|
|
187
174
|
await user.click(editButton);
|
|
188
175
|
|
|
189
|
-
const searchbox = await row.findByRole(
|
|
176
|
+
const searchbox = await row.findByRole('combobox', {
|
|
190
177
|
name: /search concepts/i,
|
|
191
178
|
});
|
|
192
179
|
|
|
193
|
-
await user.type(searchbox,
|
|
180
|
+
await user.type(searchbox, 'fedora');
|
|
194
181
|
|
|
195
|
-
expect(mockUseConceptLookup).toHaveBeenCalledWith(
|
|
182
|
+
expect(mockUseConceptLookup).toHaveBeenCalledWith('fed');
|
|
196
183
|
|
|
197
|
-
const targetConcept = await row.findByText(
|
|
184
|
+
const targetConcept = await row.findByText('Fedora');
|
|
198
185
|
|
|
199
186
|
await user.click(targetConcept);
|
|
200
|
-
await user.click(row.getByText(
|
|
187
|
+
await user.click(row.getByText('Save'));
|
|
201
188
|
|
|
202
189
|
// expect(temporaryConfigStore.setState).toHaveBeenCalledWith({
|
|
203
190
|
// config: {
|
|
@@ -207,159 +194,149 @@ describe("Configuration", () => {
|
|
|
207
194
|
}
|
|
208
195
|
});
|
|
209
196
|
|
|
210
|
-
it(
|
|
197
|
+
it('displays correct number value and editor', async () => {
|
|
211
198
|
const user = userEvent.setup();
|
|
212
199
|
|
|
213
200
|
implementerToolsConfigStore.setState({
|
|
214
201
|
config: {
|
|
215
|
-
|
|
202
|
+
'@openmrs/mario': mockImplToolsConfig['@openmrs/mario'],
|
|
216
203
|
},
|
|
217
204
|
});
|
|
218
205
|
|
|
219
206
|
renderConfiguration();
|
|
220
207
|
|
|
221
|
-
const rowElement = (await screen.findByText(
|
|
222
|
-
".cds--structured-list-row"
|
|
223
|
-
);
|
|
208
|
+
const rowElement = (await screen.findByText('numberFingers')).closest('.cds--structured-list-row');
|
|
224
209
|
expect(rowElement).toBeInTheDocument();
|
|
225
210
|
|
|
226
211
|
if (rowElement) {
|
|
227
212
|
const row = within(rowElement as HTMLElement);
|
|
228
|
-
const valueButton = row.getByText(
|
|
213
|
+
const valueButton = row.getByText('8');
|
|
229
214
|
expect(valueButton).toBeInTheDocument();
|
|
230
215
|
|
|
231
|
-
const editButton = row.getByRole(
|
|
216
|
+
const editButton = row.getByRole('button', { name: /edit/i });
|
|
232
217
|
|
|
233
218
|
await user.click(editButton);
|
|
234
219
|
|
|
235
|
-
const editor = await row.findByRole(
|
|
220
|
+
const editor = await row.findByRole('spinbutton');
|
|
236
221
|
|
|
237
|
-
expect(editor).toHaveAttribute(
|
|
222
|
+
expect(editor).toHaveAttribute('type', 'number');
|
|
238
223
|
|
|
239
224
|
await user.clear(editor);
|
|
240
|
-
await user.type(editor,
|
|
241
|
-
await user.click(row.getByText(
|
|
225
|
+
await user.type(editor, '11');
|
|
226
|
+
await user.click(row.getByText('Save'));
|
|
242
227
|
|
|
243
228
|
expect(temporaryConfigStore.setState).toHaveBeenCalledWith({
|
|
244
|
-
config: {
|
|
229
|
+
config: { '@openmrs/mario': { numberFingers: 11 } },
|
|
245
230
|
});
|
|
246
231
|
}
|
|
247
232
|
});
|
|
248
233
|
|
|
249
|
-
it(
|
|
234
|
+
it('displays correct string value and editor', async () => {
|
|
250
235
|
const user = userEvent.setup();
|
|
251
236
|
|
|
252
237
|
implementerToolsConfigStore.setState({
|
|
253
238
|
config: {
|
|
254
|
-
|
|
239
|
+
'@openmrs/mario': mockImplToolsConfig['@openmrs/mario'],
|
|
255
240
|
},
|
|
256
241
|
});
|
|
257
242
|
|
|
258
243
|
renderConfiguration();
|
|
259
244
|
|
|
260
|
-
const rowElement = (await screen.findByText(
|
|
261
|
-
".cds--structured-list-row"
|
|
262
|
-
);
|
|
245
|
+
const rowElement = (await screen.findByText('nemesisName')).closest('.cds--structured-list-row');
|
|
263
246
|
expect(rowElement).toBeInTheDocument();
|
|
264
247
|
|
|
265
248
|
if (rowElement) {
|
|
266
249
|
const row = within(rowElement as HTMLElement);
|
|
267
|
-
const editButton = row.getByRole(
|
|
250
|
+
const editButton = row.getByRole('button', { name: /edit/i });
|
|
268
251
|
|
|
269
252
|
await user.click(editButton);
|
|
270
253
|
|
|
271
|
-
const editor = await row.findByRole(
|
|
254
|
+
const editor = await row.findByRole('textbox');
|
|
272
255
|
|
|
273
256
|
await user.clear(editor);
|
|
274
|
-
await user.type(editor,
|
|
275
|
-
await user.click(row.getByText(
|
|
257
|
+
await user.type(editor, 'Bowser');
|
|
258
|
+
await user.click(row.getByText('Save'));
|
|
276
259
|
|
|
277
260
|
expect(temporaryConfigStore.setState).toHaveBeenCalledWith({
|
|
278
|
-
config: {
|
|
261
|
+
config: { '@openmrs/mario': { nemesisName: 'Bowser' } },
|
|
279
262
|
});
|
|
280
263
|
}
|
|
281
264
|
});
|
|
282
265
|
|
|
283
|
-
it(
|
|
266
|
+
it('displays correct UUID value and editor', async () => {
|
|
284
267
|
const user = userEvent.setup();
|
|
285
268
|
|
|
286
269
|
implementerToolsConfigStore.setState({
|
|
287
270
|
config: {
|
|
288
|
-
|
|
271
|
+
'@openmrs/mario': mockImplToolsConfig['@openmrs/mario'],
|
|
289
272
|
},
|
|
290
273
|
});
|
|
291
274
|
|
|
292
275
|
renderConfiguration();
|
|
293
276
|
|
|
294
|
-
const rowElement = (await screen.findByText(
|
|
295
|
-
".cds--structured-list-row"
|
|
296
|
-
);
|
|
277
|
+
const rowElement = (await screen.findByText('mustacheUuid')).closest('.cds--structured-list-row');
|
|
297
278
|
expect(rowElement).toBeInTheDocument();
|
|
298
279
|
|
|
299
280
|
if (rowElement) {
|
|
300
281
|
const row = within(rowElement as HTMLElement);
|
|
301
|
-
row.getByText(
|
|
282
|
+
row.getByText('181aee4a-5664-42da-8699-c36d28083bd0');
|
|
302
283
|
|
|
303
|
-
const editButton = row.getByRole(
|
|
284
|
+
const editButton = row.getByRole('button', { name: /edit/i });
|
|
304
285
|
|
|
305
286
|
await user.click(editButton);
|
|
306
287
|
|
|
307
|
-
const editor = await row.findByRole(
|
|
288
|
+
const editor = await row.findByRole('textbox');
|
|
308
289
|
|
|
309
290
|
await user.clear(editor);
|
|
310
|
-
const newUuid =
|
|
291
|
+
const newUuid = '34f03796-f0e2-4f64-9e9a-28fb49a94baf';
|
|
311
292
|
await user.type(editor, newUuid);
|
|
312
|
-
await user.click(row.getByText(
|
|
293
|
+
await user.click(row.getByText('Save'));
|
|
313
294
|
|
|
314
295
|
expect(temporaryConfigStore.setState).toHaveBeenCalledWith({
|
|
315
|
-
config: {
|
|
296
|
+
config: { '@openmrs/mario': { mustacheUuid: newUuid } },
|
|
316
297
|
});
|
|
317
298
|
}
|
|
318
299
|
});
|
|
319
300
|
|
|
320
|
-
it(
|
|
301
|
+
it('renders an array editor for simple arrays that behaves correctly', async () => {
|
|
321
302
|
const user = userEvent.setup();
|
|
322
303
|
|
|
323
304
|
implementerToolsConfigStore.setState({
|
|
324
305
|
config: {
|
|
325
|
-
|
|
306
|
+
'@openmrs/luigi': mockImplToolsConfig['@openmrs/luigi'],
|
|
326
307
|
},
|
|
327
308
|
});
|
|
328
309
|
renderConfiguration();
|
|
329
|
-
const rowElement = (await screen.findByText(
|
|
330
|
-
".cds--structured-list-row"
|
|
331
|
-
);
|
|
310
|
+
const rowElement = (await screen.findByText('favoriteNumbers')).closest('.cds--structured-list-row');
|
|
332
311
|
expect(rowElement).toBeInTheDocument();
|
|
333
312
|
if (rowElement) {
|
|
334
313
|
const row = within(rowElement as HTMLElement);
|
|
335
314
|
|
|
336
|
-
const inputs = row.getByText(
|
|
337
|
-
const editButton = row.getByRole(
|
|
315
|
+
const inputs = row.getByText('[ 4, 12 ]');
|
|
316
|
+
const editButton = row.getByRole('button', { name: /edit/i });
|
|
338
317
|
|
|
339
318
|
await user.click(editButton);
|
|
340
319
|
// expect(inputs[0]).toHaveValue(4);
|
|
341
320
|
// expect(inputs[1]).toHaveValue(12);
|
|
342
|
-
const firstValue = row.getByDisplayValue(
|
|
343
|
-
expect(firstValue).toHaveAttribute(
|
|
321
|
+
const firstValue = row.getByDisplayValue('4');
|
|
322
|
+
expect(firstValue).toHaveAttribute('type', 'number');
|
|
344
323
|
|
|
345
324
|
await user.clear(firstValue);
|
|
346
|
-
await user.type(firstValue,
|
|
325
|
+
await user.type(firstValue, '5');
|
|
347
326
|
|
|
348
|
-
const secondRowElement = row
|
|
349
|
-
.getByDisplayValue("12")
|
|
350
|
-
.closest(".cds--structured-list-row");
|
|
327
|
+
const secondRowElement = row.getByDisplayValue('12').closest('.cds--structured-list-row');
|
|
351
328
|
|
|
352
329
|
expect(secondRowElement).toBeInTheDocument();
|
|
353
330
|
// I can't get the add or remove buttons to work in tests.
|
|
354
331
|
if (secondRowElement) {
|
|
355
332
|
await user.click(
|
|
356
333
|
within(secondRowElement as HTMLElement)
|
|
357
|
-
.getByText(
|
|
358
|
-
.closest(
|
|
334
|
+
.getByText('Remove')
|
|
335
|
+
.closest('button') as HTMLElement,
|
|
359
336
|
);
|
|
360
337
|
// await waitForElementToBeRemoved(() => row.getByDisplayValue("12"));
|
|
361
338
|
}
|
|
362
|
-
await user.click(row.getByText(
|
|
339
|
+
await user.click(row.getByText('Add'));
|
|
363
340
|
// let rows = await row.findAllByRole("spinbutton");
|
|
364
341
|
// let newInput = rows[rows.length - 1];
|
|
365
342
|
// user.type(newInput, "11");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import EditableValue from
|
|
3
|
-
import { implementerToolsStore } from
|
|
4
|
-
import isEqual from
|
|
5
|
-
import { Subtree } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import EditableValue from './editable-value.component';
|
|
3
|
+
import { implementerToolsStore } from '../../store';
|
|
4
|
+
import isEqual from 'lodash-es/isEqual';
|
|
5
|
+
import { Subtree } from './layout/subtree.component';
|
|
6
6
|
|
|
7
7
|
export interface ConfigSubtreeProps {
|
|
8
8
|
config: Record<string, any>;
|
|
@@ -25,10 +25,7 @@ export function ConfigSubtree({ config, path = [] }: ConfigSubtreeProps) {
|
|
|
25
25
|
|
|
26
26
|
function removeActiveItemDescriptionOnMouseLeave(thisPath) {
|
|
27
27
|
const state = implementerToolsStore.getState();
|
|
28
|
-
if (
|
|
29
|
-
isEqual(state.activeItemDescription?.path, thisPath) &&
|
|
30
|
-
!isEqual(state.configPathBeingEdited, thisPath)
|
|
31
|
-
) {
|
|
28
|
+
if (isEqual(state.activeItemDescription?.path, thisPath) && !isEqual(state.configPathBeingEdited, thisPath)) {
|
|
32
29
|
implementerToolsStore.setState({ activeItemDescription: undefined });
|
|
33
30
|
}
|
|
34
31
|
}
|
|
@@ -37,19 +34,14 @@ export function ConfigSubtree({ config, path = [] }: ConfigSubtreeProps) {
|
|
|
37
34
|
<>
|
|
38
35
|
{Object.entries(config).map(([key, value], i) => {
|
|
39
36
|
const thisPath = path.concat([key]);
|
|
40
|
-
const isLeaf =
|
|
41
|
-
value.hasOwnProperty("_value") || value.hasOwnProperty("_type");
|
|
37
|
+
const isLeaf = value.hasOwnProperty('_value') || value.hasOwnProperty('_type');
|
|
42
38
|
return (
|
|
43
39
|
<Subtree
|
|
44
40
|
label={key}
|
|
45
41
|
leaf={isLeaf}
|
|
46
|
-
onMouseEnter={() =>
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
onMouseLeave={() =>
|
|
50
|
-
removeActiveItemDescriptionOnMouseLeave(thisPath)
|
|
51
|
-
}
|
|
52
|
-
key={`subtree-${thisPath.join(".")}`}
|
|
42
|
+
onMouseEnter={() => setActiveItemDescriptionOnMouseEnter(thisPath, key, value)}
|
|
43
|
+
onMouseLeave={() => removeActiveItemDescriptionOnMouseLeave(thisPath)}
|
|
44
|
+
key={`subtree-${thisPath.join('.')}`}
|
|
53
45
|
>
|
|
54
46
|
{isLeaf ? (
|
|
55
47
|
<EditableValue path={thisPath} element={value} />
|
|
@@ -1,28 +1,19 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { ExtensionSlotsConfigTree } from
|
|
3
|
-
import { ConfigSubtree } from
|
|
4
|
-
import pickBy from
|
|
5
|
-
import { TreeContainer } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ExtensionSlotsConfigTree } from './extension-slots-config-tree';
|
|
3
|
+
import { ConfigSubtree } from './config-subtree.component';
|
|
4
|
+
import pickBy from 'lodash-es/pickBy';
|
|
5
|
+
import { TreeContainer } from './layout/tree-container.component';
|
|
6
6
|
|
|
7
7
|
export interface ConfigTreeForModuleProps {
|
|
8
8
|
config: Record<string, any>;
|
|
9
9
|
moduleName: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function ConfigTreeForModule({
|
|
13
|
-
config,
|
|
14
|
-
moduleName,
|
|
15
|
-
}: ConfigTreeForModuleProps) {
|
|
12
|
+
export function ConfigTreeForModule({ config, moduleName }: ConfigTreeForModuleProps) {
|
|
16
13
|
return (
|
|
17
14
|
<TreeContainer>
|
|
18
|
-
<ExtensionSlotsConfigTree
|
|
19
|
-
|
|
20
|
-
moduleName={moduleName}
|
|
21
|
-
/>
|
|
22
|
-
<ConfigSubtree
|
|
23
|
-
config={pickBy(config, (v, key) => key !== "extensionSlots")}
|
|
24
|
-
path={[moduleName]}
|
|
25
|
-
/>
|
|
15
|
+
<ExtensionSlotsConfigTree extensionsConfig={config.extensionSlots} moduleName={moduleName} />
|
|
16
|
+
<ConfigSubtree config={pickBy(config, (v, key) => key !== 'extensionSlots')} path={[moduleName]} />
|
|
26
17
|
</TreeContainer>
|
|
27
18
|
);
|
|
28
19
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import styles from
|
|
3
|
-
import { Accordion, AccordionItem } from
|
|
4
|
-
import { ConfigTreeForModule } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './config-tree.styles.scss';
|
|
3
|
+
import { Accordion, AccordionItem } from '@carbon/react';
|
|
4
|
+
import { ConfigTreeForModule } from './config-tree-for-module.component';
|
|
5
5
|
|
|
6
6
|
export interface ConfigTreeProps {
|
|
7
7
|
config: Record<string, any>;
|
|
@@ -21,11 +21,7 @@ export function ConfigTree({ config }: ConfigTreeProps) {
|
|
|
21
21
|
className={styles.fullWidthAccordion}
|
|
22
22
|
key={`accordion-${moduleName}`}
|
|
23
23
|
>
|
|
24
|
-
<ConfigTreeForModule
|
|
25
|
-
config={moduleConfig}
|
|
26
|
-
moduleName={moduleName}
|
|
27
|
-
key={`${moduleName}-config`}
|
|
28
|
-
/>
|
|
24
|
+
<ConfigTreeForModule config={moduleConfig} moduleName={moduleName} key={`${moduleName}-config`} />
|
|
29
25
|
</AccordionItem>
|
|
30
26
|
) : null;
|
|
31
27
|
})}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import styles from
|
|
3
|
-
import { useStore } from
|
|
4
|
-
import { implementerToolsStore } from
|
|
5
|
-
import { useTranslation } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './description.styles.scss';
|
|
3
|
+
import { useStore } from '@openmrs/esm-framework';
|
|
4
|
+
import { implementerToolsStore } from '../../store';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
export function Description() {
|
|
8
8
|
const { t } = useTranslation();
|
|
@@ -11,37 +11,22 @@ export function Description() {
|
|
|
11
11
|
<div>
|
|
12
12
|
{activeItemDescription ? (
|
|
13
13
|
<>
|
|
14
|
-
<h2 className={styles.path}>
|
|
15
|
-
|
|
16
|
-
</h2>
|
|
17
|
-
<p className={styles.description}>
|
|
18
|
-
{activeItemDescription.description}
|
|
19
|
-
</p>
|
|
14
|
+
<h2 className={styles.path}>{activeItemDescription.path.slice(1).join(' → ')}</h2>
|
|
15
|
+
<p className={styles.description}>{activeItemDescription.description}</p>
|
|
20
16
|
<p className={styles.source}>
|
|
21
|
-
{activeItemDescription.source ===
|
|
22
|
-
<>
|
|
23
|
-
{t(
|
|
24
|
-
"itemDescriptionSourceDefaultText",
|
|
25
|
-
"The current value is the default."
|
|
26
|
-
)}
|
|
27
|
-
</>
|
|
17
|
+
{activeItemDescription.source === 'default' ? (
|
|
18
|
+
<>{t('itemDescriptionSourceDefaultText', 'The current value is the default.')}</>
|
|
28
19
|
) : activeItemDescription.source ? (
|
|
29
20
|
<>
|
|
30
|
-
{t(
|
|
21
|
+
{t('activeItemSourceText', 'The current value comes from ')}
|
|
31
22
|
{activeItemDescription.source}
|
|
32
23
|
</>
|
|
33
24
|
) : null}
|
|
34
25
|
</p>
|
|
35
|
-
{activeItemDescription.value ? (
|
|
36
|
-
<h4 className={styles.productiveHeading01}>
|
|
37
|
-
{t("value", "Value")}
|
|
38
|
-
</h4>
|
|
39
|
-
) : null}
|
|
26
|
+
{activeItemDescription.value ? <h4 className={styles.productiveHeading01}>{t('value', 'Value')}</h4> : null}
|
|
40
27
|
<p className={styles.value}>
|
|
41
28
|
{Array.isArray(activeItemDescription.value)
|
|
42
|
-
? activeItemDescription.value.map((v, i) =>
|
|
43
|
-
<p key={`${v}-${i}`}>{v}</p>
|
|
44
|
-
))
|
|
29
|
+
? activeItemDescription.value.map((v, i) => <p key={`${v}-${i}`}>{v}</p>)
|
|
45
30
|
: activeItemDescription.value}
|
|
46
31
|
</p>
|
|
47
32
|
</>
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
@use
|
|
2
|
-
@use
|
|
3
|
-
@import
|
|
4
|
-
@import
|
|
1
|
+
@use '@carbon/styles/scss/spacing';
|
|
2
|
+
@use '@carbon/styles/scss/type';
|
|
3
|
+
@import '~@openmrs/esm-styleguide/src/vars';
|
|
4
|
+
@import '../../implementer-tools.styles.scss';
|
|
5
5
|
|
|
6
6
|
.path {
|
|
7
|
-
@include type.type-style(
|
|
7
|
+
@include type.type-style('heading-02');
|
|
8
8
|
margin-bottom: spacing.$spacing-03;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
.description {
|
|
12
|
-
@include type.type-style(
|
|
12
|
+
@include type.type-style('body-compact-01');
|
|
13
13
|
margin-bottom: spacing.$spacing-05;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
.source {
|
|
17
|
-
@include type.type-style(
|
|
17
|
+
@include type.type-style('body-compact-01');
|
|
18
18
|
font-style: italic;
|
|
19
19
|
margin-bottom: spacing.$spacing-03;
|
|
20
20
|
}
|