Skip to content

Commit 368add2

Browse files
feat(range): add label prop (#27408)
Issue number: N/A --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> Labels on `ion-range` can only be set via the `label` slot. When only plain text is needed, this is cumbersome because you need to add an entire new element to wrap the label. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> Label prop added. If both the prop and slot are used, the prop will take priority. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> - Docs PR: ionic-team/ionic-docs#2955 - Dev build: `7.0.6-dev.11683657201.139d03f4`
1 parent 1c71bfb commit 368add2

File tree

47 files changed

+98
-13
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+98
-13
lines changed

angular/src/directives/proxies.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1619,14 +1619,14 @@ export declare interface IonRadioGroup extends Components.IonRadioGroup {
16191619

16201620

16211621
@ProxyCmp({
1622-
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
1622+
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'label', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
16231623
})
16241624
@Component({
16251625
selector: 'ion-range',
16261626
changeDetection: ChangeDetectionStrategy.OnPush,
16271627
template: '<ng-content></ng-content>',
16281628
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
1629-
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value'],
1629+
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'label', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value'],
16301630
})
16311631
export class IonRange {
16321632
protected el: HTMLElement;

core/api.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,7 @@ ion-range,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secon
10301030
ion-range,prop,debounce,number | undefined,undefined,false,false
10311031
ion-range,prop,disabled,boolean,false,false,false
10321032
ion-range,prop,dualKnobs,boolean,false,false,false
1033+
ion-range,prop,label,string | undefined,undefined,false,false
10331034
ion-range,prop,labelPlacement,"end" | "fixed" | "start",'start',false,false
10341035
ion-range,prop,legacy,boolean | undefined,undefined,false,false
10351036
ion-range,prop,max,number,100,false,false

core/src/components.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -2289,6 +2289,10 @@ export namespace Components {
22892289
* Show two knobs.
22902290
*/
22912291
"dualKnobs": boolean;
2292+
/**
2293+
* The text to display as the control's label. Use this over the `label` slot if you only need plain text. The `label` property will take priority over the `label` slot if both are used.
2294+
*/
2295+
"label"?: string;
22922296
/**
22932297
* Where to place the label relative to the range. `"start"`: The label will appear to the left of the range in LTR and to the right in RTL. `"end"`: The label will appear to the right of the range in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("...").
22942298
*/
@@ -6304,6 +6308,10 @@ declare namespace LocalJSX {
63046308
* Show two knobs.
63056309
*/
63066310
"dualKnobs"?: boolean;
6311+
/**
6312+
* The text to display as the control's label. Use this over the `label` slot if you only need plain text. The `label` property will take priority over the `label` slot if both are used.
6313+
*/
6314+
"label"?: string;
63076315
/**
63086316
* Where to place the label relative to the range. `"start"`: The label will appear to the left of the range in LTR and to the right in RTL. `"end"`: The label will appear to the right of the range in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("...").
63096317
*/

core/src/components/range/range.md.scss

+1-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
font-size: $range-md-pin-font-size;
2323
}
2424

25-
// TODO FW-2997 Remove this
26-
::slotted([slot="label"]) {
25+
::slotted([slot="label"]), .label-text {
2726
font-size: initial;
2827
}
2928

core/src/components/range/range.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@
209209
opacity: 0.3;
210210
}
211211

212-
::slotted([slot="label"]) {
212+
::slotted([slot="label"]), .label-text {
213213
/**
214214
* Label text should not extend
215215
* beyond the bounds of the range.

core/src/components/range/range.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ export class Range implements ComponentInterface {
9696
*/
9797
@Prop() name = this.rangeId;
9898

99+
/**
100+
* The text to display as the control's label. Use this over the `label` slot if
101+
* you only need plain text. The `label` property will take priority over the
102+
* `label` slot if both are used.
103+
*/
104+
@Prop() label?: string;
105+
99106
/**
100107
* Show two knobs.
101108
*/
@@ -602,7 +609,7 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
602609
}
603610

604611
private renderRange() {
605-
const { disabled, el, rangeId, pin, pressedKnob, labelPlacement } = this;
612+
const { disabled, el, rangeId, pin, pressedKnob, labelPlacement, label } = this;
606613

607614
const mode = getIonMode(this);
608615

@@ -629,7 +636,7 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
629636
'label-text-wrapper-hidden': !this.hasLabel,
630637
}}
631638
>
632-
<slot name="label"></slot>
639+
{label !== undefined ? <div class="label-text">{label}</div> : <slot name="label"></slot>}
633640
</div>
634641
<div class="native-wrapper">
635642
<slot name="start"></slot>
@@ -642,7 +649,7 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
642649
}
643650

644651
private get hasLabel() {
645-
return this.el.querySelector('[slot="label"]') !== null;
652+
return this.label !== undefined || this.el.querySelector('[slot="label"]') !== null;
646653
}
647654

648655
private renderRangeSlider() {

core/src/components/range/test/a11y/index.html

+9-5
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@
1515
<main>
1616
<h1>Range - a11y</h1>
1717

18-
<ion-range><span slot="label">my label</span></ion-range
19-
><br />
20-
<ion-range aria-label="my aria label"></ion-range><br />
18+
<ion-range><span slot="label">my label</span></ion-range>
19+
<br />
20+
<ion-range label="my label"></ion-range>
21+
<br />
22+
<ion-range aria-label="my aria label"></ion-range>
23+
<br />
2124
<ion-range>
2225
<span slot="label">temperature</span>
2326
<ion-icon name="snow" slot="start" aria-hidden="true"></ion-icon>
24-
<ion-icon name="flame" slot="end" aria-hidden="true"></ion-icon> </ion-range
25-
><br />
27+
<ion-icon name="flame" slot="end" aria-hidden="true"></ion-icon>
28+
</ion-range>
29+
<br />
2630
</main>
2731
</body>
2832
</html>

core/src/components/range/test/label/index.html

+16
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ <h2>Placement Fixed</h2>
6767
</div>
6868
</div>
6969

70+
<h1>Label Property</h1>
71+
<div class="grid">
72+
<div class="grid-item">
73+
<h2>Placement Start</h2>
74+
<ion-range label="Temperature"></ion-range>
75+
</div>
76+
<div class="grid-item">
77+
<h2>Placement End</h2>
78+
<ion-range label="Temperature" label-placement="end"></ion-range>
79+
</div>
80+
<div class="grid-item">
81+
<h2>Placement Fixed</h2>
82+
<ion-range label="Temperature" label-placement="fixed"></ion-range>
83+
</div>
84+
</div>
85+
7086
<h1>Slotted Items</h1>
7187
<div class="grid">
7288
<div class="grid-item">

core/src/components/range/test/label/range.e2e.ts

+26
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,32 @@ configs().forEach(({ title, screenshot, config }) => {
125125
expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-items-fixed`));
126126
});
127127
});
128+
129+
test.describe('range: label prop', () => {
130+
test('should render label in the start placement', async ({ page }) => {
131+
await page.setContent(`<ion-range label-placement="start" label="Volume"></ion-range>`, config);
132+
133+
const range = page.locator('ion-range');
134+
135+
expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-label-prop-start`));
136+
});
137+
138+
test('should render label in the end placement', async ({ page }) => {
139+
await page.setContent(`<ion-range label-placement="end" label="Volume"></ion-range>`, config);
140+
141+
const range = page.locator('ion-range');
142+
143+
expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-label-prop-end`));
144+
});
145+
146+
test('should render label in the fixed placement', async ({ page }) => {
147+
await page.setContent(`<ion-range label-placement="fixed" label="Volume"></ion-range>`, config);
148+
149+
const range = page.locator('ion-range');
150+
151+
expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-label-prop-fixed`));
152+
});
153+
});
128154
});
129155
});
130156

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { newSpecPage } from '@stencil/core/testing';
2+
3+
import { Range } from '../../range';
4+
5+
describe('range: label', () => {
6+
it('should prioritize the label prop over the slot', async () => {
7+
const page = await newSpecPage({
8+
components: [Range],
9+
html: `
10+
<ion-range label="Label prop">
11+
<div slot="label">Label slot</div>
12+
</ion-range>
13+
`,
14+
});
15+
16+
const range = page.body.querySelector('ion-range');
17+
const propEl = range?.shadowRoot?.querySelector('.label-text');
18+
const slotEl = range?.shadowRoot?.querySelector('slot[name="label"]');
19+
20+
expect(propEl).not.toBeNull();
21+
expect(slotEl).toBeNull();
22+
});
23+
});

packages/vue/src/proxies.ts

+1
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ export const IonRange = /*@__PURE__*/ defineContainer<JSX.IonRange, JSX.IonRange
606606
'color',
607607
'debounce',
608608
'name',
609+
'label',
609610
'dualKnobs',
610611
'min',
611612
'max',

0 commit comments

Comments
 (0)