Repository navigation
Conversation
e0dbbca to
d241216
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5072 +/- ##
========================================
Coverage 97.70% 97.71%
========================================
Files 990 993 +3
Lines 31820 32143 +323
Branches 11754 11894 +140
========================================
+ Hits 31091 31409 +318
- Misses 683 686 +3
- Partials 46 48 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
d241216 to
d22f8cb
Compare
be56129 to
524fe7a
Compare
| onKeyDown={handlePanelKeyDown} | ||
| className={styles['overflow-panel']} | ||
| > | ||
| <ul ref={listRef} role="list" className={styles['overflow-panel-list']}> |
There was a problem hiding this comment.
The list does not have an aria label/labelledby - is that expected?
| </InternalLiveRegion> | ||
| <div | ||
| ref={measureRef} | ||
| style={{ |
There was a problem hiding this comment.
Let's have some reusable styles for this kind of measurement hiding styles.
Would this work for you? If we add exactly the same snippet in the same place, I think both PRs shouldn't conflict on this.
There was a problem hiding this comment.
Once #5111 merges, the class can be replaced with the mixin.
There was a problem hiding this comment.
Later means never. Doing it here already is the best way to make sure the duplication is not left around.
| background-color: awsui.$color-background-control-default; | ||
| color: awsui.$color-text-body-default; | ||
| font-weight: bold; | ||
| cursor: pointer; |
There was a problem hiding this comment.
This element is pretty much like a select, but with a few subtle styling differences such as bold font,cursor: pointer, non-animated icon. Wouldn't it be better to keep them more consistent?
There was a problem hiding this comment.
Select have some A11y contraints which we need here we need to show options within dropdown as tokens only so neither dropdown nor select can be used
There was a problem hiding this comment.
I'm not asking to use those components, but to align more with them design-wise. For example, why is cursor: pointer used here if it is not used in select or multiselect?
| } | ||
|
|
||
| .overflow-panel { | ||
| position: absolute; |
There was a problem hiding this comment.
Why not use the internal import of the dropdown component for the overflow panel?
There was a problem hiding this comment.
Exisiting dropdown wan't the right choice for displaying tokens within it. We already had a design decision on this
There was a problem hiding this comment.
Why wasn't it the right choice? The dropdown can render arbitrary content, and handles all this overlay logic and styles for you. This is the exact kind of use case that it is meant for, unless I am missing something.
|
There will be a follow up PR for Visual Tests and Adding scenario to Control Segment Demo page |
There was a problem hiding this comment.
🟡 Changes recommended
Core overflow measurement, popup accessibility, localization, styling, and test-utils issues remain unresolved.
13 open findings
Filter overflow items before wrapping tokens · New Use stable test-utils selectors for the clear button · New Use plural-aware ICU messages for hidden token counts · New Handle ArrowLeft to move from input to token · New Preserve grouped-control context reset in token mode · New Allow read-only users to inspect overflowed tokens · New Close suggestions before opening the overflow panel · New Apply the style prop to the token trigger · New Render the dropdown in a focus-trapped portal · New Avoid reserving pill space when no pill is needed · New Invalidate measurement when token content changes · New Use SimplePage for the demo page shell · New Detect tokens mode by prop presence · New
What changed in this PR
Adds controlled inline-token support to Autosuggest, including overflow handling, keyboard interaction, localization, test utilities, and demos.
Changes:
- Adds dismissible tokens, overflow popup, focus management, and styling.
- Extends Autosuggest APIs and localized strings.
- Adds unit/integration coverage and test-utils selectors.
| File | Description |
|---|---|
src/test-utils/dom/internal/autosuggest-input.ts |
Adds internal token selectors. |
src/test-utils/dom/autosuggest/index.ts |
Exposes token test utilities. |
src/internal/components/autosuggest-input/use-token-overflow.ts |
Measures token overflow. |
src/internal/components/autosuggest-input/use-token-list-focus.ts |
Manages token focus. |
src/internal/components/autosuggest-input/test-classes/styles.scss |
Adds stable test classes. |
src/internal/components/autosuggest-input/styles.scss |
Styles token mode and overflow. |
src/internal/components/autosuggest-input/overflow-dropdown.tsx |
Implements overflow popup. |
src/internal/components/autosuggest-input/index.tsx |
Integrates token-mode behavior. |
src/internal/components/autosuggest-input/__tests__/autosuggest-input-tokens.test.tsx |
Tests token input behavior. |
src/input/styles.scss |
Supports borderless embedded input. |
src/input/internal.tsx |
Adds internal borderless mode. |
src/i18n/messages/all.zh-TW.json |
Adds token-related translations. |
src/i18n/messages/all.zh-CN.json |
Adds token-related translations. |
src/i18n/messages/all.tr.json |
Adds token-related translations. |
src/i18n/messages/all.pt-BR.json |
Adds token-related translations. |
src/i18n/messages/all.ko.json |
Adds token-related translations. |
src/i18n/messages/all.ja.json |
Adds token-related translations. |
src/i18n/messages/all.it.json |
Adds token-related translations. |
src/i18n/messages/all.id.json |
Adds token-related translations. |
src/i18n/messages/all.fr.json |
Adds token-related translations. |
src/i18n/messages/all.es.json |
Adds token-related translations. |
src/i18n/messages/all.en.json |
Adds English token messages. |
src/i18n/messages/all.en-GB.json |
Adds token-related translations. |
src/i18n/messages/all.de.json |
Adds token-related translations. |
src/i18n/messages-types.ts |
Types new message arguments. |
src/autosuggest/options-controller.ts |
Accepts resolved entered-text labels. |
src/autosuggest/internal.tsx |
Integrates tokens and i18n. |
src/autosuggest/interfaces.ts |
Adds public token APIs. |
src/autosuggest/__tests__/options-controller.test.ts |
Tests entered-text labels. |
src/autosuggest/__tests__/i18n.test.tsx |
Tests token localization. |
src/autosuggest/__tests__/autosuggest.test.tsx |
Tests token selection events. |
src/autosuggest/__tests__/autosuggest-tokens-mode.test.tsx |
Tests public token mode. |
src/autosuggest/__integ__/page-objects/autosuggest-tokens-page.ts |
Adds integration helpers. |
src/autosuggest/__integ__/autosuggest-tokens.test.ts |
Tests browser interactions. |
src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap |
Updates selector snapshots. |
src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap |
Updates API snapshots. |
pages/autosuggest/tokens-mode.page.tsx |
Adds a token-mode demo. |
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
| findInlineTokens(): Array<TokenWrapper> { | ||
| return this.findAllByClassName(autosuggestInputTestUtilStyles['token-list-item']).map( | ||
| (elementWrapper: ElementWrapper) => | ||
| new TokenWrapper(elementWrapper.findComponent(`.${TokenWrapper.rootSelector}`, TokenWrapper)!.getElement()) | ||
| ); | ||
| } |
| get clearButtonSelector() { | ||
| return `${this.scope} [class*="input-icon-end"] button`; | ||
| } |
| const resolvedTokenOverflowAriaLabel = | ||
| i18nStrings?.tokenOverflowAriaLabel ?? | ||
| i18n('i18nStrings.tokenOverflowAriaLabel', undefined, format => (n: number) => format({ count: String(n) })) ?? | ||
| ((n: number) => `${n} more items`); |
| case KeyCode.backspace: { | ||
| if (!readOnly && !disabled && isTokenMode && value === '' && tokenList.length > 0) { | ||
| focusTokenAtIndex(tokenList.length - 1); | ||
| event.preventDefault(); |
| 'aria-activedescendant': ariaActivedescendant, | ||
| }; | ||
|
|
||
| const renderDropdownShell = (trigger: React.ReactNode, wrapContent = false) => ( |
| <div | ||
| ref={panelRef} | ||
| onMouseDown={e => e.preventDefault()} | ||
| onKeyDown={handlePanelKeyDown} | ||
| className={styles['overflow-panel']} |
| const totalAll = tokenWidths.reduce((s, w) => s + w, 0) + gap * Math.max(tokenWidths.length - 1, 0); | ||
| const budget = containerWidth - paddingInline - iconWidth - inputMinWidth - pillWidth - gap - gap; | ||
|
|
||
| let newCount: number; | ||
| let newMaxWidth: number | undefined; | ||
|
|
||
| if (totalAll <= budget) { |
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [enabled, tokenCount, containerResizeCount]); |
| export default function AutosuggestTokensModePage() { | ||
| return ( | ||
| <Box margin="m"> | ||
| <SpaceBetween size="xxl"> |
| * | ||
| * Since you control whether `tokens` is provided, you can branch on your | ||
| * own state to return different text per mode: | ||
| * `(value) => tokens.length ? \`Add "${value}"\` : \`Use "${value}"\`` |





Description
Adds tokens mode to Autosuggest. When a
tokensarray is provided (including empty), accepted values display as inline dismissible token pills before the text input, with overflow into a+Npill that opens a focus-trapped portal dropdown.New props:
tokens: ReadonlyArray<AutosuggestProps.Token>— activates tokens mode when provided (controlled)i18nStrings.enteredTextLabel: (value: string) => string— replaces top-levelenteredTextLabel; returnAdd "${value}"orUse: "${value}"based on your statei18nStrings.tokenOverflowAriaLabel: (count: number) => string— accessible label for the+Noverflow pillBehaviour:
+Npill; clicking opens a dropdownAccessibility:
focusTokenAtIndexso it fires even whenfocusedTokenIndexhasn't changed (avoids React setState no-op)Kick off Doc: https://chorus.aws.dev/doc/Op6bEowqHzMY/Kick-offScope-alignment-and-Design-SO-input-flexibility
API Proposal Doc: https://chorus.aws.dev/doc/qJ3Ttxk8R7CZ/API-Proposal-Autosuggest--Inline-Tokens-Mode
How has this been tested?
Unit tests (
autosuggest-input-tokens.test.tsx) cover token rendering, add/dismiss, keyboard nav, overflow pill, ARIA, i18n resolution, and Backspace idempotency. Integration tests (autosuggest-tokens.test.ts) cover full add-token flows, overflow pill open/close/Escape, keyboard nav, and clear button.To review locally: runnpm run startand openhttp://localhost:8080/autosuggest/tokens-mode. The interactive demo at the top lets you add/dismiss tokens and trigger overflow. Static scenario rows below cover all states.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.