@orchestrator-ui/orchestrator-ui-components 5.4.0 → 5.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +6 -6
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +258 -21
- package/dist/index.js +794 -533
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/WfoForms/formFields/SelectField/SelectField.tsx +2 -1
- package/src/components/WfoForms/formFields/deprecated/ImsPortIdField.tsx +2 -1
- package/src/components/WfoForms/formFields/deprecated/IpPrefixTableField.tsx +2 -1
- package/src/components/WfoForms/formFields/deprecated/SplitPrefix.tsx +1 -2
- package/src/components/WfoForms/formFields/deprecated/SubscriptionField.tsx +2 -1
- package/src/components/WfoForms/formFields/types.ts +0 -10
- package/src/components/WfoPydanticForm/RenderFormErrors.tsx +1 -1
- package/src/components/WfoPydanticForm/WfoPydanticForm.tsx +68 -28
- package/src/components/WfoPydanticForm/fields/WfoArrayField/WfoArrayField.tsx +13 -5
- package/src/components/WfoPydanticForm/fields/WfoArrayField/index.ts +1 -1
- package/src/components/WfoPydanticForm/fields/WfoDropdown.tsx +25 -0
- package/src/components/WfoPydanticForm/fields/WfoMultiCheckboxField.tsx +68 -0
- package/src/components/WfoPydanticForm/fields/WfoObjectField/WfoObjectField.tsx +12 -1
- package/src/components/WfoPydanticForm/fields/WfoObjectField/index.ts +1 -1
- package/src/components/WfoPydanticForm/fields/WfoReactSelect/WfoReactSelect.tsx +100 -0
- package/src/components/WfoPydanticForm/fields/WfoReactSelect/index.ts +2 -0
- package/src/components/WfoPydanticForm/fields/WfoReactSelect/styles.ts +70 -0
- package/src/components/WfoPydanticForm/fields/index.ts +3 -0
- package/src/configuration/version.ts +1 -1
- package/src/messages/en-GB.json +2 -1
- package/src/messages/nl-NL.json +2 -1
- package/src/rtk/endpoints/deprecated/surfSubscriptionDropdownOptions.ts +1 -1
- package/src/rtk/endpoints/index.ts +1 -0
- package/src/types/types.ts +5 -0
- package/src/components/WfoPydanticForm/fields/WfoArrayField/arrayFieldStyles.ts +0 -34
- /package/src/components/WfoPydanticForm/fields/WfoObjectField/{getWfoObjectFieldStyles.ts → styles.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orchestrator-ui/orchestrator-ui-components",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Library of UI Components used to display the workflow orchestrator frontend",
|
|
6
6
|
"author": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"next-query-params": "^5.0.0",
|
|
49
49
|
"object-hash": "^3.0.0",
|
|
50
50
|
"prism-themes": "^1.9.0",
|
|
51
|
-
"pydantic-forms": "^0.
|
|
51
|
+
"pydantic-forms": "^0.7.3",
|
|
52
52
|
"react-diff-view": "^3.2.0",
|
|
53
53
|
"react-draggable": "^4.4.6",
|
|
54
54
|
"react-redux": "^9.1.2",
|
|
@@ -22,11 +22,12 @@ import { joinName, useField, useForm } from 'uniforms';
|
|
|
22
22
|
import { EuiFormRow, EuiText } from '@elastic/eui';
|
|
23
23
|
|
|
24
24
|
import { useWithOrchestratorTheme } from '@/hooks';
|
|
25
|
+
import type { Option } from '@/types';
|
|
25
26
|
|
|
26
27
|
import { ListField, ListFieldProps } from '../ListField';
|
|
27
28
|
import { ListItemField } from '../ListItemField';
|
|
28
29
|
import { ListSelectField } from '../ListSelectField';
|
|
29
|
-
import { FieldProps
|
|
30
|
+
import { FieldProps } from '../types';
|
|
30
31
|
import { getSelectFieldStyles } from './styles';
|
|
31
32
|
|
|
32
33
|
export type SelectFieldProps = FieldProps<
|
|
@@ -25,9 +25,10 @@ import {
|
|
|
25
25
|
useFreePortsByNodeSubscriptionIdAndSpeedQuery,
|
|
26
26
|
useGetNodeSubscriptionOptionsQuery,
|
|
27
27
|
} from '@/rtk/endpoints/formFields';
|
|
28
|
+
import type { Option } from '@/types';
|
|
28
29
|
|
|
29
30
|
import { getSelectFieldStyles } from '../SelectField/styles';
|
|
30
|
-
import { FieldProps
|
|
31
|
+
import { FieldProps } from '../types';
|
|
31
32
|
import { imsPortIdFieldStyling } from './ImsPortIdFieldStyling';
|
|
32
33
|
import { ImsPort, NodeSubscriptionOption } from './types';
|
|
33
34
|
|
|
@@ -31,8 +31,9 @@ import {
|
|
|
31
31
|
|
|
32
32
|
import { ipPrefixTableFieldStyling } from '@/components';
|
|
33
33
|
import { useIpBlocksQuery, usePrefixFiltersQuery } from '@/rtk/endpoints/ipam';
|
|
34
|
+
import type { Option } from '@/types';
|
|
34
35
|
|
|
35
|
-
import {
|
|
36
|
+
import { prop } from '../types';
|
|
36
37
|
import { IpBlock, IpPrefix, SortOption } from './types';
|
|
37
38
|
import { ipamStates } from './utils';
|
|
38
39
|
|
|
@@ -21,8 +21,7 @@ import { EuiFlexItem } from '@elastic/eui';
|
|
|
21
21
|
|
|
22
22
|
import { splitPrefixStyling } from '@/components';
|
|
23
23
|
import { useFreeSubnetsQuery } from '@/rtk/endpoints/ipam';
|
|
24
|
-
|
|
25
|
-
import { Option } from '../types';
|
|
24
|
+
import type { Option } from '@/types';
|
|
26
25
|
|
|
27
26
|
interface IProps {
|
|
28
27
|
id: string;
|
|
@@ -35,9 +35,10 @@ import {
|
|
|
35
35
|
|
|
36
36
|
import { useWithOrchestratorTheme } from '@/hooks';
|
|
37
37
|
import { useGetSurfSubscriptionDropdownOptions } from '@/hooks/deprecated/useGetSurfSubcriptionDropdownOptions';
|
|
38
|
+
import type { Option } from '@/types';
|
|
38
39
|
|
|
39
40
|
import { getSelectFieldStyles } from '../SelectField/styles';
|
|
40
|
-
import { FieldProps
|
|
41
|
+
import { FieldProps } from '../types';
|
|
41
42
|
import { subscriptionFieldStyling } from './SubscriptionFieldStyling';
|
|
42
43
|
|
|
43
44
|
declare module 'uniforms' {
|
|
@@ -36,16 +36,6 @@ export interface ContactPerson {
|
|
|
36
36
|
phone?: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export interface Option<Value = string> {
|
|
40
|
-
value: Value;
|
|
41
|
-
label: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface Option<Value = string> {
|
|
45
|
-
value: Value;
|
|
46
|
-
label: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
39
|
export function prop<T, K extends keyof T>(obj: T, key: K): T[K] {
|
|
50
40
|
return obj[key];
|
|
51
41
|
}
|
|
@@ -24,7 +24,7 @@ export const RenderFormErrors = () => {
|
|
|
24
24
|
return (
|
|
25
25
|
<em className="error backend-validation-metadata">
|
|
26
26
|
{rootError && <div>{rootError.msg}</div>}
|
|
27
|
-
{otherErrors?.length &&
|
|
27
|
+
{otherErrors?.length >= 1 &&
|
|
28
28
|
t('inputFieldsHaveValidationErrors', {
|
|
29
29
|
nrOfValidationErrors: otherErrors.length,
|
|
30
30
|
})}
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
PydanticFormLabelProvider,
|
|
10
10
|
} from 'pydantic-forms';
|
|
11
11
|
import {
|
|
12
|
+
Locale,
|
|
12
13
|
PydanticForm,
|
|
13
14
|
PydanticFormFieldFormat,
|
|
14
15
|
PydanticFormFieldType,
|
|
@@ -28,10 +29,11 @@ import {
|
|
|
28
29
|
WfoArrayField,
|
|
29
30
|
WfoCheckbox,
|
|
30
31
|
WfoDivider,
|
|
32
|
+
WfoDropdown,
|
|
31
33
|
WfoInteger,
|
|
32
34
|
WfoLabel,
|
|
35
|
+
WfoMultiCheckboxField,
|
|
33
36
|
WfoObjectField,
|
|
34
|
-
WfoRadio,
|
|
35
37
|
WfoSummary,
|
|
36
38
|
WfoText,
|
|
37
39
|
WfoTextArea,
|
|
@@ -155,16 +157,18 @@ export const useWfoPydanticFormConfig = () => {
|
|
|
155
157
|
},
|
|
156
158
|
},
|
|
157
159
|
{
|
|
158
|
-
id: '
|
|
160
|
+
id: 'dropdown',
|
|
159
161
|
ElementMatch: {
|
|
160
|
-
Element:
|
|
162
|
+
Element: WfoDropdown,
|
|
161
163
|
isControlledElement: true,
|
|
162
164
|
},
|
|
163
165
|
matcher(field) {
|
|
166
|
+
// We are looking for a single value from a set list of options.
|
|
167
|
+
// We are not using a radio button component to maintain being able to deselect options
|
|
164
168
|
return (
|
|
165
169
|
field.type === PydanticFormFieldType.STRING &&
|
|
166
|
-
field.options
|
|
167
|
-
field.options.length
|
|
170
|
+
Array.isArray(field.options) &&
|
|
171
|
+
field.options.length > 0
|
|
168
172
|
);
|
|
169
173
|
},
|
|
170
174
|
},
|
|
@@ -179,6 +183,21 @@ export const useWfoPydanticFormConfig = () => {
|
|
|
179
183
|
},
|
|
180
184
|
validator: zodValidationPresets.integer,
|
|
181
185
|
},
|
|
186
|
+
{
|
|
187
|
+
id: 'multicheckbox',
|
|
188
|
+
ElementMatch: {
|
|
189
|
+
Element: WfoMultiCheckboxField,
|
|
190
|
+
isControlledElement: true,
|
|
191
|
+
},
|
|
192
|
+
matcher(field) {
|
|
193
|
+
return (
|
|
194
|
+
field.type === PydanticFormFieldType.ARRAY &&
|
|
195
|
+
field.options?.length > 0 &&
|
|
196
|
+
field.options?.length <= 5
|
|
197
|
+
);
|
|
198
|
+
},
|
|
199
|
+
validator: zodValidationPresets.multiSelect,
|
|
200
|
+
},
|
|
182
201
|
...currentMatchers
|
|
183
202
|
.filter((matcher) => matcher.id !== 'text')
|
|
184
203
|
.filter((matcher) => matcher.id !== 'array')
|
|
@@ -241,16 +260,19 @@ export const WfoPydanticForm = ({
|
|
|
241
260
|
customTranslations,
|
|
242
261
|
} = useWfoPydanticFormConfig();
|
|
243
262
|
|
|
244
|
-
const onSuccess = (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
263
|
+
const onSuccess = useCallback(
|
|
264
|
+
(_fieldValues: object, req: object) => {
|
|
265
|
+
const request = req as { response: StartProcessResponse };
|
|
266
|
+
const response = request ? request?.response : null;
|
|
267
|
+
if (response?.id) {
|
|
268
|
+
const pfBasePath = isTask ? PATH_TASKS : PATH_WORKFLOWS;
|
|
269
|
+
router.replace(`${pfBasePath}/${response.id}`);
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
[isTask, router],
|
|
273
|
+
);
|
|
252
274
|
|
|
253
|
-
const getPydanticFormProvider = () => {
|
|
275
|
+
const getPydanticFormProvider = useCallback(() => {
|
|
254
276
|
const pydanticFormProvider: PydanticFormApiProvider = async ({
|
|
255
277
|
requestBody = [],
|
|
256
278
|
formKey,
|
|
@@ -294,12 +316,40 @@ export const WfoPydanticForm = ({
|
|
|
294
316
|
};
|
|
295
317
|
|
|
296
318
|
return pydanticFormProvider;
|
|
297
|
-
};
|
|
319
|
+
}, [startProcess, startProcessPayload]);
|
|
298
320
|
|
|
299
|
-
const handleCancel = () => {
|
|
321
|
+
const handleCancel = useCallback(() => {
|
|
300
322
|
const pfBasePath = isTask ? PATH_TASKS : PATH_WORKFLOWS;
|
|
301
323
|
router.replace(pfBasePath);
|
|
302
|
-
};
|
|
324
|
+
}, [isTask, router]);
|
|
325
|
+
|
|
326
|
+
const config = useMemo(() => {
|
|
327
|
+
const getLocale = () => {
|
|
328
|
+
if (router.locale) {
|
|
329
|
+
return router.locale as Locale;
|
|
330
|
+
}
|
|
331
|
+
return Locale.enGB; // Default to enGB if no locale is set
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
apiProvider: getPydanticFormProvider(),
|
|
336
|
+
allowUntouchedSubmit: true,
|
|
337
|
+
footerRenderer: Footer,
|
|
338
|
+
headerRenderer: Header,
|
|
339
|
+
skipSuccessNotice: true,
|
|
340
|
+
componentMatcherExtender: wfoComponentMatcherExtender,
|
|
341
|
+
labelProvider: pydanticLabelProvider,
|
|
342
|
+
rowRenderer: Row,
|
|
343
|
+
customTranslations: customTranslations,
|
|
344
|
+
locale: getLocale(),
|
|
345
|
+
};
|
|
346
|
+
}, [
|
|
347
|
+
customTranslations,
|
|
348
|
+
getPydanticFormProvider,
|
|
349
|
+
pydanticLabelProvider,
|
|
350
|
+
router.locale,
|
|
351
|
+
wfoComponentMatcherExtender,
|
|
352
|
+
]);
|
|
303
353
|
|
|
304
354
|
return (
|
|
305
355
|
<PydanticForm
|
|
@@ -307,17 +357,7 @@ export const WfoPydanticForm = ({
|
|
|
307
357
|
onSuccess={onSuccess}
|
|
308
358
|
onCancel={handleCancel}
|
|
309
359
|
loadingComponent={<WfoLoading />}
|
|
310
|
-
config={
|
|
311
|
-
apiProvider: getPydanticFormProvider(),
|
|
312
|
-
allowUntouchedSubmit: true,
|
|
313
|
-
footerRenderer: Footer,
|
|
314
|
-
headerRenderer: Header,
|
|
315
|
-
skipSuccessNotice: true,
|
|
316
|
-
componentMatcherExtender: wfoComponentMatcherExtender,
|
|
317
|
-
labelProvider: pydanticLabelProvider,
|
|
318
|
-
rowRenderer: Row,
|
|
319
|
-
customTranslations: customTranslations,
|
|
320
|
-
}}
|
|
360
|
+
config={config}
|
|
321
361
|
/>
|
|
322
362
|
);
|
|
323
363
|
};
|
|
@@ -4,6 +4,7 @@ import { useFieldArray } from 'react-hook-form';
|
|
|
4
4
|
import {
|
|
5
5
|
PydanticFormElementProps,
|
|
6
6
|
RenderFields,
|
|
7
|
+
disableField,
|
|
7
8
|
fieldToComponentMatcher,
|
|
8
9
|
itemizeArrayItem,
|
|
9
10
|
usePydanticFormContext,
|
|
@@ -11,9 +12,10 @@ import {
|
|
|
11
12
|
|
|
12
13
|
import { EuiIcon } from '@elastic/eui';
|
|
13
14
|
|
|
14
|
-
import { getWfoArrayFieldStyles } from '@/components';
|
|
15
15
|
import { useOrchestratorTheme } from '@/hooks';
|
|
16
16
|
|
|
17
|
+
import { getWfoArrayFieldStyles } from './styles';
|
|
18
|
+
|
|
17
19
|
export const MinusButton = ({
|
|
18
20
|
index,
|
|
19
21
|
onRemove,
|
|
@@ -65,6 +67,7 @@ export const WfoArrayField = ({
|
|
|
65
67
|
pydanticFormField,
|
|
66
68
|
}: PydanticFormElementProps) => {
|
|
67
69
|
const { config, rhf } = usePydanticFormContext();
|
|
70
|
+
const disabled = pydanticFormField.attributes?.disabled || false;
|
|
68
71
|
const { control } = rhf;
|
|
69
72
|
const { id: arrayName, arrayItem } = pydanticFormField;
|
|
70
73
|
const { minItems, maxItems } = pydanticFormField.validations;
|
|
@@ -75,8 +78,8 @@ export const WfoArrayField = ({
|
|
|
75
78
|
name: arrayName,
|
|
76
79
|
});
|
|
77
80
|
|
|
78
|
-
const showMinus = !minItems || fields.length > minItems;
|
|
79
|
-
const showPlus = !maxItems || fields.length < maxItems;
|
|
81
|
+
const showMinus = (!minItems || fields.length > minItems) && !disabled;
|
|
82
|
+
const showPlus = (!maxItems || fields.length < maxItems) && !disabled;
|
|
80
83
|
|
|
81
84
|
if (!arrayItem) return null;
|
|
82
85
|
|
|
@@ -86,7 +89,12 @@ export const WfoArrayField = ({
|
|
|
86
89
|
);
|
|
87
90
|
|
|
88
91
|
const renderField = (field: Record<'id', string>, index: number) => {
|
|
89
|
-
const
|
|
92
|
+
const itemizedField = itemizeArrayItem(index, arrayItem, arrayName);
|
|
93
|
+
// We have decided - for now - on the convention that all descendants of disabled fields will be disabled as well
|
|
94
|
+
// so we will not displaying any interactive elements inside a disabled element
|
|
95
|
+
const arrayItemField = disabled
|
|
96
|
+
? disableField(itemizedField)
|
|
97
|
+
: itemizedField;
|
|
90
98
|
|
|
91
99
|
return (
|
|
92
100
|
<div key={field.id} css={fieldWrapper}>
|
|
@@ -94,7 +102,7 @@ export const WfoArrayField = ({
|
|
|
94
102
|
pydanticFormComponents={[
|
|
95
103
|
{
|
|
96
104
|
Element: component.Element,
|
|
97
|
-
pydanticFormField:
|
|
105
|
+
pydanticFormField: arrayItemField,
|
|
98
106
|
},
|
|
99
107
|
]}
|
|
100
108
|
extraTriggerFields={[arrayName]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './WfoArrayField';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './styles';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { PydanticFormControlledElement } from 'pydantic-forms';
|
|
4
|
+
|
|
5
|
+
import { WfoReactSelect } from './WfoReactSelect';
|
|
6
|
+
|
|
7
|
+
export const WfoDropdown: PydanticFormControlledElement = ({
|
|
8
|
+
onChange,
|
|
9
|
+
pydanticFormField,
|
|
10
|
+
value,
|
|
11
|
+
}) => {
|
|
12
|
+
const dropDownOptions = pydanticFormField.options.map((option) => ({
|
|
13
|
+
value: option.value,
|
|
14
|
+
label: option.label,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<WfoReactSelect
|
|
19
|
+
options={dropDownOptions}
|
|
20
|
+
onChange={onChange}
|
|
21
|
+
id={pydanticFormField.id}
|
|
22
|
+
value={value}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { PydanticFormControlledElement } from 'pydantic-forms';
|
|
4
|
+
|
|
5
|
+
import { EuiCheckboxGroup } from '@elastic/eui';
|
|
6
|
+
|
|
7
|
+
type idMap = Record<string, boolean>;
|
|
8
|
+
|
|
9
|
+
export const WfoMultiCheckboxField: PydanticFormControlledElement = ({
|
|
10
|
+
pydanticFormField,
|
|
11
|
+
onChange,
|
|
12
|
+
value,
|
|
13
|
+
}) => {
|
|
14
|
+
// See: https://eui.elastic.co/docs/components/forms/selection/checkboxes-and-radios/#checkbox
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
// Setting the ids is handled here to make sure the field repopulates when navigating back from another step
|
|
18
|
+
const getInitialMap = () => {
|
|
19
|
+
const initialIdMap: idMap = (value || []).reduce(
|
|
20
|
+
(idMap: idMap, id: string) => {
|
|
21
|
+
idMap[id] = true;
|
|
22
|
+
return idMap;
|
|
23
|
+
},
|
|
24
|
+
{} as idMap,
|
|
25
|
+
);
|
|
26
|
+
return initialIdMap;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
setCheckboxIdToSelectedMap(getInitialMap());
|
|
30
|
+
}, [value]);
|
|
31
|
+
|
|
32
|
+
const [checkboxIdToSelectedMap, setCheckboxIdToSelectedMap] = useState<
|
|
33
|
+
Record<string, boolean>
|
|
34
|
+
>({});
|
|
35
|
+
|
|
36
|
+
const { options, id } = pydanticFormField;
|
|
37
|
+
|
|
38
|
+
const checkboxes = options.map((option, index) => ({
|
|
39
|
+
label: option.label,
|
|
40
|
+
id: option.value,
|
|
41
|
+
'data-test-id': `${id}-${index}`,
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const handleCheckboxChange = (optionId: string) => {
|
|
45
|
+
const newCheckboxIdToSelectedMap = {
|
|
46
|
+
...checkboxIdToSelectedMap,
|
|
47
|
+
...{
|
|
48
|
+
[optionId]: !checkboxIdToSelectedMap[optionId],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const selectedIds = Object.keys(newCheckboxIdToSelectedMap).filter(
|
|
53
|
+
(key) => newCheckboxIdToSelectedMap[key],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
onChange(selectedIds);
|
|
57
|
+
setCheckboxIdToSelectedMap(newCheckboxIdToSelectedMap);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<EuiCheckboxGroup
|
|
62
|
+
options={checkboxes}
|
|
63
|
+
idToSelectedMap={checkboxIdToSelectedMap}
|
|
64
|
+
onChange={(id) => handleCheckboxChange(id)}
|
|
65
|
+
data-testid={id}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -3,24 +3,35 @@ import React from 'react';
|
|
|
3
3
|
import {
|
|
4
4
|
PydanticFormElementProps,
|
|
5
5
|
RenderFields,
|
|
6
|
+
disableField,
|
|
6
7
|
getPydanticFormComponents,
|
|
7
8
|
usePydanticFormContext,
|
|
8
9
|
} from 'pydantic-forms';
|
|
9
10
|
|
|
10
11
|
import { EuiFlexGroup } from '@elastic/eui';
|
|
11
12
|
|
|
12
|
-
import { getWfoObjectFieldStyles } from './
|
|
13
|
+
import { getWfoObjectFieldStyles } from './styles';
|
|
13
14
|
|
|
14
15
|
export const WfoObjectField = ({
|
|
15
16
|
pydanticFormField,
|
|
16
17
|
}: PydanticFormElementProps) => {
|
|
17
18
|
const { config } = usePydanticFormContext();
|
|
19
|
+
const disabled = pydanticFormField.attributes?.disabled || false;
|
|
18
20
|
const { wfoObjectFieldStyles } = getWfoObjectFieldStyles();
|
|
19
21
|
const components = getPydanticFormComponents(
|
|
20
22
|
pydanticFormField.properties || {},
|
|
21
23
|
config?.componentMatcherExtender,
|
|
22
24
|
);
|
|
23
25
|
|
|
26
|
+
// We have decided - for now - on the convention that all descendants of disabled fields will be disabled as well
|
|
27
|
+
// so we will not displaying any interactive elements inside a disabled element
|
|
28
|
+
if (disabled) {
|
|
29
|
+
components.forEach((component) => {
|
|
30
|
+
component.pydanticFormField = disableField(
|
|
31
|
+
component.pydanticFormField,
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
24
35
|
return (
|
|
25
36
|
<EuiFlexGroup
|
|
26
37
|
data-testid={pydanticFormField.id}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './WfoObjectField';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './styles';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactSelect, { components } from 'react-select';
|
|
3
|
+
import type { GroupBase, InputProps } from 'react-select';
|
|
4
|
+
|
|
5
|
+
import { EuiButtonIcon } from '@elastic/eui';
|
|
6
|
+
|
|
7
|
+
import { WfoError } from '@/components';
|
|
8
|
+
import { useWithOrchestratorTheme } from '@/hooks';
|
|
9
|
+
import type { Option } from '@/types';
|
|
10
|
+
|
|
11
|
+
import { getWfoReactSelectStyles } from './styles';
|
|
12
|
+
|
|
13
|
+
interface WfoReactSelectProps<ValueType> {
|
|
14
|
+
options: Option<ValueType>[];
|
|
15
|
+
id: string;
|
|
16
|
+
onChange: (value: ValueType | undefined) => void;
|
|
17
|
+
value: ValueType;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
isLoading?: boolean;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
hasError?: boolean;
|
|
22
|
+
refetch?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const WfoReactSelect = <ValueType,>({
|
|
26
|
+
options,
|
|
27
|
+
id,
|
|
28
|
+
onChange,
|
|
29
|
+
value,
|
|
30
|
+
disabled = false,
|
|
31
|
+
isLoading = false,
|
|
32
|
+
placeholder = 'Select an option',
|
|
33
|
+
hasError = false,
|
|
34
|
+
refetch,
|
|
35
|
+
}: WfoReactSelectProps<ValueType>) => {
|
|
36
|
+
const selectedValue = options.find(
|
|
37
|
+
(option: Option<ValueType>) => option.value === value,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// React select allows callbacks to supply style for innercomponents: https://react-select.com/styles#inner-components
|
|
41
|
+
const {
|
|
42
|
+
reactSelectInnerComponentStyles,
|
|
43
|
+
containerStyle,
|
|
44
|
+
refreshButtonStyle,
|
|
45
|
+
reactSelectStyle,
|
|
46
|
+
} = useWithOrchestratorTheme(getWfoReactSelectStyles);
|
|
47
|
+
|
|
48
|
+
if (hasError) {
|
|
49
|
+
return <WfoError />;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Way to add data-testid based on https://react-select.com/components
|
|
53
|
+
const Input = ({
|
|
54
|
+
...props
|
|
55
|
+
}: InputProps<Option<ValueType>, false, GroupBase<Option<ValueType>>>) => {
|
|
56
|
+
return (
|
|
57
|
+
<components.Input {...props} data-testid={`${id}.search-input`} />
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div css={containerStyle}>
|
|
63
|
+
{refetch && (
|
|
64
|
+
<EuiButtonIcon
|
|
65
|
+
className="reload-subscriptions-icon-button"
|
|
66
|
+
css={refreshButtonStyle}
|
|
67
|
+
aria-label={`refetch-${id}`}
|
|
68
|
+
id={`refresh-icon-${id}`}
|
|
69
|
+
iconType="refresh"
|
|
70
|
+
iconSize="l"
|
|
71
|
+
disabled={disabled}
|
|
72
|
+
onClick={() => {
|
|
73
|
+
refetch();
|
|
74
|
+
}}
|
|
75
|
+
data-testid={`${id}-refresh-button`}
|
|
76
|
+
/>
|
|
77
|
+
)}
|
|
78
|
+
<ReactSelect<Option<ValueType>, false>
|
|
79
|
+
id={id}
|
|
80
|
+
inputId={`${id}.search`}
|
|
81
|
+
onChange={(option) => {
|
|
82
|
+
const selectedValue = option?.value;
|
|
83
|
+
onChange(selectedValue);
|
|
84
|
+
}}
|
|
85
|
+
css={reactSelectStyle}
|
|
86
|
+
isLoading={isLoading}
|
|
87
|
+
styles={reactSelectInnerComponentStyles}
|
|
88
|
+
options={options}
|
|
89
|
+
value={selectedValue || null}
|
|
90
|
+
isSearchable={true}
|
|
91
|
+
isClearable={true}
|
|
92
|
+
placeholder={placeholder}
|
|
93
|
+
isDisabled={disabled}
|
|
94
|
+
components={{
|
|
95
|
+
Input,
|
|
96
|
+
}}
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
|
|
3
|
+
import type { WfoTheme } from '@/hooks';
|
|
4
|
+
|
|
5
|
+
export const getWfoReactSelectStyles = (wfoTheme: WfoTheme) => {
|
|
6
|
+
const { theme } = wfoTheme;
|
|
7
|
+
|
|
8
|
+
const reactSelectInnerComponentStyles = {
|
|
9
|
+
option: (
|
|
10
|
+
baseStyles: object,
|
|
11
|
+
state: { isSelected: boolean; isDisabled: boolean },
|
|
12
|
+
) => ({
|
|
13
|
+
...baseStyles,
|
|
14
|
+
borderBottom: theme.border.thin,
|
|
15
|
+
borderColor: theme.colors.lightShade,
|
|
16
|
+
backgroundColor: theme.colors.lightestShade,
|
|
17
|
+
color: state.isSelected
|
|
18
|
+
? theme.colors.primaryText
|
|
19
|
+
: theme.colors.text,
|
|
20
|
+
}),
|
|
21
|
+
control: (baseStyles: object, state: { isFocused: boolean }) => {
|
|
22
|
+
return {
|
|
23
|
+
...baseStyles,
|
|
24
|
+
backgroundColor: state.isFocused
|
|
25
|
+
? theme.colors.emptyShade
|
|
26
|
+
: theme.colors.lightestShade,
|
|
27
|
+
color: theme.colors.text,
|
|
28
|
+
border: `1px solid ${theme.colors.lightShade}`,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
input: (baseStyles: object) => ({
|
|
32
|
+
...baseStyles,
|
|
33
|
+
color: theme.colors.text,
|
|
34
|
+
}),
|
|
35
|
+
singleValue: (baseStyles: object, state: { isDisabled: boolean }) => {
|
|
36
|
+
const opacity = state.isDisabled ? 0.3 : 1;
|
|
37
|
+
const transition = 'opacity 300ms';
|
|
38
|
+
return {
|
|
39
|
+
...baseStyles,
|
|
40
|
+
opacity,
|
|
41
|
+
transition,
|
|
42
|
+
color: theme.colors.text,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
menu: (baseStyles: object) => ({
|
|
46
|
+
...baseStyles,
|
|
47
|
+
backgroundColor: theme.colors.lightestShade,
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const containerStyle = css({
|
|
52
|
+
display: 'flex',
|
|
53
|
+
flexDirection: 'row',
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const reactSelectStyle = css({
|
|
58
|
+
width: '100%',
|
|
59
|
+
});
|
|
60
|
+
const refreshButtonStyle = css({
|
|
61
|
+
marginRight: theme.base / 2,
|
|
62
|
+
cursor: 'pointer',
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
reactSelectInnerComponentStyles,
|
|
66
|
+
refreshButtonStyle,
|
|
67
|
+
containerStyle,
|
|
68
|
+
reactSelectStyle,
|
|
69
|
+
};
|
|
70
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '5.
|
|
1
|
+
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '5.5.1';
|
package/src/messages/en-GB.json
CHANGED
|
@@ -139,7 +139,8 @@
|
|
|
139
139
|
"invalidFiletype": "Invalid filetype!",
|
|
140
140
|
"errorUploading": "Error uploading file!",
|
|
141
141
|
"fileToBig": "File to large. Maximum file size: {fileSizeLimit}",
|
|
142
|
-
"initialPromptText": "Select or drag and drop a file"
|
|
142
|
+
"initialPromptText": "Select or drag and drop a file",
|
|
143
|
+
"supportedFileTypes": "Supported file types: pdf"
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
},
|