Skip to content

Commit c251305

Browse files
committed
Add optional sort button for Dual List Select
1 parent 3370ea8 commit c251305

File tree

6 files changed

+117
-20
lines changed

6 files changed

+117
-20
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export interface SortConfig {
2+
left: 'asc' | 'desc';
3+
right: 'asc' | 'desc';
4+
}
5+
6+
export type SetSortConfig = (newSortConfig: SortConfig) => void;
7+
8+
export interface DualListContextValue {
9+
sortConfig: SortConfig;
10+
setSortConfig: SetSortConfig;
11+
}
12+
13+
export interface DualListContext {
14+
value: DualListContextValue;
15+
}
16+
17+
export default DualListContext;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createContext } from 'react';
2+
3+
const DualListSelectContext = createContext({});
4+
5+
export default DualListSelectContext;

packages/pf4-component-mapper/src/files/dual-list-select.js

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,36 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import PropTypes from 'prop-types';
33
import { DualListSelector } from '@patternfly/react-core';
44
import { useFieldApi } from '@data-driven-forms/react-form-renderer';
55
import isEqual from 'lodash/isEqual';
66

77
import './dual-list-select.scss';
88
import FormGroup from '../common/form-group';
9+
import DualListContext from './dual-list-context';
910

1011
const DualList = (props) => {
11-
const { label, isRequired, helperText, meta, description, hideLabel, id, input, FormGroupProps, options, getValueFromNode, ...rest } = useFieldApi({
12+
const {
13+
label,
14+
isRequired,
15+
helperText,
16+
meta,
17+
description,
18+
hideLabel,
19+
id,
20+
input,
21+
FormGroupProps,
22+
options,
23+
getValueFromNode,
24+
isSearchable,
25+
isSortable,
26+
...rest
27+
} = useFieldApi({
1228
...props,
1329
isEqual: (current, initial) => isEqual([...(current || [])].sort(), [...(initial || [])].sort())
1430
});
1531

32+
const [sortConfig, setSortConfig] = useState(() => ({ left: isSortable && 'asc', right: isSortable && 'asc' }));
33+
1634
const value = input.value || [];
1735

1836
let leftOptions;
@@ -46,6 +64,18 @@ const DualList = (props) => {
4664
filterOption = (option, input) => (option.value ? option.value.includes(input) : getValueFromNode(option).includes(input));
4765
}
4866

67+
if (isSortable) {
68+
const sort = (direction, a, b) => (direction === 'asc' ? a.localeCompare(b) : b.localeCompare(a));
69+
70+
if (!getValueFromNode) {
71+
leftOptions = leftOptions.sort((a, b) => sort(sortConfig.left, a.label || a, b.label || b));
72+
rightOptions = rightOptions.sort((a, b) => sort(sortConfig.right, a.label || a, b.label || b));
73+
} else {
74+
leftOptions = leftOptions.sort((a, b) => sort(sortConfig.left, getValueFromNode(a.label || a), getValueFromNode(b.label || b)));
75+
rightOptions = rightOptions.sort((a, b) => sort(sortConfig.right, getValueFromNode(a.label || a), getValueFromNode(b.label || b)));
76+
}
77+
}
78+
4979
return (
5080
<FormGroup
5181
label={label}
@@ -57,23 +87,24 @@ const DualList = (props) => {
5787
id={id || input.name}
5888
FormGroupProps={FormGroupProps}
5989
>
60-
<DualListSelector
61-
availableOptions={leftOptions}
62-
chosenOptions={rightOptions}
63-
onListChange={onListChange}
64-
id={id || input.name}
65-
isSearchable
66-
{...(getValueFromNode && {
67-
addAll: onListChange,
68-
addSelected: onListChange,
69-
filterOption,
70-
onOptionSelect: onListChange,
71-
removeAll: onListChange,
72-
removeSelected: onListChange
73-
})}
74-
{...rest}
75-
/>
76-
{JSON.stringify(input.value, null, 2)}
90+
<DualListContext.Provider value={{ sortConfig, setSortConfig }}>
91+
<DualListSelector
92+
availableOptions={leftOptions}
93+
chosenOptions={rightOptions}
94+
onListChange={onListChange}
95+
id={id || input.name}
96+
isSearchable={isSearchable}
97+
{...(getValueFromNode && {
98+
addAll: onListChange,
99+
addSelected: onListChange,
100+
filterOption,
101+
onOptionSelect: onListChange,
102+
removeAll: onListChange,
103+
removeSelected: onListChange
104+
})}
105+
{...rest}
106+
/>
107+
</DualListContext.Provider>
77108
</FormGroup>
78109
);
79110
};
@@ -85,7 +116,9 @@ DualList.propTypes = {
85116
description: PropTypes.node,
86117
hideLabel: PropTypes.bool,
87118
id: PropTypes.string,
88-
getValueFromNode: PropTypes.func
119+
getValueFromNode: PropTypes.func,
120+
isSearchable: PropTypes.bool,
121+
isSortable: PropTypes.bool
89122
};
90123

91124
export default DualList;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface DualListSortButtonProps {
2+
position: 'left' | 'right';
3+
}
4+
5+
declare const DualListSortButton: React.ComponentType<DualListSortButtonProps>;
6+
7+
export default DualListSortButton;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React, { useContext } from 'react';
2+
import PropTypes from 'prop-types';
3+
import { Button, ButtonVariant } from '@patternfly/react-core';
4+
5+
import SortAsc from '@patternfly/react-icons/dist/js/icons/pficon-sort-common-asc-icon';
6+
import SortDesc from '@patternfly/react-icons/dist/js/icons/pficon-sort-common-desc-icon';
7+
8+
import DualListContext from './dual-list-context';
9+
10+
const DualListSortButton = ({ position, ...rest }) => {
11+
const { sortConfig, setSortConfig } = useContext(DualListContext);
12+
13+
return (
14+
<Button
15+
variant={ButtonVariant.plain}
16+
onClick={
17+
sortConfig[position] === 'asc'
18+
? () => setSortConfig({ ...sortConfig, [position]: 'desc' })
19+
: () => setSortConfig({ ...sortConfig, [position]: 'asc' })
20+
}
21+
aria-label="Sort"
22+
{...rest}
23+
>
24+
{sortConfig[position] === 'asc' ? <SortAsc /> : <SortDesc />}
25+
</Button>
26+
);
27+
};
28+
29+
DualListSortButton.propTypes = {
30+
position: PropTypes.oneOf(['left', 'right'])
31+
};
32+
33+
export default DualListSortButton;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export { default as FormTemplate } from './files/form-template';
33
export { default as Checkbox } from './files/checkbox';
44
export { default as DatePicker } from './files/date-picker';
55
export { default as DualListSelect } from './files/dual-list-select';
6+
export { default as DualListContext } from './files/dual-list-context';
7+
export { default as DualListSortButton } from './files/dual-list-sort-button';
68
export { default as FieldArray } from './files/field-array';
79
export { default as PlainText } from './files/plain-text';
810
export { default as Radio } from './files/radio';

0 commit comments

Comments
 (0)