@evoke-platform/ui-components 1.10.0-dev.0 → 1.10.0-dev.2
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/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +0 -2
- package/dist/published/components/custom/FormV2/components/Footer.js +1 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +1 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +16 -13
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +706 -74
- package/dist/published/stories/FormRendererContainer.stories.d.ts +0 -10
- package/dist/published/stories/FormRendererContainer.stories.js +7 -3
- package/package.json +1 -1
|
@@ -24,8 +24,6 @@ export type FormRendererContainerProps = BaseProps & {
|
|
|
24
24
|
fieldHeight?: 'small' | 'medium';
|
|
25
25
|
};
|
|
26
26
|
actionId?: string;
|
|
27
|
-
stickyFooter?: boolean;
|
|
28
|
-
hideButtons?: boolean;
|
|
29
27
|
objectId: string;
|
|
30
28
|
richTextEditor?: ComponentType<SimpleEditorProps>;
|
|
31
29
|
onSubmit?: (submission: Record<string, unknown>, defaultSubmitHandler: (submission: Record<string, unknown>) => Promise<void>) => Promise<void>;
|
|
@@ -32,7 +32,6 @@ const styles = {
|
|
|
32
32
|
},
|
|
33
33
|
icon: {
|
|
34
34
|
color: '#fff',
|
|
35
|
-
zIndex: 40,
|
|
36
35
|
fontSize: '16px',
|
|
37
36
|
},
|
|
38
37
|
deleteIcon: {
|
|
@@ -81,7 +80,7 @@ export const Image = (props) => {
|
|
|
81
80
|
accept: { 'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.svg'] },
|
|
82
81
|
});
|
|
83
82
|
return (React.createElement(React.Fragment, null, image ? (React.createElement(Box, { sx: styles.imageContainer },
|
|
84
|
-
React.createElement(Box, { sx: { position: 'relative', left: 0
|
|
83
|
+
React.createElement(Box, { sx: { position: 'relative', left: 0 } },
|
|
85
84
|
React.createElement(CardMedia, { component: "img", image: image, alt: 'Uploaded Image', sx: styles.image }),
|
|
86
85
|
canUpdateProperty && (React.createElement(IconButton, { onClick: handleRemove, "aria-label": "Remove image", sx: styles.deleteIcon },
|
|
87
86
|
React.createElement(ClearRounded, { sx: styles.icon })))))) : canUpdateProperty ? (React.createElement(Box, { sx: {
|
|
@@ -2,13 +2,14 @@ import { useApiServices } from '@evoke-platform/context';
|
|
|
2
2
|
import { Clear, Search } from '@mui/icons-material';
|
|
3
3
|
import { debounce, get, startCase } from 'lodash';
|
|
4
4
|
import { DateTime } from 'luxon';
|
|
5
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
|
-
import { Button, IconButton, InputAdornment, TextField, Typography, } from '../../../../../core';
|
|
5
|
+
import React, { useCallback, useEffect, useId, useMemo, useState } from 'react';
|
|
6
|
+
import { Button, IconButton, InputAdornment, InputLabel, TextField, Typography, } from '../../../../../core';
|
|
7
7
|
import { Box, Grid } from '../../../../../layout';
|
|
8
8
|
import BuilderGrid from '../../../../BuilderGrid';
|
|
9
9
|
import { getPrefixedUrl } from '../../utils';
|
|
10
10
|
const SearchField = (props) => {
|
|
11
11
|
const { searchableColumns, filter, setFilter, searchString, setSearchString } = props;
|
|
12
|
+
const id = useId();
|
|
12
13
|
const clearSearch = () => {
|
|
13
14
|
setSearchString('');
|
|
14
15
|
setFilter(undefined);
|
|
@@ -37,17 +38,19 @@ const SearchField = (props) => {
|
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
|
-
return (React.createElement(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
return (React.createElement(React.Fragment, null,
|
|
42
|
+
React.createElement(InputLabel, { htmlFor: id }, "Search"),
|
|
43
|
+
React.createElement(TextField, { id: id, autoFocus: true, placeholder: "Search", value: searchString, onChange: (e) => handleSearch(e), size: "medium", fullWidth: true, sx: { marginBottom: '15px' }, InputProps: {
|
|
44
|
+
endAdornment: (React.createElement(InputAdornment, { position: "end" },
|
|
45
|
+
React.createElement(IconButton, { sx: {
|
|
46
|
+
visibility: searchString.length > 0 ? 'visible' : 'hidden',
|
|
47
|
+
}, onClick: () => clearSearch() },
|
|
48
|
+
React.createElement(Clear, { sx: {
|
|
49
|
+
fontSize: '22px',
|
|
50
|
+
} })))),
|
|
51
|
+
startAdornment: (React.createElement(InputAdornment, { position: "start" },
|
|
52
|
+
React.createElement(Search, { sx: { fontSize: '22px', color: '#637381' } }))),
|
|
53
|
+
} })));
|
|
51
54
|
};
|
|
52
55
|
const InstanceLookup = (props) => {
|
|
53
56
|
const { object, setSelectedInstance, setRelationType, filter: criteriaFilter, mode, layout } = props;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as matchers from '@testing-library/jest-dom/matchers';
|
|
2
|
-
import { render, screen, waitFor, within } from '@testing-library/react';
|
|
2
|
+
import { render as baseRender, screen, waitFor, within } from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { isEmpty, isEqual, set } from 'lodash';
|
|
5
5
|
import { http, HttpResponse } from 'msw';
|
|
@@ -16,13 +16,10 @@ global.ResizeObserver = class ResizeObserver {
|
|
|
16
16
|
unobserve() { }
|
|
17
17
|
disconnect() { }
|
|
18
18
|
};
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
portalSelectors.forEach((selector) => {
|
|
22
|
-
// eslint-disable-next-line testing-library/no-node-access
|
|
23
|
-
document.querySelectorAll(selector).forEach((el) => el.remove());
|
|
24
|
-
});
|
|
19
|
+
const WithProviders = ({ children }) => {
|
|
20
|
+
return React.createElement(MemoryRouter, null, children);
|
|
25
21
|
};
|
|
22
|
+
const render = (ui, options) => baseRender(ui, { wrapper: WithProviders, ...options });
|
|
26
23
|
describe('Form component', () => {
|
|
27
24
|
let server;
|
|
28
25
|
beforeAll(() => {
|
|
@@ -75,7 +72,6 @@ describe('Form component', () => {
|
|
|
75
72
|
});
|
|
76
73
|
afterEach(() => {
|
|
77
74
|
server.resetHandlers();
|
|
78
|
-
removePoppers();
|
|
79
75
|
});
|
|
80
76
|
describe('validation criteria', () => {
|
|
81
77
|
it(`filters related object field with validation criteria that references a related object's nested data`, async () => {
|
|
@@ -103,8 +99,7 @@ describe('Form component', () => {
|
|
|
103
99
|
return React.createElement("div", null, "Render error");
|
|
104
100
|
}
|
|
105
101
|
};
|
|
106
|
-
render(React.createElement(
|
|
107
|
-
React.createElement(FormWithState, null)));
|
|
102
|
+
render(React.createElement(FormWithState, null));
|
|
108
103
|
const license = await screen.findByRole('combobox', { name: 'License' });
|
|
109
104
|
// Step 1: Open Specialty Type and verify all options
|
|
110
105
|
await user.click(await screen.findByRole('combobox', { name: 'Specialty Type' }));
|
|
@@ -134,23 +129,21 @@ describe('Form component', () => {
|
|
|
134
129
|
describe('visibility configuration', () => {
|
|
135
130
|
it('shows fields based on instance data using JsonLogic', async () => {
|
|
136
131
|
server.use(http.get('/data/objects/license/instances/rnLicense', () => HttpResponse.json(rnLicense)));
|
|
137
|
-
render(React.createElement(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
} })));
|
|
132
|
+
render(React.createElement(FormRenderer, { form: jsonLogicDisplayTestSpecialtyForm, onChange: () => { }, instance: {
|
|
133
|
+
id: '123',
|
|
134
|
+
objectId: 'specialty',
|
|
135
|
+
name: 'Test Specialty Object Instance',
|
|
136
|
+
} }));
|
|
143
137
|
// Validate that specialty type dropdown renders
|
|
144
138
|
await screen.findByRole('combobox', { name: 'Specialty Type' });
|
|
145
139
|
});
|
|
146
140
|
it('hides fields based on instance data using JsonLogic', async () => {
|
|
147
141
|
server.use(http.get('/data/objects/license/instances/rnLicense', () => HttpResponse.json(rnLicense)));
|
|
148
|
-
render(React.createElement(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
} })));
|
|
142
|
+
render(React.createElement(FormRenderer, { form: jsonLogicDisplayTestSpecialtyForm, onChange: () => { }, instance: {
|
|
143
|
+
id: '123',
|
|
144
|
+
objectId: 'specialty',
|
|
145
|
+
name: 'Test Specialty Object Instance -- hidden',
|
|
146
|
+
} }));
|
|
154
147
|
// Validate that license dropdown renders
|
|
155
148
|
await screen.findByRole('combobox', { name: 'License' });
|
|
156
149
|
// Validate that specialty type dropdown does not render
|
|
@@ -158,23 +151,21 @@ describe('Form component', () => {
|
|
|
158
151
|
});
|
|
159
152
|
it('shows fields based on instance data using simple conditions', async () => {
|
|
160
153
|
server.use(http.get('/data/objects/license/instances/rnLicense', () => HttpResponse.json(rnLicense)));
|
|
161
|
-
render(React.createElement(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
} })));
|
|
154
|
+
render(React.createElement(FormRenderer, { form: simpleConditionDisplayTestSpecialtyForm, onChange: () => { }, instance: {
|
|
155
|
+
id: '123',
|
|
156
|
+
objectId: 'specialty',
|
|
157
|
+
name: 'Test Specialty Object Instance',
|
|
158
|
+
} }));
|
|
167
159
|
// Validate that specialty type dropdown renders
|
|
168
160
|
await screen.findByRole('combobox', { name: 'Specialty Type' });
|
|
169
161
|
});
|
|
170
162
|
it('hides fields based on instance data using simple conditions', async () => {
|
|
171
163
|
server.use(http.get('/data/objects/license/instances/rnLicense', () => HttpResponse.json(rnLicense)));
|
|
172
|
-
render(React.createElement(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} })));
|
|
164
|
+
render(React.createElement(FormRenderer, { form: simpleConditionDisplayTestSpecialtyForm, onChange: () => { }, instance: {
|
|
165
|
+
id: '123',
|
|
166
|
+
objectId: 'specialty',
|
|
167
|
+
name: 'Test Specialty Object Instance -- hidden',
|
|
168
|
+
} }));
|
|
178
169
|
// Validate that license dropdown renders
|
|
179
170
|
await screen.findByRole('combobox', { name: 'License' });
|
|
180
171
|
// Validate that specialty type dropdown does not render
|
|
@@ -185,9 +176,7 @@ describe('Form component', () => {
|
|
|
185
176
|
describe('508 accessibility compliance', () => {
|
|
186
177
|
it('supports keyboard navigation back and forth through Related Object dropdowns', async () => {
|
|
187
178
|
const user = userEvent.setup();
|
|
188
|
-
render(React.createElement(
|
|
189
|
-
React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } }),
|
|
190
|
-
","));
|
|
179
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } }));
|
|
191
180
|
await waitFor(() => {
|
|
192
181
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
193
182
|
});
|
|
@@ -203,9 +192,7 @@ describe('Form component', () => {
|
|
|
203
192
|
});
|
|
204
193
|
it('supports keyboard navigation back and forth through User dropdowns', async () => {
|
|
205
194
|
const user = userEvent.setup();
|
|
206
|
-
render(React.createElement(
|
|
207
|
-
React.createElement(FormRenderer, { form: UpdateAccessibilityFormTwo, onChange: () => { } }),
|
|
208
|
-
","));
|
|
195
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormTwo, onChange: () => { } }));
|
|
209
196
|
await waitFor(() => {
|
|
210
197
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
211
198
|
});
|
|
@@ -223,9 +210,7 @@ describe('Form component', () => {
|
|
|
223
210
|
});
|
|
224
211
|
it('supports keyboard selection of dropdown values using Enter key on Related Objects', async () => {
|
|
225
212
|
const user = userEvent.setup();
|
|
226
|
-
render(React.createElement(
|
|
227
|
-
React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } }),
|
|
228
|
-
","));
|
|
213
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } }));
|
|
229
214
|
await waitFor(() => {
|
|
230
215
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
231
216
|
});
|
|
@@ -246,9 +231,7 @@ describe('Form component', () => {
|
|
|
246
231
|
});
|
|
247
232
|
it('supports keyboard selection of dropdown values using Enter key on Users', async () => {
|
|
248
233
|
const user = userEvent.setup();
|
|
249
|
-
render(React.createElement(
|
|
250
|
-
React.createElement(FormRenderer, { form: UpdateAccessibilityFormTwo, onChange: () => { } }),
|
|
251
|
-
","));
|
|
234
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormTwo, onChange: () => { } }));
|
|
252
235
|
await waitFor(() => {
|
|
253
236
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
254
237
|
});
|
|
@@ -268,9 +251,7 @@ describe('Form component', () => {
|
|
|
268
251
|
});
|
|
269
252
|
it('supports navigating between dropdown options using arrow keys on Related Objects', async () => {
|
|
270
253
|
const user = userEvent.setup();
|
|
271
|
-
render(React.createElement(
|
|
272
|
-
React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } }),
|
|
273
|
-
","));
|
|
254
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } }));
|
|
274
255
|
await waitFor(() => {
|
|
275
256
|
expect(screen.getByLabelText('License')).toBeInTheDocument();
|
|
276
257
|
});
|
|
@@ -290,9 +271,7 @@ describe('Form component', () => {
|
|
|
290
271
|
});
|
|
291
272
|
it('supports navigating between dropdown options using arrow keys on User dropdowns', async () => {
|
|
292
273
|
const user = userEvent.setup();
|
|
293
|
-
render(React.createElement(
|
|
294
|
-
React.createElement(FormRenderer, { form: UpdateAccessibilityFormTwo, onChange: () => { } }),
|
|
295
|
-
","));
|
|
274
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormTwo, onChange: () => { } }));
|
|
296
275
|
await waitFor(() => {
|
|
297
276
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
298
277
|
});
|
|
@@ -310,16 +289,15 @@ describe('Form component', () => {
|
|
|
310
289
|
});
|
|
311
290
|
it('supports clearing selection with the clear button on Related Objects', async () => {
|
|
312
291
|
const user = userEvent.setup();
|
|
313
|
-
render(React.createElement(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
} })));
|
|
292
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { }, value: {
|
|
293
|
+
id: '123',
|
|
294
|
+
objectId: 'accessibility508Object',
|
|
295
|
+
name: 'Test Accessibility 508 Object Instance',
|
|
296
|
+
license: {
|
|
297
|
+
id: 'rnLicense',
|
|
298
|
+
name: 'RN License',
|
|
299
|
+
},
|
|
300
|
+
} }));
|
|
323
301
|
// Set up a selection first
|
|
324
302
|
await waitFor(() => {
|
|
325
303
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
@@ -339,16 +317,15 @@ describe('Form component', () => {
|
|
|
339
317
|
});
|
|
340
318
|
it('supports clearing selection with the clear button on User dropdowns', async () => {
|
|
341
319
|
const user = userEvent.setup();
|
|
342
|
-
render(React.createElement(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
} })));
|
|
320
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormTwo, onChange: () => { }, value: {
|
|
321
|
+
id: '123',
|
|
322
|
+
objectId: 'accessibility508Object',
|
|
323
|
+
name: 'Test Accessibility 508 Object Instance',
|
|
324
|
+
user: {
|
|
325
|
+
id: 'user1',
|
|
326
|
+
name: 'User 1',
|
|
327
|
+
},
|
|
328
|
+
} }));
|
|
352
329
|
// Set up a selection first
|
|
353
330
|
await waitFor(() => {
|
|
354
331
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
@@ -370,8 +347,7 @@ describe('Form component', () => {
|
|
|
370
347
|
});
|
|
371
348
|
it('supports navigating to Add New option with arrow keys and opens modal', async () => {
|
|
372
349
|
const user = userEvent.setup();
|
|
373
|
-
render(React.createElement(
|
|
374
|
-
React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } })));
|
|
350
|
+
render(React.createElement(FormRenderer, { form: UpdateAccessibilityFormOne, onChange: () => { } }));
|
|
375
351
|
await waitFor(() => {
|
|
376
352
|
expect(screen.getByLabelText('Name')).toBeInTheDocument();
|
|
377
353
|
});
|
|
@@ -389,4 +365,660 @@ describe('Form component', () => {
|
|
|
389
365
|
});
|
|
390
366
|
});
|
|
391
367
|
});
|
|
368
|
+
describe('when passed a related object entry', () => {
|
|
369
|
+
const setupTestMocks = (object, form, instances) => {
|
|
370
|
+
server.use(http.get(`/api/data/objects/${object.id}/effective`, () => HttpResponse.json(object)), http.get(`/api/data/forms/${form.id}`, () => HttpResponse.json(form)), http.get(`/api/data/forms?filter={"where":{"actionId":"${form.actionId}","objectId":"${object.id}"}}`, () => HttpResponse.json([form])), http.get(`/api/data/objects/${object.id}/instances`, () => HttpResponse.json(instances || [])));
|
|
371
|
+
};
|
|
372
|
+
describe('when in table view', () => {
|
|
373
|
+
describe('when mode is existing records only', () => {
|
|
374
|
+
const form = {
|
|
375
|
+
id: 'relatedObjectTestForm',
|
|
376
|
+
name: 'Related Object Test Form',
|
|
377
|
+
entries: [
|
|
378
|
+
{
|
|
379
|
+
type: 'input',
|
|
380
|
+
parameterId: 'specialtyType',
|
|
381
|
+
display: {
|
|
382
|
+
label: 'Speciality Type',
|
|
383
|
+
mode: 'existingOnly',
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
actionId: '_update',
|
|
388
|
+
objectId: 'relatedObjectTestForm',
|
|
389
|
+
};
|
|
390
|
+
beforeEach(() => {
|
|
391
|
+
const relatedObjectTestFormObject = {
|
|
392
|
+
id: 'relatedObjectTestForm',
|
|
393
|
+
name: 'Related Object Test Form',
|
|
394
|
+
actions: [
|
|
395
|
+
{
|
|
396
|
+
id: '_update',
|
|
397
|
+
name: 'Update',
|
|
398
|
+
type: 'update',
|
|
399
|
+
parameters: [
|
|
400
|
+
{
|
|
401
|
+
id: 'specialtyType',
|
|
402
|
+
name: 'Related Object',
|
|
403
|
+
type: 'object',
|
|
404
|
+
objectId: 'specialtyType',
|
|
405
|
+
},
|
|
406
|
+
],
|
|
407
|
+
outputEvent: 'updated',
|
|
408
|
+
},
|
|
409
|
+
],
|
|
410
|
+
properties: [
|
|
411
|
+
{
|
|
412
|
+
id: 'specialtyType',
|
|
413
|
+
name: 'Related Object',
|
|
414
|
+
type: 'object',
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
};
|
|
418
|
+
setupTestMocks(relatedObjectTestFormObject, form);
|
|
419
|
+
const specialtyTypeObject = {
|
|
420
|
+
id: 'specialtyType',
|
|
421
|
+
name: 'Specialty Type',
|
|
422
|
+
actions: [
|
|
423
|
+
{
|
|
424
|
+
id: '_create',
|
|
425
|
+
name: 'Create',
|
|
426
|
+
type: 'create',
|
|
427
|
+
parameters: [],
|
|
428
|
+
outputEvent: 'created',
|
|
429
|
+
defaultFormId: 'specialtyType',
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
id: '_update',
|
|
433
|
+
name: 'Update',
|
|
434
|
+
type: 'update',
|
|
435
|
+
parameters: [],
|
|
436
|
+
outputEvent: 'updated',
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
properties: [],
|
|
440
|
+
};
|
|
441
|
+
const specialtyTypeForm = {
|
|
442
|
+
id: 'specialtyTypeForm',
|
|
443
|
+
name: 'Specialty Type Form',
|
|
444
|
+
entries: [],
|
|
445
|
+
actionId: '_update',
|
|
446
|
+
objectId: 'specialtyType',
|
|
447
|
+
};
|
|
448
|
+
setupTestMocks(specialtyTypeObject, specialtyTypeForm);
|
|
449
|
+
});
|
|
450
|
+
it('does not display radio buttons if mode is existing records only', async () => {
|
|
451
|
+
const user = userEvent.setup();
|
|
452
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
453
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
454
|
+
// Make sure dialog loads
|
|
455
|
+
await screen.findByRole('dialog');
|
|
456
|
+
expect(screen.queryByRole('radiogroup', { name: 'Relation Type' })).not.toBeInTheDocument();
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
describe('when object has no searchable properties', () => {
|
|
460
|
+
const form = {
|
|
461
|
+
id: 'relatedObjectTestForm',
|
|
462
|
+
name: 'Related Object Test Form',
|
|
463
|
+
entries: [
|
|
464
|
+
{
|
|
465
|
+
type: 'input',
|
|
466
|
+
parameterId: 'specialtyType',
|
|
467
|
+
display: {
|
|
468
|
+
label: 'Speciality Type',
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
],
|
|
472
|
+
actionId: '_update',
|
|
473
|
+
objectId: 'relatedObjectTestForm',
|
|
474
|
+
};
|
|
475
|
+
beforeEach(async () => {
|
|
476
|
+
const relatedObjectTestFormObject = {
|
|
477
|
+
id: 'relatedObjectTestForm',
|
|
478
|
+
name: 'Related Object Test Form',
|
|
479
|
+
actions: [
|
|
480
|
+
{
|
|
481
|
+
id: '_update',
|
|
482
|
+
name: 'Update',
|
|
483
|
+
type: 'update',
|
|
484
|
+
parameters: [
|
|
485
|
+
{
|
|
486
|
+
id: 'specialtyType',
|
|
487
|
+
name: 'Related Object',
|
|
488
|
+
type: 'object',
|
|
489
|
+
objectId: 'specialtyType',
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
outputEvent: 'updated',
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
properties: [
|
|
496
|
+
{
|
|
497
|
+
id: 'specialtyType',
|
|
498
|
+
name: 'Related Object',
|
|
499
|
+
type: 'object',
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
};
|
|
503
|
+
setupTestMocks(relatedObjectTestFormObject, form);
|
|
504
|
+
const specialtyTypeObject = {
|
|
505
|
+
id: 'specialtyType',
|
|
506
|
+
name: 'Specialty Type',
|
|
507
|
+
actions: [
|
|
508
|
+
{
|
|
509
|
+
id: '_create',
|
|
510
|
+
name: 'Create',
|
|
511
|
+
type: 'create',
|
|
512
|
+
parameters: [],
|
|
513
|
+
outputEvent: 'created',
|
|
514
|
+
defaultFormId: 'specialtyType',
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
id: '_update',
|
|
518
|
+
name: 'Update',
|
|
519
|
+
type: 'update',
|
|
520
|
+
parameters: [],
|
|
521
|
+
outputEvent: 'updated',
|
|
522
|
+
},
|
|
523
|
+
],
|
|
524
|
+
properties: [],
|
|
525
|
+
};
|
|
526
|
+
const specialtyTypeForm = {
|
|
527
|
+
id: 'specialtyTypeForm',
|
|
528
|
+
name: 'Specialty Type Form',
|
|
529
|
+
entries: [],
|
|
530
|
+
actionId: '_update',
|
|
531
|
+
objectId: 'specialtyType',
|
|
532
|
+
};
|
|
533
|
+
setupTestMocks(specialtyTypeObject, specialtyTypeForm);
|
|
534
|
+
});
|
|
535
|
+
it("displays an error message if there aren't any searchable properties on a related object", async () => {
|
|
536
|
+
const user = userEvent.setup();
|
|
537
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
538
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
539
|
+
await screen.findByRole('dialog');
|
|
540
|
+
await waitFor(() => {
|
|
541
|
+
expect(screen.getByText('There are no searchable properties configured for this object')).toBeInTheDocument();
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
describe('when object has searchable properties', () => {
|
|
546
|
+
const form = {
|
|
547
|
+
id: 'relatedObjectTestForm',
|
|
548
|
+
name: 'Related Object Test Form',
|
|
549
|
+
entries: [
|
|
550
|
+
{
|
|
551
|
+
type: 'input',
|
|
552
|
+
parameterId: 'specialtyType',
|
|
553
|
+
display: {
|
|
554
|
+
label: 'Speciality Type',
|
|
555
|
+
relatedObjectDisplay: 'dialogBox',
|
|
556
|
+
mode: 'existingOnly',
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
],
|
|
560
|
+
actionId: '_update',
|
|
561
|
+
objectId: 'relatedObjectTestForm',
|
|
562
|
+
};
|
|
563
|
+
beforeEach(async () => {
|
|
564
|
+
const relatedObjectTestFormObject = {
|
|
565
|
+
id: 'relatedObjectTestForm',
|
|
566
|
+
name: 'Related Object Test Form',
|
|
567
|
+
actions: [
|
|
568
|
+
{
|
|
569
|
+
id: '_update',
|
|
570
|
+
name: 'Update',
|
|
571
|
+
type: 'update',
|
|
572
|
+
parameters: [
|
|
573
|
+
{
|
|
574
|
+
id: 'specialtyType',
|
|
575
|
+
name: 'Related Object',
|
|
576
|
+
type: 'object',
|
|
577
|
+
objectId: 'specialtyType',
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
outputEvent: 'updated',
|
|
581
|
+
},
|
|
582
|
+
],
|
|
583
|
+
properties: [
|
|
584
|
+
{
|
|
585
|
+
id: 'specialtyType',
|
|
586
|
+
name: 'Related Object',
|
|
587
|
+
type: 'object',
|
|
588
|
+
},
|
|
589
|
+
],
|
|
590
|
+
};
|
|
591
|
+
setupTestMocks(relatedObjectTestFormObject, form);
|
|
592
|
+
const specialtyTypeObject = {
|
|
593
|
+
id: 'specialtyType',
|
|
594
|
+
name: 'Specialty Type',
|
|
595
|
+
actions: [
|
|
596
|
+
{
|
|
597
|
+
id: '_create',
|
|
598
|
+
name: 'Create',
|
|
599
|
+
type: 'create',
|
|
600
|
+
parameters: [],
|
|
601
|
+
outputEvent: 'created',
|
|
602
|
+
defaultFormId: 'specialtyType',
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
properties: [
|
|
606
|
+
{
|
|
607
|
+
id: 'name',
|
|
608
|
+
name: 'Name',
|
|
609
|
+
type: 'string',
|
|
610
|
+
searchable: true,
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
};
|
|
614
|
+
const specialtyTypeForm = {
|
|
615
|
+
id: 'specialtyTypeForm',
|
|
616
|
+
name: 'Specialty Type Form',
|
|
617
|
+
entries: [
|
|
618
|
+
{
|
|
619
|
+
type: 'input',
|
|
620
|
+
parameterId: 'specialtyType',
|
|
621
|
+
display: {
|
|
622
|
+
label: 'Speciality Type',
|
|
623
|
+
relatedObjectDisplay: 'dialogBox',
|
|
624
|
+
mode: 'existingOnly',
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
],
|
|
628
|
+
actionId: '_update',
|
|
629
|
+
objectId: 'specialtyType',
|
|
630
|
+
};
|
|
631
|
+
setupTestMocks(specialtyTypeObject, specialtyTypeForm);
|
|
632
|
+
});
|
|
633
|
+
it('displays message to refine search if no search results are found', async () => {
|
|
634
|
+
const user = userEvent.setup();
|
|
635
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
636
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
637
|
+
await screen.findByRole('dialog');
|
|
638
|
+
const searchField = screen.getByRole('textbox', { name: 'Search' });
|
|
639
|
+
await user.type(searchField, 'Nonexistent Instance');
|
|
640
|
+
await screen.findByText(/refine your search/i);
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
describe('when mode is default (allows both new and existing)', () => {
|
|
644
|
+
const form = {
|
|
645
|
+
id: 'relatedObjectTestForm',
|
|
646
|
+
name: 'Related Object Test Form',
|
|
647
|
+
entries: [
|
|
648
|
+
{
|
|
649
|
+
type: 'input',
|
|
650
|
+
parameterId: 'specialtyType',
|
|
651
|
+
display: {
|
|
652
|
+
label: 'Speciality Type',
|
|
653
|
+
},
|
|
654
|
+
},
|
|
655
|
+
],
|
|
656
|
+
actionId: '_update',
|
|
657
|
+
objectId: 'relatedObjectTestForm',
|
|
658
|
+
};
|
|
659
|
+
beforeEach(async () => {
|
|
660
|
+
const relatedObjectTestFormObject = {
|
|
661
|
+
id: 'relatedObjectTestForm',
|
|
662
|
+
name: 'Related Object Test Form',
|
|
663
|
+
actions: [
|
|
664
|
+
{
|
|
665
|
+
id: '_update',
|
|
666
|
+
name: 'Update',
|
|
667
|
+
type: 'update',
|
|
668
|
+
parameters: [
|
|
669
|
+
{
|
|
670
|
+
id: 'specialtyType',
|
|
671
|
+
name: 'Related Object',
|
|
672
|
+
type: 'object',
|
|
673
|
+
objectId: 'specialtyType',
|
|
674
|
+
},
|
|
675
|
+
],
|
|
676
|
+
outputEvent: 'updated',
|
|
677
|
+
},
|
|
678
|
+
],
|
|
679
|
+
properties: [
|
|
680
|
+
{
|
|
681
|
+
id: 'specialtyType',
|
|
682
|
+
name: 'Related Object',
|
|
683
|
+
type: 'object',
|
|
684
|
+
},
|
|
685
|
+
],
|
|
686
|
+
};
|
|
687
|
+
setupTestMocks(relatedObjectTestFormObject, form);
|
|
688
|
+
const specialtyTypeForm = {
|
|
689
|
+
id: 'specialtyTypeForm',
|
|
690
|
+
name: 'Specialty Type Form',
|
|
691
|
+
entries: [
|
|
692
|
+
{
|
|
693
|
+
type: 'content',
|
|
694
|
+
html: '<div>Specialty Type Form Content</div>',
|
|
695
|
+
},
|
|
696
|
+
],
|
|
697
|
+
actionId: '_create',
|
|
698
|
+
objectId: 'specialtyType',
|
|
699
|
+
};
|
|
700
|
+
const specialtyTypeObject = {
|
|
701
|
+
id: 'specialtyType',
|
|
702
|
+
name: 'Specialty Type',
|
|
703
|
+
actions: [
|
|
704
|
+
{
|
|
705
|
+
id: '_create',
|
|
706
|
+
name: 'Create',
|
|
707
|
+
type: 'create',
|
|
708
|
+
parameters: [],
|
|
709
|
+
outputEvent: 'created',
|
|
710
|
+
defaultFormId: 'specialtyTypeForm',
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
id: '_update',
|
|
714
|
+
name: 'Update',
|
|
715
|
+
type: 'update',
|
|
716
|
+
parameters: [],
|
|
717
|
+
outputEvent: 'updated',
|
|
718
|
+
},
|
|
719
|
+
],
|
|
720
|
+
properties: [],
|
|
721
|
+
};
|
|
722
|
+
setupTestMocks(specialtyTypeObject, specialtyTypeForm);
|
|
723
|
+
});
|
|
724
|
+
it('displays an add button for related object fields', async () => {
|
|
725
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
726
|
+
await screen.findByRole('button', { name: 'Add' });
|
|
727
|
+
});
|
|
728
|
+
it('shows a close icon in instance lookup mode', async () => {
|
|
729
|
+
const user = userEvent.setup();
|
|
730
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
731
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
732
|
+
await screen.findByRole('button', { name: 'Close' });
|
|
733
|
+
});
|
|
734
|
+
it('allows users to close modal using close icon in instance lookup mode', async () => {
|
|
735
|
+
const user = userEvent.setup();
|
|
736
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
737
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
738
|
+
const dialog = await screen.findByRole('dialog');
|
|
739
|
+
await user.click(await screen.findByRole('button', { name: 'Close' }));
|
|
740
|
+
await waitFor(() => expect(dialog).not.toBeInTheDocument());
|
|
741
|
+
});
|
|
742
|
+
it('shows a cancel button in instance lookup mode', async () => {
|
|
743
|
+
const user = userEvent.setup();
|
|
744
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
745
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
746
|
+
await screen.findByRole('button', { name: 'Cancel' });
|
|
747
|
+
});
|
|
748
|
+
it('closes dialog in instance lookup mode when clicking cancel', async () => {
|
|
749
|
+
const user = userEvent.setup();
|
|
750
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
751
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
752
|
+
const dialog = await screen.findByRole('dialog');
|
|
753
|
+
await user.click(await screen.findByRole('button', { name: 'Cancel' }));
|
|
754
|
+
await waitFor(() => expect(dialog).not.toBeInTheDocument());
|
|
755
|
+
});
|
|
756
|
+
it('displays radio buttons for new or existing record selection', async () => {
|
|
757
|
+
const user = userEvent.setup();
|
|
758
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
759
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
760
|
+
await screen.findByRole('radiogroup', { name: 'Relation Type' });
|
|
761
|
+
});
|
|
762
|
+
it('closes dialog if cancel button is clicked when creating a new record', async () => {
|
|
763
|
+
const user = userEvent.setup();
|
|
764
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
765
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
766
|
+
const dialog = await screen.findByRole('dialog');
|
|
767
|
+
await screen.findByRole('radiogroup', { name: 'Relation Type' });
|
|
768
|
+
const existingRecordButton = await screen.findByRole('radio', { name: /existing/i });
|
|
769
|
+
expect(existingRecordButton).toBeChecked();
|
|
770
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
771
|
+
await user.click(newRecordButton);
|
|
772
|
+
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
|
|
773
|
+
await user.click(cancelButton);
|
|
774
|
+
await waitFor(() => expect(dialog).not.toBeInTheDocument());
|
|
775
|
+
});
|
|
776
|
+
it('displays form if user switches to creating a new record', async () => {
|
|
777
|
+
const user = userEvent.setup();
|
|
778
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
779
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
780
|
+
await screen.findByRole('radiogroup', { name: 'Relation Type' });
|
|
781
|
+
const existingRecordButton = await screen.findByRole('radio', { name: /existing/i });
|
|
782
|
+
expect(existingRecordButton).toBeChecked();
|
|
783
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
784
|
+
await user.click(newRecordButton);
|
|
785
|
+
await screen.findByText('Specialty Type Form Content');
|
|
786
|
+
});
|
|
787
|
+
it('displays a close icon in record creation mode', async () => {
|
|
788
|
+
const user = userEvent.setup();
|
|
789
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
790
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
791
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
792
|
+
await user.click(newRecordButton);
|
|
793
|
+
await screen.findByText('Specialty Type Form Content');
|
|
794
|
+
await screen.findByRole('button', { name: 'Close' });
|
|
795
|
+
});
|
|
796
|
+
it('displays a cancel button in record creation mode', async () => {
|
|
797
|
+
const user = userEvent.setup();
|
|
798
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
799
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
800
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
801
|
+
await user.click(newRecordButton);
|
|
802
|
+
await screen.findByText('Specialty Type Form Content');
|
|
803
|
+
await screen.findByRole('button', { name: 'Cancel' });
|
|
804
|
+
});
|
|
805
|
+
it('displays a not found error in record creation mode if a form could not be found', async () => {
|
|
806
|
+
const user = userEvent.setup();
|
|
807
|
+
server.use(http.get('/api/data/forms/specialtyTypeForm', () => {
|
|
808
|
+
return HttpResponse.json({ error: 'Not found' }, { status: 404 });
|
|
809
|
+
}));
|
|
810
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
811
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
812
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
813
|
+
await user.click(newRecordButton);
|
|
814
|
+
await screen.findByText(/not found/i);
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
});
|
|
818
|
+
describe('when in dropdown view', () => {
|
|
819
|
+
describe('when mode is existing records only', () => {
|
|
820
|
+
const form = {
|
|
821
|
+
id: 'relatedObjectTestForm',
|
|
822
|
+
name: 'Related Object Test Form',
|
|
823
|
+
entries: [
|
|
824
|
+
{
|
|
825
|
+
type: 'input',
|
|
826
|
+
parameterId: 'specialtyType',
|
|
827
|
+
display: {
|
|
828
|
+
label: 'Speciality Type',
|
|
829
|
+
relatedObjectDisplay: 'dropdown',
|
|
830
|
+
mode: 'existingOnly',
|
|
831
|
+
},
|
|
832
|
+
},
|
|
833
|
+
],
|
|
834
|
+
actionId: '_update',
|
|
835
|
+
objectId: 'relatedObjectTestForm',
|
|
836
|
+
};
|
|
837
|
+
beforeEach(() => {
|
|
838
|
+
const relatedObjectTestFormObject = {
|
|
839
|
+
id: 'relatedObjectTestForm',
|
|
840
|
+
name: 'Related Object Test Form',
|
|
841
|
+
actions: [
|
|
842
|
+
{
|
|
843
|
+
id: '_update',
|
|
844
|
+
name: 'Update',
|
|
845
|
+
type: 'update',
|
|
846
|
+
parameters: [
|
|
847
|
+
{
|
|
848
|
+
id: 'specialtyType',
|
|
849
|
+
name: 'Related Object',
|
|
850
|
+
type: 'object',
|
|
851
|
+
objectId: 'specialtyType',
|
|
852
|
+
},
|
|
853
|
+
],
|
|
854
|
+
outputEvent: 'updated',
|
|
855
|
+
},
|
|
856
|
+
],
|
|
857
|
+
properties: [
|
|
858
|
+
{
|
|
859
|
+
id: 'specialtyType',
|
|
860
|
+
name: 'Related Object',
|
|
861
|
+
type: 'object',
|
|
862
|
+
},
|
|
863
|
+
],
|
|
864
|
+
};
|
|
865
|
+
setupTestMocks(relatedObjectTestFormObject, form);
|
|
866
|
+
const specialtyTypeObject = {
|
|
867
|
+
id: 'specialtyType',
|
|
868
|
+
name: 'Specialty Type',
|
|
869
|
+
actions: [
|
|
870
|
+
{
|
|
871
|
+
id: '_create',
|
|
872
|
+
name: 'Create',
|
|
873
|
+
type: 'create',
|
|
874
|
+
parameters: [],
|
|
875
|
+
outputEvent: 'created',
|
|
876
|
+
defaultFormId: 'specialtyType',
|
|
877
|
+
},
|
|
878
|
+
],
|
|
879
|
+
properties: [],
|
|
880
|
+
};
|
|
881
|
+
const specialtyTypeForm = {
|
|
882
|
+
id: 'specialtyTypeForm',
|
|
883
|
+
name: 'Specialty Type Form',
|
|
884
|
+
entries: [
|
|
885
|
+
{
|
|
886
|
+
type: 'content',
|
|
887
|
+
html: '<div>Specialty Type Form Content</div>',
|
|
888
|
+
},
|
|
889
|
+
],
|
|
890
|
+
actionId: '_update',
|
|
891
|
+
objectId: 'specialtyType',
|
|
892
|
+
};
|
|
893
|
+
setupTestMocks(specialtyTypeObject, specialtyTypeForm, [
|
|
894
|
+
{
|
|
895
|
+
id: 'rnSpecialtyType1',
|
|
896
|
+
name: 'RN Specialty Type #1',
|
|
897
|
+
objectId: 'specialtyType',
|
|
898
|
+
},
|
|
899
|
+
]);
|
|
900
|
+
});
|
|
901
|
+
it('does not provide option to add new object instances if mode is set to existing only', async () => {
|
|
902
|
+
const user = userEvent.setup();
|
|
903
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
904
|
+
// Navigate to and open dropdown
|
|
905
|
+
const dropdown = await screen.findByRole('combobox', { name: 'Speciality Type' });
|
|
906
|
+
await user.click(dropdown);
|
|
907
|
+
await screen.findByRole('listbox');
|
|
908
|
+
// Verify that the existing option is present
|
|
909
|
+
await screen.findByRole('option', { name: 'RN Specialty Type #1' });
|
|
910
|
+
// Verify that the "Add New" option is not present
|
|
911
|
+
expect(screen.queryByText('+ Add New')).not.toBeInTheDocument();
|
|
912
|
+
});
|
|
913
|
+
});
|
|
914
|
+
describe('when mode is default (allows both new and existing)', () => {
|
|
915
|
+
const form = {
|
|
916
|
+
id: 'relatedObjectTestForm',
|
|
917
|
+
name: 'Related Object Test Form',
|
|
918
|
+
entries: [
|
|
919
|
+
{
|
|
920
|
+
type: 'input',
|
|
921
|
+
parameterId: 'specialtyType',
|
|
922
|
+
display: {
|
|
923
|
+
label: 'Speciality Type',
|
|
924
|
+
relatedObjectDisplay: 'dropdown',
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
actionId: '_update',
|
|
929
|
+
objectId: 'relatedObjectTestForm',
|
|
930
|
+
};
|
|
931
|
+
beforeEach(() => {
|
|
932
|
+
const relatedObjectTestFormObject = {
|
|
933
|
+
id: 'relatedObjectTestForm',
|
|
934
|
+
name: 'Related Object Test Form',
|
|
935
|
+
actions: [
|
|
936
|
+
{
|
|
937
|
+
id: '_update',
|
|
938
|
+
name: 'Update',
|
|
939
|
+
type: 'update',
|
|
940
|
+
parameters: [
|
|
941
|
+
{
|
|
942
|
+
id: 'specialtyType',
|
|
943
|
+
name: 'Related Object',
|
|
944
|
+
type: 'object',
|
|
945
|
+
objectId: 'specialtyType',
|
|
946
|
+
},
|
|
947
|
+
],
|
|
948
|
+
outputEvent: 'updated',
|
|
949
|
+
},
|
|
950
|
+
],
|
|
951
|
+
properties: [
|
|
952
|
+
{
|
|
953
|
+
id: 'specialtyType',
|
|
954
|
+
name: 'Related Object',
|
|
955
|
+
type: 'object',
|
|
956
|
+
},
|
|
957
|
+
],
|
|
958
|
+
};
|
|
959
|
+
setupTestMocks(relatedObjectTestFormObject, form);
|
|
960
|
+
const specialtyTypeObject = {
|
|
961
|
+
id: 'specialtyType',
|
|
962
|
+
name: 'Specialty Type',
|
|
963
|
+
actions: [
|
|
964
|
+
{
|
|
965
|
+
id: '_create',
|
|
966
|
+
name: 'Create',
|
|
967
|
+
type: 'create',
|
|
968
|
+
parameters: [],
|
|
969
|
+
outputEvent: 'created',
|
|
970
|
+
defaultFormId: 'specialtyTypeForm',
|
|
971
|
+
},
|
|
972
|
+
],
|
|
973
|
+
properties: [],
|
|
974
|
+
};
|
|
975
|
+
const specialtyTypeForm = {
|
|
976
|
+
id: 'specialtyTypeForm',
|
|
977
|
+
name: 'Specialty Type Form',
|
|
978
|
+
entries: [
|
|
979
|
+
{
|
|
980
|
+
type: 'content',
|
|
981
|
+
html: '<div>Specialty Type Form Content</div>',
|
|
982
|
+
},
|
|
983
|
+
],
|
|
984
|
+
actionId: '_create',
|
|
985
|
+
objectId: 'specialtyType',
|
|
986
|
+
};
|
|
987
|
+
setupTestMocks(specialtyTypeObject, specialtyTypeForm, [
|
|
988
|
+
{
|
|
989
|
+
id: 'rnSpecialtyType1',
|
|
990
|
+
name: 'RN Specialty Type #1',
|
|
991
|
+
objectId: 'specialtyType',
|
|
992
|
+
},
|
|
993
|
+
]);
|
|
994
|
+
});
|
|
995
|
+
it('provides option to add new object instances', async () => {
|
|
996
|
+
const user = userEvent.setup();
|
|
997
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
998
|
+
// Navigate to and open dropdown
|
|
999
|
+
const dropdown = await screen.findByRole('combobox', { name: 'Speciality Type' });
|
|
1000
|
+
await user.click(dropdown);
|
|
1001
|
+
await waitFor(() => {
|
|
1002
|
+
expect(screen.getByText('+ Add New')).toBeInTheDocument();
|
|
1003
|
+
});
|
|
1004
|
+
await user.click(screen.getByText('+ Add New'));
|
|
1005
|
+
await screen.findByRole('dialog');
|
|
1006
|
+
await screen.findByText('Specialty Type Form Content');
|
|
1007
|
+
});
|
|
1008
|
+
it('allows related object instance selection', async () => {
|
|
1009
|
+
const user = userEvent.setup();
|
|
1010
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1011
|
+
// Navigate to and open dropdown
|
|
1012
|
+
const dropdown = await screen.findByRole('combobox', { name: 'Speciality Type' });
|
|
1013
|
+
await user.click(dropdown);
|
|
1014
|
+
const option = await screen.findByRole('option', { name: 'RN Specialty Type #1' });
|
|
1015
|
+
await user.click(option);
|
|
1016
|
+
// Verify option has been removed from the document
|
|
1017
|
+
expect(option).not.toBeInTheDocument();
|
|
1018
|
+
// Verify selection
|
|
1019
|
+
screen.getByText('RN Specialty Type #1');
|
|
1020
|
+
});
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
});
|
|
392
1024
|
});
|
|
@@ -10,8 +10,6 @@ declare const _default: import("@storybook/types").ComponentAnnotations<import("
|
|
|
10
10
|
fieldHeight?: "medium" | "small" | undefined;
|
|
11
11
|
} | undefined;
|
|
12
12
|
actionId?: string | undefined;
|
|
13
|
-
stickyFooter?: boolean | undefined;
|
|
14
|
-
hideButtons?: boolean | undefined;
|
|
15
13
|
objectId: string;
|
|
16
14
|
richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
|
|
17
15
|
onSubmit?: ((submission: Record<string, unknown>, defaultSubmitHandler: (submission: Record<string, unknown>) => Promise<void>) => Promise<void>) | undefined;
|
|
@@ -39,8 +37,6 @@ export declare const Editable: import("@storybook/types").AnnotatedStoryFn<impor
|
|
|
39
37
|
fieldHeight?: "medium" | "small" | undefined;
|
|
40
38
|
} | undefined;
|
|
41
39
|
actionId?: string | undefined;
|
|
42
|
-
stickyFooter?: boolean | undefined;
|
|
43
|
-
hideButtons?: boolean | undefined;
|
|
44
40
|
objectId: string;
|
|
45
41
|
richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
|
|
46
42
|
onSubmit?: ((submission: Record<string, unknown>, defaultSubmitHandler: (submission: Record<string, unknown>) => Promise<void>) => Promise<void>) | undefined;
|
|
@@ -67,8 +63,6 @@ export declare const NoButtons: import("@storybook/types").AnnotatedStoryFn<impo
|
|
|
67
63
|
fieldHeight?: "medium" | "small" | undefined;
|
|
68
64
|
} | undefined;
|
|
69
65
|
actionId?: string | undefined;
|
|
70
|
-
stickyFooter?: boolean | undefined;
|
|
71
|
-
hideButtons?: boolean | undefined;
|
|
72
66
|
objectId: string;
|
|
73
67
|
richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
|
|
74
68
|
onSubmit?: ((submission: Record<string, unknown>, defaultSubmitHandler: (submission: Record<string, unknown>) => Promise<void>) => Promise<void>) | undefined;
|
|
@@ -95,8 +89,6 @@ export declare const DocumentForm: import("@storybook/types").AnnotatedStoryFn<i
|
|
|
95
89
|
fieldHeight?: "medium" | "small" | undefined;
|
|
96
90
|
} | undefined;
|
|
97
91
|
actionId?: string | undefined;
|
|
98
|
-
stickyFooter?: boolean | undefined;
|
|
99
|
-
hideButtons?: boolean | undefined;
|
|
100
92
|
objectId: string;
|
|
101
93
|
richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
|
|
102
94
|
onSubmit?: ((submission: Record<string, unknown>, defaultSubmitHandler: (submission: Record<string, unknown>) => Promise<void>) => Promise<void>) | undefined;
|
|
@@ -123,8 +115,6 @@ export declare const FormWithSections: import("@storybook/types").AnnotatedStory
|
|
|
123
115
|
fieldHeight?: "medium" | "small" | undefined;
|
|
124
116
|
} | undefined;
|
|
125
117
|
actionId?: string | undefined;
|
|
126
|
-
stickyFooter?: boolean | undefined;
|
|
127
|
-
hideButtons?: boolean | undefined;
|
|
128
118
|
objectId: string;
|
|
129
119
|
richTextEditor?: React.ComponentType<import("../components/custom/FormV2/components/types").SimpleEditorProps> | undefined;
|
|
130
120
|
onSubmit?: ((submission: Record<string, unknown>, defaultSubmitHandler: (submission: Record<string, unknown>) => Promise<void>) => Promise<void>) | undefined;
|
|
@@ -2,7 +2,7 @@ import { Box } from '@mui/material';
|
|
|
2
2
|
import { http, HttpResponse } from 'msw';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { MemoryRouter } from 'react-router-dom';
|
|
5
|
-
import { FormRendererContainer } from '../components/custom';
|
|
5
|
+
import { FormRenderer, FormRendererContainer } from '../components/custom';
|
|
6
6
|
import { documentInstance } from './FormRendererData';
|
|
7
7
|
import { sharedObjectHandlers } from './sharedMswHandlers';
|
|
8
8
|
export default {
|
|
@@ -31,8 +31,12 @@ const mockProps = {
|
|
|
31
31
|
fieldHeight: 'medium',
|
|
32
32
|
},
|
|
33
33
|
actionId: '_update',
|
|
34
|
-
stickyFooter: true,
|
|
35
34
|
objectId: 'genericEvokeForm',
|
|
35
|
+
renderFooter: (footerProps) => (React.createElement(FormRenderer.Footer, { ...footerProps, sx: {
|
|
36
|
+
background: 'white',
|
|
37
|
+
position: 'sticky',
|
|
38
|
+
bottom: 0,
|
|
39
|
+
} })),
|
|
36
40
|
};
|
|
37
41
|
const Template = (args) => {
|
|
38
42
|
return (React.createElement(MemoryRouter, null,
|
|
@@ -45,7 +49,7 @@ Editable.args = {
|
|
|
45
49
|
export const NoButtons = Template.bind({});
|
|
46
50
|
NoButtons.args = {
|
|
47
51
|
...mockProps,
|
|
48
|
-
|
|
52
|
+
renderFooter: () => null,
|
|
49
53
|
};
|
|
50
54
|
export const DocumentForm = Template.bind({});
|
|
51
55
|
DocumentForm.args = {
|