Skip to content

Commit dea7daf

Browse files
committed
Add multiselects
1 parent 4ed6283 commit dea7daf

File tree

2 files changed

+156
-50
lines changed

2 files changed

+156
-50
lines changed

packages/carbon-component-mapper/demo/index.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,6 @@ class App extends React.Component {
4646
FormTemplate={(props) => <FormTemplate {...props} showFormControls={this.state.additionalOptions.showFormControls} />}
4747
onCancel={console.log}
4848
schema={this.state.schema}
49-
/*schema={{
50-
fields: [
51-
{
52-
component: 'time-picker',
53-
name: 'tp',
54-
twelveHoursFormat: true,
55-
timezones: [{ value: 'utc', label: 'UTC' }]
56-
}
57-
]
58-
}}*/
5949
{...this.state.additionalOptions}
6050
/>
6151
</div>
Lines changed: 156 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,162 @@
11
import React, { useState, useEffect } from 'react';
2-
import PropTypes from 'prop-types';
2+
import PropTypes, { bool } from 'prop-types';
33
import { useFieldApi } from '@data-driven-forms/react-form-renderer';
44

55
import DataDrivenSelect from '@data-driven-forms/common/src/select';
6-
import useIsMounted from '@data-driven-forms/common/src/hooks/use-is-mounted';
76
import fnToString from '@data-driven-forms/common/src/utils/fn-to-string';
87

98
import { Select as CarbonSelect, MultiSelect, SelectItem } from 'carbon-components-react';
109
import prepareProps from '../common/prepare-props';
1110

12-
const ClearedMultiSelectFilterable = (props) => {
13-
return 'multi.searchable';
11+
const ClearedMultiSelectFilterable = ({
12+
invalidText,
13+
hideSelectedOptions,
14+
noOptionsMessage,
15+
onInputChange,
16+
options,
17+
isFetching,
18+
invalid,
19+
isMulti,
20+
classNamePrefix,
21+
closeMenuOnSelect,
22+
onChange,
23+
originalOnChange,
24+
carbonLabel,
25+
placeholder,
26+
isDisabled,
27+
...rest
28+
}) => (
29+
<MultiSelect.Filterable
30+
disabled={isDisabled}
31+
{...rest}
32+
placeholder={carbonLabel || placeholder}
33+
onChange={originalOnChange}
34+
titleText={rest.labelText}
35+
id={rest.name}
36+
invalid={Boolean(invalidText)}
37+
invalidText={invalidText}
38+
items={options}
39+
initialSelectedItems={Array.isArray(rest.value) ? rest.value : []}
40+
/>
41+
);
42+
43+
ClearedMultiSelectFilterable.propTypes = {
44+
invalidText: PropTypes.node,
45+
hideSelectedOptions: PropTypes.any,
46+
noOptionsMessage: PropTypes.any,
47+
onInputChange: PropTypes.func,
48+
options: PropTypes.array,
49+
isFetching: PropTypes.bool,
50+
invalid: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
51+
isMulti: PropTypes.bool,
52+
classNamePrefix: PropTypes.any,
53+
closeMenuOnSelect: PropTypes.any,
54+
onChange: PropTypes.func,
55+
originalOnChange: PropTypes.func,
56+
carbonLabel: PropTypes.node,
57+
placeholder: PropTypes.node,
58+
isDisabled: PropTypes.bool
1459
};
1560

16-
const ClearedMultiSelect = (props) => {
17-
return 'multi';
61+
const ClearedMultiSelect = ({
62+
invalidText,
63+
hideSelectedOptions,
64+
noOptionsMessage,
65+
onInputChange,
66+
options,
67+
isFetching,
68+
invalid,
69+
isMulti,
70+
classNamePrefix,
71+
closeMenuOnSelect,
72+
onChange,
73+
originalOnChange,
74+
carbonLabel,
75+
placeholder,
76+
isDisabled,
77+
...rest
78+
}) => (
79+
<MultiSelect
80+
disabled={isDisabled}
81+
{...rest}
82+
label={carbonLabel || placeholder}
83+
onChange={originalOnChange}
84+
titleText={rest.labelText}
85+
id={rest.name}
86+
invalid={Boolean(invalidText)}
87+
invalidText={invalidText}
88+
items={options}
89+
initialSelectedItems={Array.isArray(rest.value) ? rest.value : []}
90+
/>
91+
);
92+
93+
ClearedMultiSelect.propTypes = {
94+
invalidText: PropTypes.node,
95+
hideSelectedOptions: PropTypes.any,
96+
noOptionsMessage: PropTypes.any,
97+
onInputChange: PropTypes.func,
98+
options: PropTypes.array,
99+
isFetching: PropTypes.bool,
100+
invalid: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
101+
isMulti: PropTypes.bool,
102+
classNamePrefix: PropTypes.any,
103+
closeMenuOnSelect: PropTypes.any,
104+
onChange: PropTypes.func,
105+
originalOnChange: PropTypes.func,
106+
carbonLabel: PropTypes.node,
107+
placeholder: PropTypes.node,
108+
isDisabled: PropTypes.bool
18109
};
19110

20-
const ClearedSelect = ({ invalidText, hideSelectedOptions, noOptionsMessage, onInputChange, options, isFetching, invalid, ...rest }) => {
21-
return (
22-
<CarbonSelect {...rest} if={rest.name} invalid={Boolean(invalidText)} invalidText={invalidText}>
23-
{options.map((option, index) => (
24-
<SelectItem key={option.value || index} text={option.label} {...option} />
25-
))}
26-
</CarbonSelect>
27-
);
111+
const ClearedSelect = ({
112+
isSearchable,
113+
isClearable,
114+
isDisabled,
115+
isMulti,
116+
invalidText,
117+
hideSelectedOptions,
118+
noOptionsMessage,
119+
onInputChange,
120+
options,
121+
isFetching,
122+
invalid,
123+
classNamePrefix,
124+
closeMenuOnSelect,
125+
originalOnChange,
126+
placeholder,
127+
...rest
128+
}) => (
129+
<CarbonSelect disabled={isFetching} {...rest} id={rest.name} invalid={Boolean(invalidText)} invalidText={invalidText}>
130+
{isFetching && <SelectItem text={placeholder} value={''} />}
131+
{options.map((option, index) => (
132+
<SelectItem key={option.value || index} text={option.label} {...option} />
133+
))}
134+
</CarbonSelect>
135+
);
136+
137+
ClearedSelect.propTypes = {
138+
invalidText: PropTypes.node,
139+
hideSelectedOptions: PropTypes.any,
140+
noOptionsMessage: PropTypes.any,
141+
onInputChange: PropTypes.func,
142+
options: PropTypes.array,
143+
isFetching: PropTypes.bool,
144+
invalid: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
145+
isMulti: PropTypes.bool,
146+
classNamePrefix: PropTypes.any,
147+
closeMenuOnSelect: PropTypes.any,
148+
onChange: PropTypes.func,
149+
originalOnChange: PropTypes.func,
150+
carbonLabel: PropTypes.node,
151+
placeholder: PropTypes.node,
152+
isDisabled: PropTypes.bool,
153+
isSearchable: bool,
154+
isClearable: bool
28155
};
29156

30157
const Select = (props) => {
31158
const { isMulti, isSearchable, loadOptions, input, meta, ...rest } = useFieldApi(prepareProps(props));
32159

33-
const [isFetching, setFetching] = useState(loadOptions ? true : false);
34-
const [options, setOptions] = useState(props.options || []);
35-
const isMounted = useIsMounted();
36-
37-
// common select controls the string of loadOptions and if the string changed, then it reloads options
38-
// however we are enhancing the loadOptions here so the string is always the same
39-
// by increasing this counter, we can enforce the update
40160
const [loadOptionsChangeCounter, setCounter] = useState(0);
41161

42162
const loadOptionsStr = fnToString(loadOptions);
@@ -46,36 +166,28 @@ const Select = (props) => {
46166
// eslint-disable-next-line react-hooks/exhaustive-deps
47167
}, [loadOptionsStr]);
48168

49-
const loadOptionsEnhanced = loadOptions
50-
? (value) => {
51-
if (isMounted.current) {
52-
setFetching(true);
53-
}
54-
55-
return loadOptions(value).then((data) => {
56-
if (isMounted.current) {
57-
setOptions([...options, ...data.filter(({ value }) => !options.find((option) => option.value === value))]);
58-
setFetching(false);
59-
}
60-
61-
return data;
62-
});
63-
}
64-
: undefined;
65-
66169
const Component = isMulti && isSearchable ? ClearedMultiSelectFilterable : isMulti ? ClearedMultiSelect : ClearedSelect;
67170

68171
const invalidText = (meta.touched && meta.error) || '';
69172

173+
const specialOnChange = ({ selectedItems }) => {
174+
if (rest.simpleValue) {
175+
return input.onChange(selectedItems.map(({ value }) => value));
176+
} else {
177+
return input.onChange(selectedItems);
178+
}
179+
};
180+
70181
return (
71182
<DataDrivenSelect
72183
SelectComponent={Component}
184+
simpleValue={false}
73185
{...rest}
74186
{...input}
187+
loadOptions={loadOptions}
75188
invalidText={invalidText}
76189
loadOptionsChangeCounter={loadOptionsChangeCounter}
77-
loadOptions={loadOptionsEnhanced}
78-
simpleValue={false}
190+
originalOnChange={specialOnChange}
79191
/>
80192
);
81193
};
@@ -90,4 +202,8 @@ Select.propTypes = {
90202
)
91203
};
92204

205+
Select.defaultProps = {
206+
loadingMessage: 'Loading...'
207+
};
208+
93209
export default Select;

0 commit comments

Comments
 (0)