feat(checkbox, radio, toggle, range): stacked labels for form controls (#28075)
Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com> Co-authored-by: ionitron <hi@ionicframework.com>
@ -135,6 +135,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Placement Stacked</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Align Start</h2>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-toggle label-placement="stacked" alignment="start">Enable Notifications</ion-toggle>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Align Center</h2>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-toggle label-placement="stacked" alignment="center">Enable Notifications</ion-toggle>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Multiline Label</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
|
||||
@ -70,4 +70,23 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co
|
||||
await expect(list).toHaveScreenshot(screenshot(`toggle-long-label-in-item`));
|
||||
});
|
||||
});
|
||||
|
||||
test.describe(title('toggle: stacked label in item'), () => {
|
||||
test('should render margins correctly when using stacked label in item', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-list>
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-toggle label-placement="stacked">Enable Notifications</ion-toggle>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</ion-list>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const list = page.locator('ion-list');
|
||||
await expect(list).toHaveScreenshot(screenshot(`toggle-stacked-label-in-item`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
@ -102,6 +102,19 @@
|
||||
<ion-toggle label-placement="fixed" justify="space-between">Enable Notifications</ion-toggle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Placement Stacked</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Align Start</h2>
|
||||
<ion-toggle label-placement="stacked" alignment="start">Enable Notifications</ion-toggle>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Align Center</h2>
|
||||
<ion-toggle label-placement="stacked" alignment="center">Enable Notifications</ion-toggle>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
@ -126,4 +126,30 @@ configs().forEach(({ title, screenshot, config }) => {
|
||||
await expect(toggle).toHaveScreenshot(screenshot(`toggle-label-fixed-justify-space-between`));
|
||||
});
|
||||
});
|
||||
|
||||
test.describe(title('toggle: stacked placement'), () => {
|
||||
test('should align the label to the start of the container in the stacked position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-toggle label-placement="stacked" alignment="start" style="width: 200px">This is a long label</ion-toggle>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const toggle = page.locator('ion-toggle');
|
||||
await expect(toggle).toHaveScreenshot(screenshot(`toggle-label-stacked-align-start`));
|
||||
});
|
||||
|
||||
test('should align the label to the center of the container in the stacked position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-toggle label-placement="stacked" alignment="center" style="width: 200px">This is a long label</ion-toggle>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const toggle = page.locator('ion-toggle');
|
||||
await expect(toggle).toHaveScreenshot(screenshot(`toggle-label-stacked-align-center`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
@ -94,8 +94,6 @@ input {
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
align-items: center;
|
||||
|
||||
height: inherit;
|
||||
|
||||
transition: background-color 15ms linear;
|
||||
@ -125,6 +123,14 @@ input {
|
||||
@include margin($toggle-item-label-margin-top, null, $toggle-item-label-margin-bottom, null);
|
||||
}
|
||||
|
||||
:host(.in-item.toggle-label-placement-stacked) .label-text-wrapper {
|
||||
@include margin($toggle-item-label-margin-top, null, $form-control-label-margin, null);
|
||||
}
|
||||
|
||||
:host(.in-item.toggle-label-placement-stacked) .native-wrapper {
|
||||
@include margin(null, null, $toggle-item-label-margin-bottom, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* If no label text is placed into the slot
|
||||
* then the element should be hidden otherwise
|
||||
@ -158,6 +164,18 @@ input {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
// Toggle Align
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
:host(.toggle-alignment-start) .toggle-wrapper {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
:host(.toggle-alignment-center) .toggle-wrapper {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// Toggle Label Placement - Start
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@ -222,6 +240,25 @@ input {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
// Toggle Label Placement - Stacked
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Label is on top of the toggle.
|
||||
*/
|
||||
:host(.toggle-label-placement-stacked) .toggle-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:host(.toggle-label-placement-stacked) .label-text-wrapper {
|
||||
/**
|
||||
* The margin between the label and
|
||||
* the toggle should be on the bottom
|
||||
* when the label sits on top.
|
||||
*/
|
||||
@include margin(null, 0, $form-control-label-margin, 0);
|
||||
}
|
||||
|
||||
// Toggle Background Track: Unchecked
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
@ -90,8 +90,9 @@ export class Toggle implements ComponentInterface {
|
||||
* `"start"`: The label will appear to the left of the toggle in LTR and to the right in RTL.
|
||||
* `"end"`: The label will appear to the right of the toggle 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 ("...").
|
||||
* `"stacked"`: The label will appear above the toggle regardless of the direction. The alignment of the label can be controlled with the `alignment` property.
|
||||
*/
|
||||
@Prop() labelPlacement: 'start' | 'end' | 'fixed' = 'start';
|
||||
@Prop() labelPlacement: 'start' | 'end' | 'fixed' | 'stacked' = 'start';
|
||||
|
||||
/**
|
||||
* Set the `legacy` property to `true` to forcibly use the legacy form control markup.
|
||||
@ -115,6 +116,13 @@ export class Toggle implements ComponentInterface {
|
||||
*/
|
||||
@Prop() justify: 'start' | 'end' | 'space-between' = 'space-between';
|
||||
|
||||
/**
|
||||
* How to control the alignment of the toggle and label on the cross axis.
|
||||
* ``"start"`: The label and control will appear on the left of the cross axis in LTR, and on the right side in RTL.
|
||||
* `"center"`: The label and control will appear at the center of the cross axis in both LTR and RTL.
|
||||
*/
|
||||
@Prop() alignment: 'start' | 'center' = 'center';
|
||||
|
||||
/**
|
||||
* Emitted when the user switches the toggle on or off. Does not emit
|
||||
* when programmatically changing the value of the `checked` property.
|
||||
@ -319,7 +327,7 @@ export class Toggle implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderToggle() {
|
||||
const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name } = this;
|
||||
const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name, alignment } = this;
|
||||
|
||||
const mode = getIonMode(this);
|
||||
const value = this.getValue();
|
||||
@ -336,6 +344,7 @@ export class Toggle implements ComponentInterface {
|
||||
'toggle-checked': checked,
|
||||
'toggle-disabled': disabled,
|
||||
[`toggle-justify-${justify}`]: true,
|
||||
[`toggle-alignment-${alignment}`]: true,
|
||||
[`toggle-label-placement-${labelPlacement}`]: true,
|
||||
[`toggle-${rtl}`]: true,
|
||||
})}
|
||||
|
||||