From 3f04e20ac06c8dd28e39d218bd1e86c1eafa550e Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 25 Nov 2024 15:14:10 +0800 Subject: [PATCH 1/5] Slider sets CSS writing-mode --- .../slider/VerticalAccessibleSlider.js | 28 ----------- .../slider/VerticalAccessibleSlider.tsx | 28 ----------- .../VerticalAccessibleSlider.tsx.preview | 12 ----- .../components/slider/VerticalSlider.js | 50 +++++++++---------- .../components/slider/VerticalSlider.tsx | 50 +++++++++---------- .../data/material/components/slider/slider.md | 19 ++++--- packages/mui-material/src/Slider/useSlider.ts | 8 +++ 7 files changed, 70 insertions(+), 125 deletions(-) delete mode 100644 docs/data/material/components/slider/VerticalAccessibleSlider.js delete mode 100644 docs/data/material/components/slider/VerticalAccessibleSlider.tsx delete mode 100644 docs/data/material/components/slider/VerticalAccessibleSlider.tsx.preview diff --git a/docs/data/material/components/slider/VerticalAccessibleSlider.js b/docs/data/material/components/slider/VerticalAccessibleSlider.js deleted file mode 100644 index e9892bcc838cdd..00000000000000 --- a/docs/data/material/components/slider/VerticalAccessibleSlider.js +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Slider from '@mui/material/Slider'; - -export default function VerticalAccessibleSlider() { - function preventHorizontalKeyboardNavigation(event) { - if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { - event.preventDefault(); - } - } - - return ( - - - - ); -} diff --git a/docs/data/material/components/slider/VerticalAccessibleSlider.tsx b/docs/data/material/components/slider/VerticalAccessibleSlider.tsx deleted file mode 100644 index bc66892e7f20b3..00000000000000 --- a/docs/data/material/components/slider/VerticalAccessibleSlider.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Slider from '@mui/material/Slider'; - -export default function VerticalAccessibleSlider() { - function preventHorizontalKeyboardNavigation(event: React.KeyboardEvent) { - if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { - event.preventDefault(); - } - } - - return ( - - - - ); -} diff --git a/docs/data/material/components/slider/VerticalAccessibleSlider.tsx.preview b/docs/data/material/components/slider/VerticalAccessibleSlider.tsx.preview deleted file mode 100644 index 2f23a0596d7eee..00000000000000 --- a/docs/data/material/components/slider/VerticalAccessibleSlider.tsx.preview +++ /dev/null @@ -1,12 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/material/components/slider/VerticalSlider.js b/docs/data/material/components/slider/VerticalSlider.js index 502b71bdb8bb65..5ae94100d85d93 100644 --- a/docs/data/material/components/slider/VerticalSlider.js +++ b/docs/data/material/components/slider/VerticalSlider.js @@ -2,36 +2,13 @@ import * as React from 'react'; import Stack from '@mui/material/Stack'; import Slider from '@mui/material/Slider'; -function valuetext(value) { - return `${value}°C`; -} - -const marks = [ - { - value: 0, - label: '0°C', - }, - { - value: 20, - label: '20°C', - }, - { - value: 37, - label: '37°C', - }, - { - value: 100, - label: '100°C', - }, -]; - export default function VerticalSlider() { return ( @@ -45,7 +22,7 @@ export default function VerticalSlider() { 'Temperature'} orientation="vertical" - getAriaValueText={valuetext} + getAriaValueText={getAriaValueText} defaultValue={[20, 37]} valueLabelDisplay="auto" marks={marks} @@ -53,3 +30,26 @@ export default function VerticalSlider() { ); } + +function getAriaValueText(value) { + return `${value}°C`; +} + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; diff --git a/docs/data/material/components/slider/VerticalSlider.tsx b/docs/data/material/components/slider/VerticalSlider.tsx index 2f4e4ffa167ca8..743b7103b07703 100644 --- a/docs/data/material/components/slider/VerticalSlider.tsx +++ b/docs/data/material/components/slider/VerticalSlider.tsx @@ -2,36 +2,13 @@ import * as React from 'react'; import Stack from '@mui/material/Stack'; import Slider from '@mui/material/Slider'; -function valuetext(value: number) { - return `${value}°C`; -} - -const marks = [ - { - value: 0, - label: '0°C', - }, - { - value: 20, - label: '20°C', - }, - { - value: 37, - label: '37°C', - }, - { - value: 100, - label: '100°C', - }, -]; - export default function VerticalSlider() { return ( @@ -45,7 +22,7 @@ export default function VerticalSlider() { 'Temperature'} orientation="vertical" - getAriaValueText={valuetext} + getAriaValueText={getAriaValueText} defaultValue={[20, 37]} valueLabelDisplay="auto" marks={marks} @@ -53,3 +30,26 @@ export default function VerticalSlider() { ); } + +function getAriaValueText(value: number) { + return `${value}°C`; +} + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; diff --git a/docs/data/material/components/slider/slider.md b/docs/data/material/components/slider/slider.md index aa6afc620b1e17..42824d7f6e7391 100644 --- a/docs/data/material/components/slider/slider.md +++ b/docs/data/material/components/slider/slider.md @@ -98,17 +98,22 @@ You can learn more about this in the [overrides documentation page](/material-ui ## Vertical sliders +Set the `orientation` prop to `"vertical"` to create vertical sliders. The thumb will track vertical movement instead of horizontal movement. + {{"demo": "VerticalSlider.js"}} -**WARNING**: Chrome, Safari and newer Edge versions that is any browser based on WebKit exposes `` as horizontal ([chromium issue #40736841](https://issues.chromium.org/issues/40736841)). -By applying `-webkit-appearance: slider-vertical;` the slider is exposed as vertical. +:::warning +Chrome versions below 124 implements `aria-orientation` incorrrectly for vertical sliders and exposes them as `'horizontal'` in the accessibility tree. ([Chromium issue #40736841](https://issues.chromium.org/issues/40736841)) -However, by applying `-webkit-appearance: slider-vertical;` keyboard navigation for horizontal keys (Arrow Left, Arrow Right) is reversed ([chromium issue #40739626](https://issues.chromium.org/issues/40739626)). -Usually, up and right should increase and left and down should decrease the value. -If you apply `-webkit-appearance` you could prevent keyboard navigation for horizontal arrow keys for a truly vertical slider. -This might be less confusing to users compared to a change in direction. +The `-webkit-appearance: slider-vertical` CSS property can be used to correct this for these older versions, with the trade-off of causing a console warning in newer Chrome versions: +```css +.MuiSlider-thumb > input { + -webkit-appearance: slider-vertical; +} +``` -{{"demo": "VerticalAccessibleSlider.js"}} +For Chrome 124 and newer, the slider includes the CSS [`writing-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_writing_modes/Vertical_controls#range_sliders_meters_and_progress_bars) property that fixes this bug. +::: ## Marks placement diff --git a/packages/mui-material/src/Slider/useSlider.ts b/packages/mui-material/src/Slider/useSlider.ts index 0cd7ba016c3387..3c3bcf4f4bb7c2 100644 --- a/packages/mui-material/src/Slider/useSlider.ts +++ b/packages/mui-material/src/Slider/useSlider.ts @@ -687,6 +687,13 @@ export function useSlider(parameters: UseSliderParameters): UseSliderReturnValue }; }; + const cssWritingMode = React.useMemo(() => { + if (orientation === 'vertical') { + return isRtl ? 'vertical-rl' : 'vertical-lr'; + } + return undefined; + }, [isRtl, orientation]); + const getHiddenInputProps = = {}>( externalProps: ExternalProps = {} as ExternalProps, ): UseSliderHiddenInputProps => { @@ -724,6 +731,7 @@ export function useSlider(parameters: UseSliderParameters): UseSliderReturnValue // So that VoiceOver's focus indicator matches the thumb's dimensions width: '100%', height: '100%', + writingMode: cssWritingMode, }, }; }; From 419c8d91e92528bf59938dc0190c594dd782e2aa Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 25 Nov 2024 15:33:47 +0800 Subject: [PATCH 2/5] Fix CI --- .../src/app/material-ui/react-slider/page.tsx | 7 ------- .../src/pages/material-ui/react-slider.tsx | 7 ------- docs/data/material/components/slider/slider.md | 1 + test/regressions/index.js | 1 - 4 files changed, 1 insertion(+), 15 deletions(-) diff --git a/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx index 97fb8108c1cb09..19c93224b7a523 100644 --- a/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx +++ b/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx @@ -17,7 +17,6 @@ import RangeSlider from '../../../../../../docs/data/material/components/slider/ import SliderSizes from '../../../../../../docs/data/material/components/slider/SliderSizes'; import TrackFalseSlider from '../../../../../../docs/data/material/components/slider/TrackFalseSlider'; import TrackInvertedSlider from '../../../../../../docs/data/material/components/slider/TrackInvertedSlider'; -import VerticalAccessibleSlider from '../../../../../../docs/data/material/components/slider/VerticalAccessibleSlider'; import VerticalSlider from '../../../../../../docs/data/material/components/slider/VerticalSlider'; export default function Slider() { @@ -125,12 +124,6 @@ export default function Slider() { -
-

Vertical Accessible Slider

-
- -
-

Vertical Slider

diff --git a/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx index 963612339e2faf..a3bfac23c4c197 100644 --- a/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx +++ b/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx @@ -17,7 +17,6 @@ import RangeSlider from '../../../../../docs/data/material/components/slider/Ran import SliderSizes from '../../../../../docs/data/material/components/slider/SliderSizes.tsx'; import TrackFalseSlider from '../../../../../docs/data/material/components/slider/TrackFalseSlider.tsx'; import TrackInvertedSlider from '../../../../../docs/data/material/components/slider/TrackInvertedSlider.tsx'; -import VerticalAccessibleSlider from '../../../../../docs/data/material/components/slider/VerticalAccessibleSlider.tsx'; import VerticalSlider from '../../../../../docs/data/material/components/slider/VerticalSlider.tsx'; export default function Slider() { @@ -126,12 +125,6 @@ export default function Slider() {
-
-

Vertical Accessible Slider

-
- -
-

Vertical Slider

diff --git a/docs/data/material/components/slider/slider.md b/docs/data/material/components/slider/slider.md index 42824d7f6e7391..c12620cc4b1e19 100644 --- a/docs/data/material/components/slider/slider.md +++ b/docs/data/material/components/slider/slider.md @@ -106,6 +106,7 @@ Set the `orientation` prop to `"vertical"` to create vertical sliders. The thumb Chrome versions below 124 implements `aria-orientation` incorrrectly for vertical sliders and exposes them as `'horizontal'` in the accessibility tree. ([Chromium issue #40736841](https://issues.chromium.org/issues/40736841)) The `-webkit-appearance: slider-vertical` CSS property can be used to correct this for these older versions, with the trade-off of causing a console warning in newer Chrome versions: + ```css .MuiSlider-thumb > input { -webkit-appearance: slider-vertical; diff --git a/test/regressions/index.js b/test/regressions/index.js index 8d377681dfb032..11618772af4aba 100644 --- a/test/regressions/index.js +++ b/test/regressions/index.js @@ -178,7 +178,6 @@ const blacklist = [ 'docs-components-skeleton/Facebook.png', // Flaky image loading 'docs-components-skeleton/SkeletonChildren.png', // flaky image loading 'docs-components-skeleton/YouTube.png', // Flaky image loading - 'docs-components-slider/VerticalAccessibleSlider.png', // Redundant 'docs-components-snackbars/ConsecutiveSnackbars.png', // Needs interaction 'docs-components-snackbars/CustomizedSnackbars.png', // Redundant 'docs-components-snackbars/DirectionSnackbar.png', // Needs interaction From 7b6646e5f098089e51ecf395f420b8479d2c878e Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 25 Nov 2024 18:44:02 +0800 Subject: [PATCH 3/5] Fix PR comments --- docs/data/material/components/slider/slider.md | 4 ++-- packages/mui-material/src/Slider/useSlider.ts | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/data/material/components/slider/slider.md b/docs/data/material/components/slider/slider.md index c12620cc4b1e19..ee98d4067e5d7f 100644 --- a/docs/data/material/components/slider/slider.md +++ b/docs/data/material/components/slider/slider.md @@ -103,12 +103,12 @@ Set the `orientation` prop to `"vertical"` to create vertical sliders. The thumb {{"demo": "VerticalSlider.js"}} :::warning -Chrome versions below 124 implements `aria-orientation` incorrrectly for vertical sliders and exposes them as `'horizontal'` in the accessibility tree. ([Chromium issue #40736841](https://issues.chromium.org/issues/40736841)) +Chrome versions below 124 implements `aria-orientation` incorrectly for vertical sliders and exposes them as `'horizontal'` in the accessibility tree. ([Chromium issue #40736841](https://issues.chromium.org/issues/40736841)) The `-webkit-appearance: slider-vertical` CSS property can be used to correct this for these older versions, with the trade-off of causing a console warning in newer Chrome versions: ```css -.MuiSlider-thumb > input { +.MuiSlider-thumb input { -webkit-appearance: slider-vertical; } ``` diff --git a/packages/mui-material/src/Slider/useSlider.ts b/packages/mui-material/src/Slider/useSlider.ts index 3c3bcf4f4bb7c2..7dc3b3b2b54b99 100644 --- a/packages/mui-material/src/Slider/useSlider.ts +++ b/packages/mui-material/src/Slider/useSlider.ts @@ -687,12 +687,10 @@ export function useSlider(parameters: UseSliderParameters): UseSliderReturnValue }; }; - const cssWritingMode = React.useMemo(() => { - if (orientation === 'vertical') { - return isRtl ? 'vertical-rl' : 'vertical-lr'; - } - return undefined; - }, [isRtl, orientation]); + let cssWritingMode: 'vertical-rl' | 'vertical-lr' | undefined; + if (orientation === 'vertical') { + cssWritingMode = isRtl ? 'vertical-rl' : 'vertical-lr'; + } const getHiddenInputProps = = {}>( externalProps: ExternalProps = {} as ExternalProps, From 9dc7bf7b104f6c5d54c36ae430e2e436f25ae4fa Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 25 Nov 2024 21:13:05 +0800 Subject: [PATCH 4/5] Fix grammar --- docs/data/material/components/slider/slider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/material/components/slider/slider.md b/docs/data/material/components/slider/slider.md index ee98d4067e5d7f..e473d6375eb37b 100644 --- a/docs/data/material/components/slider/slider.md +++ b/docs/data/material/components/slider/slider.md @@ -103,7 +103,7 @@ Set the `orientation` prop to `"vertical"` to create vertical sliders. The thumb {{"demo": "VerticalSlider.js"}} :::warning -Chrome versions below 124 implements `aria-orientation` incorrectly for vertical sliders and exposes them as `'horizontal'` in the accessibility tree. ([Chromium issue #40736841](https://issues.chromium.org/issues/40736841)) +Chrome versions below 124 implement `aria-orientation` incorrectly for vertical sliders and exposes them as `'horizontal'` in the accessibility tree. ([Chromium issue #40736841](https://issues.chromium.org/issues/40736841)) The `-webkit-appearance: slider-vertical` CSS property can be used to correct this for these older versions, with the trade-off of causing a console warning in newer Chrome versions: From 2eda130eb8b3e61d29bd9999c88e273f79d3da0d Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Mon, 25 Nov 2024 22:02:34 +0800 Subject: [PATCH 5/5] Add a test --- .../mui-material/src/Slider/Slider.test.js | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/packages/mui-material/src/Slider/Slider.test.js b/packages/mui-material/src/Slider/Slider.test.js index 8b8d868434dbbd..38b2eec460e375 100644 --- a/packages/mui-material/src/Slider/Slider.test.js +++ b/packages/mui-material/src/Slider/Slider.test.js @@ -254,6 +254,50 @@ describe('', () => { expect(handleChange.args[0][1]).to.equal(80); expect(handleChange.args[1][1]).to.equal(78); }); + + describe('vertical orientation', () => { + describe('keyboard interactions', () => { + it('ArrowLeft and ArrowDown decrements the value', () => { + const { getByRole } = render(); + + const slider = getByRole('slider'); + + expect(slider).to.have.attribute('aria-valuenow', '50'); + + act(() => { + slider.focus(); + }); + + fireEvent.keyDown(slider, { key: 'ArrowLeft' }); + + expect(slider).to.have.attribute('aria-valuenow', '49'); + + fireEvent.keyDown(slider, { key: 'ArrowDown' }); + + expect(slider).to.have.attribute('aria-valuenow', '48'); + }); + + it('ArrowRight and ArrowUp increments the value', () => { + const { getByRole } = render(); + + const slider = getByRole('slider'); + + expect(slider).to.have.attribute('aria-valuenow', '50'); + + act(() => { + slider.focus(); + }); + + fireEvent.keyDown(slider, { key: 'ArrowRight' }); + + expect(slider).to.have.attribute('aria-valuenow', '51'); + + fireEvent.keyDown(slider, { key: 'ArrowUp' }); + + expect(slider).to.have.attribute('aria-valuenow', '52'); + }); + }); + }); }); describe('range', () => { @@ -986,6 +1030,112 @@ describe('', () => { expect(handleChange.args[0][1]).to.equal(80); expect(handleChange.args[1][1]).to.equal(78); }); + + describe('keyboard interactions', () => { + it('ArrowRight and ArrowDown decrements the value', () => { + const { getByRole } = render( + + + , + ); + + const slider = getByRole('slider'); + + expect(slider).to.have.attribute('aria-valuenow', '50'); + + act(() => { + slider.focus(); + }); + + fireEvent.keyDown(slider, { key: 'ArrowRight' }); + + expect(slider).to.have.attribute('aria-valuenow', '49'); + + fireEvent.keyDown(slider, { key: 'ArrowDown' }); + + expect(slider).to.have.attribute('aria-valuenow', '48'); + }); + + it('ArrowLeft and ArrowUp increments the value', () => { + const { getByRole } = render( + + + , + ); + + const slider = getByRole('slider'); + + expect(slider).to.have.attribute('aria-valuenow', '50'); + + act(() => { + slider.focus(); + }); + + fireEvent.keyDown(slider, { key: 'ArrowLeft' }); + + expect(slider).to.have.attribute('aria-valuenow', '51'); + + fireEvent.keyDown(slider, { key: 'ArrowUp' }); + + expect(slider).to.have.attribute('aria-valuenow', '52'); + }); + }); + + it('Home key sets the value to max', () => { + const { getByRole } = render( + + + , + ); + + const slider = getByRole('slider'); + + expect(slider).to.have.attribute('aria-valuenow', '50'); + + act(() => { + slider.focus(); + }); + + fireEvent.keyDown(slider, { key: 'Home' }); + + expect(slider).to.have.attribute('aria-valuenow', '99'); + }); + + it('End key sets the value to min', () => { + const { getByRole } = render( + + + , + ); + + const slider = getByRole('slider'); + + expect(slider).to.have.attribute('aria-valuenow', '50'); + + act(() => { + slider.focus(); + }); + + fireEvent.keyDown(slider, { key: 'End' }); + + expect(slider).to.have.attribute('aria-valuenow', '1'); + }); }); describe('warnings', () => {