Skip to content

Commit e54a88f

Browse files
authored
Merge pull request #562 from data-driven-forms/pf4-downshift-select
PF4 downshift select
2 parents 0aa9b98 + 33c9ccf commit e54a88f

20 files changed

+743
-481
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import componentTypes from '@data-driven-forms/react-form-renderer/dist/cjs/component-types';
2+
3+
const options = [
4+
{
5+
label: 'Morton',
6+
value: 'Jenifer'
7+
},
8+
{
9+
label: 'Vega',
10+
value: 'Cervantes'
11+
},
12+
{
13+
label: 'Gilbert',
14+
value: 'Wallace'
15+
},
16+
{
17+
label: 'Jami',
18+
value: 'Cecilia'
19+
},
20+
{
21+
label: 'Ebony',
22+
value: 'Kay'
23+
}
24+
];
25+
26+
const loadOptions = (inputValue = '') => {
27+
return new Promise((res) =>
28+
setTimeout(() => {
29+
if (inputValue.length === 0) {
30+
return res(options.slice(0, 3));
31+
}
32+
33+
return res(options.filter(({ label }) => label.toLocaleLowerCase().includes(inputValue.toLocaleLowerCase())));
34+
}, 1500)
35+
);
36+
};
37+
38+
const selectSchema = {
39+
fields: [
40+
{
41+
component: componentTypes.SELECT,
42+
name: 'simple-portal-select',
43+
label: 'Simple portal select',
44+
options,
45+
menuIsPortal: true
46+
},
47+
{
48+
component: componentTypes.SELECT,
49+
name: 'simple-async-select',
50+
label: 'Simple async select',
51+
loadOptions
52+
},
53+
{
54+
component: componentTypes.SELECT,
55+
name: 'simple-searchable-async-select',
56+
label: 'Simple searchable async select',
57+
loadOptions,
58+
isSearchable: true
59+
},
60+
{
61+
component: componentTypes.SELECT,
62+
name: 'multi-async-select',
63+
label: 'multi async select',
64+
loadOptions,
65+
isMulti: true
66+
},
67+
{
68+
component: componentTypes.SELECT,
69+
name: 'searchable-multi-async-select',
70+
label: 'Multi searchable async select',
71+
loadOptions,
72+
isSearchable: true
73+
},
74+
{
75+
component: componentTypes.SELECT,
76+
name: 'multi-simple-select',
77+
label: 'Simple multi select',
78+
options,
79+
isMulti: true
80+
},
81+
{
82+
component: componentTypes.SELECT,
83+
name: 'multi-searchable-select',
84+
label: 'Searchable multi select',
85+
options,
86+
isMulti: true,
87+
isSearchable: true
88+
},
89+
{
90+
component: componentTypes.SELECT,
91+
name: 'multi-clearable-searchable-select',
92+
label: 'Searchable clearable multi select',
93+
options,
94+
isMulti: true,
95+
isSearchable: true,
96+
isClearable: true
97+
},
98+
{
99+
component: componentTypes.SELECT,
100+
name: 'simple-select',
101+
label: 'Simple-select',
102+
options
103+
},
104+
{
105+
component: componentTypes.SELECT,
106+
name: 'disabled-select',
107+
label: 'Disabled-select',
108+
options,
109+
isDisabled: true
110+
},
111+
{
112+
component: componentTypes.SELECT,
113+
name: 'clearable-select',
114+
label: 'Clearable-select',
115+
options,
116+
isClearable: true
117+
},
118+
{
119+
component: componentTypes.SELECT,
120+
name: 'searchable-select',
121+
label: 'Clearable-select',
122+
options,
123+
isSearchable: true
124+
},
125+
{
126+
component: componentTypes.SELECT,
127+
name: 'dosbaled-option-select',
128+
label: 'Disabled-option-select',
129+
options: [...options, { label: 'Disabled option', value: 'disabled', isDisabled: true }]
130+
}
131+
]
132+
};
133+
134+
export default {
135+
...selectSchema
136+
};

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

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import FormRenderer from '@data-driven-forms/react-form-renderer';
55
import miqSchema from './demo-schemas/miq-schema';
66
import { uiArraySchema, arraySchema, array1Schema, schema, uiSchema, conditionalSchema, arraySchemaDDF } from './demo-schemas/widget-schema';
77
import { componentMapper, FormTemplate } from '../src';
8-
import { Title, Button, Toolbar, ToolbarGroup } from '@patternfly/react-core';
8+
import { Title, Button, Toolbar, ToolbarGroup, ToolbarItem, Modal } from '@patternfly/react-core';
99
import { wizardSchema, wizardSchemaWithFunction, wizardSchemaSimple, wizardSchemaSubsteps, wizardSchemaMoreSubsteps } from './demo-schemas/wizard-schema';
1010
import sandboxSchema from './demo-schemas/sandbox';
1111
import dualSchema from './demo-schemas/dual-list-schema';
1212
import demoSchema from '@data-driven-forms/common/src/demoschema';
13+
import selectSchema from './demo-schemas/select-schema';
1314

1415
const Summary = props => <div>Custom summary component.</div>;
1516

@@ -23,7 +24,7 @@ const fieldArrayState = { schema: arraySchemaDDF, additionalOptions: {
2324
class App extends React.Component {
2425
constructor(props) {
2526
super(props);
26-
this.state = fieldArrayState
27+
this.state = {schema: selectSchema, additionalOptions: {}}
2728
}
2829

2930
render() {
@@ -32,25 +33,30 @@ class App extends React.Component {
3233
<Title headingLevel="h2" size="4xl">Pf4 component mapper</Title>
3334
<Toolbar style={{ marginBottom: 20, marginTop: 20 }}>
3435
<ToolbarGroup>
35-
<Button onClick={() => this.setState(state => ({ schema: wizardSchema, additionalOptions: { showFormControls: false, wizard: true } }))}>Wizard</Button>
36-
</ToolbarGroup>
37-
<ToolbarGroup>
38-
<Button onClick={() => this.setState(state => fieldArrayState)}>arraySchema</Button>
39-
</ToolbarGroup>
40-
<ToolbarGroup>
41-
<Button onClick={() => this.setState(state => ({ schema: sandboxSchema, additionalOptions: {}}))}>Sandbox</Button>
42-
</ToolbarGroup>
43-
<ToolbarGroup>
44-
<Button onClick={() => this.setState(state => ({ schema: demoSchema, additionalOptions: {}}))}>Super schema</Button>
45-
</ToolbarGroup>
46-
<ToolbarGroup>
47-
<Button onClick={() => this.setState(state => ({ schema: dualSchema, additionalOptions: {} }))}>Dual List</Button>
36+
<ToolbarItem>
37+
<Button onClick={() => this.setState(state => ({schema: selectSchema, additionalOptions: {}}))}>select schema</Button>
38+
</ToolbarItem>
39+
<ToolbarItem>
40+
<Button onClick={() => this.setState(state => ({ schema: wizardSchema, additionalOptions: { showFormControls: false, wizard: true } }))}>Wizard</Button>
41+
</ToolbarItem>
42+
<ToolbarItem>
43+
<Button onClick={() => this.setState(state => fieldArrayState)}>arraySchema</Button>
44+
</ToolbarItem>
45+
<ToolbarItem>
46+
<Button onClick={() => this.setState(state => ({ schema: sandboxSchema, additionalOptions: {}}))}>Sandbox</Button>
47+
</ToolbarItem>
48+
<ToolbarItem>
49+
<Button onClick={() => this.setState(state => ({ schema: demoSchema, additionalOptions: {}}))}>Super schema</Button>
50+
</ToolbarItem>
51+
<ToolbarItem>
52+
<Button onClick={() => this.setState(state => ({ schema: dualSchema, additionalOptions: {} }))}>Dual List</Button>
53+
</ToolbarItem>
4854
</ToolbarGroup>
4955
</Toolbar>
5056
<FormRenderer
5157
onSubmit={console.log}
5258
initialValues={{
53-
'async-drop-down': 'async-option-2'
59+
'simple-select': 'Kay'
5460
}}
5561
componentMapper={{
5662
...componentMapper,

packages/pf4-component-mapper/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@
5757
"rollup-plugin-commonjs": "^10.1.0",
5858
"rollup-plugin-node-globals": "^1.4.0",
5959
"rollup-plugin-node-resolve": "^5.2.0",
60+
"rollup-plugin-postcss": "^3.1.2",
6061
"rollup-plugin-replace": "^2.2.0",
61-
"rollup-plugin-sass": "^1.2.2",
6262
"rollup-plugin-size-snapshot": "^0.10.0",
6363
"rollup-plugin-sourcemaps": "^0.5.0",
6464
"rollup-plugin-terser": "^5.1.2",
@@ -78,9 +78,8 @@
7878
"@patternfly/react-icons": "^4.3.5"
7979
},
8080
"dependencies": {
81-
"@patternfly/patternfly-next": "^1.0.175",
8281
"prop-types": "^15.7.2",
83-
"react-select": "^3.0.4"
82+
"downshift": "^5.4.3"
8483
},
8584
"postpublish": "export RELEASE_DEMO=true"
8685
}

packages/pf4-component-mapper/rollup.config.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import replace from 'rollup-plugin-replace';
55
import nodeGlobals from 'rollup-plugin-node-globals';
66
import { terser } from 'rollup-plugin-terser';
77
import { createFilter } from 'rollup-pluginutils';
8-
import sass from 'rollup-plugin-sass';
98
import async from 'rollup-plugin-async';
109
import sourcemaps from 'rollup-plugin-sourcemaps';
10+
import postcss from 'rollup-plugin-postcss';
1111

1212
import glob from 'glob';
1313
import path from 'path';
@@ -45,7 +45,10 @@ const babelOptions = {
4545

4646
const commonjsOptions = {
4747
ignoreGlobal: true,
48-
include: /node_modules/
48+
include: /node_modules/,
49+
namedExports: {
50+
'../../node_modules/react-is/index.js': ['isForwardRef']
51+
}
4952
};
5053

5154
const plugins = [
@@ -59,8 +62,9 @@ const plugins = [
5962
keep_classnames: true,
6063
keep_fnames: true
6164
}),
62-
sass({
63-
insert: true
65+
postcss({
66+
inject: true,
67+
extensions: ['.css', '.scss']
6468
}),
6569
sourcemaps()
6670
];
Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33

4-
import { Button, ButtonVariant } from '@patternfly/react-core';
5-
64
import { TimesCircleIcon } from '@patternfly/react-icons';
5+
import './clear-indicator.scss';
76

8-
const ClearIndicator = ({ clearValue, innerProps: { ref, ...restInnerProps } }) => (
9-
<Button {...restInnerProps} onClick={clearValue} variant={ButtonVariant.plain}>
7+
const ClearIndicator = ({ clearSelection }) => (
8+
<button
9+
onClick={(event) => {
10+
clearSelection();
11+
event.stopPropagation();
12+
}}
13+
className="pf-c-button pf-m-plain pf-c-select__toggle-clear pf-u-p-0"
14+
type="button"
15+
aria-label="Clear all"
16+
>
1017
<TimesCircleIcon />
11-
</Button>
18+
</button>
1219
);
1320

1421
ClearIndicator.propTypes = {
15-
innerProps: PropTypes.object.isRequired,
16-
clearValue: PropTypes.func
17-
};
18-
19-
ClearIndicator.defaultProps = {
20-
clearValue: () => undefined
22+
clearSelection: PropTypes.func.isRequired
2123
};
2224

2325
export default ClearIndicator;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.ddorg__pf4-component-mapper__select-clear-indicator {
2+
position: relative;
3+
display: inline-block;
4+
> svg {
5+
fill: var(--pf-global--palette--black-600)
6+
}
7+
&:hover > svg {
8+
fill: inherit
9+
}
10+
&::before {
11+
position: absolute;
12+
top: 0;
13+
bottom: 0;
14+
left: 0;
15+
right: 0;
16+
}
17+
}

packages/pf4-component-mapper/src/common/select/dropdown-indicator.js

Lines changed: 0 additions & 14 deletions
This file was deleted.
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 EmptyOptions = ({ noOptionsMessage, noResultsMessage, getInputProps, isSearchable, isFetching }) => {
5+
const { value } = getInputProps();
6+
const message = isFetching ? noOptionsMessage() : isSearchable && value ? noResultsMessage : noOptionsMessage();
7+
return <div className="pf-c-select__menu-item pf-m-disabled">{message}</div>;
8+
};
9+
10+
EmptyOptions.propTypes = {
11+
noOptionsMessage: PropTypes.func.isRequired,
12+
noResultsMessage: PropTypes.node.isRequired,
13+
getInputProps: PropTypes.func.isRequired,
14+
isSearchable: PropTypes.bool,
15+
isFetching: PropTypes.bool
16+
};
17+
18+
export default EmptyOptions;
Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
1-
import React from 'react';
2-
import { components } from 'react-select';
1+
import React, { Fragment } from 'react';
32
import PropTypes from 'prop-types';
3+
import { Divider } from '@patternfly/react-core';
44

5-
const Input = (props) => <components.Input {...props} />;
5+
import './input.scss';
6+
7+
const Input = ({ inputRef, isSearchable, isDisabled, getInputProps, value, ...props }) => {
8+
const inputProps = getInputProps({ disabled: isDisabled });
9+
return (
10+
<Fragment>
11+
<div className="pf-c-select__menu-search">
12+
<input
13+
autoFocus
14+
value=""
15+
{...props}
16+
{...(!isSearchable && { tabIndex: '-1' })}
17+
className="pf-c-form-control pf-m-search"
18+
ref={inputRef}
19+
{...{
20+
...inputProps,
21+
value: inputProps.value || '',
22+
onChange: inputProps.onChange || Function
23+
}}
24+
/>
25+
</div>
26+
<Divider />
27+
</Fragment>
28+
);
29+
};
630

731
Input.propTypes = {
8-
selectProps: PropTypes.shape({
9-
isMulti: PropTypes.bool
10-
}).isRequired
32+
inputRef: PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
33+
isSearchable: PropTypes.bool,
34+
isDisabled: PropTypes.bool,
35+
getInputProps: PropTypes.func.isRequired,
36+
value: PropTypes.string
1137
};
1238

1339
export default Input;

0 commit comments

Comments
 (0)