From 7d7e561fd1ad7784b86b1312f190cd904775a4dd Mon Sep 17 00:00:00 2001 From: Andrei Zhaleznichenka Date: Wed, 4 Dec 2024 18:19:41 +0100 Subject: [PATCH 1/6] feat: Introduces autosuggest without entered text option --- pages/autosuggest/search.page.tsx | 99 +++++++++++++++++++ .../__snapshots__/documenter.test.ts.snap | 7 ++ .../__tests__/autosuggest.test.tsx | 2 +- src/autosuggest/index.tsx | 10 +- src/autosuggest/interfaces.ts | 5 + src/autosuggest/internal.tsx | 6 +- 6 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 pages/autosuggest/search.page.tsx diff --git a/pages/autosuggest/search.page.tsx b/pages/autosuggest/search.page.tsx new file mode 100644 index 0000000000..f3e7500c9c --- /dev/null +++ b/pages/autosuggest/search.page.tsx @@ -0,0 +1,99 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useContext, useRef, useState } from 'react'; + +import { Badge, Box, Checkbox, ExpandableSection, Header, SpaceBetween } from '~components'; +import Autosuggest, { AutosuggestProps } from '~components/autosuggest'; + +import AppContext, { AppContextType } from '../app/app-context'; + +type PageContext = React.Context< + AppContextType<{ + empty?: boolean; + showEnteredTextOption?: boolean; + showMatchesCount?: boolean; + }> +>; + +const options = [ + { value: '__apple__', label: 'Apple' }, + { value: '__orange__', label: 'Orange', tags: ['sweet'] }, + { value: '__banana__', label: 'Banana', tags: ['sweet'] }, + { value: '__pineapple__', label: 'Pineapple', description: 'pine+apple' }, +]; +const enteredTextLabel = (value: string) => `Use: ${value}`; + +export default function AutosuggestPage() { + const { + urlParams: { empty = false, showEnteredTextOption = true, showMatchesCount = true }, + setUrlParams, + } = useContext(AppContext as PageContext); + const [value, setValue] = useState(''); + const [selection, setSelection] = useState(''); + const ref = useRef(null); + return ( + + +
+ Search +
+ + + setUrlParams({ empty: detail.checked })}> + Empty + + setUrlParams({ showEnteredTextOption: detail.checked })} + > + Show entered text option + + setUrlParams({ showMatchesCount: detail.checked })} + > + Show matches count + + + + setValue(event.detail.value)} + onSelect={event => { + if (options.some(o => o.value === event.detail.value)) { + setSelection(event.detail.value); + setValue(''); + } + }} + enteredTextLabel={enteredTextLabel} + ariaLabel={'simple autosuggest'} + selectedAriaLabel="Selected" + empty="No suggestions" + showEnteredTextOption={showEnteredTextOption} + filteringResultsText={ + showMatchesCount + ? matchesCount => { + matchesCount = showEnteredTextOption ? matchesCount - 1 : matchesCount; + return matchesCount ? `${matchesCount} items` : `No matches`; + } + : undefined + } + /> + + + Selection: {selection || 'none'} + {options.map(option => ( + + {option.label} + + ))} + +
+
+ ); +} diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index ebbaca80b8..d60d7f23d1 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -3513,6 +3513,13 @@ This is required to provide a good screen reader experience. For more informatio "optional": true, "type": "string", }, + { + "defaultValue": "true", + "description": "Defines whether entered text option is shown as the first option in the dropdown when value is non-empty.", + "name": "showEnteredTextOption", + "optional": true, + "type": "boolean", + }, { "defaultValue": "'finished'", "description": "Specifies the current status of loading more options. diff --git a/src/autosuggest/__tests__/autosuggest.test.tsx b/src/autosuggest/__tests__/autosuggest.test.tsx index 7bd790ccb0..df64b07e52 100644 --- a/src/autosuggest/__tests__/autosuggest.test.tsx +++ b/src/autosuggest/__tests__/autosuggest.test.tsx @@ -135,7 +135,7 @@ test('entered text option should not get screenreader override', () => { ).toBeFalsy(); }); -test('should not close dropdown when no realted target in blur', () => { +test('should not close dropdown when no related target in blur', () => { const { wrapper, container } = renderAutosuggest(
v} value="1" options={defaultOptions} /> diff --git a/src/autosuggest/index.tsx b/src/autosuggest/index.tsx index 2cd3863f14..669c114f9f 100644 --- a/src/autosuggest/index.tsx +++ b/src/autosuggest/index.tsx @@ -16,7 +16,13 @@ export { AutosuggestProps }; const Autosuggest = React.forwardRef( ( - { filteringType = 'auto', statusType = 'finished', disableBrowserAutocorrect = false, ...props }: AutosuggestProps, + { + filteringType = 'auto', + statusType = 'finished', + disableBrowserAutocorrect = false, + showEnteredTextOption = true, + ...props + }: AutosuggestProps, ref: React.Ref ) => { const baseComponentProps = useBaseComponent('Autosuggest', { @@ -27,6 +33,7 @@ const Autosuggest = React.forwardRef( filteringType, readOnly: props.readOnly, virtualScroll: props.virtualScroll, + showEnteredTextOption, }, }); @@ -45,6 +52,7 @@ const Autosuggest = React.forwardRef( filteringType={filteringType} statusType={statusType} disableBrowserAutocorrect={disableBrowserAutocorrect} + showEnteredTextOption={showEnteredTextOption} {...externalProps} {...baseComponentProps} ref={ref} diff --git a/src/autosuggest/interfaces.ts b/src/autosuggest/interfaces.ts index 785e866425..6e2468c303 100644 --- a/src/autosuggest/interfaces.ts +++ b/src/autosuggest/interfaces.ts @@ -81,6 +81,11 @@ export interface AutosuggestProps */ enteredTextLabel?: AutosuggestProps.EnteredTextLabel; + /** + * Defines whether entered text option is shown as the first option in the dropdown when value is non-empty. + */ + showEnteredTextOption?: boolean; + /** * Specifies the text to display with the number of matches at the bottom of the dropdown menu while filtering. * diff --git a/src/autosuggest/internal.tsx b/src/autosuggest/internal.tsx index 4b49ffdce6..e08300b798 100644 --- a/src/autosuggest/internal.tsx +++ b/src/autosuggest/internal.tsx @@ -52,6 +52,7 @@ const InternalAutosuggest = React.forwardRef((props: InternalAutosuggestProps, r ariaLabel, ariaRequired, enteredTextLabel, + showEnteredTextOption, filteringResultsText, onKeyDown, virtualScroll, @@ -90,7 +91,7 @@ const InternalAutosuggest = React.forwardRef((props: InternalAutosuggestProps, r filterText: value, filteringType, enteredTextLabel, - hideEnteredTextLabel: false, + hideEnteredTextLabel: !showEnteredTextOption, onSelectItem: (option: AutosuggestItem) => { const value = option.value || ''; fireNonCancelableEvent(onChange, { value }); @@ -193,7 +194,8 @@ const InternalAutosuggest = React.forwardRef((props: InternalAutosuggestProps, r hasRecoveryCallback: !!onLoadItems, }); - const shouldRenderDropdownContent = !isEmpty || !!dropdownStatus.content; + const shouldRenderDropdownContent = + autosuggestItemsState.items.length !== 0 || !!dropdownStatus.content || (showEnteredTextOption && !!value); return ( Date: Thu, 17 Jul 2025 16:58:37 +0200 Subject: [PATCH 2/6] mute prop filter test --- .../property-filter-token-editor.test.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/property-filter/__tests__/property-filter-token-editor.test.tsx b/src/property-filter/__tests__/property-filter-token-editor.test.tsx index e7dec0ddc8..ed031ccd2e 100644 --- a/src/property-filter/__tests__/property-filter-token-editor.test.tsx +++ b/src/property-filter/__tests__/property-filter-token-editor.test.tsx @@ -321,19 +321,19 @@ describe.each([false, true])('token editor, expandToViewport=%s', expandToViewpo }); }); -describe.each([false, true])('with i18n-provider %s', withI18nProvider => { - test('uses entered text label for token value autosuggest', () => { - renderComponent( - { query: { operation: 'and', tokens: [{ propertyKey: 'string', operator: '=', value: 'John' }] } }, - withI18nProvider - ); - const editor = openEditor(0, { expandToViewport: false }); - - editor.valueAutosuggest().focus(); - editor.valueAutosuggest().setInputValue('123'); - expect(editor.valueAutosuggest().findEnteredTextOption()!.getElement()).toHaveTextContent('Use: "123"'); - }); -}); +// describe.each([false, true])('with i18n-provider %s', withI18nProvider => { +// test('uses entered text label for token value autosuggest', () => { +// renderComponent( +// { query: { operation: 'and', tokens: [{ propertyKey: 'string', operator: '=', value: 'John' }] } }, +// withI18nProvider +// ); +// const editor = openEditor(0, { expandToViewport: false }); + +// editor.valueAutosuggest().focus(); +// editor.valueAutosuggest().setInputValue('123'); +// expect(editor.valueAutosuggest().findEnteredTextOption()!.getElement()).toHaveTextContent('Use: "123"'); +// }); +// }); const tokenJohn = { propertyKey: 'string', operator: '=', value: 'John' }; const tokenJane = { propertyKey: 'string', operator: '=', value: 'Jane' }; From 26d2c56cee566e54d9118930b2cfb7d8801bda7c Mon Sep 17 00:00:00 2001 From: Sera Wi Date: Wed, 20 Aug 2025 10:44:06 -0400 Subject: [PATCH 3/6] chore: Rename showEnteredTextOption to hideEnteredTextOption. --- pages/autosuggest/search.page.tsx | 14 +++++++------- .../__snapshots__/documenter.test.ts.snap | 14 +++++++------- src/autosuggest/index.tsx | 6 +++--- src/autosuggest/interfaces.ts | 2 +- src/autosuggest/internal.tsx | 6 +++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pages/autosuggest/search.page.tsx b/pages/autosuggest/search.page.tsx index f3e7500c9c..db2625a753 100644 --- a/pages/autosuggest/search.page.tsx +++ b/pages/autosuggest/search.page.tsx @@ -10,7 +10,7 @@ import AppContext, { AppContextType } from '../app/app-context'; type PageContext = React.Context< AppContextType<{ empty?: boolean; - showEnteredTextOption?: boolean; + hideEnteredTextOption?: boolean; showMatchesCount?: boolean; }> >; @@ -25,7 +25,7 @@ const enteredTextLabel = (value: string) => `Use: ${value}`; export default function AutosuggestPage() { const { - urlParams: { empty = false, showEnteredTextOption = true, showMatchesCount = true }, + urlParams: { empty = false, hideEnteredTextOption = false, showMatchesCount = true }, setUrlParams, } = useContext(AppContext as PageContext); const [value, setValue] = useState(''); @@ -46,10 +46,10 @@ export default function AutosuggestPage() { Empty setUrlParams({ showEnteredTextOption: detail.checked })} + checked={hideEnteredTextOption} + onChange={({ detail }) => setUrlParams({ hideEnteredTextOption: detail.checked })} > - Show entered text option + Hide entered text option { - matchesCount = showEnteredTextOption ? matchesCount - 1 : matchesCount; + matchesCount = hideEnteredTextOption ? matchesCount : matchesCount - 1; return matchesCount ? `${matchesCount} items` : `No matches`; } : undefined diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index d60d7f23d1..de1bef7739 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -3363,6 +3363,13 @@ Note: Manual filtering doesn't disable match highlighting.", "optional": true, "type": "string", }, + { + "defaultValue": "false", + "description": "Defines whether entered text option is shown as the first option in the dropdown when value is non-empty.", + "name": "hideEnteredTextOption", + "optional": true, + "type": "boolean", + }, { "deprecatedTag": "The usage of the \`id\` attribute is reserved for internal use cases. For testing and other use cases, use [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes). If you must @@ -3513,13 +3520,6 @@ This is required to provide a good screen reader experience. For more informatio "optional": true, "type": "string", }, - { - "defaultValue": "true", - "description": "Defines whether entered text option is shown as the first option in the dropdown when value is non-empty.", - "name": "showEnteredTextOption", - "optional": true, - "type": "boolean", - }, { "defaultValue": "'finished'", "description": "Specifies the current status of loading more options. diff --git a/src/autosuggest/index.tsx b/src/autosuggest/index.tsx index 669c114f9f..6c45b9cd02 100644 --- a/src/autosuggest/index.tsx +++ b/src/autosuggest/index.tsx @@ -20,7 +20,7 @@ const Autosuggest = React.forwardRef( filteringType = 'auto', statusType = 'finished', disableBrowserAutocorrect = false, - showEnteredTextOption = true, + hideEnteredTextOption = false, ...props }: AutosuggestProps, ref: React.Ref @@ -33,7 +33,7 @@ const Autosuggest = React.forwardRef( filteringType, readOnly: props.readOnly, virtualScroll: props.virtualScroll, - showEnteredTextOption, + hideEnteredTextOption, }, }); @@ -52,7 +52,7 @@ const Autosuggest = React.forwardRef( filteringType={filteringType} statusType={statusType} disableBrowserAutocorrect={disableBrowserAutocorrect} - showEnteredTextOption={showEnteredTextOption} + hideEnteredTextOption={hideEnteredTextOption} {...externalProps} {...baseComponentProps} ref={ref} diff --git a/src/autosuggest/interfaces.ts b/src/autosuggest/interfaces.ts index 6e2468c303..4237abb348 100644 --- a/src/autosuggest/interfaces.ts +++ b/src/autosuggest/interfaces.ts @@ -84,7 +84,7 @@ export interface AutosuggestProps /** * Defines whether entered text option is shown as the first option in the dropdown when value is non-empty. */ - showEnteredTextOption?: boolean; + hideEnteredTextOption?: boolean; /** * Specifies the text to display with the number of matches at the bottom of the dropdown menu while filtering. diff --git a/src/autosuggest/internal.tsx b/src/autosuggest/internal.tsx index e08300b798..60e4f045cf 100644 --- a/src/autosuggest/internal.tsx +++ b/src/autosuggest/internal.tsx @@ -52,7 +52,7 @@ const InternalAutosuggest = React.forwardRef((props: InternalAutosuggestProps, r ariaLabel, ariaRequired, enteredTextLabel, - showEnteredTextOption, + hideEnteredTextOption, filteringResultsText, onKeyDown, virtualScroll, @@ -91,7 +91,7 @@ const InternalAutosuggest = React.forwardRef((props: InternalAutosuggestProps, r filterText: value, filteringType, enteredTextLabel, - hideEnteredTextLabel: !showEnteredTextOption, + hideEnteredTextLabel: hideEnteredTextOption, onSelectItem: (option: AutosuggestItem) => { const value = option.value || ''; fireNonCancelableEvent(onChange, { value }); @@ -195,7 +195,7 @@ const InternalAutosuggest = React.forwardRef((props: InternalAutosuggestProps, r }); const shouldRenderDropdownContent = - autosuggestItemsState.items.length !== 0 || !!dropdownStatus.content || (showEnteredTextOption && !!value); + autosuggestItemsState.items.length !== 0 || !!dropdownStatus.content || (!hideEnteredTextOption && !!value); return ( Date: Thu, 21 Aug 2025 21:14:35 -0400 Subject: [PATCH 4/6] chore: Unmute prop filter test. --- .../property-filter-token-editor.test.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/property-filter/__tests__/property-filter-token-editor.test.tsx b/src/property-filter/__tests__/property-filter-token-editor.test.tsx index ed031ccd2e..9dd4161450 100644 --- a/src/property-filter/__tests__/property-filter-token-editor.test.tsx +++ b/src/property-filter/__tests__/property-filter-token-editor.test.tsx @@ -321,19 +321,18 @@ describe.each([false, true])('token editor, expandToViewport=%s', expandToViewpo }); }); -// describe.each([false, true])('with i18n-provider %s', withI18nProvider => { -// test('uses entered text label for token value autosuggest', () => { -// renderComponent( -// { query: { operation: 'and', tokens: [{ propertyKey: 'string', operator: '=', value: 'John' }] } }, -// withI18nProvider -// ); -// const editor = openEditor(0, { expandToViewport: false }); - -// editor.valueAutosuggest().focus(); -// editor.valueAutosuggest().setInputValue('123'); -// expect(editor.valueAutosuggest().findEnteredTextOption()!.getElement()).toHaveTextContent('Use: "123"'); -// }); -// }); +describe.each([false, true])('with i18n-provider %s', withI18nProvider => { + test('uses entered text label for token value autosuggest', () => { + renderComponent( + { query: { operation: 'and', tokens: [{ propertyKey: 'string', operator: '=', value: 'John' }] } }, + withI18nProvider + ); + const editor = openEditor(0, { expandToViewport: false }); + editor.valueAutosuggest().focus(); + editor.valueAutosuggest().setInputValue('123'); + expect(editor.valueAutosuggest().findEnteredTextOption()!.getElement()).toHaveTextContent('Use: "123"'); + }); +}); const tokenJohn = { propertyKey: 'string', operator: '=', value: 'John' }; const tokenJane = { propertyKey: 'string', operator: '=', value: 'Jane' }; From 339407702b945d0e4f68d8c799c21d41c8bd11da Mon Sep 17 00:00:00 2001 From: Andrei Zhaleznichenka Date: Tue, 28 Oct 2025 10:23:41 +0100 Subject: [PATCH 5/6] improved test page --- pages/autosuggest/search.page.tsx | 143 ++++++++++++------ .../property-filter-token-editor.test.tsx | 1 + 2 files changed, 98 insertions(+), 46 deletions(-) diff --git a/pages/autosuggest/search.page.tsx b/pages/autosuggest/search.page.tsx index db2625a753..2b597e2ef3 100644 --- a/pages/autosuggest/search.page.tsx +++ b/pages/autosuggest/search.page.tsx @@ -1,11 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import React, { useContext, useRef, useState } from 'react'; -import { Badge, Box, Checkbox, ExpandableSection, Header, SpaceBetween } from '~components'; +import React, { useContext, useState } from 'react'; + +import { Badge, Box, Checkbox, SpaceBetween } from '~components'; import Autosuggest, { AutosuggestProps } from '~components/autosuggest'; import AppContext, { AppContextType } from '../app/app-context'; +import { SimplePage } from '../app/templates'; type PageContext = React.Context< AppContextType<{ @@ -15,33 +17,88 @@ type PageContext = React.Context< }> >; -const options = [ - { value: '__apple__', label: 'Apple' }, - { value: '__orange__', label: 'Orange', tags: ['sweet'] }, - { value: '__banana__', label: 'Banana', tags: ['sweet'] }, - { value: '__pineapple__', label: 'Pineapple', description: 'pine+apple' }, +const options: AutosuggestProps.Option[] = [ + { value: '_orange_', label: 'Orange', tags: ['sweet'] }, + { value: '_banana_', label: 'Banana', tags: ['sweet'] }, + { value: '_apple_', label: 'Apple' }, + { value: '_sweet_apple_', label: 'Apple (sweet)', tags: ['sweet'] }, + { value: '_pineapple_', label: 'Pineapple XL', description: 'pine+apple' }, ]; -const enteredTextLabel = (value: string) => `Use: ${value}`; +const enteredTextLabel = (value: string) => `Search for: "${value}"`; + +// This performs a simple fuzzy-search to illustrate how options order can change when searching, +// which can be helpful to increase the search quality. +function findMatchedOptions(options: AutosuggestProps.Option[], searchText: string) { + searchText = searchText.toLowerCase(); + + const getOptionMatchScore = (option: AutosuggestProps.Option) => [ + getPropertyMatchScore(option.label), + getPropertyMatchScore(option.description), + getPropertyMatchScore((option.tags ?? []).join(' ')), + ]; + + const getPropertyMatchScore = (property = '') => { + property = property.toLowerCase(); + return property.indexOf(searchText) === -1 + ? Number.MAX_VALUE + : property.indexOf(searchText) + (property.length - searchText.length); + }; + + return ( + [...options] + // Remove not matched. + .filter(o => getOptionMatchScore(o).some(score => score !== Number.MAX_VALUE)) + // Sort the rest by best match using fuzzy-search with priorities. + .sort((a, b) => { + const aScore = getOptionMatchScore(a); + const bScore = getOptionMatchScore(b); + for (let index = 0; index < Math.min(aScore.length, bScore.length); index++) { + if (aScore[index] !== bScore[index]) { + return aScore[index] - bScore[index]; + } + } + return 0; + }) + ); +} export default function AutosuggestPage() { const { - urlParams: { empty = false, hideEnteredTextOption = false, showMatchesCount = true }, + urlParams: { empty = false, hideEnteredTextOption = true, showMatchesCount = true }, setUrlParams, } = useContext(AppContext as PageContext); - const [value, setValue] = useState(''); - const [selection, setSelection] = useState(''); - const ref = useRef(null); + const [searchText, setSearchText] = useState(''); + const [selection, setSelection] = useState(null); + const matchedOptions = findMatchedOptions(options, searchText); + + // The entered text option indicates that the search text is selectable either from the options dropdown + // or by pressing Enter. This can be used e.g. to navigate the user to a search page. + const onSelectWithFreeSearch: AutosuggestProps['onSelect'] = ({ detail }) => { + if (detail.selectedOption) { + setSelection(detail.selectedOption); + setSearchText(''); + } else { + setSelection(detail.value); + setSearchText(''); + } + }; + + // When the search text is not selectable, pressing Enter from the input can be used to select the best + // matched (first) option instead. + const onSelectWithAutoMatch: AutosuggestProps['onSelect'] = ({ detail }) => { + const selectedOption = detail.selectedOption ?? matchedOptions[0]; + if (selectedOption) { + setSelection(selectedOption); + setSearchText(''); + } + }; + + const onSelect = hideEnteredTextOption ? onSelectWithAutoMatch : onSelectWithFreeSearch; + return ( - + -
- Search -
- - + setUrlParams({ empty: detail.checked })}> Empty @@ -57,43 +114,37 @@ export default function AutosuggestPage() { > Show matches count
- + setValue(event.detail.value)} - onSelect={event => { - if (options.some(o => o.value === event.detail.value)) { - setSelection(event.detail.value); - setValue(''); - } - }} + value={searchText} + options={empty ? [] : matchedOptions} + onChange={event => setSearchText(event.detail.value)} + onSelect={onSelect} enteredTextLabel={enteredTextLabel} - ariaLabel={'simple autosuggest'} - selectedAriaLabel="Selected" + ariaLabel="website search" + selectedAriaLabel="selected" empty="No suggestions" hideEnteredTextOption={hideEnteredTextOption} filteringResultsText={ showMatchesCount - ? matchesCount => { - matchesCount = hideEnteredTextOption ? matchesCount : matchesCount - 1; - return matchesCount ? `${matchesCount} items` : `No matches`; - } + ? () => (matchedOptions.length ? `${matchedOptions.length} items` : `No matches`) : undefined } /> - - Selection: {selection || 'none'} - {options.map(option => ( - - {option.label} + + {selection && typeof selection === 'object' ? ( + + {selection?.label} ({selection?.value}) - ))} - + ) : typeof selection === 'string' ? ( + Search for "{selection}" + ) : ( + 'Nothing selected' + )} + - + ); } diff --git a/src/property-filter/__tests__/property-filter-token-editor.test.tsx b/src/property-filter/__tests__/property-filter-token-editor.test.tsx index 9dd4161450..e7dec0ddc8 100644 --- a/src/property-filter/__tests__/property-filter-token-editor.test.tsx +++ b/src/property-filter/__tests__/property-filter-token-editor.test.tsx @@ -328,6 +328,7 @@ describe.each([false, true])('with i18n-provider %s', withI18nProvider => { withI18nProvider ); const editor = openEditor(0, { expandToViewport: false }); + editor.valueAutosuggest().focus(); editor.valueAutosuggest().setInputValue('123'); expect(editor.valueAutosuggest().findEnteredTextOption()!.getElement()).toHaveTextContent('Use: "123"'); From bdbf5f64871a1237c994a3069850b7fe74a58850 Mon Sep 17 00:00:00 2001 From: Andrei Zhaleznichenka Date: Tue, 28 Oct 2025 10:59:25 +0100 Subject: [PATCH 6/6] test cov --- .../__tests__/autosuggest.test.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/autosuggest/__tests__/autosuggest.test.tsx b/src/autosuggest/__tests__/autosuggest.test.tsx index df64b07e52..b3f7396bca 100644 --- a/src/autosuggest/__tests__/autosuggest.test.tsx +++ b/src/autosuggest/__tests__/autosuggest.test.tsx @@ -127,6 +127,14 @@ test('should display entered text option/label', () => { expect(wrapper.findEnteredTextOption()!.getElement()).toHaveTextContent('Custom function with 1 placeholder'); }); +test('should not display entered text option when hideEnteredTextOption=false', () => { + const { wrapper } = renderAutosuggest( + 'X'} value="" options={defaultOptions} hideEnteredTextOption={true} /> + ); + wrapper.setInputValue('1'); + expect(wrapper.findEnteredTextOption()).toBe(null); +}); + test('entered text option should not get screenreader override', () => { const { wrapper } = renderAutosuggest(); wrapper.focus(); @@ -447,6 +455,47 @@ describe('Check if should render dropdown', () => { expect(wrapper.findDropdown().findOpenDropdown()).not.toBe(null); }); + + test('should render dropdown when the only visible option is entered text option', () => { + const { wrapper } = renderAutosuggest( + 'X'} value="" options={defaultOptions} /> + ); + + wrapper.focus(); + wrapper.setInputValue('XXX'); + + expect(wrapper.findDropdown().findOpenDropdown()).not.toBe(null); + expect(wrapper.findDropdown().findOptions()).toHaveLength(0); + expect(wrapper.findEnteredTextOption()).not.toBe(null); + }); + + test('should render dropdown when no options matched with a message', () => { + const { wrapper } = renderAutosuggest( + 'No matches'} + /> + ); + + wrapper.focus(); + wrapper.setInputValue('XXX'); + + expect(wrapper.findDropdown().findOpenDropdown()!.getElement()).toHaveTextContent('No matches'); + expect(wrapper.findDropdown().findOptions()).toHaveLength(0); + }); + + test('should not render dropdown when no options matched with no message', () => { + const { wrapper } = renderAutosuggest( + + ); + + wrapper.focus(); + wrapper.setInputValue('XXX'); + + expect(wrapper.findDropdown().findOpenDropdown()).toBe(null); + }); }); describe('Ref', () => {