Skip to content

Commit 83cff51

Browse files
authored
Merge pull request #272 from data-driven-forms/common-select
Use select componsition to style pf3 select
2 parents e3dcc62 + 333b9b6 commit 83cff51

File tree

19 files changed

+2254
-1880
lines changed

19 files changed

+2254
-1880
lines changed

packages/common/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
"description": "Common components, helpers and other pieces of code",
55
"license": "Apache-2.0",
66
"main": "index.js",
7-
"scripts": {
8-
},
7+
"scripts": {},
98
"repository": "[email protected]:data-driven-forms/react-forms.git",
109
"devDependencies": {
1110
"@babel/core": "^7.2.2",
@@ -47,7 +46,10 @@
4746
"webpack-merge": "^4.1.4"
4847
},
4948
"dependencies": {
50-
"@babel/plugin-proposal-class-properties": "^7.1.0"
49+
"@babel/plugin-proposal-class-properties": "^7.1.0",
50+
"clsx": "^1.0.4",
51+
"lodash": "^4.17.15",
52+
"react-select": "^3.0.8"
5153
},
5254
"peerDependencies": {
5355
"react": "^16.6.0",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import PropTypes from 'prop-types';
2+
3+
export const optionsPropType = PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.string.isRequired, value: PropTypes.any }));

packages/common/src/select/index.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React, { Component } from 'react';
2+
import ReactSelect from 'react-select';
3+
import PropTypes from 'prop-types';
4+
import clsx from 'clsx';
5+
import isEqual from 'lodash/isEqual';
6+
7+
const getSelectValue = (stateValue, simpleValue, isMulti, allOptions) => simpleValue
8+
? allOptions.filter(({ value }) => isMulti
9+
? stateValue.includes(value)
10+
: isEqual(value, stateValue))
11+
: stateValue;
12+
13+
const handleSelectChange = (option, simpleValue, isMulti, onChange) => {
14+
const sanitizedOption = !option && isMulti ? [] : option;
15+
return simpleValue
16+
? onChange(isMulti
17+
? sanitizedOption.map(item => item.value)
18+
: sanitizedOption ? sanitizedOption.value : undefined)
19+
: onChange(sanitizedOption);
20+
};
21+
22+
class Select extends Component {
23+
render() {
24+
const { input, invalid, classNamePrefix, simpleValue, isMulti, pluckSingleValue, options, ...props } = this.props;
25+
const { value, onChange, ...inputProps } = input;
26+
27+
const selectValue = pluckSingleValue ? isMulti ? value : Array.isArray(value) && value[0] ? value[0] : value : value;
28+
29+
return (
30+
<ReactSelect
31+
className={ clsx(classNamePrefix, {
32+
'has-error': invalid,
33+
}) }
34+
{ ...props }
35+
{ ...inputProps }
36+
options={ options }
37+
classNamePrefix={ classNamePrefix }
38+
isMulti={ isMulti }
39+
value={ getSelectValue(selectValue, simpleValue, isMulti, options) }
40+
onChange={ option => handleSelectChange(option, simpleValue, isMulti, onChange) }
41+
/>
42+
);
43+
}
44+
}
45+
46+
Select.propTypes = {
47+
options: PropTypes.array,
48+
onChange: PropTypes.func,
49+
classNamePrefix: PropTypes.string.isRequired,
50+
invalid: PropTypes.bool,
51+
simpleValue: PropTypes.bool,
52+
isMulti: PropTypes.bool,
53+
pluckSingleValue: PropTypes.bool,
54+
};
55+
56+
Select.defaultProps = {
57+
options: [],
58+
invalid: false,
59+
simpleValue: true,
60+
pluckSingleValue: true,
61+
};
62+
63+
const DataDrivenSelect = ({ multi, ...props }) => {
64+
const isMulti = props.isMulti || multi;
65+
const closeMenuOnSelect = !isMulti;
66+
return (
67+
<Select
68+
hideSelectedOptions={ false }
69+
isMulti={ isMulti }
70+
{ ...props }
71+
closeMenuOnSelect={ closeMenuOnSelect }
72+
/>
73+
);
74+
};
75+
76+
DataDrivenSelect.propTypes = {
77+
value: PropTypes.any,
78+
onChange: PropTypes.func,
79+
multi: PropTypes.bool,
80+
placeholder: PropTypes.string,
81+
isMulti: PropTypes.bool,
82+
classNamePrefix: PropTypes.string.isRequired,
83+
};
84+
85+
DataDrivenSelect.defaultProps = {
86+
placeholder: 'Choose...',
87+
isSearchable: false,
88+
isClearable: false,
89+
};
90+
91+
export default DataDrivenSelect;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const fnToString = (fn = '') => fn.toString().replace(/\s+/g, ' ');
2+
3+
export default fnToString;

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

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,111 @@ import wizardSchema from './demo-schemas/wizard-schema';
1010
import sandbox from './demo-schemas/sandbox';
1111
import Switch from "../src/form-fields/switch-field";
1212

13+
const loadOptions = () => new Promise((res) => {
14+
setTimeout(() => {
15+
fetch('https://dog.ceo/api/breeds/list/all')
16+
.then(data => data.json())
17+
.then(({ message: { bulldog } }) => bulldog.map(dog => ({ label: dog, value: dog })))
18+
.then(data => res(data))
19+
}, 250)
20+
})
21+
22+
const selectSchema = {
23+
fields: [{
24+
component: 'select-field',
25+
name: 'async-single',
26+
label: 'Async single',
27+
multi: true,
28+
loadOptions
29+
},{
30+
component: 'select-field',
31+
name: 'async-single-search',
32+
label: 'Async single search',
33+
isSearchable: true,
34+
loadOptions
35+
}, {
36+
component: 'select-field',
37+
name: 'async-multi-search',
38+
label: 'Async multi search',
39+
isSearchable: true,
40+
multi: true,
41+
loadOptions
42+
}, {
43+
component: 'select-field',
44+
name: 'select-single',
45+
label: 'Select single',
46+
isDisabled: true,
47+
options: [{
48+
label: 'foo',
49+
value: 123
50+
}, {
51+
label: 'bar',
52+
value: 231
53+
}]
54+
}, {
55+
component: 'select-field',
56+
name: 'select-search',
57+
label: 'Select search',
58+
isRequired: true,
59+
validateOnMount: true,
60+
isDisabled: true,
61+
isClearable: true,
62+
multi: true,
63+
isSearchable: true,
64+
placeholder: 'Placeholder',
65+
validate: [{
66+
type: 'required-validator'
67+
}],
68+
options: [{
69+
label: 'foo',
70+
value: 123
71+
}, {
72+
label: 'bar',
73+
value: 231
74+
}, {
75+
label: 'Super long option label, Super long option label, Super long option label, Super long option label, Super long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option label',
76+
value: 'x'
77+
}]
78+
}, {
79+
component: 'select-field',
80+
name: 'select',
81+
label: 'Select',
82+
isRequired: true,
83+
validateOnMount: true,
84+
isClearable: true,
85+
multi: false,
86+
isSearchable: true,
87+
placeholder: 'Placeholder',
88+
validate: [{
89+
type: 'required-validator'
90+
}],
91+
options: [{
92+
label: 'foo',
93+
value: 123
94+
}, {
95+
label: 'bar',
96+
value: 231
97+
}, {
98+
label: 'Super long option label, Super long option label, Super long option label, Super long option label, Super long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option label',
99+
value: 'x'
100+
}]
101+
}]
102+
}
103+
13104
class App extends React.Component {
14105
render() {
15-
const initialValues = {
16-
date_control_1: new Date()
17-
}
18106
return (
19107
<div>
20108
<h1>Pf3 component mapper</h1>
21109
<Grid>
22110
<Row>
23111
<FormRenderer
24-
initialValues={initialValues}
112+
initialValues={{}}
25113
onSubmit={console.log}
26114
schemaType="default"
27115
formFieldsMapper={formFieldsMapper}
28116
layoutMapper={layoutMapper}
29-
schema={sandbox}
117+
schema={selectSchema}
30118
onCancel={() => console.log('cancel')}
31119
/>
32120
</Row>

packages/pf3-component-mapper/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@
8888
]
8989
},
9090
"dependencies": {
91+
"clsx": "^1.0.4",
9192
"react-day-picker": "^7.3.2",
92-
"react-select": "^2.1.2"
93+
"react-select": "^3.0.8"
9394
},
9495
"resolutions": {
9596
"terser": "3.14.1"

packages/pf3-component-mapper/src/form-fields/form-fields.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import MultipleChoiceList from './multiple-choice-list';
77
import RequiredLabel from './required-label';
88
import Switch from './switch-field';
99
import { DateTimePicker } from './date-time-picker/date-time-picker';
10-
import Select from './select';
10+
1111
import RagioGroup from './radio';
1212
import PlainText from './plain-text';
13+
import Select from './select';
1314

1415
const selectComponent = ({
1516
componentType,
@@ -51,9 +52,10 @@ const selectComponent = ({
5152
),
5253
[componentTypes.SELECT_COMPONENT]: () => <div>
5354
<Select
55+
classNamePrefix="ddorg__pf3-component-mapper__select"
5456
loadOptions={ loadOptions }
5557
options={ options }
56-
invalid={ invalid }
58+
invalid={ !!invalid }
5759
input={ input }
5860
placeholder={ placeholder }
5961
isSearchable={ isSearchable }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
const ClearIndicator = ({
5+
clearValue,
6+
innerProps: { ref, ...restInnerProps },
7+
}) => (
8+
<button { ...restInnerProps } onClick={ clearValue } className="ddorg__pf3-component-mapper__select__close-indicator">
9+
<i className="fa fa-times" />
10+
</button>
11+
);
12+
13+
ClearIndicator.propTypes = {
14+
innerProps: PropTypes.object.isRequired,
15+
clearValue: PropTypes.func.isRequired,
16+
};
17+
18+
export default ClearIndicator;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import clsx from 'clsx';
4+
5+
const DropdownIndicator = ({ selectProps: { isFetching, value }}) => isFetching
6+
? <i className={ clsx('ddorg__pf3-component-mapper__select__dropdown-indicator fa fa-circle-o-notch spin', {
7+
placeholder: value.length === 0,
8+
}) } />
9+
: <i className={ clsx('ddorg__pf3-component-mapper__select__dropdown-indicator fa fa-angle-down', {
10+
placeholder: value.length === 0,
11+
}) }/>;
12+
13+
DropdownIndicator.propTypes = {
14+
selectProps: PropTypes.shape({
15+
isFetching: PropTypes.bool,
16+
value: PropTypes.any,
17+
}).isRequired,
18+
};
19+
20+
export default DropdownIndicator;

0 commit comments

Comments
 (0)