fix(range): indication when range knob is focused (#25827)
@ -81,7 +81,8 @@
|
|||||||
text-align: center;
|
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));
|
@include transform(translate3d(0, 0, 0), scale(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
:host(.ion-color) .range-bar-active,
|
:host(.ion-color) .range-bar-active,
|
||||||
:host(.ion-color) .range-knob,
|
:host(.ion-color) .range-knob,
|
||||||
|
:host(.ion-color) .range-knob::before,
|
||||||
:host(.ion-color) .range-pin,
|
:host(.ion-color) .range-pin,
|
||||||
:host(.ion-color) .range-pin::before,
|
:host(.ion-color) .range-pin::before,
|
||||||
:host(.ion-color) .range-tick {
|
:host(.ion-color) .range-tick {
|
||||||
@ -63,6 +64,27 @@
|
|||||||
transition-timing-function: ease;
|
transition-timing-function: ease;
|
||||||
|
|
||||||
z-index: 2;
|
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 {
|
.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));
|
@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);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,12 +27,6 @@ $range-md-bar-background-color: $background-color-step-250 !default
|
|||||||
/// @prop - Background of the active range bar
|
/// @prop - Background of the active range bar
|
||||||
$range-md-bar-active-background-color: current-color(base) !default;
|
$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
|
/// @prop - Background of the range knob
|
||||||
$range-md-knob-background-color: $range-md-bar-active-background-color !default;
|
$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
|
/// @prop - Background of the range pin when the value is the minimum
|
||||||
$range-md-pin-min-background-color: $range-md-bar-background-color !default;
|
$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;
|
@ -701,6 +701,8 @@ const renderKnob = (
|
|||||||
'range-knob-pressed': pressed,
|
'range-knob-pressed': pressed,
|
||||||
'range-knob-min': value === min,
|
'range-knob-min': value === min,
|
||||||
'range-knob-max': value === max,
|
'range-knob-max': value === max,
|
||||||
|
'ion-activatable': true,
|
||||||
|
'ion-focusable': true,
|
||||||
}}
|
}}
|
||||||
style={knobStyle()}
|
style={knobStyle()}
|
||||||
role="slider"
|
role="slider"
|
||||||
|
59
core/src/components/range/test/a11y/range.e2e.ts
Normal 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`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@ -101,7 +101,7 @@ export type BrowserNameOrCallback = string | ((browserName: string) => boolean);
|
|||||||
export interface E2ESkip {
|
export interface E2ESkip {
|
||||||
rtl: (reason?: string) => void;
|
rtl: (reason?: string) => void;
|
||||||
browser: (browserNameOrCallback: BrowserNameOrCallback, 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 {
|
export interface SetIonViewportOptions {
|
||||||
|