Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiRange]: Add conditional aria-valuetext when ticks prop is passed #7675

Merged
merged 20 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b6085ce
Added aria-valuetext logic to EuiRangeSlider.
1Copenut Apr 10, 2024
aab49b5
Added accessibleLabel type to EuiRangeTick, logic to build aria-value…
1Copenut Apr 10, 2024
a8517c8
Adding aria-valuenow logic for EuiRange.
1Copenut Apr 11, 2024
da14962
Adding unit test logic for aria-valuenow and aria-valuetext.
1Copenut Apr 11, 2024
5cc66a5
Updated EuiRange ticks doc with accessibleLabel.
1Copenut Apr 10, 2024
7c155dc
Updated Tick marks docs to highlight accessibleLabel property.
1Copenut Apr 11, 2024
bd2275e
Linting error on target declaration.
1Copenut Apr 11, 2024
cb7bfe8
Added changelog for 7675.
1Copenut Apr 11, 2024
20d11d6
Removing aria-valuenow attribute because it is not needed for SR anno…
1Copenut Apr 12, 2024
8372187
Refactored handleAriaValueText into one ternary from two guard clauses.
1Copenut Apr 12, 2024
7a2b1ca
Refactoring ternary for better readability.
1Copenut Apr 12, 2024
1a1f48d
Refactored handleAriaValueText to allow string labels.
1Copenut Apr 15, 2024
5ef3d4e
Updated test logic to include label typeof string.
1Copenut Apr 15, 2024
b8439df
Updated docs to illustrate aria-valuetext with label typeof string.
1Copenut Apr 15, 2024
7b7a870
Update changelogs/upcoming/7675.md
1Copenut Apr 16, 2024
08c8aa3
Update src/components/form/range/range.tsx
1Copenut Apr 16, 2024
0ccc7a4
[PR feedback] tests
cee-chen Apr 16, 2024
fed091f
sorry Trevor!
cee-chen Apr 16, 2024
f3b701e
Update src-docs/src/views/range/range_example.js
1Copenut Apr 16, 2024
87ea5d5
Update src/components/form/range/range.tsx
1Copenut Apr 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/upcoming/7675.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Accessibility**

- Added `aria-valuenow` and `aria-valuetext` attributes to `EuiRange` for improved screen reader UX
10 changes: 5 additions & 5 deletions src-docs/src/views/range/levels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ export default () => {
];

const customTicks = [
{ label: 'low', value: 0 },
{ label: 'intermediate', value: 15 },
{ label: 'moderate', value: 35 },
{ label: 'high', value: 65 },
{ label: 'severe', value: 85 },
{ label: 'low', value: 0, accessibleLabel: 'low' },
{ label: 'intermediate', value: 15, accessibleLabel: 'intermediate' },
{ label: 'moderate', value: 35, accessibleLabel: 'moderate' },
{ label: 'high', value: 65, accessibleLabel: 'high' },
{ label: 'severe', value: 85, accessibleLabel: 'severe' },
];

const customColorsLevels = [
Expand Down
10 changes: 10 additions & 0 deletions src-docs/src/views/range/range_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,16 @@ export const RangeControlExample = {
<EuiCode>label</EuiCode>. The value must be included in the range of
values (min-max), though the label may be anything you choose.
</p>
<p>
The <EuiCode>EuiRangeTick</EuiCode> interface now includes an
optional <EuiCode>accessibleLabel</EuiCode>. This property is
combined with the current <EuiCode>value</EuiCode> to render an{' '}
<EuiCode>aria-valuetext</EuiCode> attribute. If the accessible label
is not included, <EuiCode>aria-valuetext</EuiCode> will be the
computed current value. This attribute is announced to screen reader
users and is useful when values are defined differently than tick
labels.
</p>
<EuiCallOut
color="warning"
title="Minimum of 5px width per tick allowed"
Expand Down
16 changes: 8 additions & 8 deletions src-docs/src/views/range/ticks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,15 @@ export default () => {
min={0}
max={84}
ticks={[
{ label: '1 GB', value: 0 },
{ label: '2GB', value: 14 },
{ label: '4GB', value: 28 },
{ label: '8GB', value: 42 },
{ label: '16GB', value: 56 },
{ label: '32GB', value: 70 },
{ label: '64GB', value: 84 },
{ label: '1 GB', value: 0, accessibleLabel: 'one gigabyte' },
{ label: '2GB', value: 14, accessibleLabel: 'two gigabytes' },
{ label: '4GB', value: 28, accessibleLabel: 'four gigabytes' },
{ label: '8GB', value: 42, accessibleLabel: 'eight gigabytes' },
{ label: '16GB', value: 56, accessibleLabel: 'sixteen gigabytes' },
{ label: '32GB', value: 70, accessibleLabel: 'thirty-two gigabytes' },
{ label: '64GB', value: 84, accessibleLabel: 'sixty-four gigabytes' },
Copy link
Contributor

Choose a reason for hiding this comment

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

😍 Now this is an example that makes sense to me! Huzzah!

]}
aria-label="An example of EuiDualRange with no linear intervals"
aria-label="An example of EuiRange with no linear intervals"
/>
</>
);
Expand Down
119 changes: 119 additions & 0 deletions src/components/form/range/__snapshots__/range.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exports[`EuiRange allows value prop to accept a number 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
id="generated-id"
max="100"
Expand Down Expand Up @@ -67,6 +68,7 @@ exports[`EuiRange inherits fullWidth from <EuiForm /> 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="20"
class="euiRangeSlider emotion-euiRangeSlider"
id="generated-id"
max="100"
Expand All @@ -91,6 +93,7 @@ exports[`EuiRange is rendered 1`] = `
<input
aria-hidden="false"
aria-label="aria-label"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
data-test-subj="test subject string"
id="id"
Expand All @@ -115,6 +118,7 @@ exports[`EuiRange props compressed should render 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
id="generated-id"
max="100"
Expand Down Expand Up @@ -166,6 +170,7 @@ exports[`EuiRange props custom ticks should render 1`] = `
</div>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider-hasTicks"
id="generated-id"
max="100"
Expand All @@ -188,6 +193,7 @@ exports[`EuiRange props disabled should render 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
disabled=""
id="generated-id"
Expand All @@ -211,6 +217,7 @@ exports[`EuiRange props fullWidth should render 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
id="generated-id"
max="100"
Expand All @@ -223,6 +230,111 @@ exports[`EuiRange props fullWidth should render 1`] = `
</div>
`;

exports[`EuiRange props input should include aria-valuetext when value equals tick[value] 1`] = `
<div
class="euiRangeWrapper euiRange emotion-euiRangeWrapper-regular-euiRange"
>
<div
aria-hidden="false"
class="euiRangeTrack emotion-euiRangeTrack-hasTicks"
>
<div
class="euiRangeTicks emotion-euiRangeTicks-regular-isCustom"
>
<button
class="euiRangeTick emotion-euiRangeTick-isCustom-hasPseudoTickMark-regular"
style="inset-inline-start: calc(-Infinity% + 8px);"
tabindex="-1"
title="20kb"
type="button"
value="20"
>
20kb
</button>
<button
class="euiRangeTick emotion-euiRangeTick-isCustom-isMax-regular"
style="inset-inline-end: 0%; margin-inline-end: -1.25em;"
tabindex="-1"
title="100kb"
type="button"
value="100"
>
<span
aria-hidden="true"
class="euiRangeTick__pseudo emotion-euiRangeTick__pseudo"
style="margin-inline-end: calc(1.25em + 4px);"
/>
100kb
</button>
</div>
<input
aria-hidden="false"
aria-valuenow="20"
aria-valuetext="20, (twenty kilobytes)"
class="euiRangeSlider emotion-euiRangeSlider-hasTicks"
id="generated-id"
max="100"
min="0"
step="1"
type="range"
value="20"
/>
</div>
</div>
`;

exports[`EuiRange props input should not include aria-valuetext when values are different 1`] = `
<div
class="euiRangeWrapper euiRange emotion-euiRangeWrapper-regular-euiRange"
>
<div
aria-hidden="false"
class="euiRangeTrack emotion-euiRangeTrack-hasTicks"
>
<div
class="euiRangeTicks emotion-euiRangeTicks-regular-isCustom"
>
<button
class="euiRangeTick emotion-euiRangeTick-isCustom-hasPseudoTickMark-regular"
style="inset-inline-start: calc(-Infinity% + 8px);"
tabindex="-1"
title="20kb"
type="button"
value="20"
>
20kb
</button>
<button
class="euiRangeTick emotion-euiRangeTick-isCustom-isMax-regular"
style="inset-inline-end: 0%; margin-inline-end: -1.25em;"
tabindex="-1"
title="100kb"
type="button"
value="100"
>
<span
aria-hidden="true"
class="euiRangeTick__pseudo emotion-euiRangeTick__pseudo"
style="margin-inline-end: calc(1.25em + 4px);"
/>
100kb
</button>
</div>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider-hasTicks"
id="generated-id"
max="100"
min="0"
step="1"
type="range"
value="8"
/>
</div>
</div>
`;

exports[`EuiRange props input should render 1`] = `
<div
class="euiRangeWrapper euiRange testClass1 testClass2 emotion-euiRangeWrapper-regular-euiRange-hasInput-euiTestCss"
Expand All @@ -234,6 +346,7 @@ exports[`EuiRange props input should render 1`] = `
<input
aria-hidden="true"
aria-label="aria-label"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
data-test-subj="test subject string"
max="100"
Expand Down Expand Up @@ -287,6 +400,7 @@ exports[`EuiRange props labels should render 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
id="generated-id"
max="100"
Expand Down Expand Up @@ -326,6 +440,7 @@ exports[`EuiRange props levels should render 1`] = `
</div>
<input
aria-hidden="false"
aria-valuenow="20"
class="euiRangeSlider emotion-euiRangeSlider-thumb"
id="generated-id"
max="100"
Expand Down Expand Up @@ -385,6 +500,7 @@ exports[`EuiRange props range should render 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider-hasRange"
id="generated-id"
max="100"
Expand Down Expand Up @@ -480,6 +596,7 @@ exports[`EuiRange props slider should display in popover 1`] = `
<input
aria-hidden="true"
aria-label="aria-label"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider"
data-test-subj="test subject string"
max="100"
Expand Down Expand Up @@ -584,6 +701,7 @@ exports[`EuiRange props ticks should render 1`] = `
</div>
<input
aria-hidden="false"
aria-valuenow="8"
class="euiRangeSlider emotion-euiRangeSlider-hasTicks"
id="generated-id"
max="100"
Expand All @@ -606,6 +724,7 @@ exports[`EuiRange props value should render 1`] = `
>
<input
aria-hidden="false"
aria-valuenow="200"
class="euiRangeSlider emotion-euiRangeSlider"
id="generated-id"
max="100"
Expand Down
63 changes: 62 additions & 1 deletion src/components/form/range/range.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import React from 'react';
import { fireEvent } from '@testing-library/react';
import { fireEvent, getByRole } from '@testing-library/react';
import { shouldRenderCustomStyles } from '../../../test/internal';
import { requiredProps } from '../../../test/required_props';
import { render } from '../../../test/rtl';
Expand Down Expand Up @@ -143,6 +143,67 @@ describe('EuiRange', () => {
expect(container.firstChild).toMatchSnapshot();
});

test('input should include aria-valuetext when value equals tick[value]', () => {
const { container } = render(
<EuiRange
{...props}
showTicks
ticks={[
{
label: '20kb',
value: 20,
accessibleLabel: 'twenty kilobytes',
},
{
label: '100kb',
value: 100,
accessibleLabel: 'one-hundred kilobytes',
},
]}
value={20}
/>
);

const input = getByRole(container, 'slider');

expect(input).toBeInTheDocument();
expect(input.getAttribute('aria-valuenow')).toEqual('20');
expect(input.getAttribute('aria-valuetext')).toEqual(
'20, (twenty kilobytes)'
);

expect(container.firstChild).toMatchSnapshot();
});

test('input should not include aria-valuetext when values are different', () => {
const { container } = render(
<EuiRange
{...props}
showTicks
ticks={[
{
label: '20kb',
value: 20,
accessibleLabel: 'twenty kilobytes',
},
{
label: '100kb',
value: 100,
accessibleLabel: 'one-hundred kilobytes',
},
]}
/>
);

const input = getByRole(container, 'slider');

expect(input).toBeInTheDocument();
expect(input.getAttribute('aria-valuenow')).toEqual('8');
expect(input.getAttribute('aria-valuetext')).toBeFalsy();

expect(container.firstChild).toMatchSnapshot();
});

test('slider should display in popover', () => {
const { container, baseElement, getByTestSubject } = render(
<EuiRange
Expand Down
Loading
Loading