@k-int/stripes-kint-components 2.7.1 → 2.8.0
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/CHANGELOG.md +8 -1
- package/es/index.js +14 -0
- package/es/lib/ActionList/ActionList.js +7 -2
- package/es/lib/ActionList/ActionListFieldArray.js +8 -4
- package/es/lib/CustomProperties/View/CustomPropertiesViewCtx.js +1 -1
- package/es/lib/CycleButton/CycleButton.js +2 -1
- package/es/lib/EditableRefdataCategoryList/EditableRefdataCategoryList.js +8 -1
- package/es/lib/EditableRefdataList/EditableRefdataList.js +8 -1
- package/es/lib/IconSelect/IconSelect.js +105 -110
- package/es/lib/RichSelect/RichSelect.js +252 -0
- package/es/lib/RichSelect/index.js +23 -0
- package/es/lib/RichSelect/useSelectedOption.js +39 -0
- package/package.json +1 -1
- package/src/index.js +1 -0
- package/src/lib/ActionList/ActionList.js +8 -1
- package/src/lib/ActionList/ActionListFieldArray.js +11 -4
- package/src/lib/ActionList/README.md +1 -0
- package/src/lib/CustomProperties/View/CustomPropertiesViewCtx.js +1 -1
- package/src/lib/CycleButton/CycleButton.js +3 -2
- package/src/lib/EditableRefdataCategoryList/EditableRefdataCategoryList.js +8 -1
- package/src/lib/EditableRefdataList/EditableRefdataList.js +8 -1
- package/src/lib/IconSelect/IconSelect.js +94 -113
- package/src/lib/IconSelect/README.md +6 -2
- package/src/lib/RichSelect/README.md +56 -0
- package/src/lib/RichSelect/RichSelect.js +230 -0
- package/src/lib/RichSelect/index.js +2 -0
- package/src/lib/RichSelect/useSelectedOption.js +14 -0
- package/src/lib/hooks/useMutateRefdataCategory.js +3 -3
- package/styles/RichSelect.css +7 -0
- package/translations/stripes-kint-components/ar.json +2 -1
- package/translations/stripes-kint-components/cs_CZ.json +2 -1
- package/translations/stripes-kint-components/de.json +2 -1
- package/translations/stripes-kint-components/en.json +2 -1
- package/translations/stripes-kint-components/es.json +2 -1
- package/translations/stripes-kint-components/hi_IN.json +2 -1
- package/translations/stripes-kint-components/hu.json +2 -1
- package/translations/stripes-kint-components/it_IT.json +2 -1
- package/translations/stripes-kint-components/ja.json +2 -1
- package/translations/stripes-kint-components/ko.json +2 -1
- package/translations/stripes-kint-components/pt_PT.json +2 -1
- package/translations/stripes-kint-components/ru.json +2 -1
- package/translations/stripes-kint-components/sv.json +2 -1
- package/translations/stripes-kint-components/zh_CN.json +2 -1
|
@@ -1,149 +1,130 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { forwardRef, useImperativeHandle } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
2
3
|
|
|
3
4
|
import classnames from 'classnames';
|
|
4
5
|
|
|
5
6
|
import { FormattedMessage } from 'react-intl';
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
|
-
Button,
|
|
9
|
-
Dropdown,
|
|
10
|
-
DropdownMenu,
|
|
11
9
|
IconButton,
|
|
12
10
|
Icon,
|
|
13
|
-
nativeChangeFieldValue as nativeChangeField,
|
|
14
|
-
Label,
|
|
15
11
|
} from '@folio/stripes/components';
|
|
16
12
|
|
|
17
13
|
import css from '../../../styles/IconSelect.css';
|
|
18
14
|
|
|
15
|
+
import RichSelect, { useSelectedOption } from '../RichSelect';
|
|
16
|
+
|
|
19
17
|
// A form component which acts as a "Select for states with corresponding icons"
|
|
20
|
-
const IconSelect = ({
|
|
18
|
+
const IconSelect = forwardRef(({
|
|
21
19
|
ariaLabel,
|
|
22
20
|
disabled = false,
|
|
23
21
|
id,
|
|
24
|
-
input
|
|
22
|
+
input,
|
|
25
23
|
label,
|
|
26
24
|
meta,
|
|
27
25
|
notSet = {
|
|
28
|
-
icon:
|
|
29
|
-
value:
|
|
26
|
+
icon: 'ellipsis',
|
|
27
|
+
value: '',
|
|
30
28
|
label: <FormattedMessage id="stripes-kint-components.iconSelect.notSet" />
|
|
31
29
|
},
|
|
32
30
|
onChange,
|
|
33
31
|
options: userOptions = [],
|
|
34
|
-
required = false
|
|
35
|
-
|
|
32
|
+
required = false,
|
|
33
|
+
value
|
|
34
|
+
}, ref) => {
|
|
36
35
|
// Options with notSet
|
|
37
36
|
const options = [
|
|
38
37
|
notSet,
|
|
39
38
|
...userOptions
|
|
40
39
|
];
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const findSelectedOptionByValue = (val) => {
|
|
47
|
-
return options.find(opt => opt.value === val);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const [selectedOption, setSelectedOption] = useState(findSelectedOptionByValue(meta.initial ?? ''));
|
|
51
|
-
|
|
52
|
-
const handleChange = (e) => {
|
|
53
|
-
// Actually set the value in the form
|
|
54
|
-
formInput.onChange(e);
|
|
55
|
-
|
|
56
|
-
// If the user has set up an onChange, this will ensure that that fires
|
|
57
|
-
if (onChange) {
|
|
58
|
-
onChange(e, e.target.value);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
41
|
+
const [richSelectRef, selectedOption] = useSelectedOption();
|
|
42
|
+
useImperativeHandle(ref, () => ({
|
|
43
|
+
selectedOption
|
|
44
|
+
}));
|
|
61
45
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
46
|
+
const { className: iconButtonClassName, ...buttonProps } = selectedOption?.buttonProps ?? {};
|
|
47
|
+
return (
|
|
48
|
+
<RichSelect
|
|
49
|
+
ref={richSelectRef}
|
|
50
|
+
ariaLabel={ariaLabel}
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
id={id}
|
|
53
|
+
input={input}
|
|
54
|
+
label={label}
|
|
55
|
+
meta={meta}
|
|
56
|
+
onChange={onChange}
|
|
57
|
+
options={options}
|
|
58
|
+
renderOption={(opt) => {
|
|
59
|
+
if (opt.icon) {
|
|
60
|
+
return (
|
|
61
|
+
<Icon
|
|
62
|
+
icon={opt.icon}
|
|
63
|
+
{...opt.iconProps}
|
|
64
|
+
>
|
|
65
|
+
{opt.label ?? opt.value}
|
|
66
|
+
</Icon>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
78
69
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
70
|
+
return opt.label ?? opt.value;
|
|
71
|
+
}}
|
|
72
|
+
renderTrigger={({ onToggle, triggerRef, keyHandler, ariaProps, getTriggerProps }) => (
|
|
73
|
+
<IconButton
|
|
74
|
+
ref={triggerRef}
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
icon={selectedOption?.icon ?? 'ellipsis'}
|
|
77
|
+
marginBottom0
|
|
78
|
+
onClick={onToggle}
|
|
79
|
+
onKeyDown={keyHandler}
|
|
80
|
+
type="button"
|
|
81
|
+
{...{
|
|
82
|
+
className: classnames(
|
|
83
|
+
css.buttonStyle,
|
|
84
|
+
iconButtonClassName
|
|
85
|
+
),
|
|
86
|
+
...buttonProps
|
|
87
|
+
}}
|
|
88
|
+
{...getTriggerProps()}
|
|
89
|
+
{...ariaProps}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
required={required}
|
|
93
|
+
value={value}
|
|
94
|
+
/>
|
|
95
95
|
);
|
|
96
|
+
});
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
),
|
|
128
|
-
...buttonProps
|
|
129
|
-
}}
|
|
130
|
-
{...getTriggerProps()}
|
|
131
|
-
{...ariaProps}
|
|
132
|
-
/>
|
|
133
|
-
)}
|
|
134
|
-
renderMenu={renderMenu}
|
|
135
|
-
/>
|
|
136
|
-
<input
|
|
137
|
-
{...formInput}
|
|
138
|
-
ref={hiddenInput}
|
|
139
|
-
hidden
|
|
140
|
-
onChange={handleChange}
|
|
141
|
-
type="text"
|
|
142
|
-
value={value}
|
|
143
|
-
/>
|
|
144
|
-
</>
|
|
145
|
-
|
|
146
|
-
);
|
|
98
|
+
IconSelect.propTypes = {
|
|
99
|
+
ariaLabel: PropTypes.string,
|
|
100
|
+
disabled: PropTypes.bool,
|
|
101
|
+
id: PropTypes.string,
|
|
102
|
+
input: PropTypes.object,
|
|
103
|
+
label: PropTypes.oneOfType([
|
|
104
|
+
PropTypes.string,
|
|
105
|
+
PropTypes.func,
|
|
106
|
+
PropTypes.node
|
|
107
|
+
]),
|
|
108
|
+
meta: PropTypes.object,
|
|
109
|
+
notSet: PropTypes.shape({
|
|
110
|
+
icon: PropTypes.string,
|
|
111
|
+
value: PropTypes.string.isRequired,
|
|
112
|
+
label: PropTypes.oneOfType([
|
|
113
|
+
PropTypes.string,
|
|
114
|
+
PropTypes.node
|
|
115
|
+
])
|
|
116
|
+
}),
|
|
117
|
+
onChange: PropTypes.func,
|
|
118
|
+
options: PropTypes.arrayOf(PropTypes.shape({
|
|
119
|
+
icon: PropTypes.string,
|
|
120
|
+
value: PropTypes.string.isRequired,
|
|
121
|
+
label: PropTypes.oneOfType([
|
|
122
|
+
PropTypes.string,
|
|
123
|
+
PropTypes.node
|
|
124
|
+
])
|
|
125
|
+
})),
|
|
126
|
+
required: PropTypes.bool,
|
|
127
|
+
value: PropTypes.string,
|
|
147
128
|
};
|
|
148
129
|
|
|
149
130
|
export default IconSelect;
|
|
@@ -22,6 +22,9 @@ import { IconSelect } from '@k-int/stripes-kint-components'
|
|
|
22
22
|
/>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Usage with Ref
|
|
26
|
+
IconSelect uses RichSelect under the hood, so it is possible for the implementer to pass in a RichSelect ref, from `useSelectedOption` into IconSelect and optain the currently selected icon object.
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
## Props
|
|
27
30
|
Name | Type | Description | default | required
|
|
@@ -31,6 +34,7 @@ disabled | Boolean | Disables the iconButton | false | ✕ |
|
|
|
31
34
|
id | String | A string id used for accessibility reasons | | ✓ |
|
|
32
35
|
label | String/Node | A visible label for the element. If not present, an ariaLabel should be supplied. | | ✕ |
|
|
33
36
|
notSet | Object | An object to use in place of the default "not set" option in the dropdown. | `{ icon: "ellipsis", value: "", label: "Not set" }`. | ✕ |
|
|
34
|
-
onChange | Function | An onChange handler, which will be passed an event when an option is clicked.
|
|
37
|
+
onChange | Function | An onChange handler, which will be passed an event when an option is clicked. If this is being used in a form as a controlled component, the onChange will need to handle setting the form value, and currently the value will be set to the option selected momentarily before then being set to the value via the passed onChange. | | ✕ |
|
|
35
38
|
options | Array\<Object> | An array of option objects, corresponding to the choices available in the dropdown menu. Object props available are: <ul><li>`buttonProps` - properties which will be applied to the iconButton which triggers the dropdown (This allows for colouring and styling individually per option)</li><li>`icon` - an icon for the option within the dropdown and also in the iconButton. If one is not supplied, an ellipsis icon will be used for the dropdown trigger</li><li>`iconProps` - properties to pass on to the icon within the dropdown</li><li>`label (Required)` - The label to display next to the icon in the dropdown for selection</li><li>`value (Required)` - The value to be set on selection</li></ul> | [] | ✕ |
|
|
36
|
-
required | Boolean | A boolean used to set required styling on the label | false | ✕ |
|
|
39
|
+
required | Boolean | A boolean used to set required styling on the label | false | ✕ |
|
|
40
|
+
value | String | A value property to allow this component to be used as a controller form component | | ✕ |
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# RichSelect
|
|
2
|
+
A component designed to render a dropdown, for a Select-style pick between rich object choices.
|
|
3
|
+
|
|
4
|
+
## Basic Usage
|
|
5
|
+
```
|
|
6
|
+
import { RichSelect } from '@k-int/stripes-kint-components'
|
|
7
|
+
|
|
8
|
+
<RichSelect
|
|
9
|
+
label="RichSelect"
|
|
10
|
+
options={[
|
|
11
|
+
{
|
|
12
|
+
value: "option1",
|
|
13
|
+
label: "Option 1",
|
|
14
|
+
someOtherProp: true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
value: "option2",
|
|
18
|
+
label: "Option 2",
|
|
19
|
+
something: false
|
|
20
|
+
}
|
|
21
|
+
]}
|
|
22
|
+
/>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage with Ref
|
|
26
|
+
A hook is supplied, `useSelectedOption`, which will return `[ref, selectedOption]`. The object `selectedOption` will contain the full option object that is currently selected by RichSelect, thus avoiding the need to parse out form values and find the value within the options array provided manually for each RichSelect.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
import { RichSelect, useSelectedOption } from '@k-int/stripes-kint-components'
|
|
31
|
+
const [ref, selectedOption] = useSelectedOption();
|
|
32
|
+
console.log("Selected option: %o", selectedOption);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<RichSelect
|
|
36
|
+
ref={ref}
|
|
37
|
+
...
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Props
|
|
43
|
+
Name | Type | Description | default | required
|
|
44
|
+
--- | --- | --- | --- | ---
|
|
45
|
+
ariaLabel | String | A string aria-label. This is only supposed to be used when a label will not be present, so that screen readers will still have a label to understand what the button is for. | | ✕ |
|
|
46
|
+
disabled | Boolean | Disables the default renderTrigger | false | ✕ |
|
|
47
|
+
id | String | A string id used for accessibility reasons | | ✓ |
|
|
48
|
+
label | String/Node | A visible label for the element. If not present, an ariaLabel should be supplied. | | ✕ |
|
|
49
|
+
onChange | Function | An onChange handler, which will be passed an event when an option is clicked. If this is being used in a form as a controlled component, the onChange will need to handle setting the form value, and currently the value will be set to the option selected momentarily before then being set to the value via the passed onChange. | | ✕ |
|
|
50
|
+
options | Array\<Object> | An array of option objects, corresponding to the choices available in the dropdown menu. These objects _must_ contain a `value` prop, and a `label` prop is highly recommended. Beyond this the option object can take any shape required. | [] | ✕ |
|
|
51
|
+
placeholder | Node/String | A custom placeholder in the default trigger button when no value is selected | "Please select an option" | ✕ |
|
|
52
|
+
renderMenu | Function | A function to replace the entire renderMenu for the dropdown. Provided props are all those provided by Stripes `Dropdown` component normally, as well as `selectedOption`, the object containing the currently selectedOption, and `changeField`, a function which takes a string value and natively sets the inner `input`, triggering the onChange and such. | | ✕ |
|
|
53
|
+
renderOption | Function | A function which instead of replacing the entire renderMenu, simply replaces the rendered option button. The function takes a single prop containing the option to be rendered, and expects a node to be returned. | `(opt) => opt?.label ?? opt?.value` | ✕ |
|
|
54
|
+
renderTrigger | Function | A function to replace the default render trigger provided by RichSelect. Takes all properties Stripes `Dropdown` provides to the renderTrigger prop, as well as `selectedOption`, the current selected option object. | | ✕ |
|
|
55
|
+
required | Boolean | A boolean used to set required styling on the label | false | ✕ |
|
|
56
|
+
value | String | A value property to allow this component to be used as a controller form component | | ✕ |
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import { FormattedMessage } from 'react-intl';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Button,
|
|
8
|
+
Dropdown,
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
Icon,
|
|
11
|
+
// IconButton,
|
|
12
|
+
nativeChangeFieldValue as nativeChangeField,
|
|
13
|
+
Label,
|
|
14
|
+
} from '@folio/stripes/components';
|
|
15
|
+
|
|
16
|
+
import css from '../../../styles/RichSelect.css';
|
|
17
|
+
|
|
18
|
+
const RichSelect = forwardRef(({
|
|
19
|
+
ariaLabel,
|
|
20
|
+
disabled = false,
|
|
21
|
+
dropdownProps = {},
|
|
22
|
+
id,
|
|
23
|
+
input: formInput = {},
|
|
24
|
+
label,
|
|
25
|
+
meta,
|
|
26
|
+
onChange,
|
|
27
|
+
options: userOptions = [],
|
|
28
|
+
placeholder,
|
|
29
|
+
renderMenu,
|
|
30
|
+
renderOption,
|
|
31
|
+
renderTrigger,
|
|
32
|
+
required = false,
|
|
33
|
+
value
|
|
34
|
+
}, ref) => {
|
|
35
|
+
const hiddenInput = useRef(null);
|
|
36
|
+
|
|
37
|
+
const [internalValue, setInternalValue] = useState(meta?.initial ?? value ?? '');
|
|
38
|
+
|
|
39
|
+
const findSelectedOptionByValue = useCallback((val) => {
|
|
40
|
+
return userOptions.find(opt => opt.value === val);
|
|
41
|
+
}, [userOptions]);
|
|
42
|
+
|
|
43
|
+
const [selectedOption, setSelectedOption] = useState(findSelectedOptionByValue(meta?.initial ?? '') ?? {});
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
// We treat non-undefined value/formInput values as valid values, whereas undefined is only relevant when prop has not been provided
|
|
47
|
+
if (value !== undefined && !(value === selectedOption?.value && value === internalValue)) {
|
|
48
|
+
setSelectedOption(findSelectedOptionByValue(value));
|
|
49
|
+
setInternalValue(value);
|
|
50
|
+
} else if (formInput?.value !== undefined && !(value === selectedOption?.value && value === internalValue)) {
|
|
51
|
+
setSelectedOption(findSelectedOptionByValue(formInput.value));
|
|
52
|
+
setInternalValue(formInput.value);
|
|
53
|
+
}
|
|
54
|
+
}, [formInput, value, internalValue, selectedOption, findSelectedOptionByValue]);
|
|
55
|
+
|
|
56
|
+
// Way to grab internal selectedOption state, thereby avoiding having to reparse options for it
|
|
57
|
+
useImperativeHandle(ref, () => ({
|
|
58
|
+
selectedOption
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
const defaultRenderTrigger = ({ onToggle, triggerRef, keyHandler, open, ariaProps, getTriggerProps }) => {
|
|
62
|
+
return (
|
|
63
|
+
<Button
|
|
64
|
+
ref={triggerRef}
|
|
65
|
+
buttonClass={css.iconButtonText}
|
|
66
|
+
buttonStyle="none"
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
marginBottom0
|
|
69
|
+
onClick={onToggle}
|
|
70
|
+
onKeyDown={keyHandler}
|
|
71
|
+
paddingSide0
|
|
72
|
+
type="button"
|
|
73
|
+
{...getTriggerProps()}
|
|
74
|
+
{...ariaProps}
|
|
75
|
+
>
|
|
76
|
+
<Icon
|
|
77
|
+
icon={open ? 'caret-up' : 'caret-down'}
|
|
78
|
+
iconPosition="end"
|
|
79
|
+
>
|
|
80
|
+
{
|
|
81
|
+
selectedOption?.label ??
|
|
82
|
+
selectedOption?.value ??
|
|
83
|
+
(internalValue || null) ??
|
|
84
|
+
placeholder ??
|
|
85
|
+
<FormattedMessage id="stripes-kint-components.richSelect.placeholder" />
|
|
86
|
+
}
|
|
87
|
+
</Icon>
|
|
88
|
+
</Button>
|
|
89
|
+
// Awaiting result of conversation about whether we want to allow labels on IconButtons
|
|
90
|
+
/* <IconButton
|
|
91
|
+
ref={triggerRef}
|
|
92
|
+
disabled={disabled}
|
|
93
|
+
icon={open ? 'caret-up' : 'caret-down'}
|
|
94
|
+
iconPosition="end"
|
|
95
|
+
marginBottom0
|
|
96
|
+
onClick={onToggle}
|
|
97
|
+
onKeyDown={keyHandler}
|
|
98
|
+
type="button"
|
|
99
|
+
{...getTriggerProps()}
|
|
100
|
+
{...ariaProps}
|
|
101
|
+
>
|
|
102
|
+
{
|
|
103
|
+
selectedOption?.label ??
|
|
104
|
+
selectedOption?.value ??
|
|
105
|
+
(internalValue ? internalValue : null) ??
|
|
106
|
+
placeholder ??
|
|
107
|
+
<FormattedMessage id="stripes-kint-components.richSelect.placeholder" />
|
|
108
|
+
}
|
|
109
|
+
</IconButton> */
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const handleChange = (e) => {
|
|
114
|
+
// Actually set the value in the form (If formInput exists)
|
|
115
|
+
if (formInput?.onChange) {
|
|
116
|
+
formInput.onChange(e);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If the user has set up an onChange, this will ensure that that fires
|
|
120
|
+
if (onChange) {
|
|
121
|
+
onChange(e, e.target.value);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const changeField = (val) => {
|
|
126
|
+
nativeChangeField(hiddenInput, false, val);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const defaultRenderOption = (opt) => (opt.label ?? opt.value);
|
|
130
|
+
|
|
131
|
+
const defaultRenderMenu = ({ onToggle }) => (
|
|
132
|
+
<DropdownMenu>
|
|
133
|
+
{userOptions.map(opt => {
|
|
134
|
+
return (
|
|
135
|
+
<Button
|
|
136
|
+
autoFocus={selectedOption?.value === opt.value}
|
|
137
|
+
buttonStyle="dropdownItem"
|
|
138
|
+
id={`${id}-${formInput.name}-option-${opt.value}`}
|
|
139
|
+
marginBottom0
|
|
140
|
+
onClick={() => {
|
|
141
|
+
// Trigger change event
|
|
142
|
+
changeField(opt.value);
|
|
143
|
+
|
|
144
|
+
// Close the menu on select
|
|
145
|
+
onToggle();
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
{renderOption ? renderOption(opt) : defaultRenderOption(opt)}
|
|
149
|
+
</Button>
|
|
150
|
+
);
|
|
151
|
+
})}
|
|
152
|
+
</DropdownMenu>
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<>
|
|
157
|
+
{(label || ariaLabel) ? (
|
|
158
|
+
<Label
|
|
159
|
+
className={ariaLabel && 'sr-only'}
|
|
160
|
+
htmlFor={id}
|
|
161
|
+
id={`${id}-label`}
|
|
162
|
+
required={required}
|
|
163
|
+
>
|
|
164
|
+
{ariaLabel || label}
|
|
165
|
+
</Label>)
|
|
166
|
+
: ''
|
|
167
|
+
}
|
|
168
|
+
<Dropdown
|
|
169
|
+
hasPadding
|
|
170
|
+
placement="bottom-start"
|
|
171
|
+
renderMenu={(menuProps) => {
|
|
172
|
+
if (renderMenu) {
|
|
173
|
+
return renderMenu({ ...menuProps, selectedOption, changeField });
|
|
174
|
+
} else {
|
|
175
|
+
return defaultRenderMenu(menuProps);
|
|
176
|
+
}
|
|
177
|
+
}}
|
|
178
|
+
renderTrigger={(triggerProps) => {
|
|
179
|
+
if (renderTrigger) {
|
|
180
|
+
return renderTrigger({ ...triggerProps, selectedOption });
|
|
181
|
+
} else {
|
|
182
|
+
return defaultRenderTrigger(triggerProps);
|
|
183
|
+
}
|
|
184
|
+
}}
|
|
185
|
+
{...dropdownProps}
|
|
186
|
+
/>
|
|
187
|
+
<input
|
|
188
|
+
{...formInput}
|
|
189
|
+
ref={hiddenInput}
|
|
190
|
+
hidden
|
|
191
|
+
onChange={handleChange}
|
|
192
|
+
type="text"
|
|
193
|
+
value={internalValue}
|
|
194
|
+
/>
|
|
195
|
+
</>
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
RichSelect.propTypes = {
|
|
200
|
+
ariaLabel: PropTypes.string,
|
|
201
|
+
disabled: PropTypes.bool,
|
|
202
|
+
dropdownProps: PropTypes.object,
|
|
203
|
+
id: PropTypes.string,
|
|
204
|
+
input: PropTypes.object,
|
|
205
|
+
label: PropTypes.oneOfType([
|
|
206
|
+
PropTypes.string,
|
|
207
|
+
PropTypes.func,
|
|
208
|
+
PropTypes.node
|
|
209
|
+
]),
|
|
210
|
+
meta: PropTypes.object,
|
|
211
|
+
onChange: PropTypes.func,
|
|
212
|
+
options: PropTypes.arrayOf(PropTypes.shape({
|
|
213
|
+
value: PropTypes.string.isRequired,
|
|
214
|
+
label: PropTypes.oneOfType([
|
|
215
|
+
PropTypes.string,
|
|
216
|
+
PropTypes.node
|
|
217
|
+
])
|
|
218
|
+
})),
|
|
219
|
+
placeholder: PropTypes.oneOfType([
|
|
220
|
+
PropTypes.string,
|
|
221
|
+
PropTypes.node
|
|
222
|
+
]),
|
|
223
|
+
renderMenu: PropTypes.func,
|
|
224
|
+
renderOption: PropTypes.func,
|
|
225
|
+
renderTrigger: PropTypes.func,
|
|
226
|
+
required: PropTypes.bool,
|
|
227
|
+
value: PropTypes.string,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export default RichSelect;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const useSelectedOption = () => {
|
|
4
|
+
const [selectedOption, setSelectedOption] = useState();
|
|
5
|
+
const ref = useCallback(node => {
|
|
6
|
+
if (node !== null && selectedOption?.value !== node.selectedOption?.value) {
|
|
7
|
+
setSelectedOption(node.selectedOption);
|
|
8
|
+
}
|
|
9
|
+
}, [selectedOption]);
|
|
10
|
+
|
|
11
|
+
return ([ref, selectedOption]);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default useSelectedOption;
|
|
@@ -22,7 +22,8 @@ const useMutateRefdataCategory = ({
|
|
|
22
22
|
const deleteQueryObject = useMutation(
|
|
23
23
|
['stripes-kint-components', 'useMutateRefdataCategory', 'delete'],
|
|
24
24
|
async (id) => ky.delete(
|
|
25
|
-
`${endpoint}/${id}`
|
|
25
|
+
`${endpoint}/${id}`
|
|
26
|
+
).json()
|
|
26
27
|
.then(afterQueryCalls?.delete)
|
|
27
28
|
.then(() => invalidateRefdata())
|
|
28
29
|
.catch(catchQueryCalls?.delete),
|
|
@@ -37,8 +38,7 @@ const useMutateRefdataCategory = ({
|
|
|
37
38
|
...payload,
|
|
38
39
|
values: []
|
|
39
40
|
}
|
|
40
|
-
}
|
|
41
|
-
).json()
|
|
41
|
+
}).json()
|
|
42
42
|
.then(afterQueryCalls?.post)
|
|
43
43
|
.then(() => invalidateRefdata())
|
|
44
44
|
.catch(catchQueryCalls?.post),
|
|
@@ -117,5 +117,6 @@
|
|
|
117
117
|
"operator.isNot": "nicht",
|
|
118
118
|
"operator.contains": "Enthält",
|
|
119
119
|
"operator.doesNotContain": "nicht enthalten",
|
|
120
|
-
"iconSelect.notSet": "Nicht gesetzt"
|
|
120
|
+
"iconSelect.notSet": "Nicht gesetzt",
|
|
121
|
+
"richSelect.placeholder": "Bitte wählen Sie eine Option"
|
|
121
122
|
}
|