Skip to content

fix(combobox, breadcrumbs): propagate lang/dir for a single item's language without breaking layout - #6496

Open
majornista wants to merge 14 commits into
mainfrom
mijordan/SWC-2359-combobox-lang-of-parts
Open

fix(combobox, breadcrumbs): propagate lang/dir for a single item's language without breaking layout#6496
majornista wants to merge 14 commits into
mainfrom
mijordan/SWC-2359-combobox-lang-of-parts

Conversation

@majornista

@majornista majornista commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

Adds "language of parts" support to sp-combobox and sp-breadcrumbs: consumers can set lang/dir on an individual slotted item (e.g. a language name rendered in its own script) and have it render correctly, without breaking the surrounding component's layout.

  • Combobox: a slotted <sp-menu-item lang="he" dir="rtl"> (or a ComboboxOption with lang/dir) now propagates those attributes to its rendered counterpart in the popover. The text input itself also adopts the lang of the currently-selected option, so screen readers pronounce the displayed value correctly.
  • Breadcrumbs: the same propagation applies to items collapsed into the "More items" overflow menu.
  • BreadcrumbItem: lang/dir now apply only to #item-link (the text), not the host — so one item's language doesn't flip its own layout or its separator's chevron relative to its siblings. The chevron now tracks the ambient direction (nearest ancestor's dir, or the document default) instead of the item's own dir attribute, and stays correct if an ancestor's dir changes after the item has already mounted, or for the overflow-menu wrapper item (which is rendered inside Breadcrumbs' own shadow root rather than as a light-DOM child).

Two non-obvious base-class behaviors caused regressions mid-implementation and are now specifically guarded against (with tests that fail without the fix):

  • SpectrumElement overrides the native dir getter to return computed CSS direction rather than the attribute, so authored dir values must be read via getAttribute('dir').
  • CSS :dir() resolves directionality via the DOM's dir attribute chain, entirely independent of the direction property — so direction: inherit alone doesn't stop a mismatched attribute from being picked up by descendant :dir() selectors.

Motivation and context

Combobox and Breadcrumbs synthesize new elements from extracted data (rather than reusing the original slotted elements) when rendering their popover/overflow menu, so any lang/dir set on the original item was silently dropped — breaking pronunciation and bidi text shaping for mixed-language lists (e.g. a language picker).

Related issue(s)

  • fixes SWC-2359

Screenshots (if appropriate)

N/A — no visual/layout changes to default (single-language) usage. New Storybook stories (languageOfParts, LanguageOfParts) demonstrate the fix; a screenshot/VRT baseline update may be worth attaching before merge.

Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published. (not yet added — flagging before merge)
  • I have included updated documentation if my change required it. (no MDX/README changes made; consider a note in each component's docs)

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

  • Combobox popover items and input keep an item's own language

    1. Go to Storybook → Combobox → languageOfParts
    2. Open the combobox and inspect each option in devtools
    3. Expect each <sp-menu-item> in the popover to carry the same lang/dir as its source; select the Hebrew or Arabic option and expect the input's own lang attribute to update to match
  • Breadcrumbs overflow menu keeps an item's own language

    1. Go to Storybook → Breadcrumbs → LanguageOfParts (forces the overflow menu via max-visible-items)
    2. Open the "More items" menu and inspect the items in devtools
    3. Expect each <sp-menu-item> to carry the same lang/dir as the source <sp-breadcrumb-item>
  • A single item's language doesn't break the breadcrumb trail's layout

    1. In the LanguageOfParts story, locate the Hebrew/Arabic breadcrumb items amid the (LTR) trail
    2. Expect their separators (chevrons) to point the same direction as their LTR siblings, not mirrored
    3. Toggle dir="rtl" on <sp-breadcrumbs> via devtools after the story has rendered
    4. Expect all separators (including the "More items" wrapper's, if overflowed) to mirror correctly

Device review

  • Did it pass in Desktop?
  • Did it pass in (emulated) Mobile?
  • Did it pass in (emulated) iPad?

Accessibility testing checklist

  • Keyboard (required — document steps below)

    1. Go to Storybook → Combobox → languageOfParts
    2. Tab to the combobox, press ArrowDown to open, arrow through options, press Enter to select a non-Latin-script option (e.g. עברית)
    3. Expect focus to return to the input with the selected value displayed; no change in tab order or focus trapping from the lang/dir changes
  • Screen reader (required — document steps below)

    1. With a screen reader active, open Storybook → Combobox → languageOfParts and arrow through the popover options
    2. Expect each option (including עברית, العربية) to be announced using that language's pronunciation rules, not the page's default language
    3. Select a non-Latin option and expect the combobox's announced value to use the same correct pronunciation
    4. Repeat for Storybook → Breadcrumbs → LanguageOfParts, opening the "More items" overflow menu

@majornista
majornista requested a review from a team as a code owner July 10, 2026 20:54
@majornista majornista added bug Something isn't working a11y Issues or PRs related to accessibility Component:Combobox Component:Breadcrumbs 1st-gen labels Jul 10, 2026
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0830b1b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 83 packages
Name Type
@spectrum-web-components/combobox Patch
@spectrum-web-components/breadcrumbs Patch
@spectrum-web-components/base Patch
@spectrum-web-components/reactive-controllers Patch
@spectrum-web-components/bundle Patch
@spectrum-web-components/accordion Patch
@spectrum-web-components/action-bar Patch
@spectrum-web-components/action-button Patch
@spectrum-web-components/action-group Patch
@spectrum-web-components/action-menu Patch
@spectrum-web-components/alert-banner Patch
@spectrum-web-components/alert-dialog Patch
@spectrum-web-components/asset Patch
@spectrum-web-components/avatar Patch
@spectrum-web-components/badge Patch
@spectrum-web-components/button-group Patch
@spectrum-web-components/button Patch
@spectrum-web-components/card Patch
@spectrum-web-components/checkbox Patch
@spectrum-web-components/clear-button Patch
@spectrum-web-components/close-button Patch
@spectrum-web-components/coachmark Patch
@spectrum-web-components/color-area Patch
@spectrum-web-components/color-field Patch
@spectrum-web-components/color-handle Patch
@spectrum-web-components/color-loupe Patch
@spectrum-web-components/color-slider Patch
@spectrum-web-components/color-wheel Patch
@spectrum-web-components/contextual-help Patch
@spectrum-web-components/dialog Patch
@spectrum-web-components/divider Patch
@spectrum-web-components/dropzone Patch
@spectrum-web-components/field-group Patch
@spectrum-web-components/field-label Patch
@spectrum-web-components/help-text Patch
@spectrum-web-components/icon Patch
@spectrum-web-components/icons-ui Patch
@spectrum-web-components/icons-workflow Patch
@spectrum-web-components/icons Patch
@spectrum-web-components/iconset Patch
@spectrum-web-components/illustrated-message Patch
@spectrum-web-components/infield-button Patch
@spectrum-web-components/link Patch
@spectrum-web-components/menu Patch
@spectrum-web-components/meter Patch
@spectrum-web-components/modal Patch
@spectrum-web-components/number-field Patch
@spectrum-web-components/overlay Patch
@spectrum-web-components/picker-button Patch
@spectrum-web-components/picker Patch
@spectrum-web-components/popover Patch
@spectrum-web-components/progress-bar Patch
@spectrum-web-components/progress-circle Patch
@spectrum-web-components/radio Patch
@spectrum-web-components/search Patch
@spectrum-web-components/sidenav Patch
@spectrum-web-components/slider Patch
@spectrum-web-components/split-view Patch
@spectrum-web-components/status-light Patch
@spectrum-web-components/swatch Patch
@spectrum-web-components/switch Patch
@spectrum-web-components/table Patch
@spectrum-web-components/tabs Patch
@spectrum-web-components/tags Patch
@spectrum-web-components/textfield Patch
@spectrum-web-components/thumbnail Patch
@spectrum-web-components/toast Patch
@spectrum-web-components/tooltip Patch
@spectrum-web-components/top-nav Patch
@spectrum-web-components/tray Patch
@spectrum-web-components/underlay Patch
@spectrum-web-components/custom-vars-viewer Patch
@spectrum-web-components/story-decorator Patch
@spectrum-web-components/vrt-compare Patch
@spectrum-web-components/grid Patch
@spectrum-web-components/opacity-checkerboard Patch
@spectrum-web-components/shared Patch
@spectrum-web-components/styles Patch
@spectrum-web-components/theme Patch
@spectrum-web-components/truncated Patch
documentation Patch
@spectrum-web-components/eslint-plugin Patch
@spectrum-web-components/stylelint-header-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

📚 Branch Preview Links

🔍 Gen1 Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6496

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

@coveralls

coveralls commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 30754734209

Warning

No base build found for commit 795a3f7 on main.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 96.263%

Details

  • Patch coverage: 2 uncovered changes across 1 file (297 of 299 lines covered, 99.33%).

Uncovered Changes

File Changed Covered %
1st-gen/packages/breadcrumbs/src/Breadcrumbs.ts 53 51 96.23%
Total (6 files) 299 297 99.33%

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 39550
Covered Lines: 38273
Line Coverage: 96.77%
Relevant Branches: 6527
Covered Branches: 6082
Branch Coverage: 93.18%
Branches in Coverage %: Yes
Coverage Strength: 463.87 hits per line

💛 - Coveralls

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds “language of parts” support to 1st-gen sp-combobox and sp-breadcrumbs by propagating per-item lang/dir into internally-rendered menu items, and by scoping sp-breadcrumb-item’s lang/dir so a single item’s language doesn’t flip breadcrumb layout/separators.

Changes:

  • Combobox: propagate option lang/dir into rendered popover items and sync the input’s lang to the committed selection.
  • Breadcrumbs: propagate item lang/dir into overflow menu items; adjust sp-breadcrumb-item so separators mirror based on ambient direction rather than the item’s own dir.
  • Add targeted tests and Storybook stories demonstrating mixed lang/dir lists, plus update 1st-gen version typings.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
1st-gen/tools/base/src/version.d.ts Updates declared versions to match implementation.
1st-gen/packages/combobox/test/combobox-a11y.test.ts Adds coverage for lang/dir propagation and input lang syncing.
1st-gen/packages/combobox/stories/combobox.stories.ts Adds languageOfParts story demonstrating per-item language/direction.
1st-gen/packages/combobox/src/Combobox.ts Implements option lang/dir forwarding and input lang binding.
1st-gen/packages/breadcrumbs/test/breadcrumbs.test.ts Adds overflow menu propagation tests and ancestor dir-change regression test.
1st-gen/packages/breadcrumbs/test/breadcrumb-item.test.ts Adds tests ensuring per-item dir doesn’t break layout and separators track ambient direction.
1st-gen/packages/breadcrumbs/stories/breadcrumbs.stories.ts Adds LanguageOfParts story for overflow menu propagation.
1st-gen/packages/breadcrumbs/src/Breadcrumbs.ts Captures per-item lang/dir and forwards into overflow menu items.
1st-gen/packages/breadcrumbs/src/BreadcrumbItem.ts Scopes lang/dir onto #item-link and computes separator direction from ambient context with ancestor observation.
1st-gen/packages/breadcrumbs/src/breadcrumb-item.css Ensures host layout direction inherits ambient direction even when host has dir.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread 1st-gen/packages/breadcrumbs/src/BreadcrumbItem.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.

Comment thread 1st-gen/packages/combobox/src/Combobox.ts Outdated
Comment thread 1st-gen/packages/breadcrumbs/test/breadcrumb-item.test.ts Outdated
Comment thread 1st-gen/packages/breadcrumbs/test/breadcrumb-item.test.ts Outdated
Comment thread 1st-gen/packages/breadcrumbs/test/breadcrumb-item.test.ts Outdated
@majornista
majornista requested a review from Copilot July 10, 2026 21:58
@majornista
majornista force-pushed the mijordan/SWC-2359-combobox-lang-of-parts branch from 90ccb0c to a77171e Compare July 10, 2026 22:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.

Comment thread 1st-gen/packages/breadcrumbs/src/Breadcrumbs.ts
Comment thread 1st-gen/packages/breadcrumbs/src/Breadcrumbs.ts Outdated
@majornista
majornista requested a review from Copilot July 10, 2026 22:04
@majornista
majornista force-pushed the mijordan/SWC-2359-combobox-lang-of-parts branch from 0377af4 to 3c7d781 Compare July 10, 2026 22:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Comment thread 1st-gen/packages/breadcrumbs/src/breadcrumb-item.css Outdated
@majornista
majornista force-pushed the mijordan/SWC-2359-combobox-lang-of-parts branch from 3c7d781 to ff9409d Compare July 10, 2026 22:28
@majornista
majornista requested a review from Copilot July 10, 2026 22:29
@majornista
majornista force-pushed the mijordan/SWC-2359-combobox-lang-of-parts branch from c017bce to 0e255f5 Compare July 10, 2026 22:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.

Comment thread 1st-gen/packages/combobox/test/combobox-a11y.test.ts Outdated
Comment thread 1st-gen/packages/breadcrumbs/test/breadcrumbs.test.ts Outdated
@majornista
majornista requested a review from Copilot July 10, 2026 22:34
@caseyisonit

Copy link
Copy Markdown
Contributor

@majornista can you revert the golden_hash token so that we can review the VRT failures as part of our review? that is usually the last commit after getting two approvals.

@majornista
majornista force-pushed the mijordan/SWC-2359-combobox-lang-of-parts branch 2 times, most recently from 2958240 to a2a4405 Compare July 15, 2026 13:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.

Comment thread 1st-gen/packages/combobox/test/helpers.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

@Rajdeepc Rajdeepc 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.

Reviewed from an architecture/ownership lens (public API, consistency with existing patterns, a11y, release risk). Left 3 inline comments — two are blocking asks around reusing an existing pattern instead of introducing a new one, one is a question about scope. Core diagnosis of the underlying bug (SpectrumElement's dir getter returning computed style, :dir() resolving via the attribute chain) is correct and well tested; not re-litigating that part.

Comment thread 1st-gen/packages/breadcrumbs/src/BreadcrumbItem.ts
Comment thread 1st-gen/packages/breadcrumbs/src/BreadcrumbItem.ts Outdated
Comment thread 1st-gen/packages/combobox/src/Combobox.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 1 comment.

Comment thread 1st-gen/packages/breadcrumbs/test/breadcrumbs.test.ts Outdated

@Rajdeepc Rajdeepc 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.

Re-reviewed after the latest commits — the shared-observer refactor, the native attributeChangedCallback switch, and the input dir fix are all in and look good (the dir="auto" approach for uncommitted RTL search text is better than what I'd suggested). One new thing surfaced by the button-clearance follow-up fix, left inline below.

Comment thread 1st-gen/packages/combobox/src/combobox.css Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated 1 comment.

Comment thread .changeset/combobox-breadcrumbs-lang-of-parts.md

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated 1 comment.

Comment thread 1st-gen/tools/reactive-controllers/src/AttributeObserver.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

1st-gen/tools/reactive-controllers/test/attribute-observer.test.ts:21

  • The helper comment says it flushes MutationObserver callbacks “by yielding a microtask”, but the implementation yields a macrotask via setTimeout(). Either update the comment or switch the helper to an actual microtask flush so the intent matches the code.
/**
 * Flush MutationObserver callbacks by yielding a microtask.
 */
const flushObserver = (): Promise<void> =>
  new Promise((resolve) => setTimeout(resolve, 0));

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

1st-gen/tools/reactive-controllers/src/AttributeObserver.ts:89

  • reconnect() drains sharedObserver.takeRecords() after the new listener is added to the registry. If there are already-queued mutation records for the same (target, attribute) (attribute changed just before observeAttribute() but the observer callback hasn’t fired yet), those pre-subscription records will be delivered to the newly registered listener, causing spurious updates.

Drain queued records before adding the listener so only existing subscribers see earlier mutations.

  listeners.add(listener);
  reconnect();

@majornista

Copy link
Copy Markdown
Contributor Author

@caseyisonit, @Rajdeepc, @rise-erpelding this PR still needs review or approval.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated no new comments.

Michael Jordan and others added 14 commits August 2, 2026 11:36
…nguage without breaking layout

Previously, setting `lang`/`dir` on a slotted `<sp-menu-item>` (Combobox)
or `<sp-breadcrumb-item>` (Breadcrumbs) had no effect on the rendered
popover/overflow-menu counterpart, since both components synthesize new
elements from extracted data rather than reusing the originals.

- Combobox: forwards `lang`/`dir` from slotted items (or `.options` data)
  onto the rendered popover `<sp-menu-item>`, and now syncs the input's
  own `lang` to the committed option's language for correct pronunciation.
- Breadcrumbs: same propagation for the "More items" overflow menu.
- BreadcrumbItem: forwards `lang`/`dir` to `#item-link` only, so a single
  item's language does not flip its own layout or its separator's
  mirrored chevron. The separator now explicitly tracks the *ambient*
  direction (nearest ancestor `dir`, or the document default) instead of
  inheriting the host's own `dir` attribute via `:dir()`, including live
  updates when an ancestor's `dir` changes after mount, and across the
  shadow-root boundary for the overflow-menu wrapper item.

Along the way, worked around two non-obvious base-class behaviors that
caused regressions mid-fix: `SpectrumElement` overrides `dir` to return
computed CSS direction rather than the attribute (must read
`getAttribute('dir')` for authored values), and CSS `:dir()` resolves
directionality via the attribute chain, independent of the `direction`
property.

Adds Storybook stories (`languageOfParts` / `LanguageOfParts`) and test
coverage for each fix, each verified to fail without its corresponding
code change.

Jira: SWC-2359

fix: refactor `dir` attribute normalization into a utility method

fix(combobox): use correct type shape

Co-Authored-By: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-Authored-By: Michael Jordan <michael@majordan.net>
PR adds new storybook stories, so we need a new golden_images_hash
- BreadcrumbItem allocated one MutationObserver per instance to watch
every ancestor for `dir` changes plus itself for `dir`/`lang`; a deeply
nested breadcrumb trail meant O(items x ancestor depth) observer
registrations, all re-watching the same shared ancestors
- add `observeAttribute()` to reactive-controllers: a single shared
MutationObserver keyed by (target, attribute), for watching elements a
component doesn't own (ancestors, `document.documentElement`)
- route LanguageResolutionController's `addLangListener` through the
same primitive instead of its own bespoke single-target observer, so
there's one idiom for this pattern instead of two
- BreadcrumbItem now only uses the shared observer for ancestor `dir`;
its own `dir`/`lang` changes go through the native
`observedAttributes`/`attributeChangedCallback` lifecycle hooks (see
Tooltip.ts), which need no observer object at all

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- calculateBreadcrumbItemsWidth() only re-snapshots each item's lang/dir
into the cached `items` array when items are added/removed or the
layout recalculates (maxVisibleItems/compact change); renderMenu()
always renders the overflow menu's <sp-menu-item> copies from that
cache
- since BreadcrumbItem now reacts live to its own post-mount lang/dir
changes, the visible breadcrumb could update while its overflow-menu
copy stayed stale indefinitely, with no trigger to ever resync it
- add watchItemAttributes(), reusing the shared observeAttribute()
singleton, to patch just the changed item's cached lang/dir in place
whenever the corresponding live item's lang/dir attribute changes
- add a regression test covering the overflow-menu resync

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- syncing the input's `lang` to the committed option (added earlier)
fixes pronunciation, but left `dir` unsynced, so a genuinely RTL value
like Hebrew/Arabic still rendered in a directionally-LTR input with
misleading caret placement/text alignment
- use the native `dir="auto"` on the input instead of mirroring the
selected option's own `dir`: it derives directionality live from the
input's actual value via the browser's bidi algorithm, so typed-but
-not-yet-committed RTL search text gets correct alignment too, not
just a value that has already resolved to a committed option
- scoped to just the input element, so it can't affect surrounding
combobox layout (icons, popover positioning)
- add a test covering both the uncommitted-RTL-text and reverts-to-ltr
cases

fix(combobox): keep button-clearance padding on its physical side

- #input's dir="auto" (added earlier) lets its own computed direction
diverge from the host's stable ambient direction whenever the value is
RTL script, but .button isn't given dir="auto" and stays anchored to
the host's direction alone
- #input's button-clearance padding-inline-start/-end are logical
properties resolved against #input's own direction, so they flipped
out of step with .button's actual (physically fixed) side once an RTL
value made #input's direction diverge, letting text render under the
button
- capture each state's clearance value (default, invalid, pending,
quiet, and their combinations) once per spectrum-combobox.css's
existing #input rules, then apply them as physical padding-left/
padding-right keyed to the host's own :dir(), so the clearance always
matches where .button actually sits regardless of #input's own
dir="auto"-driven direction
- add a regression test confirming the padding stays anchored to its
physical side even after #input's own direction flips

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This test relies on a MutationObserver-driven resync in Breadcrumbs.watchItemAttributes(). MutationObserver callbacks run asynchronously, so await elementUpdated(el) immediately after setAttribute() can race and become flaky (it may resolve before the observer fires and schedules an update). Adding a frame/microtask boundary before awaiting elementUpdated(el) makes the test deterministic.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- the previous dir="auto" approach let #input's own computed direction
diverge from the host's stable ambient direction for RTL values, which
required physical padding overrides to keep its button-clearance space
from drifting out of step with .button's fixed position
- unicode-bidi: plaintext shapes/aligns each bidi paragraph from its own
content without changing the input's direction property at all, so it
solves the original caret/alignment concern without ever risking that
divergence, no dir attribute or padding overrides needed
- add a test confirming both that unicode-bidi: plaintext is applied
and that direction/padding-left/padding-right all stay unchanged for
an rtl value, verifying this approach can't reintroduce the
button-collision bug

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…/reactive-controllers

This changeset bumps `combobox`/breadcrumbs, but this PR also changes published dependencies (`@spectrum-web-components/base` adds `normalize-dir` exports and `@spectrum-web-components/reactive-controllers` adds `AttributeObserver`). If those packages aren’t released too, the new runtime imports (e.g. `@spectrum-web-components/base/src/normalize-dir.js`, `@spectrum-web-components/reactive-controllers/src/AttributeObserver.js`) can be missing for consumers, causing module-resolution failures.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…onnect

- reconnect() called MutationObserver.disconnect() unconditionally
whenever a target/attribute was registered or unregistered; per spec,
disconnect() also clears any records already queued but not yet
delivered to the callback
- a mutation that landed right before an unrelated component's
mount/unmount triggered reconnect() (disconnect + re-observe) could
therefore have its record silently dropped, with no later mutation to
generate a replacement, so the listener would never fire for it
- factor the record-processing logic used by the MutationObserver's own
callback into processRecords(), and call it once more with
takeRecords() immediately before disconnect() in reconnect(), so any
already-queued records are still delivered
- add a dedicated test file covering observeAttribute() directly,
including a regression test that queues a mutation and triggers
reconnect() before it would otherwise be delivered, confirming the
listener still fires

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- flushObserver() uses setTimeout(resolve, 0), which resolves as a
macrotask, but its comment described it as yielding a microtask
- the mechanism itself is correct (a macrotask is the stronger
guarantee here, since it's only scheduled after the microtask queue,
including any pending MutationObserver callback, has fully drained),
so fix the comment's wording rather than the implementation
- applies the same correction to language-resolution.test.ts's
identical helper for consistency

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PR adds new storybook stories, so we need a new golden_images_hash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1st-gen a11y Issues or PRs related to accessibility bug Something isn't working Component:Breadcrumbs Component:Combobox Contribution PRs from contributors Status:Ready for review PR ready for review or re-review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants