fix(range): indication when range knob is focused (#25827)

This commit is contained in:
Sean Perkins
2022-09-26 15:15:44 -04:00
committed by GitHub
parent 1b2ccdd0d5
commit 2c815cff13
36 changed files with 124 additions and 10 deletions

View File

@ -81,7 +81,8 @@
text-align: center;
}
.range-knob-pressed .range-pin {
.range-knob-pressed .range-pin,
.range-knob-handle.ion-focused .range-pin {
@include transform(translate3d(0, 0, 0), scale(1));
}

View File

@ -28,6 +28,7 @@
:host(.ion-color) .range-bar-active,
:host(.ion-color) .range-knob,
:host(.ion-color) .range-knob::before,
:host(.ion-color) .range-pin,
:host(.ion-color) .range-pin::before,
:host(.ion-color) .range-tick {
@ -63,6 +64,27 @@
transition-timing-function: ease;
z-index: 2;
&::before {
@include border-radius(50%);
@include position(null, null, null, 0);
position: absolute;
width: var(--knob-size);
height: var(--knob-size);
transform: scale(1);
// Value from Material Design components for the web
transition: 0.267s cubic-bezier(0, 0, 0.58, 1);
background: var(--knob-background);
content: "";
opacity: #{$range-md-knob-indicator-opacity-hover};
pointer-events: none;
}
}
.range-tick {
@ -130,11 +152,38 @@
}
}
.range-knob-pressed .range-pin {
.range-knob-pressed .range-pin,
.range-knob-handle.ion-focused .range-pin {
@include transform(translate3d(0, -24px, 0), scale(1));
}
:host(:not(.range-has-pin)) .range-knob-pressed .range-knob {
@media (any-hover: hover) {
.range-knob-handle:hover .range-knob:before {
transform: scale(2);
opacity: #{$range-md-knob-indicator-opacity-hover};
}
}
.range-knob-handle {
&.ion-activated .range-knob:before,
&.ion-focused .range-knob:before,
&.range-knob-pressed .range-knob:before {
transform: scale(2);
}
&.ion-focused .range-knob::before {
opacity: #{$range-md-knob-indicator-opacity-focus};
}
&.ion-activated .range-knob::before,
&.range-knob-pressed .range-knob::before {
opacity: #{$range-md-knob-indicator-opacity-active};
}
}
:host(:not(.range-has-pin)) .range-knob-pressed .range-knob,
:host(:not(.range-has-pin)) .range-knob-handle.ion-focused .range-knob {
transform: scale(1);
}

View File

@ -27,12 +27,6 @@ $range-md-bar-background-color: $background-color-step-250 !default
/// @prop - Background of the active range bar
$range-md-bar-active-background-color: current-color(base) !default;
/// @prop - Width of the range knob
$range-md-knob-width: 18px !default;
/// @prop - Height of the range knob
$range-md-knob-height: $range-md-knob-width !default;
/// @prop - Background of the range knob
$range-md-knob-background-color: $range-md-bar-active-background-color !default;
@ -53,3 +47,12 @@ $range-md-pin-padding-horizontal: 0 !default;
/// @prop - Background of the range pin when the value is the minimum
$range-md-pin-min-background-color: $range-md-bar-background-color !default;
/// @prop - Opacity of the indicator shown when the range knob is hovered
$range-md-knob-indicator-opacity-hover: 0.13 !default;
/// @prop - Opacity of the indicator shown when the range knob is focused
$range-md-knob-indicator-opacity-focus: 0.13 !default;
/// @prop - Opacity of the indicator shown when the range knob is active
$range-md-knob-indicator-opacity-active: 0.25 !default;

View File

@ -701,6 +701,8 @@ const renderKnob = (
'range-knob-pressed': pressed,
'range-knob-min': value === min,
'range-knob-max': value === max,
'ion-activatable': true,
'ion-focusable': true,
}}
style={knobStyle()}
role="slider"

View File

@ -0,0 +1,59 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('range: a11y', () => {
test('should not have visual regressions', async ({ page, skip }) => {
skip.rtl();
skip.mode('ios', 'iOS mode does not display hover/active/focus styles.');
await page.setContent(
`<ion-app>
<ion-content>
<ion-range min="0" max="100" value="80"></ion-range>
</ion-content>
</ion-app>
`
);
const range = page.locator('ion-range');
const rangeHandle = range.locator('.range-knob-handle');
await rangeHandle.evaluate((el) => el.classList.add('ion-focused'));
await page.waitForChanges();
expect(await range.screenshot()).toMatchSnapshot(`range-focus-${page.getSnapshotSettings()}.png`);
const box = (await rangeHandle.boundingBox())!;
const centerX = box.x + box.width / 2;
const centerY = box.y + box.height / 2;
await page.mouse.move(centerX, centerY);
await page.mouse.down();
await page.waitForChanges();
expect(await range.screenshot()).toMatchSnapshot(`range-active-${page.getSnapshotSettings()}.png`);
});
test.describe('with pin', () => {
test('should not have visual regressions', async ({ page, skip }) => {
skip.rtl();
await page.setContent(
`<ion-app>
<ion-content>
<ion-range min="0" max="100" value="50" pin="true"></ion-range>
</ion-content>
</ion-app>
`
);
const range = page.locator('ion-range');
const rangeHandle = range.locator('.range-knob-handle');
await rangeHandle.evaluate((el) => el.classList.add('ion-focused'));
await page.waitForChanges();
expect(await range.screenshot()).toMatchSnapshot(`range-focus-with-pin-${page.getSnapshotSettings()}.png`);
});
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -101,7 +101,7 @@ export type BrowserNameOrCallback = string | ((browserName: string) => boolean);
export interface E2ESkip {
rtl: (reason?: string) => void;
browser: (browserNameOrCallback: BrowserNameOrCallback, reason?: string) => void;
mode: (mode: string, reason?: string) => void;
mode: (mode: 'md' | 'ios', reason?: string) => void;
}
export interface SetIonViewportOptions {