Skip to content

feat: add hasFixedDirection to Slider for non-mirrored media controls - #10537

Open
dylanpulver wants to merge 2 commits into
adobe:mainfrom
dylanpulver:feat/slider-fixed-direction
Open

feat: add hasFixedDirection to Slider for non-mirrored media controls#10537
dylanpulver wants to merge 2 commits into
adobe:mainfrom
dylanpulver:feat/slider-fixed-direction

Conversation

@dylanpulver

@dylanpulver dylanpulver commented Aug 30, 2026

Copy link
Copy Markdown

Closes #10112

Adds hasFixedDirection to Slider, useSlider, and useSliderThumb: an opt-in that keeps a horizontal slider left-to-right in RTL locales, for media controls like a playback progress bar.

Direction is read in four places, and they mirror by two different mechanisms. useSlider (track click, track drag) and useSliderThumb (arrow keys, drag, thumb left) invert in JS, but SliderFill mirrors in CSS via insetInlineStart. Changing only the JS is the obvious fix and it pulls those apart: the thumb moves to the LTR position while the fill stays anchored to the right edge. A deliberately naive hooks-only patch failed exactly one test — the SliderFill one — which is why the fill switches to a physical left under this flag. Vertical is untouched, and a mutant that short-circuited the whole inversion was caught by the vertical case.

The flag lives on SliderState rather than being threaded through both hooks, following orientation: that is the existing channel for a layout concern shared by the hooks and SliderFill, and it means S2 picks it up through SliderBaseProps.

On naming, I took is | allows | has with a default of false from your comment, which is what rules out matchLocaleDirection (it would have to default true). Happy to rename. I deliberately left the prose docs alone — the PropTable is generated from the JSDoc, so a rename won't strand written docs.

Disclosure: this PR was written with AI assistance, pointed at CLAUDE.md and AGENTS.md per your AI contribution guidance.

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices
  • I understand every change in this PR and can explain why it's there.
  • If AI-assisted, I followed our AI contribution guidance and pointed my assistant at CLAUDE.md.

📝 Test Instructions:

Storybook: React Aria Components / Slider / Slider Fixed Direction renders two sliders in an ar-AE / dir="rtl" context. The first mirrors with the locale; the second sets hasFixedDirection and stays left-to-right, fill and thumb on the same side.

14 tests added across useSlider, useSliderThumb, and RAC Slider, covering track click, arrow keys, drag, thumb position, and fill positioning — each with an RTL default control and an LTR no-op control, using value 25 so mirrored and non-mirrored results are distinguishable. Reverting the source fails 5 of them, one per behavior site.

Tested: keyboard and mouse, LTR and RTL, horizontal and vertical, disabled, single and multi-thumb. Green across slider/color/S2 under STRICT_MODE=1 VIRT_ON=1 on React 19 (428 tests) and on React 16 and 17 via install-16/install-17 (194). yarn format and oxlint clean; check-types reports nothing in the changed files (the only errors here are pre-existing unbuilt @spectrum-icons/workflow artifacts).

Not tested: screen readers, high-contrast, and real-browser rendering. The fill/thumb agreement is asserted through inline styles in jsdom, not visually — worth a look on your side, since insetInlineStart vs left is ultimately a rendering concern.

Second commit: pointerDown installs mouseup/touchend/pointerup listeners on window, and the two stacked-thumb track tests never released the pointer. On React 16 and 17 those listeners outlived the unmounted component and fired during a later test, which is how the new RTL track-click test found it — it was the first test in the file to dispatch a bare mouseUp afterwards. Two lines, and removing them reproduces the failure on both versions.

Note: #10498 touches five of the same files. The changes are orthogonal — cherry-picked onto its head, the three conflicts are adjacency only (a destructured prop, an appended describe, an import list) and the combined tree is green. Happy to rebase behind it.

🧢 Your Project:

Personal open-source contribution.

pointerDown installs mouseup/touchend/pointerup listeners on window. The two
stacked-thumb tests never released the pointer, so on React 16 and 17 the
listeners outlived the unmounted component and fired during a later test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support passing in direction to useSlider for media controls

1 participant