Skip to content

Commit b227500

Browse files
committed
fix(pf4): added multi downshift select
1 parent ed40861 commit b227500

File tree

5 files changed

+92
-19
lines changed

5 files changed

+92
-19
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
@@ -25,6 +25,13 @@ const options = [
2525

2626
const selectSchema = {
2727
fields: [
28+
{
29+
component: componentTypes.SELECT,
30+
name: 'multi-simple-select',
31+
label: 'Simple multi select',
32+
options,
33+
isMulti: true
34+
},
2835
{
2936
component: componentTypes.SELECT,
3037
name: 'simple-select',

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ const Menu = ({
1414
getItemProps,
1515
getInputProps,
1616
highlightedIndex,
17-
selectedItem
17+
selectedItem,
18+
isMulti
1819
}) => {
1920
const filteredOptions = isSearchable ? filterOptions(options, filterValue) : options;
20-
2121
return (
2222
<ul className="pf-c-select__menu">
2323
{isSearchable && <Input inputRef={inputRef} getInputProps={getInputProps} />}
@@ -34,7 +34,7 @@ const Menu = ({
3434
item,
3535
index,
3636
isActive: highlightedIndex === index,
37-
isSelected: selectedItem === item.value
37+
isSelected: isMulti ? !!selectedItem.find(({ value }) => item.value === value) : selectedItem === item.value
3838
});
3939
return <Option key={item.value} item={item} {...itemProps} />;
4040
})}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CheckIcon } from '@patternfly/react-icons';
55

66
const Option = ({ item, isActive, isSelected, ...props }) => (
77
<li>
8-
<button {...props} className={`pf-c-select__menu-item${isSelected ? ' pf-m-selected' : ''}${isActive ? ' pf-m-focus' : ''}`}>
8+
<button {...props} type="button" className={`pf-c-select__menu-item${isSelected ? ' pf-m-selected' : ''}${isActive ? ' pf-m-focus' : ''}`}>
99
{item.label}
1010
{isSelected && (
1111
<span className="pf-c-select__menu-item-icon">

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

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,61 @@
1-
import React, { useRef } from 'react';
1+
import React, { useRef, useState } from 'react';
22
import PropTypes from 'prop-types';
33

44
import DataDrivenSelect from '@data-driven-forms/common/src/select';
55
import parseInternalValue from '@data-driven-forms/common/src/select/parse-internal-value';
66
import Downshift from 'downshift';
7-
import { CaretDownIcon } from '@patternfly/react-icons';
7+
import { CaretDownIcon, CloseIcon } from '@patternfly/react-icons';
88
import '@patternfly/react-styles/css/components/Select/select.css';
9+
import '@patternfly/react-styles/css/components/Chip/chip.css';
10+
import '@patternfly/react-styles/css/components/ChipGroup/chip-group.css';
911

1012
import './select-styles.scss';
1113
import Menu from './menu';
1214
import ClearIndicator from './clear-indicator';
1315
import ValueContainer from './value-container';
1416

15-
const itemToString = (value) => {
17+
const itemToString = (value, isMulti, showMore, handleShowMore, handleChange) => {
1618
if (!value) {
1719
return '';
1820
}
1921

2022
if (Array.isArray(value)) {
23+
if (!value || value.length === 0) {
24+
return;
25+
}
26+
27+
if (isMulti) {
28+
const visibleOptions = showMore ? value : value.slice(0, 3);
29+
return (
30+
<div className="pf-c-chip-group" onClick={(event) => event.stopPropagation()}>
31+
<ul className="pf-c-chip-group__list" aria-label="Chip group category">
32+
{visibleOptions.map((item, index) => {
33+
const label = typeof item === 'object' ? item.label : item;
34+
return (
35+
<li className="pf-c-chip-group__list-item" onClick={(event) => event.stopPropagation()} key={label}>
36+
<div className="pf-c-chip">
37+
<span className="pf-c-chip__text" id={`pf-random-id-${index}-${label}`}>
38+
{label}
39+
</span>
40+
<button onClick={() => handleChange(item)} className="pf-c-button pf-m-plain" type="button">
41+
<CloseIcon />
42+
</button>
43+
</div>
44+
</li>
45+
);
46+
})}
47+
{value.length > 3 && (
48+
<li className="pf-c-chip-group__list-item">
49+
<button type="button" onClick={handleShowMore} className="pf-c-chip pf-m-overflow">
50+
<span className="pf-c-chip__text">{showMore ? 'Show less' : `${value.length - 3} more`}</span>
51+
</button>
52+
</li>
53+
)}
54+
</ul>
55+
</div>
56+
);
57+
}
58+
2159
return value.map((item) => (typeof item === 'object' ? item.label : item)).join(',');
2260
}
2361

@@ -30,6 +68,29 @@ const itemToString = (value) => {
3068

3169
const filterOptions = (options, filterValue = '') => options.filter(({ label }) => label.toLowerCase().includes(filterValue.toLowerCase()));
3270

71+
const getValue = (isMulti, option, value) => {
72+
if (!isMulti) {
73+
return option;
74+
}
75+
76+
const isSelected = value.find(({ value }) => value === option.value);
77+
return isSelected ? value.filter(({ value }) => value !== option.value) : [...value, option];
78+
};
79+
80+
const stateReducer = (state, changes, keepMenuOpen) => {
81+
switch (changes.type) {
82+
case Downshift.stateChangeTypes.keyDownEnter:
83+
case Downshift.stateChangeTypes.clickItem:
84+
return {
85+
...changes,
86+
isOpen: keepMenuOpen ? state.isOpen : !state.isOpen,
87+
highlightedIndex: state.highlightedIndex
88+
};
89+
default:
90+
return changes;
91+
}
92+
};
93+
3394
const InternalSelect = ({
3495
noResultsMessage,
3596
noOptionsMessage,
@@ -41,33 +102,35 @@ const InternalSelect = ({
41102
isSearchable,
42103
isDisabled,
43104
isClearable,
105+
isMulti,
44106
...props
45107
}) => {
46-
// console.log(props);
108+
const [showMore, setShowMore] = useState(false);
47109
const inputRef = useRef();
48110
const parsedValue = parseInternalValue(value);
111+
const handleShowMore = () => setShowMore((prev) => !prev);
112+
const handleChange = (option) => onChange(getValue(isMulti, option, value));
49113
return (
50114
<Downshift
51115
id={props.id || props.name}
52-
onChange={(value) => {
53-
return onChange(value);
54-
}}
55-
itemToString={itemToString}
116+
onChange={handleChange}
117+
itemToString={(value) => itemToString(value, isMulti, showMore, handleShowMore, handleChange)}
56118
selectedItem={value}
119+
stateReducer={(state, changes) => stateReducer(state, changes, isMulti)}
57120
>
58121
{({ isOpen, inputValue, itemToString, selectedItem, clearSelection, getInputProps, getToggleButtonProps, getItemProps, highlightedIndex }) => {
59122
const toggleButtonProps = getToggleButtonProps();
60123
return (
61124
<div className="pf-c-select">
62-
<button disabled={isDisabled} className={`pf-c-select__toggle${isDisabled ? ' pf-m-disabled' : ''}`} {...toggleButtonProps}>
125+
<div disabled={isDisabled} className={`pf-c-select__toggle${isDisabled ? ' pf-m-disabled' : ''}`} {...toggleButtonProps}>
63126
<div className="pf-c-select_toggle-wrapper ddorg__pf4-component-mapper__select-toggle-wrapper">
64-
<ValueContainer placeholder={placeholder} value={itemToString(selectedItem)} />
127+
<ValueContainer placeholder={placeholder} value={itemToString(selectedItem, isMulti, showMore, handleShowMore, handleChange)} />
65128
</div>
66129
<span className="pf-c-select__toggle-arrow">
67130
{isClearable && parsedValue && <ClearIndicator clearSelection={clearSelection} />}
68131
<CaretDownIcon />
69132
</span>
70-
</button>
133+
</div>
71134
{isOpen && (
72135
<Menu
73136
noResultsMessage={noResultsMessage}
@@ -82,7 +145,8 @@ const InternalSelect = ({
82145
options={options}
83146
getItemProps={getItemProps}
84147
highlightedIndex={highlightedIndex}
85-
selectedItem={parsedValue}
148+
selectedItem={isMulti ? value : parsedValue}
149+
isMulti={isMulti}
86150
/>
87151
)}
88152
</div>
@@ -109,7 +173,8 @@ InternalSelect.propTypes = {
109173
isDisabled: PropTypes.bool,
110174
isClearable: PropTypes.bool,
111175
noResultsMessage: PropTypes.node,
112-
noOptionsMessage: PropTypes.func
176+
noOptionsMessage: PropTypes.func,
177+
isMulti: PropTypes.bool
113178
};
114179

115180
const Select = ({ selectVariant, menuIsPortal, ...props }) => {
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22

3-
const ValueContainer = ({ value, placeholder }) => <span className="pf-c-select__toggle-text">{value || placeholder}</span>;
4-
3+
const ValueContainer = ({ value, placeholder }) => {
4+
return <span className="pf-c-select__toggle-text">{value || placeholder}</span>;
5+
};
56

67
export default ValueContainer;

0 commit comments

Comments
 (0)