Merge remote-tracking branch 'origin/main' into chore/sync-with-main-5-3
@ -75,8 +75,14 @@ input {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:host(.in-item[slot="start"]:not(.legacy-radio)),
|
||||
:host(.in-item[slot="end"]:not(.legacy-radio)) {
|
||||
/**
|
||||
* Radio can be slotted
|
||||
* in components such as item and
|
||||
* toolbar which is why we do not
|
||||
* limit the below behavior to just ion-item.
|
||||
*/
|
||||
:host([slot="start"]:not(.legacy-radio)),
|
||||
:host([slot="end"]:not(.legacy-radio)) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
||||
@ -1,41 +1,97 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('radio: a11y', () => {
|
||||
test.beforeEach(async ({ skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('md');
|
||||
/**
|
||||
* This behavior does not vary across modes/directions.
|
||||
*/
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('radio: a11y'), () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/radio/test/a11y`, config);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/radio/test/a11y`);
|
||||
// TODO: FW-4155 - Enable tests once tab behavior is fixed for modern syntax.
|
||||
test.describe.skip(title('radio: keyboard navigation'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-app>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-radio-group id="first-group" value="huey">
|
||||
<ion-item>
|
||||
<ion-radio value="huey">Huey</ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-radio value="dewey">Dewey</ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-radio value="fooey" disabled>Fooey</ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-radio value="louie">Louie</ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</ion-list>
|
||||
<ion-list>
|
||||
<ion-radio-group id="second-group" value="huey">
|
||||
<ion-item>
|
||||
<ion-radio value="huey">Huey</ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-radio value="dewey">Dewey</ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-radio value="fooey" disabled>Fooey</ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-radio value="louie">Louie</ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
`,
|
||||
config
|
||||
);
|
||||
});
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
test('tabbing should switch between radio groups', async ({ page, pageUtils }) => {
|
||||
const firstGroupRadios = page.locator('#first-group ion-radio');
|
||||
const secondGroupRadios = page.locator('#second-group ion-radio');
|
||||
|
||||
// TODO FW-3747
|
||||
test.skip('using arrow keys should move between enabled radios within group', async ({ page, browserName }) => {
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
await page.goto(`/src/components/radio/test/a11y`);
|
||||
await pageUtils.pressKeys('Tab');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
const firstGroupRadios = page.locator('#first-group ion-radio');
|
||||
await pageUtils.pressKeys('Tab');
|
||||
await expect(secondGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
await pageUtils.pressKeys('shift+Tab');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
});
|
||||
test('using arrow keys should move between enabled radios within group', async ({ page, pageUtils }) => {
|
||||
const firstGroupRadios = page.locator('#first-group ion-radio');
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(1)).toBeFocused();
|
||||
await pageUtils.pressKeys('Tab');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
// firstGroupRadios.nth(2) is disabled so it should not receive focus.
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(1)).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
// firstGroupRadios.nth(2) is disabled so it should not receive focus.
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
@ -1,29 +1,34 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('radio: color', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
test('should apply color when checked', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio color="danger" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('radio: color'), () => {
|
||||
test('should apply color when checked', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio color="danger" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(`radio-color-checked-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-color-checked`));
|
||||
});
|
||||
|
||||
test('should not apply color when unchecked', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group>
|
||||
<ion-radio color="danger" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
test('should not apply color when unchecked', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group>
|
||||
<ion-radio color="danger" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(`radio-color-unchecked-${page.getSnapshotSettings()}.png`);
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-color-unchecked`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,31 +1,39 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('radio: item', () => {
|
||||
test('should render correctly in list', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-list>
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-radio>Enable Notifications</ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</ion-list>
|
||||
`);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(`toggle-list-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should render correctly in inset list', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-list inset="true">
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-radio>Enable Notifications</ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</ion-list>
|
||||
`);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(`radio-inset-list-${page.getSnapshotSettings()}.png`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('radio: item'), () => {
|
||||
test('should render correctly in list', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-list>
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-radio>Enable Notifications</ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</ion-list>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(screenshot(`toggle-list`));
|
||||
});
|
||||
test('should render correctly in inset list', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-list inset="true">
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-radio>Enable Notifications</ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</ion-list>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(screenshot(`radio-inset-list`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* By default ion-radio only takes up
|
||||
@ -9,119 +9,133 @@ import { test } from '@utils/test/playwright';
|
||||
* we set the width of the radio so we can
|
||||
* see the justification results.
|
||||
*/
|
||||
test.describe('radio: label', () => {
|
||||
test.describe('radio: start placement', () => {
|
||||
test('should render a start justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="start" justify="start" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-start-justify-start-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('radio: label'), () => {
|
||||
test.describe('radio: start placement', () => {
|
||||
test('should render a start justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="start" justify="start" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-start-justify-start`));
|
||||
});
|
||||
test('should render an end justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="start" justify="end" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-start-justify-end`));
|
||||
});
|
||||
test('should render a space between justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="start" justify="space-between" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-start-justify-space-between`));
|
||||
});
|
||||
});
|
||||
test('should render an end justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="start" justify="end" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-start-justify-end-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
test.describe('radio: end placement', () => {
|
||||
test('should render a start justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="end" justify="start" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-end-justify-start`));
|
||||
});
|
||||
test('should render an end justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="end" justify="end" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-end-justify-end`));
|
||||
});
|
||||
test('should render a space between justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="end" justify="space-between" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-end-justify-space-between`));
|
||||
});
|
||||
});
|
||||
test('should render a space between justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="start" justify="space-between" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-start-justify-space-between-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
});
|
||||
test.describe('radio: fixed placement', () => {
|
||||
test('should render a start justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="fixed" justify="start" style="width: 200px" value="1">This is a long label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
test.describe('radio: end placement', () => {
|
||||
test('should render a start justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="end" justify="start" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-fixed-justify-start`));
|
||||
});
|
||||
test('should render an end justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="fixed" justify="end" style="width: 200px" value="1">This is a long label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-end-justify-start-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
test('should render an end justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="end" justify="end" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-fixed-justify-end`));
|
||||
});
|
||||
test('should render a space between justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="fixed" justify="space-between" style="width: 200px" value="1">This is a long label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(`radio-label-end-justify-end-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should render a space between justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="end" justify="space-between" style="width: 200px" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-end-justify-space-between-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('radio: fixed placement', () => {
|
||||
test('should render a start justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="fixed" justify="start" style="width: 200px" value="1">This is a long label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-fixed-justify-start-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
test('should render an end justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="fixed" justify="end" style="width: 200px" value="1">This is a long label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-fixed-justify-end-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
test('should render a space between justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio label-placement="fixed" justify="space-between" style="width: 200px" value="1">This is a long label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(
|
||||
`radio-label-fixed-justify-space-between-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-fixed-justify-space-between`));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,47 +1,46 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
// TODO FW-3747
|
||||
test.describe.skip('radio: a11y', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
test('tabbing should switch between radio groups', async ({ page, browserName }) => {
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
await page.goto(`/src/components/radio/test/legacy/a11y`);
|
||||
/**
|
||||
* This behavior does not vary across modes/directions.
|
||||
*/
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('radio: a11y'), () => {
|
||||
test('tabbing should switch between radio groups', async ({ page, pageUtils }) => {
|
||||
await page.goto(`/src/components/radio/test/legacy/a11y`, config);
|
||||
|
||||
const firstGroupRadios = page.locator('#first-group ion-radio');
|
||||
const secondGroupRadios = page.locator('#second-group ion-radio');
|
||||
const firstGroupRadios = page.locator('#first-group ion-radio');
|
||||
const secondGroupRadios = page.locator('#second-group ion-radio');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
await pageUtils.pressKeys('Tab');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(secondGroupRadios.nth(0)).toBeFocused();
|
||||
await pageUtils.pressKeys('Tab');
|
||||
await expect(secondGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
await page.keyboard.press(`Shift+${tabKey}`);
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
});
|
||||
test('using arrow keys should move between enabled radios within group', async ({ page, browserName }) => {
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
await page.goto(`/src/components/radio/test/legacy/a11y`);
|
||||
await pageUtils.pressKeys('shift+Tab');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
});
|
||||
test('using arrow keys should move between enabled radios within group', async ({ page, pageUtils }) => {
|
||||
await page.goto(`/src/components/radio/test/legacy/a11y`, config);
|
||||
|
||||
const firstGroupRadios = page.locator('#first-group ion-radio');
|
||||
const firstGroupRadios = page.locator('#first-group ion-radio');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
await pageUtils.pressKeys('Tab');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(1)).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(1)).toBeFocused();
|
||||
|
||||
// firstGroupRadios.nth(2) is disabled so it should not receive focus.
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
// firstGroupRadios.nth(2) is disabled so it should not receive focus.
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(firstGroupRadios.nth(0)).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(firstGroupRadios.nth(3)).toBeFocused();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,137 +1,156 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('radio: rendering', () => {
|
||||
test('should correctly render an unchecked radio group', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('radio: rendering'), () => {
|
||||
test('should correctly render an unchecked radio group', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Sausage</ion-label>
|
||||
<ion-radio slot="start" value="sausage"></ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Sausage</ion-label>
|
||||
<ion-radio slot="start" value="sausage"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Pineapple</ion-label>
|
||||
<ion-radio slot="start" value="pineapple"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
<ion-item>
|
||||
<ion-label>Pineapple</ion-label>
|
||||
<ion-radio slot="start" value="pineapple"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
await expect(radioGroup).toHaveScreenshot(`radio-group-unchecked-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should correctly render a checked radio group', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="sausage">
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
await expect(radioGroup).toHaveScreenshot(screenshot(`radio-group-unchecked`));
|
||||
});
|
||||
test('should correctly render a checked radio group', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="sausage">
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Sausage</ion-label>
|
||||
<ion-radio slot="start" value="sausage"></ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Sausage</ion-label>
|
||||
<ion-radio slot="start" value="sausage"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Pineapple</ion-label>
|
||||
<ion-radio slot="start" value="pineapple"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
<ion-item>
|
||||
<ion-label>Pineapple</ion-label>
|
||||
<ion-radio slot="start" value="pineapple"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
await expect(radioGroup).toHaveScreenshot(`radio-group-checked-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should allow shadow parts to be styled', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="sausage">
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
await expect(radioGroup).toHaveScreenshot(screenshot(`radio-group-checked`));
|
||||
});
|
||||
test('should allow shadow parts to be styled', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="sausage">
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Sausage</ion-label>
|
||||
<ion-radio slot="start" value="sausage"></ion-radio>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Sausage</ion-label>
|
||||
<ion-radio slot="start" value="sausage"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Pineapple</ion-label>
|
||||
<ion-radio slot="start" value="pineapple"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-label>Pineapple</ion-label>
|
||||
<ion-radio slot="start" value="pineapple"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
|
||||
<style>
|
||||
ion-radio::part(container) {
|
||||
background: rgba(255, 0, 0, 0.3);
|
||||
border-color: darkred;
|
||||
}
|
||||
<style>
|
||||
ion-radio::part(container) {
|
||||
background: rgba(255, 0, 0, 0.3);
|
||||
border-color: darkred;
|
||||
}
|
||||
|
||||
ion-radio::part(mark) {
|
||||
background: hotpink;
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
ion-radio::part(mark) {
|
||||
background: hotpink;
|
||||
}
|
||||
</style>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
await expect(radioGroup).toHaveScreenshot(`radio-group-part-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should apply color correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio slot="start" value="pepperoni" color="success"></ion-radio>
|
||||
`);
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
await expect(radioGroup).toHaveScreenshot(screenshot(`radio-group-part`));
|
||||
});
|
||||
test('should apply color correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio slot="start" value="pepperoni" color="success"></ion-radio>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
await radio.click();
|
||||
await page.waitForChanges();
|
||||
await expect(radio).toHaveScreenshot(`radio-color-${page.getSnapshotSettings()}.png`, { animations: 'disabled' });
|
||||
const radio = page.locator('ion-radio');
|
||||
await radio.click();
|
||||
await page.waitForChanges();
|
||||
await expect(radio).toHaveScreenshot(screenshot(`radio-color`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('radio: interaction', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
test('radio should be checked when activated', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('radio: interaction'), () => {
|
||||
test('radio should be checked when activated', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-label>Pepperoni</ion-label>
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
const radio = page.locator('ion-radio');
|
||||
const radioGroup = page.locator('ion-radio-group');
|
||||
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', false);
|
||||
await expect(radioGroup).toHaveJSProperty('value', undefined);
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', false);
|
||||
await expect(radioGroup).toHaveJSProperty('value', undefined);
|
||||
|
||||
await radio.click();
|
||||
await page.waitForChanges();
|
||||
await radio.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', true);
|
||||
await expect(radioGroup).toHaveJSProperty('value', 'pepperoni');
|
||||
});
|
||||
test('radio should be checked when activated even without a radio group', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
`);
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', true);
|
||||
await expect(radioGroup).toHaveJSProperty('value', 'pepperoni');
|
||||
});
|
||||
test('radio should be checked when activated even without a radio group', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio slot="start" value="pepperoni"></ion-radio>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
const radio = page.locator('ion-radio');
|
||||
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', false);
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', false);
|
||||
|
||||
await radio.click();
|
||||
await page.waitForChanges();
|
||||
await radio.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', true);
|
||||
await expect(radio.locator('input')).toHaveJSProperty('checked', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,39 +1,48 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('radio: states', () => {
|
||||
test('should render disabled checked radio correctly', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('radio: states'), () => {
|
||||
test('should render disabled checked radio correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio disabled="true" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="1">
|
||||
<ion-radio disabled="true" value="1">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-checked-disabled`));
|
||||
});
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(`radio-checked-disabled-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should render checked radio correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group value="true">
|
||||
<ion-radio value="true">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
test('should render checked radio correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group value="true">
|
||||
<ion-radio value="true">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-checked`));
|
||||
});
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(`radio-checked-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should render unchecked radio correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-radio-group>
|
||||
<ion-radio value="true">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
test('should render unchecked radio correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-radio-group>
|
||||
<ion-radio value="true">Label</ion-radio>
|
||||
</ion-radio-group>
|
||||
`);
|
||||
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(`radio-unchecked-${page.getSnapshotSettings()}.png`);
|
||||
const radio = page.locator('ion-radio');
|
||||
expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-unchecked`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||