Skip to content

Commit 2929e61

Browse files
authored
feat: implement readonly property in slider and range-slider (#10985)
1 parent 4c16090 commit 2929e61

File tree

9 files changed

+116
-6
lines changed

9 files changed

+116
-6
lines changed

packages/slider/src/styles/vaadin-slider-base-styles.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export const sliderStyles = css`
3434
var(--vaadin-background-color);
3535
}
3636
37+
:host([readonly]) {
38+
--vaadin-slider-fill-background: var(--vaadin-background-color);
39+
}
40+
3741
[part='track'] {
3842
box-sizing: border-box;
3943
position: absolute;
@@ -63,6 +67,11 @@ export const sliderStyles = css`
6367
touch-action: none;
6468
}
6569
70+
:host([readonly]) [part~='thumb'],
71+
:host([readonly]) [part='track-fill'] {
72+
border: dashed 1px var(--vaadin-border-color);
73+
}
74+
6675
/* visually hidden */
6776
::slotted(input) {
6877
flex: 1;

packages/slider/src/vaadin-range-slider.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ class RangeSlider extends SliderMixin(
4747
css`
4848
:host([focus-ring][start-focused]) [part~='thumb-start'],
4949
:host([focus-ring][end-focused]) [part~='thumb-end'] {
50-
outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
50+
outline: var(--vaadin-focus-ring-width) var(--_outline-style, solid) var(--vaadin-focus-ring-color);
5151
outline-offset: 1px;
5252
}
53+
54+
:host([readonly]) {
55+
--_outline-style: dashed;
56+
}
5357
`,
5458
];
5559
}
@@ -278,16 +282,23 @@ class RangeSlider extends SliderMixin(
278282

279283
/** @private */
280284
__onKeyDown(event) {
281-
const index = this._inputElements.indexOf(event.target);
282-
283285
const prevKeys = ['ArrowLeft', 'ArrowDown'];
284286
const nextKeys = ['ArrowRight', 'ArrowUp'];
285287

288+
const isNextKey = nextKeys.includes(event.key);
289+
const isPrevKey = prevKeys.includes(event.key);
290+
291+
if (!isNextKey && !isPrevKey) {
292+
return;
293+
}
294+
295+
const index = this._inputElements.indexOf(event.target);
296+
286297
// Suppress native `input` event if start and end thumbs point to the same value,
287298
// to prevent the case where slotted range inputs would end up in broken state.
288299
if (
289-
this.__value[0] === this.__value[1] &&
290-
((index === 0 && nextKeys.includes(event.key)) || (index === 1 && prevKeys.includes(event.key)))
300+
this.readonly ||
301+
(this.__value[0] === this.__value[1] && ((index === 0 && isNextKey) || (index === 1 && isPrevKey)))
291302
) {
292303
event.preventDefault();
293304
}

packages/slider/src/vaadin-slider-mixin.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ export declare class SliderMixinClass {
2525
* The stepping interval of the slider.
2626
*/
2727
step: number;
28+
29+
/**
30+
* When true, the user cannot modify the value of the slider.
31+
* The difference between `disabled` and `readonly` is that the
32+
* read-only slider remains focusable and is announced by screen
33+
* readers.
34+
*/
35+
readonly: boolean;
2836
}

packages/slider/src/vaadin-slider-mixin.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ export const SliderMixin = (superClass) =>
3737
sync: true,
3838
},
3939

40+
/**
41+
* When true, the user cannot modify the value of the slider.
42+
* The difference between `disabled` and `readonly` is that the
43+
* read-only slider remains focusable and is announced by screen
44+
* readers.
45+
*/
46+
readonly: {
47+
type: Boolean,
48+
reflectToAttribute: true,
49+
},
50+
4051
/** @private */
4152
__value: {
4253
type: Array,
@@ -171,7 +182,7 @@ export const SliderMixin = (superClass) =>
171182
* @private
172183
*/
173184
__onPointerDown(event) {
174-
if (this.disabled || event.button !== 0) {
185+
if (this.disabled || this.readonly || event.button !== 0) {
175186
return;
176187
}
177188

packages/slider/src/vaadin-slider.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class Slider extends SliderMixin(
4848
outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
4949
outline-offset: 1px;
5050
}
51+
52+
:host([readonly][focus-ring]) [part~='thumb'] {
53+
outline-style: dashed;
54+
}
5155
`,
5256
];
5357
}
@@ -128,6 +132,7 @@ class Slider extends SliderMixin(
128132
.step="${step}"
129133
.disabled="${this.disabled}"
130134
tabindex="${this.disabled ? -1 : 0}"
135+
@keydown="${this.__onKeyDown}"
131136
@input="${this.__onInput}"
132137
@change="${this.__onChange}"
133138
/>
@@ -170,6 +175,14 @@ class Slider extends SliderMixin(
170175
__commitValue() {
171176
this.value = this.__value[0];
172177
}
178+
179+
/** @private */
180+
__onKeyDown(event) {
181+
const arrowKeys = ['ArrowLeft', 'ArrowDown', 'ArrowRight', 'ArrowUp'];
182+
if (this.readonly && arrowKeys.includes(event.key)) {
183+
event.preventDefault();
184+
}
185+
}
173186
}
174187

175188
defineCustomElement(Slider);

packages/slider/test/range-slider.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,12 @@ describe('vaadin-range-slider', () => {
254254
await sendKeys({ press: 'ArrowLeft' });
255255
expect(spy).to.be.calledOnce;
256256
});
257+
258+
it('should not change value on arrow key when readonly', async () => {
259+
slider.readonly = true;
260+
await sendKeys({ press: 'ArrowRight' });
261+
expect(slider.value).to.deep.equal([0, 100]);
262+
});
257263
});
258264
});
259265

@@ -357,6 +363,18 @@ describe('vaadin-range-slider', () => {
357363

358364
expect(slider.value).to.deep.equal([0, 100]);
359365
});
366+
367+
it('should not update value property on thumb pointermove when readonly', async () => {
368+
slider.readonly = true;
369+
370+
const { x, y } = middleOfThumb(0);
371+
372+
await sendMouseToElement({ type: 'move', element: thumbs[0] });
373+
await sendMouse({ type: 'down' });
374+
await sendMouse({ type: 'move', position: [x + 20, y] });
375+
376+
expect(slider.value).to.deep.equal([0, 100]);
377+
});
360378
});
361379

362380
describe('track', () => {
@@ -439,6 +457,17 @@ describe('vaadin-range-slider', () => {
439457

440458
expect(slider.value).to.deep.equal([20, 80]);
441459
});
460+
461+
it('should not update value property on track pointerdown when readonly', async () => {
462+
slider.readonly = true;
463+
464+
const { x, y } = middleOfThumb(0);
465+
466+
await sendMouse({ type: 'move', position: [x - 20, y] });
467+
await sendMouse({ type: 'down' });
468+
469+
expect(slider.value).to.deep.equal([20, 80]);
470+
});
442471
});
443472

444473
describe('thumbs limits', () => {

packages/slider/test/slider.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ describe('vaadin-slider', () => {
124124
await sendKeys({ press: 'ArrowRight' });
125125
expect(spy).to.be.calledOnce;
126126
});
127+
128+
it('should not change value on arrow key when readonly', async () => {
129+
slider.readonly = true;
130+
await sendKeys({ press: 'ArrowRight' });
131+
expect(slider.value).to.equal(0);
132+
});
127133
});
128134

129135
describe('pointer', () => {
@@ -246,5 +252,24 @@ describe('vaadin-slider', () => {
246252

247253
expect(slider.value).to.equal(0);
248254
});
255+
256+
it('should not update slider value property on thumb pointermove when readonly', async () => {
257+
slider.readonly = true;
258+
259+
await sendMouseToElement({ type: 'move', element: thumb });
260+
await sendMouse({ type: 'down' });
261+
await sendMouse({ type: 'move', position: [20, y] });
262+
263+
expect(slider.value).to.equal(0);
264+
});
265+
266+
it('should not update slider value property on track pointerdown when readonly', async () => {
267+
slider.readonly = true;
268+
269+
await sendMouseToElement({ type: 'move', element: track });
270+
await sendMouse({ type: 'down' });
271+
272+
expect(slider.value).to.equal(0);
273+
});
249274
});
250275
});

packages/slider/test/typings/range-slider.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ assertType<number>(slider.max);
2727
assertType<number>(slider.min);
2828
assertType<number>(slider.step);
2929
assertType<number[]>(slider.value);
30+
assertType<boolean>(slider.disabled);
31+
assertType<boolean>(slider.readonly);
3032

3133
// Mixins
3234
assertType<DisabledMixinClass>(slider);

packages/slider/test/typings/slider.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ assertType<number>(slider.max);
2727
assertType<number>(slider.min);
2828
assertType<number>(slider.step);
2929
assertType<number>(slider.value);
30+
assertType<boolean>(slider.disabled);
31+
assertType<boolean>(slider.readonly);
3032

3133
// Mixins
3234
assertType<DisabledMixinClass>(slider);

0 commit comments

Comments
 (0)