@qoretechnologies/reqraft 0.10.19 → 0.10.20
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/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +25 -11
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +6 -1
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +38 -19
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +1 -1
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/readFirst.d.ts +6 -0
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +19 -3
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +13 -2
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +4 -1
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +5 -3
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +2 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +8 -26
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +3 -3
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/boolean/Boolean.js +1 -1
- package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +5 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +29 -12
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +8 -2
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +52 -16
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/package.json +3 -3
- package/src/components/form/engine/CompactRow.tsx +46 -29
- package/src/components/form/engine/FormEngine.stories.tsx +99 -2
- package/src/components/form/engine/FormEngine.tsx +42 -10
- package/src/components/form/engine/compactRowStyles.ts +2 -1
- package/src/components/form/engine/readFirst.ts +25 -4
- package/src/components/form/expressions/ExpressionField.stories.tsx +10 -0
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +26 -19
- package/src/components/form/expressions/builder/index.tsx +12 -1
- package/src/components/form/expressions/useExpressions.ts +4 -1
- package/src/components/form/fields/Field.tsx +10 -1
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -36
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +1 -0
- package/src/components/form/fields/auto/AutoFormField.tsx +2 -0
- package/src/components/form/fields/boolean/Boolean.stories.tsx +34 -0
- package/src/components/form/fields/boolean/Boolean.tsx +1 -1
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +14 -10
- package/src/components/form/fields/select/Select.stories.tsx +4 -0
- package/src/components/form/fields/select/Select.tsx +38 -12
- package/src/components/form/fields/select/SelectCollection.tsx +78 -17
- package/src/stores/currentUser/currentUser.stories.tsx +32 -0
|
@@ -25,6 +25,7 @@ import NumberFormField from './number/Number';
|
|
|
25
25
|
import { ReqraftObjectFormField } from './object/Object';
|
|
26
26
|
import { RichTextFormField } from './rich-text/RichText';
|
|
27
27
|
import { SelectFormField } from './select/Select';
|
|
28
|
+
import { getSelectItemShortDescription } from './select/SelectCollection';
|
|
28
29
|
import { StringFormField } from './string/String';
|
|
29
30
|
import { ArrayAutoField } from './array/ArrayAutoField';
|
|
30
31
|
import { AutoFormField } from './auto/AutoFormField';
|
|
@@ -538,6 +539,10 @@ export const FormField = <T extends TFormFieldType>({
|
|
|
538
539
|
icon='SaveFill'
|
|
539
540
|
fluid
|
|
540
541
|
hideItemCount
|
|
542
|
+
forceDropdown={
|
|
543
|
+
(fieldProps as { forceDropdown?: boolean } | undefined)?.forceDropdown ??
|
|
544
|
+
(rest as { forceDropdown?: boolean }).forceDropdown
|
|
545
|
+
}
|
|
541
546
|
/>
|
|
542
547
|
);
|
|
543
548
|
}
|
|
@@ -557,7 +562,7 @@ export const FormField = <T extends TFormFieldType>({
|
|
|
557
562
|
margin='right'
|
|
558
563
|
key={index}
|
|
559
564
|
label={item.display_name ?? JSON.stringify(itemValue)}
|
|
560
|
-
tooltip={item
|
|
565
|
+
tooltip={getSelectItemShortDescription(mapAllowedValue(item))}
|
|
561
566
|
disabled={item.disabled}
|
|
562
567
|
checked={checked}
|
|
563
568
|
intent={checked ? 'info' : undefined}
|
|
@@ -578,6 +583,10 @@ export const FormField = <T extends TFormFieldType>({
|
|
|
578
583
|
value={value}
|
|
579
584
|
onChange={(val) => handleChange(val as TFormFieldValueType<T>)}
|
|
580
585
|
fluid
|
|
586
|
+
forceDropdown={
|
|
587
|
+
(fieldProps as { forceDropdown?: boolean } | undefined)?.forceDropdown ??
|
|
588
|
+
(rest as { forceDropdown?: boolean }).forceDropdown
|
|
589
|
+
}
|
|
581
590
|
/>
|
|
582
591
|
);
|
|
583
592
|
};
|
|
@@ -12,8 +12,8 @@ import { ReqoreCheckbox, ReqoreControlGroup, ReqoreSpan } from '@qoretechnologie
|
|
|
12
12
|
import { IReqoreButtonProps } from '@qoretechnologies/reqore/dist/components/Button';
|
|
13
13
|
import { IQorusAllowedValue, TQorusType } from '@qoretechnologies/ts-toolkit';
|
|
14
14
|
import { size as count, isEqual } from 'lodash';
|
|
15
|
-
import { memo,
|
|
16
|
-
import {
|
|
15
|
+
import { memo, useMemo } from 'react';
|
|
16
|
+
import { getSelectItemShortDescription } from '../select/SelectCollection';
|
|
17
17
|
import { ISelectFormFieldItem, SelectFormField as Select } from '../select/Select';
|
|
18
18
|
|
|
19
19
|
export interface IFieldAllowedValuesProps extends Pick<
|
|
@@ -30,6 +30,7 @@ export interface IFieldAllowedValuesProps extends Pick<
|
|
|
30
30
|
app?: string;
|
|
31
31
|
action?: string;
|
|
32
32
|
showSavedValues?: boolean;
|
|
33
|
+
forceDropdown?: boolean;
|
|
33
34
|
[key: string]: any;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -57,7 +58,7 @@ export const FieldAllowedValuesCheckGroup = memo(
|
|
|
57
58
|
margin='right'
|
|
58
59
|
key={item.value?.toString()}
|
|
59
60
|
label={item.display_name || JSON.stringify(item.value)}
|
|
60
|
-
tooltip={item
|
|
61
|
+
tooltip={getSelectItemShortDescription(item)}
|
|
61
62
|
disabled={rest.disabled}
|
|
62
63
|
readOnly={rest.readOnly}
|
|
63
64
|
intent={item.value === value ? 'info' : undefined}
|
|
@@ -97,49 +98,23 @@ export const FieldAllowedValues = memo(
|
|
|
97
98
|
allowCreation,
|
|
98
99
|
showSavedValues,
|
|
99
100
|
showDescription,
|
|
101
|
+
forceDropdown,
|
|
100
102
|
size,
|
|
101
103
|
disabled,
|
|
102
104
|
name,
|
|
103
105
|
readOnly,
|
|
104
106
|
}: IFieldAllowedValuesProps) => {
|
|
105
|
-
const [localValue, setLocalValue] = useState(value);
|
|
106
|
-
const [localItems, setLocalItems] = useState<IQorusAllowedValue[]>(items);
|
|
107
|
-
|
|
108
|
-
useEffect(() => {
|
|
109
|
-
setLocalItems(items);
|
|
110
|
-
}, [JSON.stringify(items)]);
|
|
111
|
-
|
|
112
|
-
const handleLocalChange = useCallback((_name: string, value: unknown) => {
|
|
113
|
-
setLocalValue(value);
|
|
114
|
-
}, []);
|
|
115
|
-
|
|
116
|
-
useDebounce(
|
|
117
|
-
() => {
|
|
118
|
-
if (localValue !== value) {
|
|
119
|
-
onChange(name, localValue);
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
200,
|
|
123
|
-
[localValue]
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
useEffect(() => {
|
|
127
|
-
if (value !== localValue) {
|
|
128
|
-
setLocalValue(value);
|
|
129
|
-
}
|
|
130
|
-
}, [value]);
|
|
131
|
-
|
|
132
107
|
const fullItems = useMemo(() => {
|
|
133
108
|
const result = [
|
|
134
109
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
135
|
-
...
|
|
110
|
+
...items.map(({ metadata, value, ...rest }) => ({
|
|
136
111
|
value: value?.value,
|
|
137
112
|
...rest,
|
|
138
113
|
})),
|
|
139
114
|
] as ISelectFormFieldItem[];
|
|
140
115
|
|
|
141
116
|
return result;
|
|
142
|
-
}, [JSON.stringify(
|
|
117
|
+
}, [JSON.stringify(items), type, value, showSavedValues, disabled, readOnly]);
|
|
143
118
|
|
|
144
119
|
const style = useMemo(() => ({ width: '100%' }), []);
|
|
145
120
|
|
|
@@ -153,8 +128,8 @@ export const FieldAllowedValues = memo(
|
|
|
153
128
|
return (
|
|
154
129
|
<FieldAllowedValuesCheckGroup
|
|
155
130
|
items={fullItems}
|
|
156
|
-
onChange={
|
|
157
|
-
value={
|
|
131
|
+
onChange={onChange}
|
|
132
|
+
value={value}
|
|
158
133
|
name={name}
|
|
159
134
|
/>
|
|
160
135
|
);
|
|
@@ -164,10 +139,11 @@ export const FieldAllowedValues = memo(
|
|
|
164
139
|
<Select
|
|
165
140
|
items={fullItems}
|
|
166
141
|
value={value}
|
|
167
|
-
onChange={(value) =>
|
|
142
|
+
onChange={(value) => onChange(name, value)}
|
|
168
143
|
fluid
|
|
169
144
|
fixed={false}
|
|
170
145
|
showDescription={Boolean(showDescription || showDescription === undefined)}
|
|
146
|
+
forceDropdown={forceDropdown}
|
|
171
147
|
style={style}
|
|
172
148
|
size={size}
|
|
173
149
|
disabled={disabled}
|
|
@@ -178,10 +154,11 @@ export const FieldAllowedValues = memo(
|
|
|
178
154
|
return (
|
|
179
155
|
<Select
|
|
180
156
|
items={fullItems}
|
|
181
|
-
onChange={(value) =>
|
|
157
|
+
onChange={(value) => onChange(name, value)}
|
|
182
158
|
fluid
|
|
183
159
|
fixed={false}
|
|
184
160
|
showDescription={false}
|
|
161
|
+
forceDropdown={forceDropdown}
|
|
185
162
|
style={style}
|
|
186
163
|
minimal
|
|
187
164
|
placeholder={'Saved & Suggested Values'}
|
|
@@ -374,6 +374,7 @@ function AutoField<T = any>({
|
|
|
374
374
|
) {
|
|
375
375
|
return (
|
|
376
376
|
<SchemaDefinitionEditor
|
|
377
|
+
{...((rest as any).fieldProps || {})}
|
|
377
378
|
value={
|
|
378
379
|
value === null || value === undefined
|
|
379
380
|
? undefined
|
|
@@ -419,6 +420,7 @@ function AutoField<T = any>({
|
|
|
419
420
|
app={rest.app}
|
|
420
421
|
action={rest.action}
|
|
421
422
|
showDescription={rest.showDescription}
|
|
423
|
+
forceDropdown={rest.forceDropdown}
|
|
422
424
|
allowCreation={rest.allowed_values_creatable}
|
|
423
425
|
showSavedValues={showSavedValues}
|
|
424
426
|
readOnly={rest.readonly}
|
|
@@ -75,6 +75,40 @@ export const Unchecked: Story = {
|
|
|
75
75
|
},
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
export const Unset: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
checked: undefined,
|
|
81
|
+
},
|
|
82
|
+
parameters: {
|
|
83
|
+
docs: {
|
|
84
|
+
description: {
|
|
85
|
+
story:
|
|
86
|
+
'Renders the Boolean field without a default choice. The switch remains visibly unset until the user explicitly chooses Yes or No.',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const UnsetSelectsYes: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
checked: undefined,
|
|
95
|
+
},
|
|
96
|
+
parameters: {
|
|
97
|
+
docs: {
|
|
98
|
+
description: {
|
|
99
|
+
story:
|
|
100
|
+
'Verifies that an unset Boolean field saves the first explicit Yes selection instead of treating the missing value as No.',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
async play({ canvasElement, args }) {
|
|
105
|
+
const canvas = within(canvasElement);
|
|
106
|
+
|
|
107
|
+
await userEvent.click(canvas.getByLabelText('Boolean'));
|
|
108
|
+
await expect(args.onChange).toHaveBeenLastCalledWith(true);
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
78
112
|
export const Disabled: Story = {
|
|
79
113
|
args: {
|
|
80
114
|
checked: true,
|
|
@@ -2,6 +2,7 @@ import { ReqoreMultiSelect } from '@qoretechnologies/reqore';
|
|
|
2
2
|
import { TReqoreMultiSelectItem } from '@qoretechnologies/reqore/dist/components/MultiSelect';
|
|
3
3
|
import { IQorusAllowedValue } from '@qoretechnologies/ts-toolkit';
|
|
4
4
|
import { memo, useMemo } from 'react';
|
|
5
|
+
import { getSelectItemShortDescription } from '../select/SelectCollection';
|
|
5
6
|
|
|
6
7
|
export interface IMultiSelectFormFieldProps {
|
|
7
8
|
/** Currently selected values (plain scalars) */
|
|
@@ -33,16 +34,19 @@ export const MultiSelectFormField = memo(
|
|
|
33
34
|
|
|
34
35
|
const reqoreItems = useMemo<TReqoreMultiSelectItem[]>(() => {
|
|
35
36
|
const base = items.map(
|
|
36
|
-
(item): TReqoreMultiSelectItem =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
(item): TReqoreMultiSelectItem => {
|
|
38
|
+
const description = getSelectItemShortDescription(item);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
value: item.value?.value as string,
|
|
42
|
+
label: item.display_name ?? String(item.value?.value ?? ''),
|
|
43
|
+
description,
|
|
44
|
+
wrap: true,
|
|
45
|
+
// IDE parity: when `*` is selected, every other item is disabled.
|
|
46
|
+
disabled:
|
|
47
|
+
!!item.disabled || (selectedValues.includes('*') && item.value?.value !== '*'),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
46
50
|
);
|
|
47
51
|
|
|
48
52
|
// IDE parity: selected values that aren't in the allowed list still
|
|
@@ -62,6 +62,7 @@ export const ItemsWithDescription: Story = {
|
|
|
62
62
|
},
|
|
63
63
|
},
|
|
64
64
|
args: {
|
|
65
|
+
forceDropdown: false,
|
|
65
66
|
items: [
|
|
66
67
|
{ display_name: 'Item 1', desc: 'This is item 1', value: 'item1', icon: 'MoneyEuroCircleFill', groups: ['Miscellaneous'] },
|
|
67
68
|
{ display_name: 'Item 2', desc: 'This is item 2', value: 'item2', image: 'https://avatars.githubusercontent.com/u/8861481?v=4' },
|
|
@@ -106,6 +107,7 @@ export const ItemsWithDescriptionAndMessages: Story = {
|
|
|
106
107
|
},
|
|
107
108
|
},
|
|
108
109
|
args: {
|
|
110
|
+
forceDropdown: false,
|
|
109
111
|
items: [
|
|
110
112
|
{
|
|
111
113
|
display_name: 'Item 1',
|
|
@@ -162,6 +164,7 @@ export const DisabledItemsWithIntent: Story = {
|
|
|
162
164
|
|
|
163
165
|
export const DisabledItemsWithIntentAndDescriptions: Story = {
|
|
164
166
|
args: {
|
|
167
|
+
forceDropdown: false,
|
|
165
168
|
items: [
|
|
166
169
|
{ display_name: 'Item 1', desc: 'This is item 1', value: 'item1' },
|
|
167
170
|
{ display_name: 'Item 2', short_desc: 'This is item 2', value: 'item2' },
|
|
@@ -326,6 +329,7 @@ export const AutoSelectWithDescriptions: Story = {
|
|
|
326
329
|
|
|
327
330
|
export const ItemValuesAreObjectsAndCanBeSelected: Story = {
|
|
328
331
|
args: {
|
|
332
|
+
forceDropdown: false,
|
|
329
333
|
value: { id: { type: 'string', value: 'item1' } },
|
|
330
334
|
items: [
|
|
331
335
|
{ value: { id: { type: 'string', value: 'item1' } } },
|
|
@@ -11,7 +11,11 @@ import { TReqoreIntent } from '@qoretechnologies/reqore/dist/constants/theme';
|
|
|
11
11
|
import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
12
12
|
import { isEqual, size } from 'lodash';
|
|
13
13
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
getSelectItemShortDescription,
|
|
16
|
+
ISelectFieldCollectionItem,
|
|
17
|
+
SelectFieldCollection,
|
|
18
|
+
} from './SelectCollection';
|
|
15
19
|
|
|
16
20
|
export type ISelectFormFieldItem = ISelectFieldCollectionItem;
|
|
17
21
|
|
|
@@ -68,7 +72,7 @@ export const SelectFormField = memo(
|
|
|
68
72
|
showPlaceholder = true,
|
|
69
73
|
showRightIcon = true,
|
|
70
74
|
hideItemCount,
|
|
71
|
-
forceDropdown,
|
|
75
|
+
forceDropdown = true,
|
|
72
76
|
fluid,
|
|
73
77
|
flat,
|
|
74
78
|
intent,
|
|
@@ -89,6 +93,7 @@ export const SelectFormField = memo(
|
|
|
89
93
|
return;
|
|
90
94
|
}
|
|
91
95
|
onChange?.(item.value);
|
|
96
|
+
setCollectionOpen(false);
|
|
92
97
|
},
|
|
93
98
|
[onChange, value]
|
|
94
99
|
);
|
|
@@ -96,7 +101,7 @@ export const SelectFormField = memo(
|
|
|
96
101
|
const getItemDescription = useCallback(
|
|
97
102
|
(itemValue: unknown) => {
|
|
98
103
|
const item = items.find((item) => isEqual(item.value, itemValue));
|
|
99
|
-
return item
|
|
104
|
+
return getSelectItemShortDescription(item);
|
|
100
105
|
},
|
|
101
106
|
[items]
|
|
102
107
|
);
|
|
@@ -133,7 +138,7 @@ export const SelectFormField = memo(
|
|
|
133
138
|
const item = items.find(
|
|
134
139
|
(item) => isEqual(item.display_name, itemName) || isEqual(item.value, itemName)
|
|
135
140
|
);
|
|
136
|
-
return
|
|
141
|
+
return getSelectItemShortDescription(item, defaultDesc);
|
|
137
142
|
},
|
|
138
143
|
[items, showDescription]
|
|
139
144
|
);
|
|
@@ -230,13 +235,35 @@ export const SelectFormField = memo(
|
|
|
230
235
|
[size(items), hideItemCount]
|
|
231
236
|
);
|
|
232
237
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
const autoSelectedItem = useMemo(
|
|
239
|
+
() =>
|
|
240
|
+
autoSelect && filteredItems.length === 1 && !filteredItems[0].disabled
|
|
241
|
+
? filteredItems[0]
|
|
242
|
+
: undefined,
|
|
243
|
+
[autoSelect, filteredItems]
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
// Selecting during render causes a parent-controlled field to synchronously
|
|
247
|
+
// update while this component is still rendering. React rejects that update
|
|
248
|
+
// and can enter an infinite render loop. Commit the derived default after
|
|
249
|
+
// rendering instead; the button below already renders the sole item's label
|
|
250
|
+
// while the controlled value catches up.
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
const valueIsUnset = value === undefined || value === null || value === '';
|
|
253
|
+
|
|
254
|
+
if (
|
|
255
|
+
autoSelectedItem &&
|
|
256
|
+
!disabled &&
|
|
257
|
+
valueIsUnset &&
|
|
258
|
+
!isEqual(autoSelectedItem.value, value)
|
|
259
|
+
) {
|
|
260
|
+
handleSelectClick(autoSelectedItem);
|
|
236
261
|
}
|
|
262
|
+
}, [autoSelectedItem, disabled, handleSelectClick, value]);
|
|
237
263
|
|
|
238
|
-
|
|
239
|
-
const
|
|
264
|
+
if (autoSelectedItem) {
|
|
265
|
+
const itemHasError = hasError(items, autoSelectedItem.value);
|
|
266
|
+
const itemHasWarning = hasWarning(items, autoSelectedItem.value);
|
|
240
267
|
|
|
241
268
|
return (
|
|
242
269
|
<ReqoreButton
|
|
@@ -247,12 +274,12 @@ export const SelectFormField = memo(
|
|
|
247
274
|
fluid={fluid}
|
|
248
275
|
flat={flat ?? false}
|
|
249
276
|
size={componentSize}
|
|
250
|
-
label={getLabel(items, value ??
|
|
277
|
+
label={getLabel(items, value ?? autoSelectedItem.value)}
|
|
251
278
|
description={getItemShortDescription(value as string) as string}
|
|
252
279
|
readOnly
|
|
253
280
|
fixed
|
|
254
281
|
minimal
|
|
255
|
-
{...getIcon(filteredItems,
|
|
282
|
+
{...getIcon(filteredItems, autoSelectedItem.value)}
|
|
256
283
|
intent={
|
|
257
284
|
itemHasError ? 'danger'
|
|
258
285
|
: itemHasWarning ?
|
|
@@ -291,7 +318,6 @@ export const SelectFormField = memo(
|
|
|
291
318
|
<SelectFieldCollection
|
|
292
319
|
items={filteredItems}
|
|
293
320
|
filters={filters}
|
|
294
|
-
getItemDescription={(v) => getItemDescription(v) as string}
|
|
295
321
|
value={value}
|
|
296
322
|
onItemSelect={handleSelectClick}
|
|
297
323
|
onClose={() => setCollectionOpen(false)}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
ReqoreControlGroup,
|
|
5
5
|
ReqoreMessage,
|
|
6
6
|
ReqoreModal,
|
|
7
|
-
ReqoreP,
|
|
8
7
|
} from '@qoretechnologies/reqore';
|
|
9
8
|
import { IReqoreCollectionItemProps } from '@qoretechnologies/reqore/dist/components/Collection/item';
|
|
10
9
|
import { TReqoreBadge } from '@qoretechnologies/reqore/dist/components/Button';
|
|
@@ -12,7 +11,7 @@ import { TReqoreIntent } from '@qoretechnologies/reqore/dist/constants/theme';
|
|
|
12
11
|
import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
13
12
|
import { capitalize, isEqual, size } from 'lodash';
|
|
14
13
|
import { useMemo, useState } from 'react';
|
|
15
|
-
import
|
|
14
|
+
import { Description } from '../../../Description';
|
|
16
15
|
|
|
17
16
|
const PositiveColorEffect = {
|
|
18
17
|
gradient: {
|
|
@@ -25,6 +24,7 @@ const PositiveColorEffect = {
|
|
|
25
24
|
|
|
26
25
|
export interface ISelectFieldCollectionItem {
|
|
27
26
|
value?: unknown;
|
|
27
|
+
name?: unknown;
|
|
28
28
|
display_name?: string;
|
|
29
29
|
desc?: string;
|
|
30
30
|
short_desc?: string;
|
|
@@ -42,16 +42,76 @@ export interface ISelectFieldCollectionItem {
|
|
|
42
42
|
export interface ISelectFieldCollectionProps {
|
|
43
43
|
onClose: () => void;
|
|
44
44
|
items: ISelectFieldCollectionItem[];
|
|
45
|
-
getItemDescription: (value: unknown) => string;
|
|
46
45
|
value?: unknown;
|
|
47
46
|
onItemSelect: (item: ISelectFieldCollectionItem) => void;
|
|
48
47
|
filters?: string[];
|
|
49
48
|
}
|
|
50
49
|
|
|
50
|
+
const normalizeSelectItemDescription = (value: unknown): string | undefined => {
|
|
51
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const seen = new Set<string>();
|
|
56
|
+
|
|
57
|
+
return value
|
|
58
|
+
.split('\n')
|
|
59
|
+
.filter((line) => {
|
|
60
|
+
const normalized = line.trim().replace(/\s+/g, ' ').toLocaleLowerCase();
|
|
61
|
+
|
|
62
|
+
if (!normalized) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (seen.has(normalized)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
seen.add(normalized);
|
|
71
|
+
return true;
|
|
72
|
+
})
|
|
73
|
+
.join('\n')
|
|
74
|
+
.trim();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const getSelectItemDescription = (
|
|
78
|
+
item: Pick<ISelectFieldCollectionItem, 'desc' | 'short_desc'>
|
|
79
|
+
): string | undefined => {
|
|
80
|
+
const desc = normalizeSelectItemDescription(item.desc);
|
|
81
|
+
const shortDesc = normalizeSelectItemDescription(item.short_desc);
|
|
82
|
+
return desc ?? shortDesc;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const getSelectItemShortDescription = (
|
|
86
|
+
item: Pick<ISelectFieldCollectionItem, 'desc' | 'short_desc'> | undefined,
|
|
87
|
+
defaultDesc = ''
|
|
88
|
+
): string | undefined => {
|
|
89
|
+
if (!item) {
|
|
90
|
+
return defaultDesc || undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const shortDescription = normalizeSelectItemDescription(item.short_desc);
|
|
94
|
+
|
|
95
|
+
return shortDescription || defaultDesc || undefined;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const getSelectItemDescriptionProps = (
|
|
99
|
+
item: Pick<ISelectFieldCollectionItem, 'desc' | 'short_desc'>
|
|
100
|
+
): { longDescription?: string; shortDescription?: string } => {
|
|
101
|
+
const longDescription = normalizeSelectItemDescription(item.desc);
|
|
102
|
+
|
|
103
|
+
if (longDescription) {
|
|
104
|
+
return { longDescription };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const shortDescription = normalizeSelectItemDescription(item.short_desc);
|
|
108
|
+
|
|
109
|
+
return { shortDescription };
|
|
110
|
+
};
|
|
111
|
+
|
|
51
112
|
export const SelectFieldCollection = ({
|
|
52
113
|
onClose,
|
|
53
114
|
items,
|
|
54
|
-
getItemDescription,
|
|
55
115
|
value,
|
|
56
116
|
onItemSelect,
|
|
57
117
|
filters,
|
|
@@ -100,8 +160,10 @@ export const SelectFieldCollection = ({
|
|
|
100
160
|
transparent={false}
|
|
101
161
|
size='tiny'
|
|
102
162
|
showLayoutSwitch={false}
|
|
103
|
-
items={filteredItems.map(
|
|
104
|
-
|
|
163
|
+
items={filteredItems.map((item): IReqoreCollectionItemProps => {
|
|
164
|
+
const { longDescription, shortDescription } = getSelectItemDescriptionProps(item);
|
|
165
|
+
|
|
166
|
+
return {
|
|
105
167
|
label: item.display_name || item.value?.toString(),
|
|
106
168
|
size: 'tiny',
|
|
107
169
|
groups: item.groups,
|
|
@@ -109,8 +171,14 @@ export const SelectFieldCollection = ({
|
|
|
109
171
|
responsiveTitle: false,
|
|
110
172
|
content: (
|
|
111
173
|
<ReqoreControlGroup vertical fluid>
|
|
112
|
-
{
|
|
113
|
-
|
|
174
|
+
{longDescription || shortDescription ? (
|
|
175
|
+
<Description
|
|
176
|
+
longDescription={longDescription || ''}
|
|
177
|
+
shortDescription={shortDescription}
|
|
178
|
+
size='small'
|
|
179
|
+
margin='none'
|
|
180
|
+
/>
|
|
181
|
+
) : null}
|
|
114
182
|
{item.actions?.map(({ as: Component = ReqoreButton, props, ...rest }, index) => (
|
|
115
183
|
<Component
|
|
116
184
|
key={index}
|
|
@@ -148,13 +216,6 @@ export const SelectFieldCollection = ({
|
|
|
148
216
|
size: '20px',
|
|
149
217
|
rounded: true,
|
|
150
218
|
},
|
|
151
|
-
tooltip: !!item.desc
|
|
152
|
-
? {
|
|
153
|
-
delay: 800,
|
|
154
|
-
content: <ReactMarkdown>{getItemDescription(item.value)}</ReactMarkdown>,
|
|
155
|
-
maxWidth: '70vw',
|
|
156
|
-
}
|
|
157
|
-
: undefined,
|
|
158
219
|
onClick: !item.disabled
|
|
159
220
|
? (e) => {
|
|
160
221
|
if (e.currentTarget.contains(e.target as Node)) {
|
|
@@ -163,8 +224,8 @@ export const SelectFieldCollection = ({
|
|
|
163
224
|
}
|
|
164
225
|
}
|
|
165
226
|
: undefined,
|
|
166
|
-
}
|
|
167
|
-
)}
|
|
227
|
+
};
|
|
228
|
+
})}
|
|
168
229
|
actions={filters?.map((filter) => ({
|
|
169
230
|
label: capitalize(filter.replace('_', ' ')),
|
|
170
231
|
badge: items.filter((item) => item[filter]).length,
|
|
@@ -6,6 +6,38 @@ import { currentUserStore } from './currentUser';
|
|
|
6
6
|
|
|
7
7
|
const meta = {
|
|
8
8
|
title: 'Stores/Current User',
|
|
9
|
+
parameters: {
|
|
10
|
+
mockData: [
|
|
11
|
+
{
|
|
12
|
+
url: 'https://hq.qoretechnologies.com:8092/api/latest/users?action=current',
|
|
13
|
+
method: 'GET',
|
|
14
|
+
status: 200,
|
|
15
|
+
response: {
|
|
16
|
+
provider: 'local',
|
|
17
|
+
username: 'david',
|
|
18
|
+
name: 'David Nichols',
|
|
19
|
+
has_default: true,
|
|
20
|
+
roles: ['admin'],
|
|
21
|
+
permissions: ['USER-CONTROL'],
|
|
22
|
+
workflows: [],
|
|
23
|
+
services: [],
|
|
24
|
+
jobs: [],
|
|
25
|
+
mappers: [],
|
|
26
|
+
vmaps: [],
|
|
27
|
+
groups: [],
|
|
28
|
+
fsms: [],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
beforeEach: () => {
|
|
34
|
+
currentUserStore.setState({
|
|
35
|
+
currentUser: undefined,
|
|
36
|
+
loading: false,
|
|
37
|
+
error: undefined,
|
|
38
|
+
errorData: undefined,
|
|
39
|
+
});
|
|
40
|
+
},
|
|
9
41
|
render: () => {
|
|
10
42
|
const { load, loading, currentUser = {}, errorData } = currentUserStore();
|
|
11
43
|
|