diff --git a/packages/core/upload/admin/src/components/SelectTree/SelectTree.js b/packages/core/upload/admin/src/components/SelectTree/SelectTree.js index 325f8962c6..db6e0a2d02 100644 --- a/packages/core/upload/admin/src/components/SelectTree/SelectTree.js +++ b/packages/core/upload/admin/src/components/SelectTree/SelectTree.js @@ -5,23 +5,22 @@ import { ReactSelect as Select } from '@strapi/helper-plugin'; import Option from './Option'; import flattenTree from './utils/flattenTree'; +import getOpenValues from './utils/getOpenValues'; const hasParent = option => !option.parent; const hasParentOrMatchesValue = (option, 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 toplevelDefaultOptions = useMemo(() => flatDefaultOptions.filter(hasParent), [ - flatDefaultOptions, - ]); - const [options, setOptions] = useState(toplevelDefaultOptions); - const [openValues, setOpenValues] = useState([]); + const optionsFiltered = useMemo(() => flatDefaultOptions.filter(hasParent), [flatDefaultOptions]); + const [options, setOptions] = useState(optionsFiltered); + const [openValues, setOpenValues] = useState(getOpenValues(flatDefaultOptions, defaultValue)); useEffect(() => { if (openValues.length === 0) { - setOptions(toplevelDefaultOptions); + setOptions(optionsFiltered); } openValues.forEach(value => { @@ -31,7 +30,7 @@ const SelectTree = ({ options: defaultOptions, maxDisplayDepth, ...props }) => { setOptions(filtered); }); - }, [openValues, flatDefaultOptions, toplevelDefaultOptions]); + }, [openValues, flatDefaultOptions, optionsFiltered]); function handleToggle(e, value) { e.preventDefault(); @@ -58,13 +57,14 @@ const SelectTree = ({ options: defaultOptions, maxDisplayDepth, ...props }) => { ), }} options={options} + defaultValue={defaultValue} {...props} /> ); }; const OptionShape = PropTypes.shape({ - value: PropTypes.number.isRequired, + value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, label: PropTypes.string.isRequired, children: PropTypes.array, }); @@ -76,10 +76,14 @@ OptionShape.defaultProps = { }; SelectTree.defaultProps = { + defaultValue: undefined, maxDisplayDepth: 5, }; SelectTree.propTypes = { + defaultValue: PropTypes.shape({ + value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + }), maxDisplayDepth: PropTypes.number, options: PropTypes.arrayOf(OptionShape).isRequired, }; diff --git a/packages/core/upload/admin/src/components/SelectTree/SelectTree.stories.mdx b/packages/core/upload/admin/src/components/SelectTree/SelectTree.stories.mdx index e9b10be018..e826145459 100644 --- a/packages/core/upload/admin/src/components/SelectTree/SelectTree.stories.mdx +++ b/packages/core/upload/admin/src/components/SelectTree/SelectTree.stories.mdx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { Meta, ArgsTable, Canvas, Story } from '@storybook/addon-docs'; import SelectTree from './index'; @@ -13,6 +14,7 @@ into `react-select` are forwarded. {() => { + const [value, setValue] = useState(null); const options = [ { value: 1, @@ -47,7 +49,12 @@ into `react-select` are forwarded. label: 'Folder 3' } ]; - return ( console.log(props)} />) + return ( + <> + setValue(value)} /> +

Selected Value: {value}

+ + ) }}
diff --git a/packages/core/upload/admin/src/components/SelectTree/utils/getOpenValues.js b/packages/core/upload/admin/src/components/SelectTree/utils/getOpenValues.js new file mode 100644 index 0000000000..d1c2e4fbdf --- /dev/null +++ b/packages/core/upload/admin/src/components/SelectTree/utils/getOpenValues.js @@ -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; diff --git a/packages/core/upload/admin/src/components/SelectTree/utils/tests/getOpenvalues.test.js b/packages/core/upload/admin/src/components/SelectTree/utils/tests/getOpenvalues.test.js new file mode 100644 index 0000000000..018f019e8b --- /dev/null +++ b/packages/core/upload/admin/src/components/SelectTree/utils/tests/getOpenvalues.test.js @@ -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']); + }); +});