refactor(toggle): remove legacy property and support for legacy syntax (#29037)

BREAKING CHANGE:

The `legacy` property and support for the legacy syntax, which involved placing an `ion-toggle` inside of an `ion-item` with an `ion-label`, have been removed from toggle. For more information on migrating from the legacy toggle syntax, refer to the [Toggle documentation](https://ionicframework.com/docs/api/toggle#migrating-from-legacy-toggle-syntax).
This commit is contained in:
Liam DeBeasi
2024-02-13 17:30:17 -05:00
committed by GitHub
parent 6bd446f681
commit c72ecedc09
86 changed files with 21 additions and 1161 deletions

View File

@ -39,10 +39,6 @@
<ion-label>Disabled Item Anchor</ion-label>
</ion-item>
<ion-item>
<ion-toggle disabled checked> Disabled Toggle </ion-toggle>
</ion-item>
<ion-item>
<ion-checkbox disabled checked> Disabled Checkbox </ion-checkbox>
</ion-item>
@ -85,7 +81,6 @@
<ion-item>
<ion-checkbox aria-label="Checkbox" slot="start"></ion-checkbox>
<ion-toggle disabled value="45">Checkbox + Toggle</ion-toggle>
</ion-item>
<ion-item>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -38,12 +38,6 @@
<ion-item disabled href="#">
<ion-label>Disabled Item Anchor</ion-label>
</ion-item>
<ion-item>
<ion-label>Disabled Toggle</ion-label>
<ion-toggle disabled checked slot="end" legacy="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Disabled Checkbox</ion-label>
<ion-checkbox disabled checked slot="start" legacy="true"></ion-checkbox>
@ -85,12 +79,6 @@
<ion-range disabled value="45"></ion-range>
</ion-item>
<ion-item>
<ion-checkbox slot="start" legacy="true"></ion-checkbox>
<ion-label>Checkbox + Toggle</ion-label>
<ion-toggle disabled value="45"></ion-toggle>
</ion-item>
<ion-item>
<ion-checkbox disabled slot="start" legacy="true"></ion-checkbox>
<ion-label>Checkbox + Buttons</ion-label>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -1,162 +0,0 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Toggle - Basic</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Toggle - Basic</ion-title>
<ion-buttons slot="primary">
<ion-toggle legacy="true"></ion-toggle>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content id="content">
<ion-list>
<ion-item>
<ion-label>Orange</ion-label>
<ion-toggle legacy="true" id="orange" slot="start" name="orange"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Apple</ion-label>
<ion-toggle legacy="true" slot="start" name="apple" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Banana</ion-label>
<ion-toggle legacy="true" slot="start" name="banana" checked class="toggle-part"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Cherry, disabled</ion-label>
<ion-toggle legacy="true" slot="start" color="danger" name="cherry" disabled></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Grape, checked, disabled</ion-label>
<ion-toggle
legacy="true"
slot="start"
id="grapeChecked"
value="grape"
name="grape"
checked
disabled
></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Kiwi, (ionChange) Secondary color</ion-label>
<ion-toggle legacy="true" slot="start" color="secondary" (ionChange)="kiwiChange($event)"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Strawberry, (ionChange) checked="true"</ion-label>
<ion-toggle
legacy="true"
slot="start"
color="light"
(ionChange)="strawberryChange($event)"
checked="true"
></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Checkbox right, checked, really long text that should ellipsis</ion-label>
<ion-toggle legacy="true" slot="end" color="danger" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Checkbox right side</ion-label>
<ion-toggle legacy="true" slot="end" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Button w/ right side default icon, really long text that should ellipsis</ion-label>
<ion-icon name="information-circle" slot="end"></ion-icon>
</ion-item>
<ion-item>
<ion-label>Custom</ion-label>
<ion-toggle
legacy="true"
slot="start"
style="--border-radius: 0px; --handle-border-radius: 0px"
checked
></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Stop Immediate Event Propagation</ion-label>
<ion-toggle legacy="true" slot="start" checked id="eventPropagation"></ion-toggle>
</ion-item>
</ion-list>
<p aria-hidden="true" class="ion-text-center">
<ion-button onClick="toggleBoolean('grapeChecked', 'checked')" fill="outline" size="small"
>Grape Checked</ion-button
>
<ion-button onClick="toggleBoolean('grapeChecked', 'disabled')" fill="outline" size="small"
>Grape Disabled</ion-button
>
<ion-button onClick="printForm()" fill="outline" size="small">Print Form</ion-button>
</p>
<p>
<ion-toggle id="standAloneChecked" legacy="true"></ion-toggle>
Stand-alone toggle:
<span id="standAloneCheckedSpan"></span>
</p>
</ion-content>
<style>
.toggle-part.toggle-checked::part(handle) {
background: red;
}
.toggle-part.toggle-checked::part(track) {
background: purple;
}
.toggle-part::part(handle) {
background: green;
}
.toggle-part::part(track) {
background: hotpink;
}
</style>
<script>
function printForm(ev) {}
function toggleBoolean(id, prop) {
var el = document.getElementById(id);
var isTrue = el[prop] ? false : true;
el[prop] = isTrue;
}
document.getElementById('eventPropagation').addEventListener('click', (evt) => {
evt.stopImmediatePropagation();
console.log('clicked');
});
</script>
</ion-app>
</body>
</html>

View File

@ -1,104 +0,0 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs().forEach(({ title, screenshot, config }) => {
test.describe(title('toggle: rendering'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/toggle/test/legacy/basic`, config);
await page.setIonViewport();
await expect(page).toHaveScreenshot(screenshot(`toggle-diff`));
});
});
});
/**
* This behavior does not vary across modes/directions.
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('toggle: functionality'), () => {
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/toggle/test/legacy/basic`, config);
});
test('should have proper class and aria role when checked', async ({ page }) => {
const toggle = page.locator('#orange');
await expect(toggle).not.toHaveClass(/toggle-checked/);
await expect(toggle).toHaveAttribute('aria-checked', 'false');
await toggle.click();
await page.waitForChanges();
await expect(toggle).toHaveClass(/toggle-checked/);
await expect(toggle).toHaveAttribute('aria-checked', 'true');
await toggle.click();
await page.waitForChanges();
await expect(toggle).not.toHaveClass(/toggle-checked/);
await expect(toggle).toHaveAttribute('aria-checked', 'false');
});
test('should fire change event with detail', async ({ page }) => {
const toggle = page.locator('#orange');
const ionChange = await page.spyOnEvent('ionChange');
await toggle.click();
await page.waitForChanges();
expect(ionChange).toHaveReceivedEventDetail({
checked: true,
value: 'on',
});
await toggle.click();
await page.waitForChanges();
expect(ionChange).toHaveReceivedEventDetail({
checked: false,
value: 'on',
});
});
test('should not fire change event if checked prop is changed directly', async ({ page }) => {
const toggle = page.locator('#orange');
const ionChange = await page.spyOnEvent('ionChange');
await toggle.evaluate((el: HTMLIonToggleElement) => (el.checked = true));
await page.waitForChanges();
expect(ionChange).toHaveReceivedEventTimes(0);
});
test('should pass properties down to hidden input', async ({ page }) => {
const toggle = page.locator('#grapeChecked');
await expect(toggle).toHaveJSProperty('disabled', true);
await expect(toggle).toHaveJSProperty('value', 'grape');
await expect(toggle).toHaveJSProperty('name', 'grape');
const hiddenInput = page.locator('#grapeChecked input[type=hidden]');
await expect(hiddenInput).toBeDisabled();
await expect(hiddenInput).toHaveJSProperty('value', 'grape');
await expect(hiddenInput).toHaveJSProperty('name', 'grape');
await toggle.evaluate((el: HTMLIonToggleElement) => {
el.disabled = false;
el.checked = false;
el.value = 'new-value';
el.name = 'new-name';
});
await page.waitForChanges();
await expect(hiddenInput).not.toBeDisabled();
await expect(hiddenInput).toHaveJSProperty('name', 'new-name');
// shouldn't have a value because it's unchecked
// note: using toHaveJSProperty to check empty string triggers error for some reason
const value = await hiddenInput.evaluate((el: HTMLInputElement) => el.value);
expect(value).toBe('');
});
});
});

View File

@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Toggle - enableOnOffLabels</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
/*
* Dark Colors
* ------------------
*/
body.dark {
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme
* -------------------
*/
.ios body.dark {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-toolbar-background: #0d0d0d;
--ion-item-background: #1c1c1c;
--ion-item-background-activated: #313131;
}
/*
* Material Design Dark Theme
* ------------------------------
*/
.md body.dark {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1a1b1e;
}
/* Optional CSS, this is added for the flashing that happens when toggling between themes */
ion-item {
--transition: none;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Toggle - enableOnOffLabels</ion-title>
<ion-buttons slot="end">
<ion-button id="popover-trigger">Options</ion-button>
</ion-buttons>
<ion-popover class="options-popover" trigger="popover-trigger">
<ion-list lines="none">
<ion-item id="dark-mode">
<ion-label>Dark Mode</ion-label>
<ion-checkbox slot="end"></ion-checkbox>
</ion-item>
</ion-list>
</ion-popover>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-label>Unchecked</ion-label>
<ion-toggle slot="end" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Checked</ion-label>
<ion-toggle slot="end" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Secondary Unchecked</ion-label>
<ion-toggle slot="end" color="secondary" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Secondary Checked</ion-label>
<ion-toggle slot="end" color="secondary" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Success Unchecked</ion-label>
<ion-toggle slot="end" color="success" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Success Checked</ion-label>
<ion-toggle slot="end" color="success" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Danger Unchecked</ion-label>
<ion-toggle slot="end" color="danger" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Danger Checked</ion-label>
<ion-toggle slot="end" color="danger" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Tertiary Unchecked</ion-label>
<ion-toggle slot="end" color="tertiary" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Tertiary Checked</ion-label>
<ion-toggle slot="end" color="tertiary" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Light Unchecked</ion-label>
<ion-toggle slot="end" color="light" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Light Checked</ion-label>
<ion-toggle slot="end" color="light" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Medium Unchecked</ion-label>
<ion-toggle slot="end" color="medium" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Medium Checked</ion-label>
<ion-toggle slot="end" color="medium" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Dark Unchecked</ion-label>
<ion-toggle slot="end" color="dark" enable-on-off-labels="true"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Dark Checked</ion-label>
<ion-toggle slot="end" color="dark" enable-on-off-labels="true" checked></ion-toggle>
</ion-item>
</ion-list>
</ion-content>
</ion-app>
<script>
const darkModeCheckbox = document.querySelector('ion-checkbox');
darkModeCheckbox.addEventListener('ionChange', (ev) => {
if (ev.detail.checked) {
document.body.classList.add('dark');
} else {
document.body.classList.remove('dark');
}
});
</script>
</body>
</html>

View File

@ -1,43 +0,0 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs().forEach(({ title, screenshot, config }) => {
test.describe(title('toggle: enableOnOffLabels'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/toggle/test/legacy/enable-on-off-labels`, config);
await page.setIonViewport();
await expect(page).toHaveScreenshot(screenshot(`toggle-on-off-labels-diff`));
});
});
});
/**
* This behavior does not vary across directions.
*/
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('toggle: dark mode'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/toggle/test/legacy/enable-on-off-labels`, config);
const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent');
const ionPopoverDidDismiss = await page.spyOnEvent('ionPopoverDidDismiss');
await page.click('#popover-trigger');
await ionPopoverDidPresent.next();
await page.click('#dark-mode');
await page.evaluate(() => {
const popover = document.querySelector('ion-popover');
return popover?.dismiss();
});
await ionPopoverDidDismiss.next();
await page.waitForChanges();
await page.setIonViewport();
await expect(page).toHaveScreenshot(screenshot(`toggle-on-off-labels-dark-mode-diff`));
});
});
});

View File

@ -1,230 +0,0 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Toggle - Sizes</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Toggle - Sizes</ion-title>
<ion-buttons slot="end">
<ion-button onClick="toggleDynamic()">Toggle</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-horizontal">
<h1>Default</h1>
<ion-toggle legacy="true"></ion-toggle>
<ion-toggle checked legacy="true"></ion-toggle>
<ion-toggle color="danger" legacy="true"></ion-toggle>
<ion-toggle color="danger" checked legacy="true"></ion-toggle>
<ion-toggle color="tertiary" class="toggle-activated" legacy="true"></ion-toggle>
<ion-toggle color="tertiary" checked class="toggle-activated" legacy="true"></ion-toggle>
<h1>Custom Widths</h1>
<ion-toggle color="secondary" class="width-small" legacy="true"></ion-toggle>
<ion-toggle color="secondary" checked class="width-small" legacy="true"></ion-toggle>
<ion-toggle color="secondary" class="width-large" legacy="true"></ion-toggle>
<ion-toggle color="secondary" checked class="width-large" legacy="true"></ion-toggle>
<ion-toggle color="tertiary" class="width-large toggle-activated" legacy="true"></ion-toggle>
<ion-toggle color="tertiary" checked class="width-large toggle-activated" legacy="true"></ion-toggle>
<h1>Custom Heights</h1>
<div style="display: flex; flex-flow: column; float: left">
<ion-toggle class="height-small" legacy="true"></ion-toggle>
<ion-toggle checked class="height-small" legacy="true"></ion-toggle>
</div>
<ion-toggle class="height-large" legacy="true"></ion-toggle>
<ion-toggle checked class="height-large" legacy="true"></ion-toggle>
<ion-toggle class="handle-height-large" legacy="true"></ion-toggle>
<ion-toggle checked class="handle-height-large" legacy="true"></ion-toggle>
<ion-toggle checked class="handle-height-large toggle-activated" legacy="true"></ion-toggle>
<h1>Dynamic Sizes</h1>
<ion-toggle color="tertiary" class="dynamic-small width-small height-small" legacy="true"></ion-toggle>
<ion-toggle color="tertiary" checked class="dynamic-small width-small height-small" legacy="true"></ion-toggle>
<ion-toggle color="tertiary" class="dynamic-large width-large height-large" legacy="true"></ion-toggle>
<ion-toggle color="tertiary" checked class="dynamic-large width-large height-large" legacy="true"></ion-toggle>
<h1>Complex Custom Toggles</h1>
<ion-toggle mode="ios" class="all-custom" legacy="true"></ion-toggle>
<ion-toggle mode="ios" checked class="all-custom" legacy="true"></ion-toggle>
<ion-toggle class="custom-overflow" legacy="true"></ion-toggle>
<ion-toggle checked class="custom-overflow" legacy="true"></ion-toggle>
<ion-toggle mode="ios" color="dark" class="custom-spacing" legacy="true"></ion-toggle>
<ion-toggle mode="ios" color="dark" checked class="custom-spacing" legacy="true"></ion-toggle>
<ion-toggle mode="md" color="dark" class="custom-spacing" legacy="true"></ion-toggle>
<ion-toggle mode="md" color="dark" checked class="custom-spacing" legacy="true"></ion-toggle>
<ion-toggle mode="ios" class="icon-custom" legacy="true"></ion-toggle>
<ion-toggle mode="ios" checked class="icon-custom" legacy="true"></ion-toggle>
</ion-content>
</ion-app>
<style>
ion-toggle {
padding: 5px;
}
.width-small {
width: 40px;
}
.width-large {
width: 100px;
}
.ios.height-small {
height: 20px;
/* The height of the handle is 100% of the track height - 4px */
--handle-width: 16px;
}
.md.height-small {
height: 10px;
/* The height of the handle is 100% of the track height + 6px */
--handle-width: 16px;
}
.height-large {
height: 60px;
--border-radius: 4px;
--handle-border-radius: 4px;
--handle-width: 50%;
}
.handle-height-large {
height: 60px;
/*
* The max height of the handle is 100% of the track height - handle spacing
* so this will never show as being 150%
*/
--handle-height: 150%;
--handle-width: 23px;
--handle-border-radius: 14px 4px 4px 14px;
--handle-transition: border-radius 120ms ease-in-out 80ms;
}
.handle-height-large.toggle-checked {
--handle-border-radius: 4px 14px 14px 4px;
}
.icon-custom {
--handle-background-checked: #fff url(/src/components/toggle/test/legacy/sizes/power-outline.svg) no-repeat
center / contain;
}
.icon-custom::before {
position: absolute;
content: 'ON';
color: white;
top: 16px;
left: 10px;
z-index: 1;
font-size: 8px;
}
.all-custom {
height: 20px;
width: 40px;
--track-background: #fff;
--track-background-checked: #0366a6;
--handle-background: #bbbbbb;
--handle-width: 20px;
--handle-height: 20px;
--handle-spacing: 0;
--handle-box-shadow: none;
border: 2px solid #bbb;
border-radius: 16px;
}
.all-custom.toggle-checked {
border-color: #0366a6;
--handle-box-shadow: 0 3px 12px rgba(0, 0, 0, 0.16), 0 3px 1px rgba(0, 0, 0, 0.1);
}
.custom-overflow {
overflow: visible;
contain: none;
--track-background-checked: #d6ffd6;
--handle-box-shadow: 0 3px 12px rgba(255, 0, 0, 0.6), 0 3px 1px rgba(50, 70, 255, 0.6);
}
.ios.custom-spacing {
--handle-height: 100%;
--handle-width: 32px;
--handle-spacing: 0;
}
.md.custom-spacing {
--handle-height: 100%;
--handle-width: 14px;
--handle-spacing: 0;
}
</style>
<script>
const smallToggles = document.querySelectorAll('.dynamic-small');
const largeToggles = document.querySelectorAll('.dynamic-large');
function toggleDynamic() {
for (var i = 0; i < smallToggles.length; i++) {
const toggle = smallToggles[i];
const hasClass = toggle.classList.contains('height-small');
if (hasClass) {
toggle.classList.remove('height-small');
toggle.classList.remove('width-small');
} else {
toggle.classList.add('height-small');
toggle.classList.add('width-small');
}
}
for (var i = 0; i < largeToggles.length; i++) {
const toggle = largeToggles[i];
const hasClass = toggle.classList.contains('height-large');
if (hasClass) {
toggle.classList.remove('height-large');
toggle.classList.remove('width-large');
} else {
toggle.classList.add('height-large');
toggle.classList.add('width-large');
}
}
}
</script>
</body>
</html>

View File

@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' width='512' height='512' viewBox='0 0 512 512'><title>ionicons-v5-p</title><path d='M378,108a191.41,191.41,0,0,1,70,148c0,106-86,192-192,192S64,362,64,256a192,192,0,0,1,69-148' style='fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px'/><line x1='256' y1='64' x2='256' y2='256' style='fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px'/></svg>

Before

Width:  |  Height:  |  Size: 448 B

View File

@ -1,14 +0,0 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs().forEach(({ title, screenshot, config }) => {
test.describe(title('toggle: sizes'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/toggle/test/legacy/sizes`, config);
await page.setIonViewport();
await expect(page).toHaveScreenshot(screenshot(`toggle-sizes-diff`));
});
});
});

View File

@ -172,8 +172,8 @@
}
.icon-custom {
--handle-background-checked: #fff url(/src/components/toggle/test/legacy/sizes/power-outline.svg) no-repeat
center / contain;
--handle-background-checked: #fff url(/src/components/toggle/test/sizes/power-outline.svg) no-repeat center /
contain;
}
.icon-custom::before {

View File

@ -19,15 +19,6 @@
--handle-transition: #{$toggle-ios-transition};
}
:host(.legacy-toggle) {
width: $toggle-ios-width;
height: $toggle-ios-height;
contain: strict;
overflow: hidden;
}
// Toggle Native Wrapper
// ----------------------------------------------------------------
@ -180,25 +171,3 @@
:host(.toggle-disabled) {
opacity: $toggle-ios-disabled-opacity;
}
// iOS Toggle Within An Item
// ----------------------------------------------------------
:host(.in-item.legacy-toggle) {
@include margin($toggle-ios-media-margin);
@include padding(
$toggle-ios-item-end-padding-top,
$toggle-ios-item-end-padding-end,
$toggle-ios-item-end-padding-bottom,
$toggle-ios-item-end-padding-start
);
}
:host(.in-item.legacy-toggle[slot="start"]) {
@include padding(
$toggle-ios-item-start-padding-top,
$toggle-ios-item-start-padding-end,
$toggle-ios-item-start-padding-bottom,
$toggle-ios-item-start-padding-start
);
}

View File

@ -37,9 +37,6 @@ $toggle-ios-handle-box-shadow: 0 3px 4px rgba(0, 0, 0, .06), 0 3px 8px r
/// @prop - Background color of the toggle handle
$toggle-ios-handle-background-color: #ffffff !default;
/// @prop - Margin of the toggle handle
$toggle-ios-media-margin: 0 !default;
/// @prop - Transition duration of the toggle icon
$toggle-ios-transition-duration: 300ms !default;
@ -49,29 +46,5 @@ $toggle-ios-transition: transform $toggle-ios-transition-duration
/// @prop - Opacity of the disabled toggle
$toggle-ios-disabled-opacity: .3 !default;
/// @prop - Padding top of the toggle positioned on the start in an item
$toggle-ios-item-start-padding-top: 6px !default;
/// @prop - Padding end of the toggle positioned on the start in an item
$toggle-ios-item-start-padding-end: 16px !default;
/// @prop - Padding bottom of the toggle positioned on the start in an item
$toggle-ios-item-start-padding-bottom: 5px !default;
/// @prop - Padding start of the toggle positioned on the start in an item
$toggle-ios-item-start-padding-start: 0 !default;
/// @prop - Padding top of the toggle positioned on the end in an item
$toggle-ios-item-end-padding-top: 6px !default;
/// @prop - Padding end of the toggle positioned on the end in an item
$toggle-ios-item-end-padding-end: 0 !default;
/// @prop - Padding bottom of the toggle positioned on the end in an item
$toggle-ios-item-end-padding-bottom: 5px !default;
/// @prop - Padding start of the toggle positioned on the end in an item
$toggle-ios-item-end-padding-start: $item-ios-padding-start !default;
/// @prop - The text color of the on/off labels when the toggle is checked
$toggle-ios-on-off-label-checked-color: #fff !default;

View File

@ -19,14 +19,6 @@
--handle-transition: #{$toggle-md-transition};
}
:host(.legacy-toggle) {
@include padding($toggle-md-padding-top, $toggle-md-padding-end, $toggle-md-padding-bottom, $toggle-md-padding-start);
width: $toggle-md-track-width;
height: $toggle-md-track-height;
contain: strict;
}
// Toggle Native Wrapper
// ----------------------------------------------------------------
@ -84,32 +76,3 @@
:host(.toggle-disabled) {
opacity: $toggle-md-disabled-opacity;
}
// Material Design Toggle Within An Item
// ----------------------------------------------------------
:host(.in-item.legacy-toggle) {
@include margin(
$toggle-md-media-margin-top,
$toggle-md-media-margin-end,
$toggle-md-media-margin-bottom,
$toggle-md-media-margin-start
);
@include padding(
$toggle-md-item-end-padding-top,
$toggle-md-item-end-padding-end,
$toggle-md-item-end-padding-bottom,
$toggle-md-item-end-padding-start
);
cursor: pointer;
}
:host(.in-item.legacy-toggle[slot="start"]) {
@include padding(
$toggle-md-item-start-padding-top,
$toggle-md-item-start-padding-end,
$toggle-md-item-start-padding-bottom,
$toggle-md-item-start-padding-start
);
}

View File

@ -34,18 +34,6 @@ $toggle-md-handle-box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .
/// @prop - Background color of the toggle handle
$toggle-md-handle-background-color-off: #ffffff !default;
/// @prop - Margin top of the toggle
$toggle-md-media-margin-top: 0 !default;
/// @prop - Margin end of the toggle
$toggle-md-media-margin-end: $toggle-md-media-margin-top !default;
/// @prop - Margin bottom of the toggle
$toggle-md-media-margin-bottom: $toggle-md-media-margin-top !default;
/// @prop - Margin start of the toggle
$toggle-md-media-margin-start: $toggle-md-media-margin-end !default;
/// @prop - Transition duration of the toggle icon
$toggle-md-transition-duration: 160ms !default;
@ -55,42 +43,6 @@ $toggle-md-transition: transform $toggle-md-transitio
/// @prop - Opacity of the disabled toggle
$toggle-md-disabled-opacity: $form-control-md-disabled-opacity !default;
/// @prop - Padding top of standalone toggle
$toggle-md-padding-top: 12px !default;
/// @prop - Padding end of standalone toggle
$toggle-md-padding-end: $toggle-md-padding-top !default;
/// @prop - Padding bottom of standalone toggle
$toggle-md-padding-bottom: $toggle-md-padding-top !default;
/// @prop - Padding start of standalone toggle
$toggle-md-padding-start: $toggle-md-padding-end !default;
/// @prop - Padding top of the toggle positioned on the start in an item
$toggle-md-item-start-padding-top: 12px !default;
/// @prop - Padding end of the toggle positioned on the start in an item
$toggle-md-item-start-padding-end: 18px !default;
/// @prop - Padding bottom of the toggle positioned on the start in an item
$toggle-md-item-start-padding-bottom: 12px !default;
/// @prop - Padding start the toggle positioned on the start in an item
$toggle-md-item-start-padding-start: 2px !default;
/// @prop - Padding top of the toggle positioned on the end in an item
$toggle-md-item-end-padding-top: 12px !default;
/// @prop - Padding end of the toggle positioned on the end in an item
$toggle-md-item-end-padding-end: 0 !default;
/// @prop - Padding bottom of the toggle positioned on the end in an item
$toggle-md-item-end-padding-bottom: 12px !default;
/// @prop - Padding start of the toggle positioned on the end in an item
$toggle-md-item-end-padding-start: $item-md-padding-start !default;
/// @prop - The text color of the on/off labels
$toggle-md-on-off-label-color: #000 !default;

View File

@ -38,7 +38,7 @@
z-index: $z-index-item-input;
}
:host(.in-item:not(.legacy-toggle)) {
:host(.in-item) {
width: 100%;
height: 100%;
}
@ -49,17 +49,11 @@
* toolbar which is why we do not
* limit the below behavior to just ion-item.
*/
:host([slot="start"]:not(.legacy-toggle)),
:host([slot="end"]:not(.legacy-toggle)) {
:host([slot="start"]),
:host([slot="end"]) {
width: auto;
}
:host(.legacy-toggle) {
contain: content;
touch-action: none;
}
:host(.ion-focused) input {
border: 2px solid #5e9ed6;
}
@ -68,18 +62,6 @@
pointer-events: none;
}
:host(.legacy-toggle) label {
@include input-cover();
display: flex;
align-items: center;
opacity: 0;
pointer-events: none;
}
input {
@include visually-hidden();
}
@ -112,7 +94,7 @@ input {
overflow: hidden;
}
:host(.in-item:not(.legacy-toggle)) .label-text-wrapper {
:host(.in-item) .label-text-wrapper {
@include margin($toggle-item-label-margin-top, null, $toggle-item-label-margin-bottom, null);
}

View File

@ -1,10 +1,7 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, State, Watch, h } from '@stencil/core';
import type { LegacyFormController } from '@utils/forms';
import { createLegacyFormController } from '@utils/forms';
import { getAriaLabel, renderHiddenInput, inheritAriaAttributes } from '@utils/helpers';
import { renderHiddenInput, inheritAriaAttributes } from '@utils/helpers';
import type { Attributes } from '@utils/helpers';
import { printIonWarning } from '@utils/logging';
import { hapticSelection } from '@utils/native/haptic';
import { isRTL } from '@utils/rtl';
import { createColorClasses, hostContext } from '@utils/theme';
@ -12,7 +9,7 @@ import { checkmarkOutline, removeOutline, ellipseOutline } from 'ionicons/icons'
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type { Color, Gesture, GestureDetail, Mode, StyleEventDetail } from '../../interface';
import type { Color, Gesture, GestureDetail, Mode } from '../../interface';
import type { ToggleChangeEventDetail } from './toggle-interface';
@ -38,14 +35,10 @@ export class Toggle implements ComponentInterface {
private gesture?: Gesture;
private focusEl?: HTMLElement;
private lastDrag = 0;
private legacyFormController!: LegacyFormController;
private inheritedAttributes: Attributes = {};
private toggleTrack?: HTMLElement;
private didLoad = false;
// This flag ensures we log the deprecation warning at most once.
private hasLoggedDeprecationWarning = false;
@Element() el!: HTMLIonToggleElement;
@State() activated = false;
@ -95,17 +88,6 @@ export class Toggle implements ComponentInterface {
*/
@Prop() labelPlacement: 'start' | 'end' | 'fixed' | 'stacked' = 'start';
/**
* Set the `legacy` property to `true` to forcibly use the legacy form control markup.
* Ionic will only opt components in to the modern form markup when they are
* using either the `aria-label` attribute or the default slot that contains
* the label text. As a result, the `legacy` property should only be used as
* an escape hatch when you want to avoid this automatic opt-in behavior.
* Note that this property will be removed in an upcoming major release
* of Ionic, and all form components will be opted-in to using the modern form markup.
*/
@Prop() legacy?: boolean;
/**
* How to pack the label and toggle within a line.
* `"start"`: The label and toggle will appear on the left in LTR and
@ -140,15 +122,8 @@ export class Toggle implements ComponentInterface {
*/
@Event() ionBlur!: EventEmitter<void>;
/**
* Emitted when the styles change.
* @internal
*/
@Event() ionStyle!: EventEmitter<StyleEventDetail>;
@Watch('disabled')
disabledChanged() {
this.emitStyle();
if (this.gesture) {
this.gesture.enable(!this.disabled);
}
@ -167,8 +142,6 @@ export class Toggle implements ComponentInterface {
}
async connectedCallback() {
this.legacyFormController = createLegacyFormController(this.el);
/**
* If we have not yet rendered
* ion-toggle, then toggleTrack is not defined.
@ -211,23 +184,9 @@ export class Toggle implements ComponentInterface {
}
componentWillLoad() {
this.emitStyle();
if (!this.legacyFormController.hasLegacyControl()) {
this.inheritedAttributes = {
...inheritAriaAttributes(this.el),
};
}
}
private emitStyle() {
if (this.legacyFormController.hasLegacyControl()) {
this.ionStyle.emit({
'interactive-disabled': this.disabled,
// TODO(FW-2990): remove this
legacy: !!this.legacy,
});
}
this.inheritedAttributes = {
...inheritAriaAttributes(this.el),
};
}
private onStart() {
@ -328,12 +287,6 @@ export class Toggle implements ComponentInterface {
}
render() {
const { legacyFormController } = this;
return legacyFormController.hasLegacyControl() ? this.renderLegacyToggle() : this.renderToggle();
}
private renderToggle() {
const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name, alignment } = this;
const mode = getIonMode(this);
@ -387,72 +340,6 @@ export class Toggle implements ComponentInterface {
</Host>
);
}
private renderLegacyToggle() {
if (!this.hasLoggedDeprecationWarning) {
printIonWarning(
`ion-toggle now requires providing a label with either the default slot or the "aria-label" attribute. To migrate, remove any usage of "ion-label" and pass the label text to either the component or the "aria-label" attribute.
Example: <ion-toggle>Email</ion-toggle>
Example with aria-label: <ion-toggle aria-label="Email"></ion-toggle>
Developers can use the "legacy" property to continue using the legacy form markup. This property will be removed in an upcoming major release of Ionic where this form control will use the modern form markup.`,
this.el
);
if (this.legacy) {
printIonWarning(
`ion-toggle is being used with the "legacy" property enabled which will forcibly enable the legacy form markup. This property will be removed in an upcoming major release of Ionic where this form control will use the modern form markup.
Developers can dismiss this warning by removing their usage of the "legacy" property and using the new toggle syntax.`,
this.el
);
}
this.hasLoggedDeprecationWarning = true;
}
const { activated, color, checked, disabled, el, inputId, name } = this;
const mode = getIonMode(this);
const { label, labelId, labelText } = getAriaLabel(el, inputId);
const value = this.getValue();
const rtl = isRTL(el) ? 'rtl' : 'ltr';
renderHiddenInput(true, el, name, checked ? value : '', disabled);
return (
<Host
onClick={this.onClick}
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
class={createColorClasses(color, {
[mode]: true,
'in-item': hostContext('ion-item', el),
'toggle-activated': activated,
'toggle-checked': checked,
'toggle-disabled': disabled,
'legacy-toggle': true,
interactive: true,
[`toggle-${rtl}`]: true,
})}
>
{this.renderToggleControl()}
<label htmlFor={inputId}>{labelText}</label>
<input
type="checkbox"
role="switch"
aria-checked={`${checked}`}
disabled={disabled}
id={inputId}
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
ref={(focusEl) => (this.focusEl = focusEl)}
/>
</Host>
);
}
}
const shouldToggle = (rtl: boolean, checked: boolean, deltaX: number, margin: number): boolean => {