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>
This commit is contained in:
Maria Hutt
2023-09-01 09:30:59 -07:00
committed by GitHub
parent cbafa6b40d
commit e6c7bb60e7
148 changed files with 604 additions and 36 deletions

View File

@ -108,6 +108,14 @@
@include margin($checkbox-item-label-margin-top, null, $checkbox-item-label-margin-bottom, null);
}
:host(.in-item.checkbox-label-placement-stacked) .label-text-wrapper {
@include margin($checkbox-item-label-margin-top, null, $form-control-label-margin, null);
}
:host(.in-item.checkbox-label-placement-stacked) .native-wrapper {
@include margin(null, null, $checkbox-item-label-margin-bottom, null);
}
/**
* If no label text is placed into the slot
* then the element should be hidden otherwise
@ -181,6 +189,17 @@ input {
justify-content: end;
}
// Align Items
// ---------------------------------------------
:host(.checkbox-alignment-start) .checkbox-wrapper {
align-items: start;
}
:host(.checkbox-alignment-center) .checkbox-wrapper {
align-items: center;
}
// Label Placement - Start
// ----------------------------------------------------------------
@ -248,6 +267,24 @@ input {
max-width: 200px;
}
// Label Placement - Stacked
// ----------------------------------------------------------------
/**
* Label is on top of the checkbox.
*/
:host(.checkbox-label-placement-stacked) .checkbox-wrapper {
flex-direction: column;
}
:host(.checkbox-label-placement-stacked) .label-text-wrapper {
/**
* The margin between the label and
* the checkbox should be on the bottom
* when the label sits at the top.
*/
@include margin(null, 0, $form-control-label-margin, 0);
}
// Checked / Indeterminate Checkbox
// ---------------------------------------------

View File

@ -81,8 +81,9 @@ export class Checkbox implements ComponentInterface {
* `"start"`: The label will appear to the left of the checkbox in LTR and to the right in RTL.
* `"end"`: The label will appear to the right of the checkbox 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 checkbox 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';
/**
* How to pack the label and checkbox within a line.
@ -95,6 +96,13 @@ export class Checkbox implements ComponentInterface {
*/
@Prop() justify: 'start' | 'end' | 'space-between' = 'space-between';
/**
* How to control the alignment of the checkbox 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';
// TODO(FW-3100): remove this
/**
* Set the `legacy` property to `true` to forcibly use the legacy form control markup.
@ -224,6 +232,7 @@ export class Checkbox implements ComponentInterface {
labelPlacement,
name,
value,
alignment,
} = this;
const mode = getIonMode(this);
const path = getSVGPath(mode, indeterminate);
@ -240,6 +249,7 @@ export class Checkbox implements ComponentInterface {
'checkbox-indeterminate': indeterminate,
interactive: true,
[`checkbox-justify-${justify}`]: true,
[`checkbox-alignment-${alignment}`]: true,
[`checkbox-label-placement-${labelPlacement}`]: true,
})}
>

View File

@ -70,4 +70,21 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co
await expect(list).toHaveScreenshot(screenshot(`checkbox-long-label-in-item`));
});
});
test.describe(title('checkbox: stacked label in item'), () => {
test('should render margins correctly when using stacked label in item', async ({ page }) => {
await page.setContent(
`
<ion-list>
<ion-item>
<ion-checkbox label-placement="stacked">Enable Notifications</ion-checkbox>
</ion-item>
</ion-list>
`,
config
);
const list = page.locator('ion-list');
await expect(list).toHaveScreenshot(screenshot(`checkbox-stacked-label-in-item`));
});
});
});

View File

@ -137,6 +137,27 @@
</div>
</div>
<h1>Placement Stacked</h1>
<div class="grid">
<div class="grid-item">
<h2>Align Start</h2>
<ion-list>
<ion-item>
<ion-checkbox label-placement="stacked" alignment="start">Enable Notifications</ion-checkbox>
</ion-item>
</ion-list>
</div>
<div class="grid-item">
<h2>Align Center</h2>
<ion-list>
<ion-item>
<ion-checkbox label-placement="stacked" alignment="center">Enable Notifications</ion-checkbox>
</ion-item>
</ion-list>
</div>
</div>
<h1>States</h1>
<div class="grid">
<div class="grid-item">

View File

@ -138,5 +138,31 @@ configs().forEach(({ title, screenshot, config }) => {
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-label-fixed-justify-space-between`));
});
});
test.describe('checkbox: stacked placement', () => {
test('should align the label to the start of the container in the stacked position', async ({ page }) => {
await page.setContent(
`
<ion-checkbox label-placement="stacked" alignment="start" style="width: 200px">This is a long label</ion-checkbox>
`,
config
);
const checkbox = page.locator('ion-checkbox');
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-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-checkbox label-placement="stacked" alignment="center" style="width: 200px">This is a long label</ion-checkbox>
`,
config
);
const checkbox = page.locator('ion-checkbox');
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-label-stacked-align-center`));
});
});
});
});

View File

@ -104,6 +104,19 @@
<ion-checkbox label-placement="fixed" justify="space-between">Enable Notifications</ion-checkbox>
</div>
</div>
<h1>Placement Stacked</h1>
<div class="grid">
<div class="grid-item">
<h2>Align Start</h2>
<ion-checkbox label-placement="stacked" alignment="start">Enable Notifications</ion-checkbox>
</div>
<div class="grid-item">
<h2>Align Center</h2>
<ion-checkbox label-placement="stacked" alignment="center">Enable Notifications</ion-checkbox>
</div>
</div>
</ion-content>
</ion-app>
</body>