refactor(checkbox): remove legacy property and support for legacy syntax (#29043)
BREAKING CHANGE: The `legacy` property and support for the legacy syntax, which involved placing an `ion-checkbox` inside of an `ion-item` with an `ion-label`, have been removed from checkbox. For more information on migrating from the legacy checkbox syntax, refer to the [Checkbox documentation](https://ionicframework.com/docs/api/checkbox#migrating-from-legacy-checkbox-syntax).
@@ -18,6 +18,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
|
||||
- [Global Styles](#version-8x-global-styles)
|
||||
- [Components](#version-8x-components)
|
||||
- [Button](#version-8x-button)
|
||||
- [Checkbox](#version-8x-checkbox)
|
||||
- [Content](#version-8x-content)
|
||||
- [Datetime](#version-8x-datetime)
|
||||
- [Input](#version-8x-input)
|
||||
@@ -140,6 +141,10 @@ For more information on the dynamic font, refer to the [Dynamic Font Scaling doc
|
||||
|
||||
- Button text now wraps by default. If this behavior is not desired, add the `ion-text-nowrap` class from the [CSS Utilities](https://ionicframework.com/docs/layout/css-utilities).
|
||||
|
||||
<h4 id="version-8x-checkbox">Checkbox</h4>
|
||||
|
||||
The `legacy` property and support for the legacy syntax, which involved placing an `ion-checkbox` inside of an `ion-item` with an `ion-label`, have been removed. For more information on migrating from the legacy checkbox syntax, refer to the [Checkbox documentation](https://ionicframework.com/docs/api/checkbox#migrating-from-legacy-checkbox-syntax).
|
||||
|
||||
<h4 id="version-8x-content">Content</h4>
|
||||
|
||||
- Content no longer sets the `--background` custom property when the `.outer-content` class is set on the host.
|
||||
|
||||
@@ -297,7 +297,6 @@ ion-checkbox,prop,disabled,boolean,false,false,false
|
||||
ion-checkbox,prop,indeterminate,boolean,false,false,false
|
||||
ion-checkbox,prop,justify,"end" | "space-between" | "start",'space-between',false,false
|
||||
ion-checkbox,prop,labelPlacement,"end" | "fixed" | "stacked" | "start",'start',false,false
|
||||
ion-checkbox,prop,legacy,boolean | undefined,undefined,false,false
|
||||
ion-checkbox,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-checkbox,prop,name,string,this.inputId,false,false
|
||||
ion-checkbox,prop,value,any,'on',false,false
|
||||
|
||||
13
core/src/components.d.ts
vendored
@@ -630,10 +630,6 @@ export namespace Components {
|
||||
* Where to place the label relative to the checkbox. `"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.
|
||||
*/
|
||||
"labelPlacement": 'start' | 'end' | 'fixed' | 'stacked';
|
||||
/**
|
||||
* Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt checkboxes in to the modern form markup when they are using either the `aria-label` attribute or have text in the default slot. 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.
|
||||
*/
|
||||
"legacy"?: boolean;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
@@ -3639,7 +3635,6 @@ declare global {
|
||||
"ionChange": CheckboxChangeEventDetail;
|
||||
"ionFocus": void;
|
||||
"ionBlur": void;
|
||||
"ionStyle": StyleEventDetail;
|
||||
}
|
||||
interface HTMLIonCheckboxElement extends Components.IonCheckbox, HTMLStencilElement {
|
||||
addEventListener<K extends keyof HTMLIonCheckboxElementEventMap>(type: K, listener: (this: HTMLIonCheckboxElement, ev: IonCheckboxCustomEvent<HTMLIonCheckboxElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
@@ -5323,10 +5318,6 @@ declare namespace LocalJSX {
|
||||
* Where to place the label relative to the checkbox. `"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.
|
||||
*/
|
||||
"labelPlacement"?: 'start' | 'end' | 'fixed' | 'stacked';
|
||||
/**
|
||||
* Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt checkboxes in to the modern form markup when they are using either the `aria-label` attribute or have text in the default slot. 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.
|
||||
*/
|
||||
"legacy"?: boolean;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
@@ -5347,10 +5338,6 @@ declare namespace LocalJSX {
|
||||
* Emitted when the checkbox has focus.
|
||||
*/
|
||||
"onIonFocus"?: (event: IonCheckboxCustomEvent<void>) => void;
|
||||
/**
|
||||
* Emitted when the styles change.
|
||||
*/
|
||||
"onIonStyle"?: (event: IonCheckboxCustomEvent<StyleEventDetail>) => void;
|
||||
/**
|
||||
* The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
|
||||
*/
|
||||
|
||||
@@ -31,21 +31,3 @@
|
||||
:host(.checkbox-disabled) {
|
||||
opacity: $checkbox-ios-disabled-opacity;
|
||||
}
|
||||
|
||||
|
||||
// iOS Checkbox Within An Item
|
||||
// TODO(FW-3100): remove this
|
||||
// -----------------------------------------
|
||||
|
||||
:host(.in-item.legacy-checkbox) {
|
||||
// end position by default
|
||||
@include margin($checkbox-ios-item-end-margin-top, $checkbox-ios-item-end-margin-end, $checkbox-ios-item-end-margin-bottom, $checkbox-ios-item-end-margin-start);
|
||||
|
||||
display: block;
|
||||
|
||||
position: static;
|
||||
}
|
||||
|
||||
:host(.in-item.legacy-checkbox[slot="start"]) {
|
||||
@include margin($checkbox-ios-item-start-margin-top, $checkbox-ios-item-start-margin-end, $checkbox-ios-item-start-margin-bottom, $checkbox-ios-item-start-margin-start);
|
||||
}
|
||||
|
||||
@@ -26,29 +26,5 @@ $checkbox-ios-icon-border-radius: 50% !default;
|
||||
/// @prop - Opacity of the disabled checkbox
|
||||
$checkbox-ios-disabled-opacity: $form-control-ios-disabled-opacity !default;
|
||||
|
||||
/// @prop - Margin top of the left checkbox item
|
||||
$checkbox-ios-item-start-margin-top: 8px !default;
|
||||
|
||||
/// @prop - Margin end of the left checkbox item
|
||||
$checkbox-ios-item-start-margin-end: $item-ios-padding-end !default;
|
||||
|
||||
/// @prop - Margin bottom of the left checkbox item
|
||||
$checkbox-ios-item-start-margin-bottom: $checkbox-ios-item-start-margin-top !default;
|
||||
|
||||
/// @prop - Margin start of the left checkbox item
|
||||
$checkbox-ios-item-start-margin-start: 2px !default;
|
||||
|
||||
/// @prop - Margin top of the right checkbox item
|
||||
$checkbox-ios-item-end-margin-top: 10px !default;
|
||||
|
||||
/// @prop - Margin end of the right checkbox item
|
||||
$checkbox-ios-item-end-margin-end: 8px !default;
|
||||
|
||||
/// @prop - Margin bottom of the right checkbox item
|
||||
$checkbox-ios-item-end-margin-bottom: 9px !default;
|
||||
|
||||
/// @prop - Margin start of the right checkbox item
|
||||
$checkbox-ios-item-end-margin-start: 0 !default;
|
||||
|
||||
/// @prop - Checkmark width of the checkbox icon
|
||||
$checkbox-ios-icon-checkmark-width: 1.5px !default;
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
// opacity set by its spec, while the label
|
||||
// should match the other form controls
|
||||
|
||||
:host(.legacy-checkbox.checkbox-disabled),
|
||||
:host(.checkbox-disabled) .label-text-wrapper {
|
||||
opacity: $checkbox-md-disabled-opacity;
|
||||
}
|
||||
@@ -52,21 +51,3 @@
|
||||
:host(.checkbox-disabled) .native-wrapper {
|
||||
opacity: $checkbox-md-icon-disabled-opacity;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Checkbox Within An Item
|
||||
// TODO(FW-3100): remove this
|
||||
// --------------------------------------------------------
|
||||
|
||||
:host(.in-item.legacy-checkbox) {
|
||||
// end position by default
|
||||
@include margin($checkbox-md-item-end-margin-top, $checkbox-md-item-end-margin-end, $checkbox-md-item-end-margin-bottom, $checkbox-md-item-end-margin-start);
|
||||
|
||||
display: block;
|
||||
|
||||
position: static;
|
||||
}
|
||||
|
||||
:host(.in-item.legacy-checkbox[slot="start"]) {
|
||||
@include margin($checkbox-md-item-start-margin-top, $checkbox-md-item-start-margin-end, $checkbox-md-item-start-margin-bottom, $checkbox-md-item-start-margin-start);
|
||||
}
|
||||
|
||||
@@ -30,30 +30,6 @@ $checkbox-md-transition-duration: 180ms !default;
|
||||
/// @prop - Transition easing of the checkbox
|
||||
$checkbox-md-transition-easing: cubic-bezier(.4, 0, .2, 1) !default;
|
||||
|
||||
/// @prop - Margin top of the start checkbox item
|
||||
$checkbox-md-item-start-margin-top: 18px !default;
|
||||
|
||||
/// @prop - Margin end of the start checkbox item
|
||||
$checkbox-md-item-start-margin-end: 36px !default;
|
||||
|
||||
/// @prop - Margin bottom of the start checkbox item
|
||||
$checkbox-md-item-start-margin-bottom: $checkbox-md-item-start-margin-top !default;
|
||||
|
||||
/// @prop - Margin start of the start checkbox item
|
||||
$checkbox-md-item-start-margin-start: 4px !default;
|
||||
|
||||
/// @prop - Margin top of the end checkbox item
|
||||
$checkbox-md-item-end-margin-top: 18px !default;
|
||||
|
||||
/// @prop - Margin end of the end checkbox item
|
||||
$checkbox-md-item-end-margin-end: 0 !default;
|
||||
|
||||
/// @prop - Margin bottom of the end checkbox item
|
||||
$checkbox-md-item-end-margin-bottom: $checkbox-md-item-end-margin-top !default;
|
||||
|
||||
/// @prop - Margin start of the end checkbox item
|
||||
$checkbox-md-item-end-margin-start: 0 !default;
|
||||
|
||||
/// @prop - Opacity of the disabled checkbox
|
||||
/// This value is used because the checkbox color is set to
|
||||
/// `rgb(0, 0, 0, 0.60)` when enabled and we need it to be
|
||||
|
||||
@@ -48,34 +48,17 @@
|
||||
* toolbar which is why we do not
|
||||
* limit the below behavior to just ion-item.
|
||||
*/
|
||||
:host([slot="start"]:not(.legacy-checkbox)),
|
||||
:host([slot="end"]:not(.legacy-checkbox)) {
|
||||
:host([slot="start"]),
|
||||
:host([slot="end"]) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
:host(.legacy-checkbox) {
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
}
|
||||
|
||||
:host(.ion-color) {
|
||||
--checkbox-background-checked: #{current-color(base)};
|
||||
--border-color-checked: #{current-color(base)};
|
||||
--checkmark-color: #{current-color(contrast)};
|
||||
}
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
:host(.legacy-checkbox) label {
|
||||
@include input-cover();
|
||||
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
|
||||
@@ -96,7 +79,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:host(.in-item:not(.legacy-checkbox)) .label-text-wrapper {
|
||||
:host(.in-item) .label-text-wrapper {
|
||||
@include margin($checkbox-item-label-margin-top, null, $checkbox-item-label-margin-bottom, null);
|
||||
}
|
||||
|
||||
@@ -132,6 +115,9 @@ input {
|
||||
|
||||
position: relative;
|
||||
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
|
||||
transition: var(--transition);
|
||||
|
||||
border-width: var(--border-width);
|
||||
@@ -143,20 +129,6 @@ input {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
:host(.legacy-checkbox) .checkbox-icon {
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// TODO(FW-3100): merge this with other .checkbox-icon styles above
|
||||
:host(:not(.legacy-checkbox)) .checkbox-icon {
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
}
|
||||
|
||||
.checkbox-icon path {
|
||||
fill: none;
|
||||
stroke: var(--checkmark-color);
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
||||
import { Component, Element, Event, Host, Prop, Watch, h } from '@stencil/core';
|
||||
import type { LegacyFormController } from '@utils/forms';
|
||||
import { createLegacyFormController } from '@utils/forms';
|
||||
import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
|
||||
import type { Attributes } from '@utils/helpers';
|
||||
import { getAriaLabel, inheritAriaAttributes, renderHiddenInput } from '@utils/helpers';
|
||||
import { printIonWarning } from '@utils/logging';
|
||||
import { inheritAriaAttributes, renderHiddenInput } from '@utils/helpers';
|
||||
import { createColorClasses, hostContext } from '@utils/theme';
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import type { Color, Mode, StyleEventDetail } from '../../interface';
|
||||
import type { Color, Mode } from '../../interface';
|
||||
|
||||
import type { CheckboxChangeEventDetail } from './checkbox-interface';
|
||||
|
||||
@@ -32,13 +29,8 @@ import type { CheckboxChangeEventDetail } from './checkbox-interface';
|
||||
export class Checkbox implements ComponentInterface {
|
||||
private inputId = `ion-cb-${checkboxIds++}`;
|
||||
private focusEl?: HTMLElement;
|
||||
private legacyFormController!: LegacyFormController; // TODO(FW-3100): remove this
|
||||
private inheritedAttributes: Attributes = {};
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
// This flag ensures we log the deprecation warning at most once.
|
||||
private hasLoggedDeprecationWarning = false;
|
||||
|
||||
@Element() el!: HTMLIonCheckboxElement;
|
||||
|
||||
/**
|
||||
@@ -104,19 +96,6 @@ export class Checkbox implements ComponentInterface {
|
||||
*/
|
||||
@Prop() alignment: 'start' | 'center' = 'center';
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
/**
|
||||
* Set the `legacy` property to `true` to forcibly use the legacy form control markup.
|
||||
* Ionic will only opt checkboxes in to the modern form markup when they are
|
||||
* using either the `aria-label` attribute or have text in the default slot. 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;
|
||||
|
||||
/**
|
||||
* Emitted when the checked property has changed
|
||||
* as a result of a user action such as a click.
|
||||
@@ -135,46 +114,10 @@ export class Checkbox implements ComponentInterface {
|
||||
*/
|
||||
@Event() ionBlur!: EventEmitter<void>;
|
||||
|
||||
/**
|
||||
* Emitted when the styles change.
|
||||
* @internal
|
||||
*/
|
||||
@Event() ionStyle!: EventEmitter<StyleEventDetail>;
|
||||
|
||||
connectedCallback() {
|
||||
this.legacyFormController = createLegacyFormController(this.el); // TODO(FW-3100): remove this
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.emitStyle();
|
||||
|
||||
// TODO(FW-3100): remove check
|
||||
if (!this.legacyFormController.hasLegacyControl()) {
|
||||
this.inheritedAttributes = {
|
||||
...inheritAriaAttributes(this.el),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('checked')
|
||||
@Watch('disabled')
|
||||
protected styleChanged() {
|
||||
this.emitStyle();
|
||||
}
|
||||
|
||||
private emitStyle() {
|
||||
const style: StyleEventDetail = {
|
||||
'interactive-disabled': this.disabled,
|
||||
// TODO(FW-3100): remove this
|
||||
legacy: !!this.legacy,
|
||||
this.inheritedAttributes = {
|
||||
...inheritAriaAttributes(this.el),
|
||||
};
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
if (this.legacyFormController.hasLegacyControl()) {
|
||||
style['checkbox-checked'] = this.checked;
|
||||
}
|
||||
|
||||
this.ionStyle.emit(style);
|
||||
}
|
||||
|
||||
private setFocus() {
|
||||
@@ -221,14 +164,7 @@ export class Checkbox implements ComponentInterface {
|
||||
this.toggleChecked(ev);
|
||||
};
|
||||
|
||||
// TODO(FW-3100): run contents of renderCheckbox directly instead
|
||||
render() {
|
||||
const { legacyFormController } = this;
|
||||
|
||||
return legacyFormController.hasLegacyControl() ? this.renderLegacyCheckbox() : this.renderCheckbox();
|
||||
}
|
||||
|
||||
private renderCheckbox() {
|
||||
const {
|
||||
color,
|
||||
checked,
|
||||
@@ -299,72 +235,6 @@ export class Checkbox implements ComponentInterface {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
private renderLegacyCheckbox() {
|
||||
if (!this.hasLoggedDeprecationWarning) {
|
||||
printIonWarning(
|
||||
`ion-checkbox 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-checkbox>Label</ion-checkbox>
|
||||
Example with aria-label: <ion-checkbox aria-label="Label"></ion-checkbox>
|
||||
|
||||
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-checkbox 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 checkbox syntax.`,
|
||||
this.el
|
||||
);
|
||||
}
|
||||
|
||||
this.hasLoggedDeprecationWarning = true;
|
||||
}
|
||||
|
||||
const { color, checked, disabled, el, getSVGPath, indeterminate, inputId, name, value } = this;
|
||||
const mode = getIonMode(this);
|
||||
const { label, labelId, labelText } = getAriaLabel(el, inputId);
|
||||
const path = getSVGPath(mode, indeterminate);
|
||||
|
||||
renderHiddenInput(true, el, name, checked ? value : '', disabled);
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
class={createColorClasses(color, {
|
||||
[mode]: true,
|
||||
'in-item': hostContext('ion-item', el),
|
||||
'checkbox-checked': checked,
|
||||
'checkbox-disabled': disabled,
|
||||
'checkbox-indeterminate': indeterminate,
|
||||
'legacy-checkbox': true,
|
||||
interactive: true,
|
||||
})}
|
||||
onClick={this.onClick}
|
||||
>
|
||||
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container">
|
||||
{path}
|
||||
</svg>
|
||||
<label htmlFor={inputId}>{labelText}</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
id={inputId}
|
||||
onChange={this.toggleChecked}
|
||||
onFocus={() => this.onFocus()}
|
||||
onBlur={() => this.onBlur()}
|
||||
ref={(focusEl) => (this.focusEl = focusEl)}
|
||||
/>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
|
||||
private getSVGPath(mode: Mode, indeterminate: boolean): HTMLElement {
|
||||
let path = indeterminate ? (
|
||||
<path d="M6 12L18 12" part="mark" />
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: basic (legacy)'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/legacy/basic`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(screenshot(`checkbox-legacy-basic`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('checkbox: ionChange'), () => {
|
||||
test('should fire ionChange when interacting with checkbox', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await checkbox.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await checkbox.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should fire ionChange when interacting with checkbox in item', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-item>
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
</ion-item>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const item = page.locator('ion-item');
|
||||
|
||||
await item.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await item.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should not fire when programmatically setting a value', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await checkbox.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
|
||||
await expect(ionChange).not.toHaveReceivedEvent();
|
||||
});
|
||||
|
||||
test('clicking padded space within item should click the checkbox', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-item>
|
||||
<ion-checkbox legacy="true" value="my-checkbox"></ion-checkbox>
|
||||
</ion-item>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const itemNative = page.locator('.item-native');
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
|
||||
// Clicks the padded space within the item
|
||||
await itemNative.click({
|
||||
position: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
},
|
||||
});
|
||||
|
||||
expect(ionChange).toHaveReceivedEvent();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 30 KiB |
@@ -1,103 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Checkbox - 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>
|
||||
<style>
|
||||
ion-checkbox.checkbox-part::part(mark) {
|
||||
transform: scale(0.5);
|
||||
transform-origin: center;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Checkbox - Basic</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<ion-item>
|
||||
<ion-label>Unchecked by Default</ion-label>
|
||||
<ion-checkbox></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checked by Default</ion-label>
|
||||
<ion-checkbox checked></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Primary</ion-label>
|
||||
<ion-checkbox checked color="primary" slot="end"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Secondary, disabled</ion-label>
|
||||
<ion-checkbox disabled checked color="secondary"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Tertiary</ion-label>
|
||||
<ion-checkbox checked color="tertiary"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Success</ion-label>
|
||||
<ion-checkbox checked color="success"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Warning</ion-label>
|
||||
<ion-checkbox checked color="warning"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Dark, --size</ion-label>
|
||||
<ion-checkbox checked color="dark" style="--size: 100px"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Danger</ion-label>
|
||||
<ion-checkbox checked color="danger" slot="start"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Light</ion-label>
|
||||
<ion-checkbox checked color="light" slot="start"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Medium</ion-label>
|
||||
<ion-checkbox checked color="medium" slot="start"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checkmark width</ion-label>
|
||||
<ion-checkbox checked color="dark" slot="start" style="--checkmark-width: 7"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checkmark ::part</ion-label>
|
||||
<ion-checkbox checked color="dark" slot="start" class="checkbox-part"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled</ion-label>
|
||||
<ion-checkbox disabled></ion-checkbox>
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,13 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: indeterminate (legacy)'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/legacy/indeterminate`, config);
|
||||
|
||||
const content = page.locator('#checkboxes');
|
||||
await expect(content).toHaveScreenshot(screenshot(`checkbox-legacy-indeterminate`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -1,64 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Checkbox - Indeterminate</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>Checkbox - Indeterminate</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<div id="checkboxes">
|
||||
<ion-item>
|
||||
<ion-label>Indeterminate</ion-label>
|
||||
<ion-checkbox slot="end" indeterminate></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checked / Indeterminate</ion-label>
|
||||
<ion-checkbox slot="end" checked indeterminate></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-list-header>
|
||||
<ion-label> Colors </ion-label>
|
||||
</ion-list-header>
|
||||
<div class="ion-padding-start">
|
||||
<ion-checkbox indeterminate></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="secondary"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="success"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="warning"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="danger"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="dark"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="medium"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="light"></ion-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
var indeterminateCheckboxes = document.getElementsByClassName('indeterminate');
|
||||
|
||||
for (var i = 0; i < indeterminateCheckboxes.length; i++) {
|
||||
var checkbox = indeterminateCheckboxes[i];
|
||||
checkbox.indeterminate = true;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -39,44 +39,12 @@
|
||||
<ion-label>Disabled Item Anchor</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled checked> Disabled Checkbox </ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-range disabled value="10">
|
||||
<span slot="label">Disabled Range</span>
|
||||
</ion-range>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>Multiple Input Disabled Items</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox slot="start" aria-label="Checkbox"></ion-checkbox>
|
||||
<ion-label>Checkbox + Range</ion-label>
|
||||
<ion-range disabled value="45" aria-label="Range"></ion-range>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox aria-label="Checkbox" slot="start"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox aria-label="Checkbox" disabled slot="start"></ion-checkbox>
|
||||
<ion-label>Checkbox + Buttons</ion-label>
|
||||
<ion-button slot="end">Default</ion-button>
|
||||
<ion-button slot="end">Buttons</ion-button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox slot="start" disabled aria-label="Checkbox"></ion-checkbox>
|
||||
<ion-searchbar></ion-searchbar>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
|
||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 15 KiB |
@@ -38,46 +38,12 @@
|
||||
<ion-item disabled href="#">
|
||||
<ion-label>Disabled Item Anchor</ion-label>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Disabled Checkbox</ion-label>
|
||||
<ion-checkbox disabled checked slot="start" legacy="true"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Range</ion-label>
|
||||
<ion-range disabled value="10" legacy="true"></ion-range>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>Multiple Input Disabled Items</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled slot="start" legacy="true"></ion-checkbox>
|
||||
<ion-label>Checkbox</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox slot="start" legacy="true"></ion-checkbox>
|
||||
<ion-label>Checkbox + Range</ion-label>
|
||||
<ion-range disabled value="45"></ion-range>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled slot="start" legacy="true"></ion-checkbox>
|
||||
<ion-label>Checkbox + Buttons</ion-label>
|
||||
<ion-button slot="end">Default</ion-button>
|
||||
<ion-button slot="end">Buttons</ion-button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox slot="start" disabled legacy="true"></ion-checkbox>
|
||||
<ion-label>Disabled Checkbox</ion-label>
|
||||
<ion-searchbar></ion-searchbar>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 15 KiB |
@@ -507,14 +507,14 @@ export declare interface IonCardTitle extends Components.IonCardTitle {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-checkbox',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'value'],
|
||||
})
|
||||
export class IonCheckbox {
|
||||
protected el: HTMLElement;
|
||||
|
||||
@@ -23,7 +23,6 @@ const CHECKBOX_INPUTS = [
|
||||
'indeterminate',
|
||||
'justify',
|
||||
'labelPlacement',
|
||||
'legacy',
|
||||
'mode',
|
||||
'name',
|
||||
'value',
|
||||
|
||||
@@ -216,11 +216,9 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer<JSX.IonCheckbox, JSX.Io
|
||||
'labelPlacement',
|
||||
'justify',
|
||||
'alignment',
|
||||
'legacy',
|
||||
'ionChange',
|
||||
'ionFocus',
|
||||
'ionBlur',
|
||||
'ionStyle'
|
||||
'ionBlur'
|
||||
],
|
||||
'checked', 'ion-change');
|
||||
|
||||
|
||||