SelectTree: Handle react-select defaultValue

This commit is contained in:
Gustav Hansen 2022-04-08 15:54:43 +02:00
parent 447713434f
commit 07737dad1a
4 changed files with 91 additions and 10 deletions

View File

@ -5,23 +5,22 @@ import { ReactSelect as Select } from '@strapi/helper-plugin';
import Option from './Option'; import Option from './Option';
import flattenTree from './utils/flattenTree'; import flattenTree from './utils/flattenTree';
import getOpenValues from './utils/getOpenValues';
const hasParent = option => !option.parent; const hasParent = option => !option.parent;
const hasParentOrMatchesValue = (option, value) => const hasParentOrMatchesValue = (option, value) =>
option.value === value || option.parent === value; option.value === value || option.parent === value;
const SelectTree = ({ options: defaultOptions, maxDisplayDepth, ...props }) => { const SelectTree = ({ options: defaultOptions, maxDisplayDepth, defaultValue, ...props }) => {
const flatDefaultOptions = useMemo(() => flattenTree(defaultOptions), [defaultOptions]); const flatDefaultOptions = useMemo(() => flattenTree(defaultOptions), [defaultOptions]);
const toplevelDefaultOptions = useMemo(() => flatDefaultOptions.filter(hasParent), [ const optionsFiltered = useMemo(() => flatDefaultOptions.filter(hasParent), [flatDefaultOptions]);
flatDefaultOptions, const [options, setOptions] = useState(optionsFiltered);
]); const [openValues, setOpenValues] = useState(getOpenValues(flatDefaultOptions, defaultValue));
const [options, setOptions] = useState(toplevelDefaultOptions);
const [openValues, setOpenValues] = useState([]);
useEffect(() => { useEffect(() => {
if (openValues.length === 0) { if (openValues.length === 0) {
setOptions(toplevelDefaultOptions); setOptions(optionsFiltered);
} }
openValues.forEach(value => { openValues.forEach(value => {
@ -31,7 +30,7 @@ const SelectTree = ({ options: defaultOptions, maxDisplayDepth, ...props }) => {
setOptions(filtered); setOptions(filtered);
}); });
}, [openValues, flatDefaultOptions, toplevelDefaultOptions]); }, [openValues, flatDefaultOptions, optionsFiltered]);
function handleToggle(e, value) { function handleToggle(e, value) {
e.preventDefault(); e.preventDefault();
@ -58,13 +57,14 @@ const SelectTree = ({ options: defaultOptions, maxDisplayDepth, ...props }) => {
), ),
}} }}
options={options} options={options}
defaultValue={defaultValue}
{...props} {...props}
/> />
); );
}; };
const OptionShape = PropTypes.shape({ const OptionShape = PropTypes.shape({
value: PropTypes.number.isRequired, value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
label: PropTypes.string.isRequired, label: PropTypes.string.isRequired,
children: PropTypes.array, children: PropTypes.array,
}); });
@ -76,10 +76,14 @@ OptionShape.defaultProps = {
}; };
SelectTree.defaultProps = { SelectTree.defaultProps = {
defaultValue: undefined,
maxDisplayDepth: 5, maxDisplayDepth: 5,
}; };
SelectTree.propTypes = { SelectTree.propTypes = {
defaultValue: PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
}),
maxDisplayDepth: PropTypes.number, maxDisplayDepth: PropTypes.number,
options: PropTypes.arrayOf(OptionShape).isRequired, options: PropTypes.arrayOf(OptionShape).isRequired,
}; };

View File

@ -1,3 +1,4 @@
import { useState } from 'react';
import { Meta, ArgsTable, Canvas, Story } from '@storybook/addon-docs'; import { Meta, ArgsTable, Canvas, Story } from '@storybook/addon-docs';
import SelectTree from './index'; import SelectTree from './index';
@ -13,6 +14,7 @@ into `react-select` are forwarded.
<Canvas> <Canvas>
<Story name="base"> <Story name="base">
{() => { {() => {
const [value, setValue] = useState(null);
const options = [ const options = [
{ {
value: 1, value: 1,
@ -47,7 +49,12 @@ into `react-select` are forwarded.
label: 'Folder 3' label: 'Folder 3'
} }
]; ];
return (<SelectTree options={options} onChange={props => console.log(props)} />) return (
<>
<SelectTree options={options} defaultValue={{ value: 22 }} onChange={({ value }) => setValue(value)} />
<p>Selected Value: {value}</p>
</>
)
}} }}
</Story> </Story>
</Canvas> </Canvas>

View File

@ -0,0 +1,25 @@
function getOpenValues(options, defaultValue) {
let values = [];
const { value } = defaultValue;
const option = options.find(option => option.value === value);
if (!option) {
return values;
}
values.push(option.value);
let { parent } = option;
while (parent) {
// eslint-disable-next-line no-loop-func
const option = options.find(({ value }) => value === parent);
values.push(option.value);
parent = option.parent;
}
return values;
}
export default getOpenValues;

View File

@ -0,0 +1,45 @@
import flattenTree from '../flattenTree';
import getOpenValues from '../getOpenValues';
const FIXTURE = flattenTree([
{
value: 'f-1',
label: 'Folder 1',
},
{
value: 'f-2',
label: 'Folder 2',
children: [
{
value: 'f-2-1',
label: 'Folder 2-1',
},
{
value: 'f-2-2',
label: 'Folder 2-2',
children: [
{
value: 'f-2-2-1',
label: 'Folder 2-2-1',
},
],
},
],
},
]);
describe('getOpenValues', () => {
test('returns 1 value for depth = 1', () => {
expect(getOpenValues(FIXTURE, 'f-1')).toStrictEqual(['f-1']);
});
test('returns 2 values for depth = 2', () => {
expect(getOpenValues(FIXTURE, 'f-2-1')).toStrictEqual(['f-2-1', 'f-2']);
});
test('returns 3 values for depth = 3', () => {
expect(getOpenValues(FIXTURE, 'f-2-2-1')).toStrictEqual(['f-2-2-1', 'f-2-2', 'f-2']);
});
});