@evoke-platform/ui-components 1.1.0-testing.0 → 1.1.0-testing.10

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.
Files changed (32) hide show
  1. package/dist/published/components/core/DateTimePicker/DateTimePicker.js +7 -1
  2. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.d.ts +4 -5
  3. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +103 -31
  4. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +4 -10
  5. package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +39 -14
  6. package/dist/published/components/custom/Form/Common/Form.d.ts +7 -1
  7. package/dist/published/components/custom/Form/Common/Form.js +112 -94
  8. package/dist/published/components/custom/Form/Common/FormComponentWrapper.d.ts +5 -0
  9. package/dist/published/components/custom/Form/Common/FormComponentWrapper.js +15 -4
  10. package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.d.ts +1 -0
  11. package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.js +1 -1
  12. package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectPropertyInput.js +7 -3
  13. package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ManyToMany/DropdownRepeatableField.js +6 -2
  14. package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ManyToMany/DropdownRepeatableFieldInput.js +1 -0
  15. package/dist/published/components/custom/Form/index.d.ts +2 -1
  16. package/dist/published/components/custom/Form/utils.js +21 -4
  17. package/dist/published/components/custom/FormField/FormField.d.ts +2 -0
  18. package/dist/published/components/custom/FormField/FormField.js +3 -1
  19. package/dist/published/components/custom/FormField/Select/Select.js +28 -4
  20. package/dist/published/components/custom/FormField/Select/Select.test.js +41 -0
  21. package/dist/published/components/custom/OverflowTextField/OverflowTextField.d.ts +4 -0
  22. package/dist/published/components/custom/OverflowTextField/OverflowTextField.js +13 -0
  23. package/dist/published/components/custom/OverflowTextField/index.d.ts +2 -0
  24. package/dist/published/components/custom/OverflowTextField/index.js +2 -0
  25. package/dist/published/components/custom/index.d.ts +3 -2
  26. package/dist/published/components/custom/index.js +2 -2
  27. package/dist/published/index.d.ts +6 -3
  28. package/dist/published/index.js +4 -2
  29. package/dist/published/stories/FormField.stories.js +2 -0
  30. package/dist/published/stories/OverflowTextField.stories.d.ts +5 -0
  31. package/dist/published/stories/OverflowTextField.stories.js +28 -0
  32. package/package.json +2 -8
@@ -17,8 +17,9 @@ const usePrevious = (value) => {
17
17
  };
18
18
  Utils.Evaluator.noeval = true;
19
19
  export function Form(props) {
20
- const { clearable, closeModal, onSave, submitButtonLabel, instance, object, objectInputCommonProps, actionId, actionType, associatedObject, onAutoSave, apiServices, navigateTo, document, queryAddresses, user, isReadOnly, fieldHeight, richTextEditor, } = props;
20
+ const { clearable, closeModal, onSave, submitButtonLabel, instance, object, objectInputCommonProps, actionId, actionType, associatedObject, onAutoSave, apiServices, navigateTo, document, queryAddresses, user, isReadOnly, fieldHeight, richTextEditor, hideButtons, formRef, } = props;
21
21
  const [formKey, setFormKey] = useState();
22
+ const [formData, setFormData] = useState();
22
23
  const [componentProps, setComponentProps] = useState([]);
23
24
  const [snackbarError, setSnackbarError] = useState();
24
25
  const prevFormKey = usePrevious(formKey);
@@ -70,6 +71,14 @@ export function Form(props) {
70
71
  });
71
72
  buildComponents();
72
73
  };
74
+ useEffect(() => {
75
+ if (formRef) {
76
+ formRef.current = {
77
+ submit: saveHandler,
78
+ data: formData,
79
+ };
80
+ }
81
+ }, [formRef, formData]);
73
82
  useEffect(() => {
74
83
  // TODO: Check why formKey is not being set for create actions
75
84
  if (((actionType === 'update' || actionType === 'delete') && !!instance) || !!document) {
@@ -130,112 +139,117 @@ export function Form(props) {
130
139
  apiServices,
131
140
  user: userAccount,
132
141
  }, undefined, isReadOnly, allDefaultPages, navigateTo, queryAddresses, apiServices, !!closeModal, fieldHeight, richTextEditor);
133
- const components = isReadOnly ? newComponentProps : [...newComponentProps, BottomButtons];
134
- setComponentProps(components);
142
+ if (!hideButtons && !isReadOnly) {
143
+ newComponentProps.push(BottomButtons);
144
+ }
145
+ setComponentProps(newComponentProps);
135
146
  }
136
147
  else {
137
- // this condition is used for the delete action's modal form
138
- setComponentProps([
139
- ...(await addObjectPropertiesToComponentProps(visibleObjectProperties, [
140
- {
141
- html: `<p>${action?.type === 'delete' ? 'This action cannot be undone.' : 'Are you sure?'}</p>`,
142
- label: 'Content',
143
- customClass: '',
144
- refreshOnChange: false,
145
- hidden: false,
146
- modalEdit: false,
147
- conditional: {
148
- show: null,
149
- when: null,
150
- eq: '',
151
- },
152
- type: 'Content',
153
- key: 'content',
154
- input: false,
155
- placeholder: '',
156
- prefix: '',
157
- suffix: '',
148
+ const components = await addObjectPropertiesToComponentProps(visibleObjectProperties, [
149
+ {
150
+ html: `<p>${action?.type === 'delete' ? 'This action cannot be undone.' : 'Are you sure?'}</p>`,
151
+ label: 'Content',
152
+ customClass: '',
153
+ refreshOnChange: false,
154
+ hidden: false,
155
+ modalEdit: false,
156
+ conditional: {
157
+ show: null,
158
+ when: null,
159
+ eq: '',
160
+ },
161
+ type: 'Content',
162
+ key: 'content',
163
+ input: false,
164
+ placeholder: '',
165
+ prefix: '',
166
+ suffix: '',
167
+ multiple: false,
168
+ defaultValue: null,
169
+ protected: false,
170
+ unique: false,
171
+ persistent: true,
172
+ clearOnHide: true,
173
+ refreshOn: '',
174
+ redrawOn: '',
175
+ tableView: false,
176
+ dataGridLabel: false,
177
+ labelPosition: 'top',
178
+ description: '',
179
+ errorLabel: '',
180
+ tooltip: '',
181
+ hideLabel: false,
182
+ tabindex: '',
183
+ disabled: false,
184
+ autofocus: false,
185
+ dbIndex: false,
186
+ customDefaultValue: '',
187
+ calculateValue: '',
188
+ calculateServer: false,
189
+ widget: null,
190
+ attributes: {},
191
+ validateOn: 'change',
192
+ validate: {
193
+ required: false,
194
+ custom: '',
195
+ customPrivate: false,
196
+ strictDateValidation: false,
158
197
  multiple: false,
159
- defaultValue: null,
160
- protected: false,
161
198
  unique: false,
162
- persistent: true,
163
- clearOnHide: true,
164
- refreshOn: '',
165
- redrawOn: '',
166
- tableView: false,
167
- dataGridLabel: false,
168
- labelPosition: 'top',
169
- description: '',
170
- errorLabel: '',
171
- tooltip: '',
172
- hideLabel: false,
173
- tabindex: '',
174
- disabled: false,
175
- autofocus: false,
176
- dbIndex: false,
177
- customDefaultValue: '',
178
- calculateValue: '',
179
- calculateServer: false,
180
- widget: null,
181
- attributes: {},
182
- validateOn: 'change',
183
- validate: {
184
- required: false,
185
- custom: '',
186
- customPrivate: false,
187
- strictDateValidation: false,
188
- multiple: false,
189
- unique: false,
190
- },
191
- overlay: {
192
- style: '',
193
- left: '',
194
- top: '',
195
- width: '',
196
- height: '',
197
- },
198
- allowCalculateOverride: false,
199
- encrypted: false,
200
- showCharCount: false,
201
- showWordCount: false,
202
- properties: {},
203
- allowMultipleMasks: false,
204
- addons: [],
205
- id: 'eahbwo',
206
199
  },
207
- ], instance, {
208
- ...objectInputCommonProps,
209
- defaultPages: allDefaultPages,
210
- navigateTo,
211
- apiServices,
212
- user: userAccount,
213
- }, undefined, undefined, undefined, undefined, undefined, undefined, !!closeModal, fieldHeight, richTextEditor)),
214
- BottomButtons,
215
- ]);
200
+ overlay: {
201
+ style: '',
202
+ left: '',
203
+ top: '',
204
+ width: '',
205
+ height: '',
206
+ },
207
+ allowCalculateOverride: false,
208
+ encrypted: false,
209
+ showCharCount: false,
210
+ showWordCount: false,
211
+ properties: {},
212
+ allowMultipleMasks: false,
213
+ addons: [],
214
+ id: 'eahbwo',
215
+ },
216
+ ], instance, {
217
+ ...objectInputCommonProps,
218
+ defaultPages: allDefaultPages,
219
+ navigateTo,
220
+ apiServices,
221
+ user: userAccount,
222
+ }, undefined, undefined, undefined, undefined, undefined, undefined, !!closeModal, fieldHeight, richTextEditor);
223
+ if (!hideButtons) {
224
+ components.push(BottomButtons);
225
+ }
226
+ // this condition is used for the delete action's modal form
227
+ setComponentProps(components);
216
228
  }
217
229
  }
218
230
  else if (object?.properties) {
219
231
  // If form.io form is not configured and no inputProperties are
220
232
  // set, use object properties to build form.
221
233
  const propertiesInForm = object.properties.filter((prop) => prop.id !== associatedObject?.propertyId && (action?.type !== 'create' || prop.type !== 'document'));
222
- setComponentProps([
223
- ...buildComponentPropsFromObjectProperties(propertiesInForm, object.id, instance, {
224
- ...objectInputCommonProps,
225
- defaultPages: allDefaultPages,
226
- navigateTo,
227
- apiServices,
228
- user,
229
- }, !!action, undefined, isReadOnly, queryAddresses, !!closeModal, fieldHeight, richTextEditor),
230
- !isReadOnly && BottomButtons,
231
- ]);
234
+ const components = buildComponentPropsFromObjectProperties(propertiesInForm, object.id, instance, {
235
+ ...objectInputCommonProps,
236
+ defaultPages: allDefaultPages,
237
+ navigateTo,
238
+ apiServices,
239
+ user,
240
+ }, !!action, undefined, isReadOnly, queryAddresses, !!closeModal, fieldHeight, richTextEditor);
241
+ if (!isReadOnly && !hideButtons) {
242
+ components.push(BottomButtons);
243
+ }
244
+ setComponentProps(components);
232
245
  }
233
246
  else if (document) {
234
247
  const documentProperties = toPairs(flatten(document ?? {}));
235
- setComponentProps([
236
- ...buildComponentPropsFromDocumentProperties(documentProperties, isReadOnly, undefined, fieldHeight),
237
- !isReadOnly && BottomButtons,
238
- ]);
248
+ const components = buildComponentPropsFromDocumentProperties(documentProperties, isReadOnly, undefined, fieldHeight);
249
+ if (!isReadOnly && !hideButtons) {
250
+ components.push(BottomButtons);
251
+ }
252
+ setComponentProps(components);
239
253
  }
240
254
  };
241
255
  const uploadDocuments = async (files, metadata) => {
@@ -427,7 +441,11 @@ export function Form(props) {
427
441
  ],
428
442
  };
429
443
  return (React.createElement(Box, null,
430
- componentProps.length ? (React.createElement(FormIO, { key: closeModal ? undefined : formKey, form: {
444
+ componentProps.length ? (React.createElement(FormIO
445
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
446
+ , {
447
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
448
+ onChange: (e) => setFormData(e.data), key: closeModal ? undefined : formKey, form: {
431
449
  display: 'form',
432
450
  components: componentProps,
433
451
  }, formReady: handleFormReady })) : (React.createElement(Box, null,
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { ObjectProperty } from '../../../../types';
2
3
  type FormComponentWrapperProps = {
3
4
  inputId: string;
4
5
  label: string;
@@ -17,6 +18,10 @@ type FormComponentWrapperProps = {
17
18
  viewOnly: boolean;
18
19
  children: React.ReactNode;
19
20
  key: string;
21
+ displayOption?: 'radioButton' | 'dropdown' | 'dialogBox';
22
+ onChange?: (key: string, value: unknown) => void;
23
+ property?: ObjectProperty;
24
+ readOnly?: boolean;
20
25
  };
21
26
  /**
22
27
  * A component that wraps a FormField and adds a label,
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
- import { ErrorRounded, Help } from '../../../../icons';
2
+ import { ErrorRounded, Help, HighlightOffOutlined } from '../../../../icons';
3
3
  import { IconButton, Tooltip, Typography } from '../../../core';
4
4
  import { Box } from '../../../layout';
5
5
  const underFieldStyles = {
@@ -13,6 +13,11 @@ const descriptionStyles = {
13
13
  whiteSpace: 'normal',
14
14
  paddingBottom: '4px',
15
15
  };
16
+ const clearBtnStyles = {
17
+ color: '#637381',
18
+ fontSize: '1.2rem',
19
+ marginBottom: '2px',
20
+ };
16
21
  const PrefixSuffix = (props) => {
17
22
  const { prefix, suffix, height } = props;
18
23
  const text = prefix || suffix;
@@ -46,7 +51,7 @@ const PrefixSuffix = (props) => {
46
51
  * description, tooltip, prefix, suffix and word/char counts
47
52
  */
48
53
  export const FormComponentWrapper = (props) => {
49
- const { inputId, label, description, tooltip, prefix, suffix, value, validate, errorMessage, showCharCount, type, viewOnly, children, } = props;
54
+ const { inputId, label, description, tooltip, prefix, suffix, value, validate, errorMessage, showCharCount, type, viewOnly, children, displayOption, onChange, property, readOnly, } = props;
50
55
  const [fieldHeight, setFieldHeight] = useState(40);
51
56
  const { maxLength } = validate;
52
57
  const fieldRef = useRef(null);
@@ -62,7 +67,7 @@ export const FormComponentWrapper = (props) => {
62
67
  charCount = maxLength - charCount;
63
68
  return (React.createElement(Box, null,
64
69
  React.createElement(Box, { sx: { padding: '10px 0' } },
65
- React.createElement(Typography, { variant: "body2", color: viewOnly ? 'textSecondary' : 'textPrimary', component: "label", htmlFor: inputId },
70
+ React.createElement(Typography, { variant: "body2", color: viewOnly ? 'textSecondary' : 'textPrimary', component: "label", htmlFor: inputId, sx: { ...(displayOption === 'radioButton' && value && { marginRight: '8px' }) } },
66
71
  label,
67
72
  validate.required ? (React.createElement(Typography, { component: 'span', sx: { color: 'red', fontSize: '12px' } },
68
73
  ` *`,
@@ -70,7 +75,13 @@ export const FormComponentWrapper = (props) => {
70
75
  tooltip && (React.createElement(Tooltip, { placement: "right", title: tooltip },
71
76
  React.createElement(IconButton, null,
72
77
  React.createElement(Help, { sx: { fontSize: '14px' } }))))),
73
- React.createElement(Typography, { variant: "caption", sx: descriptionStyles }, description),
78
+ displayOption === 'radioButton' && onChange && !viewOnly && !readOnly && value && (React.createElement(Tooltip, { title: `Clear` },
79
+ React.createElement("span", null,
80
+ React.createElement(IconButton, { "aria-label": `Clear`, sx: { padding: '0px' }, onClick: () => {
81
+ property && onChange(property.id, '');
82
+ } },
83
+ React.createElement(HighlightOffOutlined, { sx: clearBtnStyles }))))),
84
+ React.createElement(Typography, { variant: "caption", sx: { ...descriptionStyles, ...(displayOption === 'radioButton' && { display: 'flex' }) } }, description),
74
85
  React.createElement(Box, { sx: { display: 'flex', flexDirection: 'row' } },
75
86
  React.createElement(PrefixSuffix, { prefix: prefix, height: fieldHeight }),
76
87
  React.createElement(Box, { sx: { width: '100%', paddingTop: '6px' } }, children),
@@ -10,6 +10,7 @@ type FormFieldComponentProps = Omit<BaseFormComponentProps, 'property'> & {
10
10
  addressPropertyId?: string;
11
11
  isAddressLine1?: boolean;
12
12
  initialValue?: string;
13
+ displayOption?: 'radioButton' | 'dropdown';
13
14
  };
14
15
  export declare class FormFieldComponent extends ReactComponent {
15
16
  [x: string]: any;
@@ -448,7 +448,7 @@ export class FormFieldComponent extends ReactComponent {
448
448
  * It'll cause issues with: field-level errors not showing up, conditional visibility not working, focus moving out of the form on keypress
449
449
  * Will need to be revisited later. Possibly look into using this.ref */
450
450
  return ReactDOM.render(React.createElement("div", null,
451
- React.createElement(FormComponentWrapper, { ...this.component, inputId: inputId, errorMessage: this.errorMessages(), value: this.dataValue },
451
+ React.createElement(FormComponentWrapper, { ...this.component, inputId: inputId, errorMessage: this.errorMessages(), value: this.dataValue, onChange: this.handleChange },
452
452
  React.createElement(FormField, { onChange: this.handleChange, onBlur: (e) => {
453
453
  // no mask errors when field is empty and not required
454
454
  const componentError = this.root.errors.find((error) => error.component.key === this.component.key);
@@ -78,7 +78,11 @@ export const ObjectPropertyInput = (props) => {
78
78
  updatedFilter = {};
79
79
  }
80
80
  updatedFilter.limit = 100;
81
- updatedFilter.order = 'name ASC';
81
+ const { propertyId, direction } = layout?.sort ?? {
82
+ propertyId: 'name',
83
+ direction: 'asc',
84
+ };
85
+ updatedFilter.order = `${propertyId} ${direction}`;
82
86
  const where = name
83
87
  ? transformToWhere({
84
88
  name: {
@@ -103,7 +107,7 @@ export const ObjectPropertyInput = (props) => {
103
107
  setLoadingOptions(false);
104
108
  }
105
109
  });
106
- }, [relatedObject, setLoadingOptions, setOptions, property, filter]);
110
+ }, [relatedObject, setLoadingOptions, setOptions, property, filter, layout]);
107
111
  useEffect(() => {
108
112
  if (displayOption === 'dropdown') {
109
113
  getDropdownOptions();
@@ -281,7 +285,7 @@ export const ObjectPropertyInput = (props) => {
281
285
  caretColor: 'white',
282
286
  }
283
287
  : {}),
284
- } })), readOnly: !loadingOptions && !canUpdateProperty, error: error }))) : (React.createElement(Box, { sx: {
288
+ } })), readOnly: !loadingOptions && !canUpdateProperty, error: error, sortBy: "NONE" }))) : (React.createElement(Box, { sx: {
285
289
  padding: (instance?.[property.id]?.name ?? selectedInstance?.name)
286
290
  ? '16.5px 14px'
287
291
  : '10.5px 0',
@@ -78,9 +78,13 @@ export const DropdownRepeatableField = (props) => {
78
78
  setLoading(true);
79
79
  const endObjectProperty = middleObject.properties?.find((currProperty) => property.manyToManyPropertyId === currProperty.id);
80
80
  if (endObjectProperty?.objectId) {
81
+ const { propertyId, direction } = layout?.sort ?? {
82
+ propertyId: 'name',
83
+ direction: 'asc',
84
+ };
81
85
  const filter = {
82
86
  limit: 100,
83
- order: 'name ASC',
87
+ order: `${propertyId} ${direction}`,
84
88
  };
85
89
  let searchCriteria = criteria && !isEmpty(criteria) ? transformToWhere(criteria) : {};
86
90
  if (searchedName?.length) {
@@ -106,7 +110,7 @@ export const DropdownRepeatableField = (props) => {
106
110
  });
107
111
  }
108
112
  }
109
- }, [property.objectId, property.manyToManyPropertyId, apiServices, middleObject]);
113
+ }, [property.objectId, property.manyToManyPropertyId, apiServices, middleObject, layout]);
110
114
  const debouncedEndObjectSearch = useCallback(debounce(fetchEndObjectInstances, 500), [fetchEndObjectInstances]);
111
115
  useEffect(() => {
112
116
  debouncedEndObjectSearch(searchValue);
@@ -86,6 +86,7 @@ export const DropdownRepeatableFieldInput = (props) => {
86
86
  setSearchValue(event.target.value);
87
87
  } })),
88
88
  loading: loading,
89
+ sortBy: 'NONE',
89
90
  } }),
90
91
  React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({ isError: snackbarError.isError, showAlert: false }), message: snackbarError.message, error: snackbarError.isError })))) : (React.createElement(Typography, null, selectedOptions && selectedOptions.map((option) => option.label).join(', ')))));
91
92
  };
@@ -1,3 +1,4 @@
1
- import { Form } from './Common';
1
+ import { Form, FormRef } from './Common';
2
2
  export { Form };
3
+ export type { FormRef };
3
4
  export default Form;
@@ -195,7 +195,7 @@ export function convertFormToComponents(entries, parameters, object) {
195
195
  displayOptions?.defaultValue &&
196
196
  typeof displayOptions.defaultValue !== 'string'
197
197
  ? displayOptions.defaultValue?.sortBy
198
- : undefined,
198
+ : displayOptions?.choicesDisplay?.sortBy,
199
199
  orderBy: parameter.type === 'object' &&
200
200
  displayOptions?.defaultValue &&
201
201
  typeof displayOptions.defaultValue !== 'string'
@@ -205,7 +205,11 @@ export function convertFormToComponents(entries, parameters, object) {
205
205
  inputMaskPlacholderChar: displayOptions?.placeholderChar,
206
206
  placeholder: displayOptions?.placeholder,
207
207
  tableView: false,
208
- displayOption: parameter.type === 'object' ? displayOptions?.relatedObjectDisplay : undefined,
208
+ displayOption: parameter.type === 'object'
209
+ ? displayOptions?.relatedObjectDisplay
210
+ : parameter.type === 'string' && parameter.enum
211
+ ? displayOptions?.choicesDisplay?.type
212
+ : undefined,
209
213
  labelPosition: 'top',
210
214
  dataSrc: property.enum?.length ? 'values' : undefined,
211
215
  showCharCount: displayOptions?.charCount,
@@ -367,7 +371,20 @@ export function convertComponentsToForm(components) {
367
371
  ...(component.readOnly ? { readOnly: component.readOnly } : {}),
368
372
  ...(component.mode ? { mode: component.mode } : {}),
369
373
  ...(component.validate?.required ? { required: component.validate?.required } : {}),
370
- ...(component.displayOption ? { relatedObjectDisplay: component.displayOption } : {}),
374
+ ...((component.displayOption || component.sortBy) &&
375
+ component.property?.enum &&
376
+ component.property?.type !== 'array' &&
377
+ component.property?.type !== 'object'
378
+ ? {
379
+ choicesDisplay: {
380
+ ...(component.displayOption ? { type: component.displayOption } : {}),
381
+ ...(component.sortBy ? { sortBy: component.sortBy } : {}),
382
+ },
383
+ }
384
+ : {}),
385
+ ...(component.displayOption && component?.property?.type === 'object'
386
+ ? { relatedObjectDisplay: component.displayOption }
387
+ : {}),
371
388
  ...(component.defaultToCurrentDate ||
372
389
  component.defaultToCurrentTime ||
373
390
  component.initialValue ||
@@ -384,7 +401,7 @@ export function convertComponentsToForm(components) {
384
401
  ...(component.orderBy ? { sortBy: component.orderBy } : {}),
385
402
  }
386
403
  : isArray(component.initialValue)
387
- ? component.initialValue.map((c) => c.value)
404
+ ? component.initialValue.map((c) => typeof c === 'string' ? c : c.value)
388
405
  : component.initialValue?.value
389
406
  ? component.initialValue?.value
390
407
  : component.initialValue,
@@ -28,6 +28,8 @@ export type FormFieldProps = {
28
28
  getOptionLabel?: (option: AutocompleteOption) => string;
29
29
  disableCloseOnSelect?: boolean;
30
30
  additionalProps?: Record<string, unknown>;
31
+ displayOption?: 'dropdown' | 'radioButton';
32
+ sortBy?: 'ASC' | 'DESC' | 'NONE';
31
33
  };
32
34
  declare const FormField: (props: FormFieldProps) => JSX.Element;
33
35
  export default FormField;
@@ -8,7 +8,7 @@ import InputFieldComponent from './InputFieldComponent/InputFieldComponent';
8
8
  import Select from './Select/Select';
9
9
  import TimePickerSelect from './TimePickerSelect/TimePickerSelect';
10
10
  const FormField = (props) => {
11
- const { id, defaultValue, error, onChange, property, readOnly, selectOptions, required, size, placeholder, errorMessage, onBlur, mask, max, min, isMultiLineText, rows, inputMaskPlaceholderChar, queryAddresses, isOptionEqualToValue, renderOption, disableCloseOnSelect, getOptionLabel, additionalProps, } = props;
11
+ const { id, defaultValue, error, onChange, property, readOnly, selectOptions, required, size, placeholder, errorMessage, onBlur, mask, max, min, isMultiLineText, rows, inputMaskPlaceholderChar, queryAddresses, isOptionEqualToValue, renderOption, disableCloseOnSelect, getOptionLabel, additionalProps, displayOption, sortBy, } = props;
12
12
  let control;
13
13
  const commonProps = {
14
14
  id: id ?? property.id,
@@ -30,6 +30,8 @@ const FormField = (props) => {
30
30
  getOptionLabel,
31
31
  disableCloseOnSelect,
32
32
  additionalProps,
33
+ displayOption,
34
+ sortBy,
33
35
  };
34
36
  if (queryAddresses) {
35
37
  control = (React.createElement(AddressFieldComponent, { ...commonProps, mask: mask, inputMaskPlaceholderChar: inputMaskPlaceholderChar, isMultiLineText: isMultiLineText, rows: rows, queryAddresses: queryAddresses }));
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect, useState } from 'react';
2
- import { Autocomplete, TextField } from '../../../core';
2
+ import { Autocomplete, FormControl, FormControlLabel, Radio, RadioGroup, TextField, } from '../../../core';
3
3
  import InputFieldComponent from '../InputFieldComponent/InputFieldComponent';
4
4
  const Select = (props) => {
5
- const { id, property, defaultValue, error, errorMessage, onBlur, readOnly, selectOptions, required, size, isOptionEqualToValue, renderOption, getOptionLabel, disableCloseOnSelect, additionalProps, } = props;
5
+ const { id, property, defaultValue, error, errorMessage, onBlur, readOnly, selectOptions, required, size, isOptionEqualToValue, renderOption, getOptionLabel, disableCloseOnSelect, additionalProps, displayOption, sortBy, } = props;
6
6
  const [value, setValue] = useState(defaultValue);
7
7
  const [inputValue, setInputValue] = useState('');
8
8
  useEffect(() => {
@@ -28,10 +28,34 @@ const Select = (props) => {
28
28
  setInputValue(selectValue);
29
29
  }
30
30
  };
31
- return readOnly ? (React.createElement(InputFieldComponent, { ...props })) : (React.createElement(Autocomplete, { multiple: property?.type === 'array' ? true : false, id: id, renderInput: (params) => (React.createElement(TextField, { ...params, value: value, fullWidth: true, onBlur: onBlur })), value: value ?? (property?.type === 'array' ? [] : undefined), onChange: handleChange, options: selectOptions ?? property?.enum ?? [], disableClearable: true, inputValue: inputValue ?? '', error: error, errorMessage: errorMessage, required: required, onInputChange: handleInputValueChange, size: size, isOptionEqualToValue: isOptionEqualToValue
31
+ const sortedOptions = (() => {
32
+ if (!selectOptions)
33
+ return [];
34
+ const options = [...selectOptions];
35
+ switch (sortBy) {
36
+ case 'NONE':
37
+ return options;
38
+ case 'DESC':
39
+ return options.sort((a, b) => typeof a === 'string' ? b.localeCompare(a) : b.label.localeCompare(a.label));
40
+ default:
41
+ return options.sort((a, b) => typeof a === 'string' ? a.localeCompare(b) : a.label.localeCompare(b.label));
42
+ }
43
+ })();
44
+ return readOnly ? (React.createElement(InputFieldComponent, { ...props })) : displayOption === 'radioButton' ? (React.createElement(FormControl, { error: error, required: required },
45
+ React.createElement(RadioGroup, { name: `radioGroup-${id}`, value: value, onChange: handleChange, sx: { paddingLeft: '12px' } }, sortedOptions.map((option, index) => (React.createElement(FormControlLabel, { key: index, value: typeof option === 'string' ? option : option.value, control: React.createElement(Radio, { size: "small", sx: {
46
+ ...(size === 'small' && { paddingTop: '2px', paddingBottom: '2px' }),
47
+ ...(error && {
48
+ color: '#FF4842',
49
+ '&.Mui-checked': { color: '#FF4842' },
50
+ }),
51
+ } }), label: typeof option === 'string' ? option : option.label })))))) : (React.createElement(Autocomplete, { multiple: property?.type === 'array' ? true : false, id: id, sortBy: sortBy, renderInput: (params) => (React.createElement(TextField, { ...params, value: value, fullWidth: true, onBlur: onBlur })), value: value ?? (property?.type === 'array' ? [] : undefined), onChange: handleChange, options: selectOptions ?? property?.enum ?? [], inputValue: inputValue ?? '', error: error, errorMessage: errorMessage, required: required, onInputChange: handleInputValueChange, size: size, isOptionEqualToValue: isOptionEqualToValue
32
52
  ? (option, value) => isOptionEqualToValue(option, value)
33
53
  : undefined, getOptionLabel: getOptionLabel ? (option) => getOptionLabel(option) : undefined, renderOption: renderOption
34
54
  ? (props, option, state) => renderOption(props, option, state)
35
- : undefined, disableCloseOnSelect: disableCloseOnSelect, ...(additionalProps ?? {}) }));
55
+ : undefined, disableCloseOnSelect: disableCloseOnSelect, sx: {
56
+ '& button.MuiButtonBase-root': {
57
+ visibility: 'visible',
58
+ },
59
+ }, ...(additionalProps ?? {}) }));
36
60
  };
37
61
  export default Select;
@@ -32,6 +32,20 @@ describe('Single select', () => {
32
32
  await screen.findByRole('option', { name: 'option 2' });
33
33
  expect(screen.queryByRole('option', { name: 'something different' })).not.toBeInTheDocument();
34
34
  });
35
+ test.each([
36
+ { sortBy: 'ASC', expectedValues: ['option 1', 'option 2', 'option 3'] },
37
+ { sortBy: 'NONE', expectedValues: ['option 2', 'option 1', 'option 3'] },
38
+ { sortBy: 'DESC', expectedValues: ['option 3', 'option 2', 'option 1'] },
39
+ ])('shows options in $sortBy order as dropdown display', async ({ sortBy, expectedValues }) => {
40
+ const options = ['option 2', 'option 1', 'option 3'];
41
+ render(React.createElement(Select, { id: "testSelect", property: choiceProperty, selectOptions: options, displayOption: 'dropdown', sortBy: sortBy, onChange: jest.fn() }));
42
+ const user = userEvent.setup();
43
+ const input = screen.getByRole('combobox');
44
+ await user.click(input);
45
+ const allOptions = screen.getAllByRole('option');
46
+ const optionLabels = allOptions.map((option) => option.textContent);
47
+ expect(optionLabels).toEqual(expectedValues);
48
+ });
35
49
  });
36
50
  describe('Multi select', () => {
37
51
  // Right now an object property is required for this to function, but eventually this should go
@@ -57,3 +71,30 @@ describe('Multi select', () => {
57
71
  expect(onChangeMock).lastCalledWith('multiSelect', ['option 2', 'option 3'], multiChoiceProperty);
58
72
  });
59
73
  });
74
+ describe('Radio Single select', () => {
75
+ const choiceProperty = {
76
+ id: 'selectOptions',
77
+ name: 'Select Options',
78
+ type: 'choices',
79
+ };
80
+ test('returns selected radio option', async () => {
81
+ const user = userEvent.setup();
82
+ const onChangeMock = jest.fn((name, value, property) => { });
83
+ const options = ['option 1', 'option 2', 'option 3'];
84
+ render(React.createElement(Select, { id: "testSelect", property: choiceProperty, selectOptions: options, displayOption: 'radioButton', sortBy: 'ASC', onChange: onChangeMock }));
85
+ const option2 = await screen.findByRole('radio', { name: 'option 2' });
86
+ await user.click(option2);
87
+ expect(onChangeMock).toBeCalledWith('selectOptions', expect.stringContaining('option 2'), choiceProperty);
88
+ });
89
+ test.each([
90
+ { sortBy: 'ASC', expectedValues: ['option 1', 'option 2', 'option 3'] },
91
+ { sortBy: 'NONE', expectedValues: ['option 2', 'option 1', 'option 3'] },
92
+ { sortBy: 'DESC', expectedValues: ['option 3', 'option 2', 'option 1'] },
93
+ ])('shows options in $sortBy order as radio display', async ({ sortBy, expectedValues }) => {
94
+ const options = ['option 2', 'option 1', 'option 3'];
95
+ render(React.createElement(Select, { id: "testSelect", property: choiceProperty, selectOptions: options, displayOption: 'radioButton', sortBy: sortBy, onChange: jest.fn() }));
96
+ const radioButtons = screen.getAllByRole('radio');
97
+ const radioValues = radioButtons.map((radioButton) => radioButton.value);
98
+ expect(radioValues).toEqual(expectedValues);
99
+ });
100
+ });
@@ -0,0 +1,4 @@
1
+ import { TextFieldProps } from '@mui/material';
2
+ import { FC } from 'react';
3
+ declare const OverflowTextField: FC<TextFieldProps>;
4
+ export default OverflowTextField;
@@ -0,0 +1,13 @@
1
+ import { TextField, Tooltip } from '@mui/material';
2
+ import React, { useRef, useState } from 'react';
3
+ const OverflowTextField = (props) => {
4
+ const { value, ...rest } = props;
5
+ const inputRef = useRef(null);
6
+ const [isOpen, setIsOpen] = useState(false);
7
+ const textField = (React.createElement(TextField, { ...rest, value: value, InputProps: {
8
+ ...rest.InputProps,
9
+ inputRef: inputRef,
10
+ } }));
11
+ return (React.createElement(Tooltip, { open: isOpen, onOpen: () => inputRef.current && inputRef.current.scrollWidth > inputRef.current.clientWidth && setIsOpen(true), onClose: () => setIsOpen(false), title: value }, textField));
12
+ };
13
+ export default OverflowTextField;
@@ -0,0 +1,2 @@
1
+ import OverflowTextField from './OverflowTextField';
2
+ export { OverflowTextField };
@@ -0,0 +1,2 @@
1
+ import OverflowTextField from './OverflowTextField';
2
+ export { OverflowTextField };