Skip to content

feat: Add tokens mode with inline dismissible token pills - #5072

Open
srungta08 wants to merge 4 commits into
mainfrom
dev-v3-srungta-input-leading-content
Open

srungta08 wants to merge 4 commits into
mainfrom
dev-v3-srungta-input-leading-content

Conversation

@srungta08

@srungta08 srungta08 commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Description

Adds tokens mode to Autosuggest. When a tokens array is provided (including empty), accepted values display as inline dismissible token pills before the text input, with overflow into a +N pill 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-level enteredTextLabel; return Add "${value}" or Use: "${value}" based on your state
  • i18nStrings.tokenOverflowAriaLabel: (count: number) => string — accessible label for the +N overflow pill

Behaviour:

  • Selecting an item (Enter or dropdown click) adds it as a token
  • Tokens overflow into a +N pill; clicking opens a dropdown
  • Clear button (×) appears when input has typed text

Accessibility:

  • Keyboard: ArrowLeft/Right navigate between tokens and input; Backspace on empty input focuses last token; Backspace/Delete on a focused token dismisses it; focus returns to input after last token dismissed
  • Backspace focus uses imperative focusTokenAtIndex so it fires even when focusedTokenIndex hasn'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: run npm run start and open http://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.

@srungta08
srungta08 force-pushed the dev-v3-srungta-input-leading-content branch from e0dbbca to d241216 Compare September 28, 2026 18:21
@srungta08 srungta08 changed the title feat(autosuggest): Add tokens mode with inline dismissible token pills feat: Add tokens mode with inline dismissible token pills Sep 28, 2026
@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.48603% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.71%. Comparing base (fdcc7a2) to head (10d705c).

Files with missing lines Patch % Lines
...rc/internal/components/autosuggest-input/index.tsx 95.97% 5 Missing and 2 partials ⚠️
...components/autosuggest-input/use-token-overflow.ts 96.49% 2 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@srungta08
srungta08 force-pushed the dev-v3-srungta-input-leading-content branch from d241216 to d22f8cb Compare September 29, 2026 08:16
Comment thread test/definitions/visual/autosuggest.ts Outdated
Comment thread src/test-utils/dom/internal/autosuggest-input.ts
Comment thread src/internal/components/autosuggest-input/use-token-overflow.ts Outdated
Comment thread src/autosuggest/__integ__/autosuggest-tokens.test.ts
onKeyDown={handlePanelKeyDown}
className={styles['overflow-panel']}
>
<ul ref={listRef} role="list" className={styles['overflow-panel-list']}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list does not have an aria label/labelledby - is that expected?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will check with A11y

@pan-kot

pan-kot commented Oct 8, 2026

Copy link
Copy Markdown
Member

The disabled state is not too strict, apparently.

Screenshot 2026-10-08 at 11 47 09

Comment thread src/autosuggest/interfaces.ts
Comment thread src/i18n/messages/all.th.json Outdated
Comment thread src/internal/components/autosuggest-input/index.tsx
Comment thread src/internal/components/autosuggest-input/index.tsx
Comment thread src/input/styles.scss
Comment thread src/autosuggest/interfaces.ts Outdated
Comment thread src/internal/components/autosuggest-input/index.tsx Outdated
</InternalLiveRegion>
<div
ref={measureRef}
style={{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once #5111 merges, the class can be replaced with the mixin.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later means never. Doing it here already is the best way to make sure the duplication is not left around.

Comment thread src/internal/components/autosuggest-input/use-token-list-focus.ts Outdated
background-color: awsui.$color-background-control-default;
color: awsui.$color-text-body-default;
font-weight: bold;
cursor: pointer;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the internal import of the dropdown component for the overflow panel?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exisiting dropdown wan't the right choice for displaying tokens within it. We already had a design decision on this

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@srungta08

Copy link
Copy Markdown
Member Author

The disabled state is not too strict, apparently.

Screenshot 2026-10-08 at 11 47 09

Fixed

@srungta08

Copy link
Copy Markdown
Member Author

There will be a follow up PR for Visual Tests and Adding scenario to Control Segment Demo page

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Core overflow measurement, popup accessibility, localization, styling, and test-utils issues remain unresolved.

13 open findings
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.

Comment on lines +196 to +201
findInlineTokens(): Array<TokenWrapper> {
return this.findAllByClassName(autosuggestInputTestUtilStyles['token-list-item']).map(
(elementWrapper: ElementWrapper) =>
new TokenWrapper(elementWrapper.findComponent(`.${TokenWrapper.rootSelector}`, TokenWrapper)!.getElement())
);
}
Comment on lines +81 to +83
get clearButtonSelector() {
return `${this.scope} [class*="input-icon-end"] button`;
}
Comment on lines +101 to +104
const resolvedTokenOverflowAriaLabel =
i18nStrings?.tokenOverflowAriaLabel ??
i18n('i18nStrings.tokenOverflowAriaLabel', undefined, format => (n: number) => format({ count: String(n) })) ??
((n: number) => `${n} more items`);
Comment on lines +315 to +318
case KeyCode.backspace: {
if (!readOnly && !disabled && isTokenMode && value === '' && tokenList.length > 0) {
focusTokenAtIndex(tokenList.length - 1);
event.preventDefault();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required

'aria-activedescendant': ariaActivedescendant,
};

const renderDropdownShell = (trigger: React.ReactNode, wrapContent = false) => (
Comment on lines +97 to +101
<div
ref={panelRef}
onMouseDown={e => e.preventDefault()}
onKeyDown={handlePanelKeyDown}
className={styles['overflow-panel']}
Comment on lines +83 to +89
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) {
Comment on lines +111 to +112
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [enabled, tokenCount, containerResizeCount]);
Comment on lines +197 to +200
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}"\``

This branch is being deployed

1 queued and 2 active deployments
allure-report — 10d705c9 Deployed Oct 8, 2026 by srungta08 via Visual regression / Deploy Allure Report / deploy #2638
dev-pages-react16 — 10d705c9 Deployed Oct 8, 2026 by srungta08 via deploy (React 16) / deploy #2638
dev-pages-react18 — 10d705c9 Deployed Oct 8, 2026 by srungta08 via deploy (React 18) / deploy #2638
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants