From 55f85b127434153f7f5a2605fa30d17a87015ec4 Mon Sep 17 00:00:00 2001 From: Gethin Webster Date: Tue, 7 Oct 2025 11:15:19 +0200 Subject: [PATCH] fix: Hide autosuggest results text when not relevant --- pages/autosuggest/simple.page.tsx | 1 + .../__snapshots__/documenter.test.ts.snap | 5 ++- .../autosuggest-dropdown-states.test.tsx | 40 ++++++++++++++----- src/autosuggest/interfaces.ts | 3 ++ src/autosuggest/internal.tsx | 3 +- .../__tests__/dropdown-status.test.tsx | 6 --- .../components/dropdown-status/index.tsx | 5 +-- src/multiselect/use-multiselect.tsx | 1 - src/select/internal.tsx | 1 - 9 files changed, 41 insertions(+), 24 deletions(-) diff --git a/pages/autosuggest/simple.page.tsx b/pages/autosuggest/simple.page.tsx index c777f1e3f8..81e275eee8 100644 --- a/pages/autosuggest/simple.page.tsx +++ b/pages/autosuggest/simple.page.tsx @@ -31,6 +31,7 @@ export default function AutosuggestPage() { ariaLabel={'simple autosuggest'} selectedAriaLabel="Selected" empty={empty} + finishedText="Finished" filteringResultsText={matchesCount => `${matchesCount} items`} /> diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index 38946fd1c7..da3778c54d 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -3295,7 +3295,10 @@ because fixed positioning results in a slight, visible lag when scrolling comple "type": "boolean", }, { - "description": "Specifies the text to display with the number of matches at the bottom of the dropdown menu while filtering.", + "description": "Specifies the text to display with the number of matches at the bottom of the dropdown menu while filtering. + +Note that the \`matchesCount\` includes the \`enteredTextLabel\` ("Use \${value}") item, so in most cases you +should subtract 1 from \`matchesCount\`. If using manual filtering, you should provide your own value for \`totalCount\`.", "inlineType": { "name": "(matchesCount: number, totalCount: number) => string", "parameters": [ diff --git a/src/autosuggest/__tests__/autosuggest-dropdown-states.test.tsx b/src/autosuggest/__tests__/autosuggest-dropdown-states.test.tsx index c80c634d55..461afcf049 100644 --- a/src/autosuggest/__tests__/autosuggest-dropdown-states.test.tsx +++ b/src/autosuggest/__tests__/autosuggest-dropdown-states.test.tsx @@ -30,8 +30,13 @@ const defaultProps: AutosuggestProps = { clearAriaLabel: 'clear input', }; +const ControlledAutosuggest = (props: AutosuggestProps) => { + const [value, setValue] = React.useState(props.value); + return setValue(event.detail.value)} />; +}; + function renderAutosuggest(props: Partial) { - const { container } = render(); + const { container } = render(); const wrapper = createWrapper(container).findAutosuggest()!; return { container, wrapper }; } @@ -127,8 +132,9 @@ describe('footer types', () => { }); test('results', async () => { - renderAutosuggest({ value: 'x', filteringResultsText: () => '3 items' }); + const { wrapper } = renderAutosuggest({ filteringResultsText: () => '3 items' }); focusInput(); + wrapper.setInputValue('x'); expectDropdown(); expectFooterSticky(true); expectFooterContent('3 items'); @@ -171,9 +177,10 @@ describe('filtering results', () => { expectFooterContent('No options'); }); - test('displays results footer when value is not empty', () => { - renderAutosuggest({ value: ' ', options: [], filteringResultsText: () => '0 items' }); + test('displays results footer when value is entered but not filtering', () => { + const { wrapper } = renderAutosuggest({ options: [], filteringResultsText: () => '0 items' }); focusInput(); + wrapper.setInputValue(' '); expectFooterContent('0 items'); }); }); @@ -185,9 +192,16 @@ describe('filtering results', () => { expectNoFooter(); }); - test('displays results footer when value is not empty', () => { + test('displays no footer when value is not empty', () => { renderAutosuggest({ value: ' ', statusType: 'pending', filteringResultsText: () => '3 items' }); focusInput(); + expectNoFooter(); + }); + + test('displays results footer when value is entered', () => { + const { wrapper } = renderAutosuggest({ statusType: 'pending', filteringResultsText: () => '3 items' }); + focusInput(); + wrapper.setInputValue(' '); expectFooterContent('3 items'); }); }); @@ -233,15 +247,23 @@ describe('filtering results', () => { expectFooterContent('finished!'); }); - test('displays results footer when finished w/o finished text and value is not empty', () => { - renderAutosuggest({ value: ' ', finishedText: undefined, filteringResultsText: () => '3 items' }); + test('displays finished footer when finished w/ finished text and value is present but not filtering', () => { + renderAutosuggest({ value: ' ', filteringResultsText: () => '3 items' }); + focusInput(); + expectFooterContent('finished!'); + }); + + test('displays results footer when finished w/o finished text and value is entered', () => { + const { wrapper } = renderAutosuggest({ finishedText: undefined, filteringResultsText: () => '3 items' }); focusInput(); + wrapper.setInputValue(' '); expectFooterContent('3 items'); }); - test('displays results footer when finished w/ finished text and value is not empty', () => { - renderAutosuggest({ value: ' ', filteringResultsText: () => '3 items' }); + test('displays results footer when finished w/ finished text and value is entered', () => { + const { wrapper } = renderAutosuggest({ filteringResultsText: () => '3 items' }); focusInput(); + wrapper.setInputValue(' '); expectFooterContent('3 items'); }); }); diff --git a/src/autosuggest/interfaces.ts b/src/autosuggest/interfaces.ts index df45d2ebbc..785e866425 100644 --- a/src/autosuggest/interfaces.ts +++ b/src/autosuggest/interfaces.ts @@ -83,6 +83,9 @@ export interface AutosuggestProps /** * Specifies the text to display with the number of matches at the bottom of the dropdown menu while filtering. + * + * Note that the `matchesCount` includes the `enteredTextLabel` ("Use ${value}") item, so in most cases you + * should subtract 1 from `matchesCount`. If using manual filtering, you should provide your own value for `totalCount`. */ filteringResultsText?: (matchesCount: number, totalCount: number) => string; diff --git a/src/autosuggest/internal.tsx b/src/autosuggest/internal.tsx index 675fe26a32..4b49ffdce6 100644 --- a/src/autosuggest/internal.tsx +++ b/src/autosuggest/internal.tsx @@ -179,14 +179,13 @@ const InternalAutosuggest = React.forwardRef((props: InternalAutosuggestProps, r const highlightedOptionId = autosuggestItemsState.highlightedOption ? highlightedOptionIdSource : undefined; const isEmpty = !value && !autosuggestItemsState.items.length; - const isFiltered = !!value && value.length !== 0; + const isFiltered = !!value && value.length !== 0 && !(filteringType === 'auto' && autosuggestItemsState.showAll); const filteredText = isFiltered ? filteringResultsText?.(autosuggestItemsState.items.length, options?.length ?? 0) : undefined; const dropdownStatus = useDropdownStatus({ ...props, isEmpty, - isFiltered, recoveryText, errorIconAriaLabel, onRecoveryClick: handleRecoveryClick, diff --git a/src/internal/components/dropdown-status/__tests__/dropdown-status.test.tsx b/src/internal/components/dropdown-status/__tests__/dropdown-status.test.tsx index 75e296492f..d5bc7c6767 100644 --- a/src/internal/components/dropdown-status/__tests__/dropdown-status.test.tsx +++ b/src/internal/components/dropdown-status/__tests__/dropdown-status.test.tsx @@ -132,7 +132,6 @@ describe('useDropdownStatus', () => { const { getContent } = renderComponent({ statusType: 'pending', filteringResultsText: '2 matches', - isFiltered: true, }); expect(getContent()).toBe('2 matches'); }); @@ -141,7 +140,6 @@ describe('useDropdownStatus', () => { const { getContent } = renderComponent({ statusType: 'loading', filteringResultsText: '2 matches', - isFiltered: true, loadingText: 'Loading', }); expect(getContent()).toBe('Loading'); @@ -151,7 +149,6 @@ describe('useDropdownStatus', () => { const { getContent } = renderComponent({ statusType: 'error', filteringResultsText: '2 matches', - isFiltered: true, errorText: 'We got a problem', recoveryText: 'do not worry', hasRecoveryCallback: true, @@ -162,8 +159,6 @@ describe('useDropdownStatus', () => { test('render finished text when finished and not filtered', () => { const { getContent } = renderComponent({ statusType: 'finished', - filteringResultsText: '10 out of 10 items', - isFiltered: false, finishedText: 'End of results', }); expect(getContent()).toBe('End of results'); @@ -173,7 +168,6 @@ describe('useDropdownStatus', () => { const { getContent } = renderComponent({ statusType: 'finished', filteringResultsText: '10 out of 10 items', - isFiltered: true, finishedText: 'End of results', }); expect(getContent()).toBe('10 out of 10 items'); diff --git a/src/internal/components/dropdown-status/index.tsx b/src/internal/components/dropdown-status/index.tsx index 3180cdb7ae..6ed598af3d 100644 --- a/src/internal/components/dropdown-status/index.tsx +++ b/src/internal/components/dropdown-status/index.tsx @@ -15,7 +15,6 @@ export { DropdownStatusProps }; export interface DropdownStatusPropsExtended extends DropdownStatusProps { isEmpty?: boolean; isNoMatch?: boolean; - isFiltered?: boolean; noMatch?: React.ReactNode; filteringResultsText?: string; /** @@ -46,7 +45,6 @@ type UseDropdownStatus = ({ recoveryText, isEmpty, isNoMatch, - isFiltered, noMatch, hasRecoveryCallback, onRecoveryClick, @@ -68,7 +66,6 @@ export const useDropdownStatus: UseDropdownStatus = ({ recoveryText, isEmpty, isNoMatch, - isFiltered, noMatch, onRecoveryClick, hasRecoveryCallback = false, @@ -106,7 +103,7 @@ export const useDropdownStatus: UseDropdownStatus = ({ statusResult.content = empty; } else if (isNoMatch && noMatch) { statusResult.content = noMatch; - } else if (isFiltered && filteringResultsText) { + } else if (filteringResultsText) { statusResult.content = filteringResultsText; } else if (statusType === 'finished' && finishedText) { statusResult.content = finishedText; diff --git a/src/multiselect/use-multiselect.tsx b/src/multiselect/use-multiselect.tsx index b664bb7c58..1449fffec3 100644 --- a/src/multiselect/use-multiselect.tsx +++ b/src/multiselect/use-multiselect.tsx @@ -232,7 +232,6 @@ export function useMultiselect({ isEmpty, isNoMatch, noMatch, - isFiltered, filteringResultsText: filteredText, onRecoveryClick: handleRecoveryClick, errorIconAriaLabel: errorIconAriaLabel, diff --git a/src/select/internal.tsx b/src/select/internal.tsx index 7f008157dc..e0da536b00 100644 --- a/src/select/internal.tsx +++ b/src/select/internal.tsx @@ -195,7 +195,6 @@ const InternalSelect = React.forwardRef( isEmpty, isNoMatch, noMatch, - isFiltered, filteringResultsText: filteredText, errorIconAriaLabel, onRecoveryClick: handleRecoveryClick,