Skip to content

Commit

Permalink
STCOM-1313 Update Selection internal state when dataOptions are recei…
Browse files Browse the repository at this point in the history
…ved (#2318)

* update Selection internal state when dataOptions are received

* log changes

* simplify getSelectedOption logic with find vs findIndex
  • Loading branch information
JohnC-80 authored Jul 10, 2024
1 parent 8acd4d5 commit fa1c84e
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* Use `isEqual` to dedupe multiSelection values list rather that `===`. Refs STCOM-1311.
* Set `<MultiSelection>`'s popper modifiers to avoid overlap with the control when rendered within an Editable list. Refs STCOM-1309.
* Conform `<Selection>`'s internal state when value prop changes after initial render. Refs STCOM-1312.
* Conform `<Selection>`'s internal state when dataOptions prop changes after initial render. Refs STCOM-1313.

## [12.1.0](https://github.com/folio-org/stripes-components/tree/v12.1.0) (2024-03-12)
[Full Changelog](https://github.com/folio-org/stripes-components/compare/v12.0.0...v12.1.0)
Expand Down
16 changes: 13 additions & 3 deletions lib/Selection/Selection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useMemo, useCallback } from 'react';
import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
import { useCombobox } from 'downshift';
Expand Down Expand Up @@ -121,6 +121,7 @@ const Selection = ({
const { formatMessage } = useIntl();
const [filterValue, updateFilterValue] = useState('');
const [selectedItem, updateSelectedItem] = useState(value ? getSelectedObject(value, dataOptions) : null);
const dataLength = useRef(dataOptions?.length || 0);
const controlRef = useProvidedRefOrCreate(inputRef);
const options = useMemo(
() => (asyncFilter ? dataOptions :
Expand All @@ -130,6 +131,15 @@ const Selection = ({

const testId = useProvidedIdOrCreate(id, 'selection-');

useEffect(() => {
// if dataOptions populate after the initial render, update the selectedItem state
// if one hasn't been found yet.
if (dataOptions?.length !== dataLength.current && value && selectedItem === null) {
updateSelectedItem(getSelectedObject(value, dataOptions));
dataLength.current = dataOptions.length;
}
}, [dataOptions, selectedItem, value])

// we need to skip over group headings since those can neither be selectable or cursored over.
const reducedListItems = reduceOptions(options);
const {
Expand Down Expand Up @@ -157,7 +167,7 @@ const Selection = ({
const labelId = `sl-label-${testId}`;
const valueId = `selected-${testId}-item`;

if (selectedItem?.value !== value) {
if (selectedItem !== null && selectedItem?.value !== value) {
// conform to post-render value prop changes from outside of the component,
// whether the changed value is something empty like '' or null;
const newValue = getSelectedObject(value, dataOptions) || { value }
Expand Down Expand Up @@ -260,7 +270,7 @@ const Selection = ({
})}
onClick={() => {}}
onChange={(e) => updateFilterValue(e.target.value)}
aria-label={formatMessage({ id: 'stripes-components.selection.filterOptionsLabel', values: { label } })}
aria-label={formatMessage({ id: 'stripes-components.selection.filterOptionsLabel' }, { label })}
className={css.selectionFilter}
placeholder={formatMessage({ id: 'stripes-components.selection.filterOptionsPlaceholder' })}
/>
Expand Down
27 changes: 26 additions & 1 deletion lib/Selection/tests/Selection-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ describe('Selection', () => {
);
});

it('renders initial value as provided', () => selection.has({singleValue: 'Option 2'}));
it('renders initial value as provided', () => selection.has({ singleValue: 'Option 2' }));

describe('Reseting the value', () => {
beforeEach(async () => {
Expand All @@ -601,4 +601,29 @@ describe('Selection', () => {
})
});
});

describe('Changing data options after initial render', () => {
const changeSpy = Sinon.spy();
beforeEach(async () => {
await mountWithContext(
<SingleSelectionHarness
label="test selection"
initValue="test2"
options={[]}
delayedOptions={listOptions}
onChange={changeSpy}
/>
);
});

it('does not display the value', () => selection.has({ singleValue: '' }));

describe('loading options', () => {
beforeEach(async () => {
await Button('fillData').click();
});

it('displays the value', () => selection.has({ singleValue: 'Option 2' }))
});
});
});
10 changes: 6 additions & 4 deletions lib/Selection/tests/SingleSelectionHarness.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ import Button from '../../Button/Button';
const SingleSelectionHarness = ({
initValue,
label,
options,
options: optionsProp,
delayedOptions = [],
onChange = () => {},
}) => {
const [fieldVal, setFieldVal] = useState(initValue);

const [options, updateOptions] = useState(optionsProp)
return (
<>
<Button onClick={()=>setFieldVal('')}>reset</Button>
<Button onClick={() => setFieldVal('')}>reset</Button>
<Button onClick={() => setTimeout(updateOptions(delayedOptions), 100)}>fillData</Button>
<Selection
label={label}
value={fieldVal}
dataOptions={options}
onChange={(val) => { setFieldVal(val); onChange(val)}}
onChange={(val) => { setFieldVal(val); onChange(val) }}
/>
</>
);
Expand Down
6 changes: 2 additions & 4 deletions lib/Selection/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,10 @@ export const getSelectedObject = (value, dataOptions = []) => {
if (dataOptions.length > 0) {
if (typeof value !== 'undefined') {
const flattenedOptions = flattenOptionList(dataOptions);
// in the case of RF, nothing selected, so cursor the 1st...
const valueIndex = flattenedOptions.findIndex((o) => o.value === value);
return flattenedOptions[valueIndex];
return flattenedOptions.find((o) => o.value === value) || null;
}
}
return undefined;
return null;
};

/** ensureValuedOption
Expand Down

0 comments on commit fa1c84e

Please sign in to comment.