@evoke-platform/ui-components 1.1.0-testing.6 → 1.1.0-testing.8
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/core/DateTimePicker/DateTimePicker.js +7 -1
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +2 -2
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +20 -14
- package/dist/published/components/custom/Form/Common/FormComponentWrapper.d.ts +5 -0
- package/dist/published/components/custom/Form/Common/FormComponentWrapper.js +15 -4
- package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.d.ts +1 -0
- package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.js +1 -1
- package/dist/published/components/custom/Form/utils.js +20 -3
- package/dist/published/components/custom/FormField/FormField.d.ts +2 -0
- package/dist/published/components/custom/FormField/FormField.js +3 -1
- package/dist/published/components/custom/FormField/Select/Select.js +28 -4
- package/dist/published/components/custom/FormField/Select/Select.test.js +41 -0
- package/dist/published/stories/FormField.stories.js +2 -0
- package/package.json +1 -1
@@ -24,6 +24,12 @@ const DateTimePicker = (props) => {
|
|
24
24
|
handleChange(newValue, keyboardInputValue);
|
25
25
|
};
|
26
26
|
return (React.createElement(UIThemeProvider, null,
|
27
|
-
React.createElement(MUIDateTimePicker, { value: value, onChange: onChange, renderInput: (params) => React.createElement(TextField, { ...params }),
|
27
|
+
React.createElement(MUIDateTimePicker, { value: value, onChange: onChange, renderInput: (params) => React.createElement(TextField, { ...params }), PaperProps: {
|
28
|
+
sx: {
|
29
|
+
'&.MuiPickersPopper-paper': {
|
30
|
+
borderRadius: '12px',
|
31
|
+
},
|
32
|
+
},
|
33
|
+
}, ...rest })));
|
28
34
|
};
|
29
35
|
export default DateTimePicker;
|
@@ -198,7 +198,7 @@ const customSelector = (props) => {
|
|
198
198
|
};
|
199
199
|
return (React.createElement(React.Fragment, null, isTreeViewEnabled ? (React.createElement(PropertyTree, { value: val ?? value, rootObject: object, fetchObject: fetchObject, handleTreePropertySelect: handleTreePropertySelect })) : (React.createElement(Autocomplete, { options: opts, value: val ?? null, getOptionLabel: (option) => {
|
200
200
|
if (typeof option === 'string') {
|
201
|
-
return opts.find((o) => option === o.name)?.label ||
|
201
|
+
return opts.find((o) => option === o.name)?.label || option;
|
202
202
|
}
|
203
203
|
return option.label;
|
204
204
|
}, isOptionEqualToValue: (option, value) => {
|
@@ -215,7 +215,7 @@ const customSelector = (props) => {
|
|
215
215
|
}, renderInput: (params) => (React.createElement(TextField, { ...params, placeholder: placeholder, size: "small", inputProps: {
|
216
216
|
...params.inputProps,
|
217
217
|
'aria-label': placeholder,
|
218
|
-
} })), sx: { width: width, background: '#fff' }, disableClearable: true, readOnly: readOnly }))));
|
218
|
+
} })), sx: { width: width, background: readOnly ? '#f4f6f8' : '#fff' }, disableClearable: true, readOnly: readOnly }))));
|
219
219
|
};
|
220
220
|
const customCombinator = (props) => {
|
221
221
|
const { value, handleOnChange, context, level, path } = props;
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import { Instant, LocalDate, LocalDateTime, LocalTime, ZoneId } from '@js-joda/core';
|
2
2
|
import { ClearRounded } from '@mui/icons-material';
|
3
3
|
import { Box, darken, lighten, styled } from '@mui/material';
|
4
|
-
import {
|
4
|
+
import { TimePicker } from '@mui/x-date-pickers';
|
5
5
|
import React, { useEffect, useRef, useState } from 'react';
|
6
|
-
import { Autocomplete, Chip, DatePicker, LocalizationProvider, Menu, MenuItem, TextField, Typography, } from '../../core';
|
6
|
+
import { Autocomplete, Chip, DatePicker, DateTimePicker, LocalizationProvider, Menu, MenuItem, TextField, Typography, } from '../../core';
|
7
7
|
import { NumericFormat } from '../FormField/InputFieldComponent';
|
8
8
|
const GroupHeader = styled('div')(({ theme }) => ({
|
9
9
|
position: 'sticky',
|
@@ -43,6 +43,16 @@ const ValueEditor = (props) => {
|
|
43
43
|
const isPresetValueSelected = presetValues && typeof value === 'string' && isPresetValue(value);
|
44
44
|
const presetDisplayValue = presetValues?.find((option) => option.value.name === value)?.label ?? '';
|
45
45
|
let readOnly = false;
|
46
|
+
if (context.disabledCriteria) {
|
47
|
+
readOnly =
|
48
|
+
Object.entries(context.disabledCriteria.criteria).some(([key, value]) => key === rule.field && value === rule.value && rule.operator === '=') && level === context.disabledCriteria.level;
|
49
|
+
}
|
50
|
+
const styles = {
|
51
|
+
input: {
|
52
|
+
width: '33%',
|
53
|
+
background: readOnly ? '#f4f6f8' : '#fff',
|
54
|
+
},
|
55
|
+
};
|
46
56
|
useEffect(() => {
|
47
57
|
if (!['in', 'notIn'].includes(operator) && Array.isArray(value)) {
|
48
58
|
handleOnChange('');
|
@@ -51,10 +61,6 @@ const ValueEditor = (props) => {
|
|
51
61
|
handleOnChange([]);
|
52
62
|
}
|
53
63
|
}, [operator]);
|
54
|
-
if (context.disabledCriteria) {
|
55
|
-
readOnly =
|
56
|
-
Object.entries(context.disabledCriteria.criteria).some(([key, value]) => key === rule.field && value === rule.value && rule.operator === '=') && level === context.disabledCriteria.level;
|
57
|
-
}
|
58
64
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
59
65
|
const onClick = (e) => {
|
60
66
|
// if property is date and date picker is open, don't open preset values
|
@@ -93,11 +99,11 @@ const ValueEditor = (props) => {
|
|
93
99
|
if (inputType === 'date') {
|
94
100
|
// date editor
|
95
101
|
return (React.createElement(LocalizationProvider, null,
|
96
|
-
React.createElement(DatePicker, { inputRef: inputRef, disabled: disabled, value: disabled ? null : value, onChange: handleOnChange, onClose: onClose, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx:
|
102
|
+
React.createElement(DatePicker, { inputRef: inputRef, disabled: disabled, value: disabled ? null : value, onChange: handleOnChange, onClose: onClose, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx: styles.input })), readOnly: readOnly })));
|
97
103
|
}
|
98
104
|
else if (inputType === 'time') {
|
99
105
|
return (React.createElement(LocalizationProvider, null,
|
100
|
-
React.createElement(TimePicker, { inputRef: inputRef, disabled: disabled, value: disabled || !value ? null : value, onChange: handleOnChange, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx:
|
106
|
+
React.createElement(TimePicker, { inputRef: inputRef, disabled: disabled, value: disabled || !value ? null : value, onChange: handleOnChange, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx: styles.input })), readOnly: readOnly })));
|
101
107
|
}
|
102
108
|
else if (inputType === 'date-time') {
|
103
109
|
const dateTimeValue = parseISOStringToLocalDateTime(value);
|
@@ -118,7 +124,7 @@ const ValueEditor = (props) => {
|
|
118
124
|
handleOnChange(new Date(localDateTime.toString()).toISOString());
|
119
125
|
}, onClose: onClose, PopperProps: {
|
120
126
|
anchorEl,
|
121
|
-
}, renderInput: (params) => (React.createElement(Box, { sx:
|
127
|
+
}, renderInput: (params) => (React.createElement(Box, { sx: styles.input, ref: setAnchorEl },
|
122
128
|
React.createElement(TextField, { ...params, disabled: disabled, onClick: onClick, placeholder: "Value", size: "small", inputRef: inputRef }))), readOnly: readOnly })));
|
123
129
|
}
|
124
130
|
else if (inputType === 'number' || inputType === 'integer') {
|
@@ -133,7 +139,7 @@ const ValueEditor = (props) => {
|
|
133
139
|
handleOnChange(uniqueSelections.length ? uniqueSelections : '');
|
134
140
|
},
|
135
141
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
136
|
-
isOptionEqualToValue: (option, value) => option === value, renderInput: (params) => (React.createElement(TextField, { label: params.label, ...params, size: "small"
|
142
|
+
isOptionEqualToValue: (option, value) => option === value, renderInput: (params) => (React.createElement(TextField, { label: params.label, ...params, size: "small" })), groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sx: styles.input, readOnly: readOnly }));
|
137
143
|
}
|
138
144
|
else {
|
139
145
|
return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => {
|
@@ -145,7 +151,7 @@ const ValueEditor = (props) => {
|
|
145
151
|
}
|
146
152
|
}, ...(inputType === 'number'
|
147
153
|
? { InputProps: { inputComponent: NumericFormat } }
|
148
|
-
: { type: 'number' }), placeholder: "Value", size: "small", onClick: onClick, sx:
|
154
|
+
: { type: 'number' }), placeholder: "Value", size: "small", onClick: onClick, sx: styles.input, readOnly: readOnly }));
|
149
155
|
}
|
150
156
|
}
|
151
157
|
else {
|
@@ -187,7 +193,7 @@ const ValueEditor = (props) => {
|
|
187
193
|
}
|
188
194
|
}, onInputChange: (event, newInputValue) => {
|
189
195
|
setInputValue(newInputValue);
|
190
|
-
}, inputValue: inputValue, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params?.label, ...params, size: "small"
|
196
|
+
}, inputValue: inputValue, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params?.label, ...params, size: "small" })),
|
191
197
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
192
198
|
getOptionLabel: (option) => {
|
193
199
|
if (typeof option === 'string') {
|
@@ -204,10 +210,10 @@ const ValueEditor = (props) => {
|
|
204
210
|
else {
|
205
211
|
return option?.label === value?.label;
|
206
212
|
}
|
207
|
-
}, groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx:
|
213
|
+
}, groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx: styles.input, readOnly: readOnly }));
|
208
214
|
}
|
209
215
|
else {
|
210
|
-
return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => handleOnChange(e.target.value), onClick: onClick, placeholder: "Value", size: "small", sx:
|
216
|
+
return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => handleOnChange(e.target.value), onClick: onClick, placeholder: "Value", size: "small", sx: styles.input, readOnly: readOnly }));
|
211
217
|
}
|
212
218
|
}
|
213
219
|
};
|
@@ -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(
|
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);
|
@@ -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
|
-
:
|
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'
|
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
|
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 ||
|
@@ -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
|
-
|
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,
|
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
|
+
});
|