Skip to content

Commit ed40861

Browse files
committed
fix(pf4): added searchable prop to downshift
1 parent a704938 commit ed40861

File tree

6 files changed

+134
-107
lines changed

6 files changed

+134
-107
lines changed

packages/pf4-component-mapper/demo/demo-schemas/select-schema.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ const selectSchema = {
4444
label: 'Clearable-select',
4545
options,
4646
isClearable: true
47+
},
48+
{
49+
component: componentTypes.SELECT,
50+
name: 'searchable-select',
51+
label: 'Clearable-select',
52+
options,
53+
isSearchable: true
4754
}
4855
]
4956
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
const EmptyOptions = ({ noOptionsMessage, noResultsMessage, getInputProps, isSearchable }) => {
5+
const { value } = getInputProps();
6+
const message = 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+
};
16+
17+
export default EmptyOptions;

packages/pf4-component-mapper/src/common/select/input.js

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,26 @@
1-
import React from 'react';
1+
import React, { Fragment } from 'react';
22
import PropTypes from 'prop-types';
3+
import { Divider } from '@patternfly/react-core';
34

45
import './input.scss';
56

6-
const getInputLabel = (value) => {
7-
if (!value) {
8-
return '';
9-
}
10-
11-
if (Array.isArray(value)) {
12-
return value.map((item) => (typeof item === 'object' ? item.label : item)).join(',');
13-
}
14-
15-
if (typeof value === 'object') {
16-
return value.label;
17-
}
18-
19-
return value;
20-
};
21-
22-
const Input = ({ value, inputRef, isSearchable, ...props }) => {
7+
const Input = ({ inputRef, isSearchable, isDisabled, getInputProps, ...props }) => {
238
return (
24-
<input
25-
{...props}
26-
{...(!isSearchable && { tabIndex: '-1' })}
27-
className="ddorg__pf4-component-mapper__select-input"
28-
value={getInputLabel(value)}
29-
onClick={console.log}
30-
ref={inputRef}
31-
/>
9+
<Fragment>
10+
<div className="pf-c-select__menu-search">
11+
<input
12+
autoFocus
13+
{...props}
14+
{...(!isSearchable && { tabIndex: '-1' })}
15+
className="pf-c-form-control pf-m-search"
16+
ref={inputRef}
17+
{...getInputProps({
18+
disabled: isDisabled
19+
})}
20+
/>
21+
</div>
22+
<Divider />
23+
</Fragment>
3224
);
3325
};
3426

packages/pf4-component-mapper/src/common/select/menu.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,35 @@
11
import React from 'react';
22
import Option from './option';
3+
import Input from './input';
4+
import EmptyOption from './empty-options';
5+
6+
const Menu = ({
7+
noResultsMessage,
8+
noOptionsMessage,
9+
filterOptions,
10+
inputRef,
11+
isSearchable,
12+
filterValue,
13+
options,
14+
getItemProps,
15+
getInputProps,
16+
highlightedIndex,
17+
selectedItem
18+
}) => {
19+
const filteredOptions = isSearchable ? filterOptions(options, filterValue) : options;
320

4-
const Menu = ({ options, getItemProps, highlightedIndex, selectedItem }) => {
521
return (
622
<ul className="pf-c-select__menu">
7-
{options.map((item, index) => {
23+
{isSearchable && <Input inputRef={inputRef} getInputProps={getInputProps} />}
24+
{filteredOptions.length === 0 && (
25+
<EmptyOption
26+
isSearchable={isSearchable}
27+
noOptionsMessage={noOptionsMessage}
28+
noResultsMessage={noResultsMessage}
29+
getInputProps={getInputProps}
30+
/>
31+
)}
32+
{filteredOptions.map((item, index) => {
833
const itemProps = getItemProps({
934
item,
1035
index,

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

Lines changed: 63 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,41 @@ import { CaretDownIcon } from '@patternfly/react-icons';
88
import '@patternfly/react-styles/css/components/Select/select.css';
99

1010
import './select-styles.scss';
11-
import Input from './input';
1211
import Menu from './menu';
1312
import ClearIndicator from './clear-indicator';
13+
import ValueContainer from './value-container';
1414

15-
const InternalSelect = ({ onChange, options, value, simpleValue, placeholder, isSearchable, isDisabled, isClearable, ...props }) => {
15+
const itemToString = (value) => {
16+
if (!value) {
17+
return '';
18+
}
19+
20+
if (Array.isArray(value)) {
21+
return value.map((item) => (typeof item === 'object' ? item.label : item)).join(',');
22+
}
23+
24+
if (typeof value === 'object') {
25+
return value.label;
26+
}
27+
28+
return value;
29+
};
30+
31+
const filterOptions = (options, filterValue = '') => options.filter(({ label }) => label.toLowerCase().includes(filterValue.toLowerCase()));
32+
33+
const InternalSelect = ({
34+
noResultsMessage,
35+
noOptionsMessage,
36+
onChange,
37+
options,
38+
value,
39+
simpleValue,
40+
placeholder,
41+
isSearchable,
42+
isDisabled,
43+
isClearable,
44+
...props
45+
}) => {
1646
// console.log(props);
1747
const inputRef = useRef();
1848
const parsedValue = parseInternalValue(value);
@@ -22,41 +52,39 @@ const InternalSelect = ({ onChange, options, value, simpleValue, placeholder, is
2252
onChange={(value) => {
2353
return onChange(value);
2454
}}
55+
itemToString={itemToString}
2556
selectedItem={value}
2657
>
27-
{({ isOpen, clearSelection, getInputProps, getToggleButtonProps, getItemProps, highlightedIndex }) => {
28-
const toggleButtonProps = { ...getToggleButtonProps() };
29-
const enhancedToggleButtonProps = {
30-
...toggleButtonProps,
31-
onClick: (...args) => {
32-
if (isSearchable) {
33-
inputRef.current.focus();
34-
}
35-
36-
return toggleButtonProps.onClick(...args);
37-
}
38-
};
58+
{({ isOpen, inputValue, itemToString, selectedItem, clearSelection, getInputProps, getToggleButtonProps, getItemProps, highlightedIndex }) => {
59+
const toggleButtonProps = getToggleButtonProps();
3960
return (
4061
<div className="pf-c-select">
41-
<button disabled={isDisabled} className={`pf-c-select__toggle${isDisabled ? ' pf-m-disabled' : ''}`} {...enhancedToggleButtonProps}>
62+
<button disabled={isDisabled} className={`pf-c-select__toggle${isDisabled ? ' pf-m-disabled' : ''}`} {...toggleButtonProps}>
4263
<div className="pf-c-select_toggle-wrapper ddorg__pf4-component-mapper__select-toggle-wrapper">
43-
<Input
44-
inputRef={inputRef}
45-
isSearchable={isSearchable}
46-
placeholder={placeholder}
47-
className="pf-c-select_toggle-text"
48-
{...getInputProps({
49-
disabled: isDisabled
50-
})}
51-
value={value}
52-
/>
64+
<ValueContainer placeholder={placeholder} value={itemToString(selectedItem)} />
5365
</div>
5466
<span className="pf-c-select__toggle-arrow">
5567
{isClearable && parsedValue && <ClearIndicator clearSelection={clearSelection} />}
5668
<CaretDownIcon />
5769
</span>
5870
</button>
59-
{isOpen && <Menu options={options} getItemProps={getItemProps} highlightedIndex={highlightedIndex} selectedItem={parsedValue} />}
71+
{isOpen && (
72+
<Menu
73+
noResultsMessage={noResultsMessage}
74+
noOptionsMessage={noOptionsMessage}
75+
inputRef={inputRef}
76+
isDisabled={isDisabled}
77+
placeholder={placeholder}
78+
isSearchable={isSearchable}
79+
getInputProps={getInputProps}
80+
filterOptions={filterOptions}
81+
filterValue={inputValue}
82+
options={options}
83+
getItemProps={getItemProps}
84+
highlightedIndex={highlightedIndex}
85+
selectedItem={parsedValue}
86+
/>
87+
)}
6088
</div>
6189
);
6290
}}
@@ -79,7 +107,9 @@ InternalSelect.propTypes = {
79107
id: PropTypes.string,
80108
name: PropTypes.string.isRequired,
81109
isDisabled: PropTypes.bool,
82-
isClearable: PropTypes.bool
110+
isClearable: PropTypes.bool,
111+
noResultsMessage: PropTypes.node,
112+
noOptionsMessage: PropTypes.func
83113
};
84114

85115
const Select = ({ selectVariant, menuIsPortal, ...props }) => {
@@ -108,9 +138,10 @@ Select.propTypes = {
108138
loadOptions: PropTypes.func,
109139
loadingMessage: PropTypes.node,
110140
updatingMessage: PropTypes.node,
111-
noOptionsMessage: PropTypes.func,
112141
menuIsPortal: PropTypes.bool,
113-
placeholder: PropTypes.string
142+
placeholder: PropTypes.string,
143+
noResultsMessage: PropTypes.node,
144+
noOptionsMessage: PropTypes.node
114145
};
115146

116147
Select.defaultProps = {
@@ -123,7 +154,9 @@ Select.defaultProps = {
123154
menuIsPortal: false,
124155
placeholder: 'Choose...',
125156
isSearchable: false,
126-
isClearable: false
157+
isClearable: false,
158+
noResultsMessage: 'No results found',
159+
noOptionsMessage: 'No options'
127160
};
128161

129162
export default Select;
Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,6 @@
1-
import React, { Component } from 'react';
2-
import PropTypes from 'prop-types';
1+
import React from 'react';
32

4-
import { Button } from '@patternfly/react-core';
3+
const ValueContainer = ({ value, placeholder }) => <span className="pf-c-select__toggle-text">{value || placeholder}</span>;
54

6-
class ValueContainer extends Component {
7-
state = {
8-
showAll: false
9-
};
10-
render() {
11-
const { isMulti, ...props } = this.props;
12-
const { showAll } = this.state;
13-
if (isMulti && props.children) {
14-
return (
15-
<div className="ddorg__pf4-component-mapper__select__value--container">
16-
{showAll ? props.children[0] : props.children[0] && props.children[0][0] ? props.children[0][0] : props.children[0]}
17-
{props.children[0] && props.children[0].length > 1 && (
18-
<Button
19-
className="ddorg__pf4-component-mapper__select__value--container-chipgroup"
20-
onClick={() => this.setState(({ showAll }) => ({ showAll: !showAll }))}
21-
variant="plain"
22-
>
23-
<span>{showAll ? props.selectProps.showLessLabel : `${props.children[0].length - 1} ${props.selectProps.showMoreLabel}`}</span>
24-
</Button>
25-
)}
26-
{Array.isArray(props.children) ? props.children[1] && props.children[1] : props.children}
27-
</div>
28-
);
29-
}
30-
31-
return props.children;
32-
}
33-
}
34-
35-
ValueContainer.propTypes = {
36-
isMulti: PropTypes.bool,
37-
getStyles: PropTypes.func.isRequired,
38-
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]).isRequired,
39-
selectProps: PropTypes.shape({
40-
showLessLabel: PropTypes.node,
41-
showMoreLabel: PropTypes.node
42-
})
43-
};
44-
45-
ValueContainer.defaultProps = {
46-
isMulti: false,
47-
selectProps: {
48-
showLessLabel: 'Show less',
49-
showMoreLabel: 'more'
50-
}
51-
};
525

536
export default ValueContainer;

0 commit comments

Comments
 (0)