mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-09 08:09:32 +08:00
chore: sync with main
This commit is contained in:
@ -211,6 +211,10 @@ export class Checkbox implements ComponentInterface {
|
||||
};
|
||||
|
||||
private onClick = (ev: MouseEvent) => {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.toggleChecked(ev);
|
||||
};
|
||||
|
||||
|
||||
24
core/src/components/checkbox/test/checkbox.spec.ts
Normal file
24
core/src/components/checkbox/test/checkbox.spec.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Checkbox } from '../checkbox';
|
||||
|
||||
describe('ion-checkbox: disabled', () => {
|
||||
it('clicking disabled checkbox should not toggle checked state', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Checkbox],
|
||||
html: `
|
||||
<ion-checkbox disabled="true">Checkbox</ion-checkbox>
|
||||
`,
|
||||
});
|
||||
|
||||
const checkbox = page.body.querySelector('ion-checkbox');
|
||||
|
||||
expect(checkbox.checked).toBe(false);
|
||||
|
||||
checkbox.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(checkbox.checked).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -113,7 +113,7 @@ export class RadioGroup implements ComponentInterface {
|
||||
* using the `name` attribute.
|
||||
*/
|
||||
const selectedRadio = ev.target && (ev.target as HTMLElement).closest('ion-radio');
|
||||
if (selectedRadio) {
|
||||
if (selectedRadio && selectedRadio.disabled === false) {
|
||||
const currentValue = this.value;
|
||||
const newValue = selectedRadio.value;
|
||||
if (newValue !== currentValue) {
|
||||
|
||||
@ -200,7 +200,11 @@ export class Radio implements ComponentInterface {
|
||||
};
|
||||
|
||||
private onClick = () => {
|
||||
const { radioGroup, checked } = this;
|
||||
const { radioGroup, checked, disabled } = this;
|
||||
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The legacy control uses a native input inside
|
||||
|
||||
@ -31,3 +31,27 @@ describe('ion-radio', () => {
|
||||
expect(radio.classList.contains('radio-checked')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ion-radio: disabled', () => {
|
||||
it('clicking disabled radio should not set checked state', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Radio, RadioGroup],
|
||||
html: `
|
||||
<ion-radio-group>
|
||||
<ion-radio disabled="true" value="a">Radio</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
});
|
||||
|
||||
const radio = page.body.querySelector('ion-radio');
|
||||
const radioGroup = page.body.querySelector('ion-radio-group');
|
||||
|
||||
expect(radioGroup.value).toBe(undefined);
|
||||
|
||||
radio.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(radioGroup.value).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@ -316,29 +316,46 @@ export class Select implements ComponentInterface {
|
||||
|
||||
// focus selected option for popovers
|
||||
if (this.interface === 'popover') {
|
||||
let indexOfSelected = this.childOpts.map((o) => o.value).indexOf(this.value);
|
||||
indexOfSelected = indexOfSelected > -1 ? indexOfSelected : 0; // default to first option if nothing selected
|
||||
const selectedItem = overlay.querySelector<HTMLElement>(
|
||||
`.select-interface-option:nth-child(${indexOfSelected + 1})`
|
||||
);
|
||||
const indexOfSelected = this.childOpts.map((o) => o.value).indexOf(this.value);
|
||||
|
||||
if (selectedItem) {
|
||||
focusElement(selectedItem);
|
||||
if (indexOfSelected > -1) {
|
||||
const selectedItem = overlay.querySelector<HTMLElement>(
|
||||
`.select-interface-option:nth-child(${indexOfSelected + 1})`
|
||||
);
|
||||
|
||||
if (selectedItem) {
|
||||
focusElement(selectedItem);
|
||||
|
||||
/**
|
||||
* Browsers such as Firefox do not
|
||||
* correctly delegate focus when manually
|
||||
* focusing an element with delegatesFocus.
|
||||
* We work around this by manually focusing
|
||||
* the interactive element.
|
||||
* ion-radio and ion-checkbox are the only
|
||||
* elements that ion-select-popover uses, so
|
||||
* we only need to worry about those two components
|
||||
* when focusing.
|
||||
*/
|
||||
const interactiveEl = selectedItem.querySelector<HTMLElement>('ion-radio, ion-checkbox');
|
||||
if (interactiveEl) {
|
||||
interactiveEl.focus();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Browsers such as Firefox do not
|
||||
* correctly delegate focus when manually
|
||||
* focusing an element with delegatesFocus.
|
||||
* We work around this by manually focusing
|
||||
* the interactive element.
|
||||
* ion-radio and ion-checkbox are the only
|
||||
* elements that ion-select-popover uses, so
|
||||
* we only need to worry about those two components
|
||||
* when focusing.
|
||||
* If no value is set then focus the first enabled option.
|
||||
*/
|
||||
const interactiveEl = selectedItem.querySelector<HTMLElement>('ion-radio, ion-checkbox');
|
||||
if (interactiveEl) {
|
||||
interactiveEl.focus();
|
||||
const firstEnabledOption = overlay.querySelector<HTMLElement>(
|
||||
'ion-radio:not(.radio-disabled), ion-checkbox:not(.checkbox-disabled)'
|
||||
);
|
||||
if (firstEnabledOption) {
|
||||
focusElement(firstEnabledOption.closest('ion-item')!);
|
||||
|
||||
/**
|
||||
* Focus the option for the same reason as we do above.
|
||||
*/
|
||||
firstEnabledOption.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
core/src/components/select/test/disabled/select.e2e.ts
Normal file
36
core/src/components/select/test/disabled/select.e2e.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('select: disabled options'), () => {
|
||||
test('should not focus a disabled option when no value is set', async ({ page, skip }) => {
|
||||
// TODO (FW-2979)
|
||||
skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.');
|
||||
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/28284',
|
||||
});
|
||||
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-select interface="popover">
|
||||
<ion-select-option value="a" disabled="true">A</ion-select-option>
|
||||
<ion-select-option value="b">B</ion-select-option>
|
||||
</ion-select>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const select = page.locator('ion-select');
|
||||
const popover = page.locator('ion-popover');
|
||||
const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent');
|
||||
|
||||
await select.click();
|
||||
await ionPopoverDidPresent.next();
|
||||
|
||||
const popoverOption = popover.locator('.select-interface-option:nth-of-type(2) ion-radio');
|
||||
await expect(popoverOption).toBeFocused();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -41,3 +41,24 @@ describe('toggle', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ion-toggle: disabled', () => {
|
||||
it('clicking disabled toggle should not toggle checked state', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Toggle],
|
||||
html: `
|
||||
<ion-toggle disabled="true">Toggle</ion-toggle>
|
||||
`,
|
||||
});
|
||||
|
||||
const toggle = page.body.querySelector('ion-toggle');
|
||||
|
||||
expect(toggle.checked).toBe(false);
|
||||
|
||||
toggle.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(toggle.checked).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -259,6 +259,10 @@ export class Toggle implements ComponentInterface {
|
||||
}
|
||||
|
||||
private onClick = (ev: MouseEvent) => {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev.preventDefault();
|
||||
|
||||
if (this.lastDrag + 300 < Date.now()) {
|
||||
|
||||
Reference in New Issue
Block a user