Skip to content

Commit fac1a66

Browse files
feat(range): add classes to the range when the value is at the min or max (#30932)
## What is the current behavior? Range adds classes to the knobs at `min` and `max`, but the host element doesn't reflect those states. ## What is the new behavior? - Adds `range-value-min` and `range-value-max` when the value is at the `min` or `max`, respectively. - Adds a spec test verifying the classes are applied properly. --------- Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
1 parent 66e1dc0 commit fac1a66

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

core/src/components/range/range.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ export class Range implements ComponentInterface {
883883
}
884884

885885
render() {
886-
const { disabled, el, hasLabel, rangeId, pin, pressedKnob, labelPlacement, label } = this;
886+
const { disabled, el, hasLabel, rangeId, pin, pressedKnob, labelPlacement, label, dualKnobs, min, max } = this;
887887

888888
const inItem = hostContext('ion-item', el);
889889

@@ -906,6 +906,13 @@ export class Range implements ComponentInterface {
906906

907907
const mode = getIonMode(this);
908908

909+
/**
910+
* Determine if any knob is at the min or max value to
911+
* apply Host classes for styling.
912+
*/
913+
const valueAtMin = dualKnobs ? this.valA === min || this.valB === min : this.valA === min;
914+
const valueAtMax = dualKnobs ? this.valA === max || this.valB === max : this.valA === max;
915+
909916
renderHiddenInput(true, el, this.name, JSON.stringify(this.getValue()), disabled);
910917

911918
return (
@@ -922,6 +929,8 @@ export class Range implements ComponentInterface {
922929
[`range-label-placement-${labelPlacement}`]: true,
923930
'range-item-start-adjustment': needsStartAdjustment,
924931
'range-item-end-adjustment': needsEndAdjustment,
932+
'range-value-min': valueAtMin,
933+
'range-value-max': valueAtMax,
925934
})}
926935
>
927936
<label class="range-wrapper" id="range-label">

core/src/components/range/test/range.spec.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,101 @@ describe('range: item adjustments', () => {
253253
});
254254
});
255255
});
256+
257+
describe('range: value state classes', () => {
258+
it('should apply range-value-min class when value is at min', async () => {
259+
const page = await newSpecPage({
260+
components: [Range],
261+
html: `<ion-range min="0" max="100" value="0"></ion-range>`,
262+
});
263+
264+
const range = page.body.querySelector('ion-range')!;
265+
266+
expect(range.classList.contains('range-value-min')).toBe(true);
267+
expect(range.classList.contains('range-value-max')).toBe(false);
268+
});
269+
270+
it('should apply range-value-max class when value is at max', async () => {
271+
const page = await newSpecPage({
272+
components: [Range],
273+
html: `<ion-range min="0" max="100" value="100"></ion-range>`,
274+
});
275+
276+
const range = page.body.querySelector('ion-range')!;
277+
278+
expect(range.classList.contains('range-value-max')).toBe(true);
279+
expect(range.classList.contains('range-value-min')).toBe(false);
280+
});
281+
282+
it('should not apply range-value-min or range-value-max classes when value is in the middle', async () => {
283+
const page = await newSpecPage({
284+
components: [Range],
285+
html: `<ion-range min="0" max="100" value="50"></ion-range>`,
286+
});
287+
288+
const range = page.body.querySelector('ion-range')!;
289+
290+
expect(range.classList.contains('range-value-min')).toBe(false);
291+
expect(range.classList.contains('range-value-max')).toBe(false);
292+
});
293+
294+
it('should apply range-value-min class when lower knob is at min in dual knobs', async () => {
295+
const page = await newSpecPage({
296+
components: [Range],
297+
html: `<ion-range dual-knobs="true" min="0" max="100"></ion-range>`,
298+
});
299+
300+
const range = page.body.querySelector('ion-range')!;
301+
range.value = { lower: 0, upper: 50 };
302+
303+
await page.waitForChanges();
304+
305+
expect(range.classList.contains('range-value-min')).toBe(true);
306+
expect(range.classList.contains('range-value-max')).toBe(false);
307+
});
308+
309+
it('should apply range-value-max class when upper knob is at max in dual knobs', async () => {
310+
const page = await newSpecPage({
311+
components: [Range],
312+
html: `<ion-range dual-knobs="true" min="0" max="100"></ion-range>`,
313+
});
314+
315+
const range = page.body.querySelector('ion-range')!;
316+
range.value = { lower: 50, upper: 100 };
317+
318+
await page.waitForChanges();
319+
320+
expect(range.classList.contains('range-value-max')).toBe(true);
321+
expect(range.classList.contains('range-value-min')).toBe(false);
322+
});
323+
324+
it('should apply range-value-min and range-value-max classes for dual knobs when both are at boundaries', async () => {
325+
const page = await newSpecPage({
326+
components: [Range],
327+
html: `<ion-range dual-knobs="true" min="0" max="100"></ion-range>`,
328+
});
329+
330+
const range = page.body.querySelector('ion-range')!;
331+
range.value = { lower: 0, upper: 100 };
332+
333+
await page.waitForChanges();
334+
335+
expect(range.classList.contains('range-value-min')).toBe(true);
336+
expect(range.classList.contains('range-value-max')).toBe(true);
337+
});
338+
339+
it('should not apply range-value-min or range-value-max classes for dual knobs when neither is at boundaries', async () => {
340+
const page = await newSpecPage({
341+
components: [Range],
342+
html: `<ion-range dual-knobs="true" min="0" max="100"></ion-range>`,
343+
});
344+
345+
const range = page.body.querySelector('ion-range')!;
346+
range.value = { lower: 25, upper: 75 };
347+
348+
await page.waitForChanges();
349+
350+
expect(range.classList.contains('range-value-min')).toBe(false);
351+
expect(range.classList.contains('range-value-max')).toBe(false);
352+
});
353+
});

0 commit comments

Comments
 (0)