merge release-6.1.6

Release 6.1.6
This commit is contained in:
Liam DeBeasi
2022-05-18 09:18:21 -04:00
committed by GitHub
398 changed files with 963 additions and 1577 deletions

View File

@ -140,12 +140,13 @@ Before creating a pull request, please read our requirements that explains the m
#### Modifying Documentation
1. Locate the `readme.md` file in the component's directory.
2. Modify the documentation **above** the line that says `<!-- Auto Generated Below -->` in this file.
3. To update any of the auto generated documentation below that line, make the relevant changes in the following places:
- `Usage`: update the component's usage examples in the component's `usage/` directory
- `Properties`, `Events`, or `Methods`: update the component's TypeScript file (`*.tsx`)
- `CSS Custom Properties`: update the component's main Sass file (`*.scss`)
- Changes to manually written documentation should be made in the `ionic-docs` repo: https://github.com/ionic-team/ionic-docs/tree/main/docs
- In your `ionic-docs` PR, please add a link back to the related `ionic-framework` PR.
- Changes to auto generated documentation should be made in the `ionic-framework` repo. These can be done in the same PR as your fix or feature.
- Run `npm run build` and commit all updates to ensure your changes make it into the generated documentation.
- `Usage`: update the component's usage examples in the component's `usage/` directory.
- `Properties`, `Events`, or `Methods`: update the component's TypeScript file (`*.tsx`).
- `CSS Custom Properties`: update the component's main Sass file (`*.scss`).
#### Modifying Tests

View File

@ -5,6 +5,7 @@
Please check if your PR fulfills the following requirements:
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features)
- Some docs updates need to be made in the `ionic-docs` repo, in a separate PR. See the [contributing guide](https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation) for details.
- [ ] Build (`npm run build`) was run locally and any changes were pushed
- [ ] Lint (`npm run lint`) has passed locally and any fixes were made for failures

View File

@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.1.6](https://github.com/ionic-team/ionic-framework/compare/v6.1.5...v6.1.6) (2022-05-18)
### Bug Fixes
* **loading:** spinner now respects —spinner-color ([#25261](https://github.com/ionic-team/ionic-framework/issues/25261)) ([65f4c74](https://github.com/ionic-team/ionic-framework/commit/65f4c742e7a5e5756f6f72dd853e38e885f90385)), closes [#25180](https://github.com/ionic-team/ionic-framework/issues/25180)
* **modal:** reset breakpoint to initial breakpoint on present ([#25246](https://github.com/ionic-team/ionic-framework/issues/25246)) ([2557bf3](https://github.com/ionic-team/ionic-framework/commit/2557bf3c3eed9e33e89e07a8d73489da8d81bee3)), closes [#25245](https://github.com/ionic-team/ionic-framework/issues/25245)
* **scroll-assist:** touch end events continue to bubble on inputs ([#25282](https://github.com/ionic-team/ionic-framework/issues/25282)) ([780f16d](https://github.com/ionic-team/ionic-framework/commit/780f16d9e04ee5aaaf91bb7c6ef15c72cc8aeb45)), closes [#25229](https://github.com/ionic-team/ionic-framework/issues/25229)
## [6.1.5](https://github.com/ionic-team/ionic-framework/compare/v6.1.4...v6.1.5) (2022-05-11)

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.1.6](https://github.com/ionic-team/ionic/compare/v6.1.5...v6.1.6) (2022-05-18)
**Note:** Version bump only for package @ionic/angular
## [6.1.5](https://github.com/ionic-team/ionic/compare/v6.1.4...v6.1.5) (2022-05-11)

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "6.1.5",
"version": "6.1.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "6.1.5",
"version": "6.1.6",
"license": "MIT",
"dependencies": {
"@ionic/core": "^6.1.5",
"@ionic/core": "^6.1.6",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
},
@ -1023,9 +1023,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.5.tgz",
"integrity": "sha512-YEpFheFDGV7lifbYNqctcPXRPqEOKiDy5KgSPriFzrrPUbwrv/tnXHZq7hFVPCMUYFBS9QJts4r5FOYTqAfvtw==",
"version": "6.1.6",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.6.tgz",
"integrity": "sha512-AsYGEHKVHy082RST3RBrIiOZX6VXNy6qYSYtf6TwOwmF/YV+/ASaB1TqVO/jP658ML106nNcjUM0fTkbm9UXRA==",
"dependencies": {
"@stencil/core": "^2.14.2",
"ionicons": "^6.0.0",
@ -7951,9 +7951,9 @@
"dev": true
},
"@ionic/core": {
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.5.tgz",
"integrity": "sha512-YEpFheFDGV7lifbYNqctcPXRPqEOKiDy5KgSPriFzrrPUbwrv/tnXHZq7hFVPCMUYFBS9QJts4r5FOYTqAfvtw==",
"version": "6.1.6",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.6.tgz",
"integrity": "sha512-AsYGEHKVHy082RST3RBrIiOZX6VXNy6qYSYtf6TwOwmF/YV+/ASaB1TqVO/jP658ML106nNcjUM0fTkbm9UXRA==",
"requires": {
"@stencil/core": "^2.14.2",
"ionicons": "^6.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "6.1.5",
"version": "6.1.6",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@ -44,7 +44,7 @@
"validate": "npm i && npm run lint && npm run test && npm run build"
},
"dependencies": {
"@ionic/core": "^6.1.5",
"@ionic/core": "^6.1.6",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
},

View File

@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.1.6](https://github.com/ionic-team/ionic/compare/v6.1.5...v6.1.6) (2022-05-18)
### Bug Fixes
* **loading:** spinner now respects —spinner-color ([#25261](https://github.com/ionic-team/ionic/issues/25261)) ([65f4c74](https://github.com/ionic-team/ionic/commit/65f4c742e7a5e5756f6f72dd853e38e885f90385)), closes [#25180](https://github.com/ionic-team/ionic/issues/25180)
* **modal:** reset breakpoint to initial breakpoint on present ([#25246](https://github.com/ionic-team/ionic/issues/25246)) ([2557bf3](https://github.com/ionic-team/ionic/commit/2557bf3c3eed9e33e89e07a8d73489da8d81bee3)), closes [#25245](https://github.com/ionic-team/ionic/issues/25245)
* **scroll-assist:** touch end events continue to bubble on inputs ([#25282](https://github.com/ionic-team/ionic/issues/25282)) ([780f16d](https://github.com/ionic-team/ionic/commit/780f16d9e04ee5aaaf91bb7c6ef15c72cc8aeb45)), closes [#25229](https://github.com/ionic-team/ionic/issues/25229)
## [6.1.5](https://github.com/ionic-team/ionic/compare/v6.1.4...v6.1.5) (2022-05-11)

View File

@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "6.1.5",
"version": "6.1.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "6.1.5",
"version": "6.1.6",
"license": "MIT",
"dependencies": {
"@stencil/core": "^2.14.2",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "6.1.5",
"version": "6.1.6",
"description": "Base components for Ionic",
"keywords": [
"ionic",

View File

@ -0,0 +1,12 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('breadcrumbs: basic', () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/breadcrumbs/test/basic`);
await page.setIonViewport();
expect(await page.screenshot()).toMatchSnapshot(`breadcrumb-diff-${page.getSnapshotSettings()}.png`);
});
});

View File

@ -1,19 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
test('breadcrumbs: basic', async () => {
const page = await newE2EPage({
url: '/src/components/breadcrumbs/test/basic?ionic:_testing=true',
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});
test('breadcrumbs: basic-rtl', async () => {
const page = await newE2EPage({
url: '/src/components/breadcrumbs/test/basic?ionic:_testing=true&rtl=true',
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@ -0,0 +1,12 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('breadcrumbs: collapsed', () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/breadcrumbs/test/collapsed`);
await page.setIonViewport();
expect(await page.screenshot()).toMatchSnapshot(`breadcrumb-collapsed-diff-${page.getSnapshotSettings()}.png`);
});
});

View File

@ -1,10 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
test('breadcrumbs: collapsed', async () => {
const page = await newE2EPage({
url: '/src/components/breadcrumbs/test/collapsed?ionic:_testing=true',
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@ -0,0 +1,12 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('breadcrumbs: standalone', () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/breadcrumbs/test/standalone`);
expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(
`breadcrumb-standalone-diff-${page.getSnapshotSettings()}.png`
);
});
});

View File

@ -1,10 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
test('breadcrumbs: standalone', async () => {
const page = await newE2EPage({
url: '/src/components/breadcrumbs/test/standalone?ionic:_testing=true',
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@ -67,11 +67,6 @@
z-index: $z-index-overlay-wrapper;
}
.spinner-lines,
.spinner-lines-small,
.spinner-bubbles,
.spinner-circles,
.spinner-crescent,
.spinner-dots {
ion-spinner {
color: var(--spinner-color);
}

View File

@ -56,7 +56,7 @@
<ion-button
id="custom-class-loading"
expand="block"
onclick="openLoading({duration: 5000,message: 'Please wait...', cssClass: 'custom-class custom-loading'})"
onclick="openLoading({duration: 5000,message: 'Please wait...', spinner: 'lines-sharp', cssClass: 'custom-class custom-loading'})"
>Show Loading with cssClass</ion-button
>
<ion-button id="backdrop-loading" expand="block" onclick="openLoading({backdropDismiss: true})"
@ -112,15 +112,15 @@
}
.custom-loading {
--background: rgba(37, 210, 223, 0.8);
--spinner-color: white;
--background: rgba(255, 255, 255, 0.8);
--spinner-color: #ea445a;
--height: 100%;
--max-height: auto;
--width: 100%;
--max-width: auto;
color: white;
color: #3478f6;
}
f {

View File

@ -39,7 +39,7 @@ test.describe('loading: basic', () => {
await runVisualTest(page, '#translucent-loading', 'translucent');
});
test('should open a loader with a custom class', async ({ page }) => {
await runVisualTest(page, '#no-spinner-loading', 'no-spinner');
await runVisualTest(page, '#custom-class-loading', 'custom-class');
});
test('should open a loader with html content', async ({ page }) => {
await runVisualTest(page, '#html-content-loading', 'html-content');

View File

@ -442,6 +442,12 @@ export class Modal implements ComponentInterface, OverlayInterface {
await this.currentTransition;
}
/**
* If the modal is presented multiple times (inline modals), we
* need to reset the current breakpoint to the initial breakpoint.
*/
this.currentBreakpoint = this.initialBreakpoint;
const data = {
...this.componentProps,
modal: this.el,
@ -668,7 +674,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
enteringAnimation.forEach((ani) => ani.destroy());
}
this.currentBreakpoint = undefined;
this.currentTransition = undefined;
this.animation = undefined;
return dismissed;

View File

@ -43,8 +43,6 @@ test.describe('modal: focus trapping', () => {
page,
browserName,
}) => {
// TODO FW-1436
test.skip(browserName === 'firefox', 'Focus is flaky on Firefox');
await page.goto('/src/components/modal/test/basic');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');

View File

@ -133,4 +133,65 @@ test.describe('sheet modal: setting the breakpoint', () => {
expect(ionBreakpointDidChange.events.length).toBe(1);
});
});
test('it should reset the breakpoint value on dismiss', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/25245',
});
await page.setContent(`
<ion-content>
<ion-button id="open-modal">Open</ion-button>
<ion-modal trigger="open-modal" initial-breakpoint="0.25">
<ion-content>
<ion-button id="dismiss" onclick="modal.dismiss();">Dismiss</ion-button>
<ion-button id="set-breakpoint">Set breakpoint</ion-button>
</ion-content>
</ion-modal>
</ion-content>
<script>
const modal = document.querySelector('ion-modal');
const setBreakpointButton = document.querySelector('#set-breakpoint');
modal.breakpoints = [0.25, 0.5, 1];
setBreakpointButton.addEventListener('click', () => {
modal.setCurrentBreakpoint(0.5);
});
</script>
`);
const modal = page.locator('ion-modal');
const dismissButton = page.locator('#dismiss');
const openButton = page.locator('#open-modal');
const setBreakpointButton = page.locator('#set-breakpoint');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
const ionBreakpointDidChange = await page.spyOnEvent('ionBreakpointDidChange');
await openButton.click();
await ionModalDidPresent.next();
await setBreakpointButton.click();
await ionBreakpointDidChange.next();
await dismissButton.click();
await ionModalDidDismiss.next();
await openButton.click();
await ionModalDidPresent.next();
const breakpoint = await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint());
expect(breakpoint).toBe(0.25);
await setBreakpointButton.click();
await ionBreakpointDidChange.next();
const updatedBreakpoint = await modal.evaluate((el: HTMLIonModalElement) => el.getCurrentBreakpoint());
expect(updatedBreakpoint).toBe(0.5);
});
});

View File

@ -14,7 +14,7 @@ test.describe('picker-internal', () => {
test.describe('within overlay:', () => {
// TODO (FW-1397): Remove this test.skip when the issue is fixed.
test.skip('Flaky test', 'Mobile Safari and Chrome on Linux renders the selected option incorrectly');
test.skip(true, 'Mobile Safari and Chrome on Linux renders the selected option incorrectly');
test('popover: should not have visual regression', async ({ page }) => {
await page.goto(`/src/components/picker-internal/test/basic`);

View File

@ -1,61 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
test('popover - arrow side: top', async () => {
await testPopover('top', false);
});
test('popover - arrow side: right', async () => {
await testPopover('right', false);
});
test('popover - arrow side: bottom', async () => {
await testPopover('bottom', false);
});
test('popover - arrow side: left', async () => {
await testPopover('left', false);
});
test('popover - arrow side: start', async () => {
await testPopover('start', false);
});
test('popover - arrow side: end', async () => {
await testPopover('end', false);
});
test('popover - arrow side: start, rtl', async () => {
await testPopover('start', true);
});
test('popover - arrow side: end, rtl', async () => {
await testPopover('end', true);
});
const testPopover = async (side: string, isRTL = false) => {
const rtl = isRTL ? '&rtl=true' : '';
const page = await newE2EPage({ url: `/src/components/popover/test/arrow?ionic:_testing=true${rtl}` });
const POPOVER_CLASS = `${side}-popover`;
const TRIGGER_ID = `${side}-trigger`;
const screenshotCompares = [];
const trigger = await page.find(`#${TRIGGER_ID}`);
await page.evaluate((POPOVER_TRIGGER_ID) => {
const popoverTrigger = document.querySelector(`#${POPOVER_TRIGGER_ID}`);
popoverTrigger?.scrollIntoView({ block: 'center' });
}, TRIGGER_ID);
trigger.click();
await page.waitForSelector(`.${POPOVER_CLASS}`);
const popover = await page.find(`.${POPOVER_CLASS}`);
await popover.waitForVisible();
screenshotCompares.push(await page.compareScreenshot());
for (const screenshotCompare of screenshotCompares) {
expect(screenshotCompare).toMatchScreenshot();
}
};

View File

@ -13,10 +13,13 @@
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
ion-app > ion-content {
--background: #dddddd;
}
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 20px;
grid-row-gap: 80px;
grid-column-gap: 20px;
padding: 200px;
@ -54,42 +57,48 @@
<div class="grid-item">
<h2>Top</h2>
<ion-button id="top-trigger">Click to Open</ion-button>
<ion-popover class="top-popover" trigger="top-trigger" side="top" size="cover">
<ion-popover show-backdrop="false" class="top-popover" trigger="top-trigger" side="top" size="cover">
<ion-content class="ion-padding"> Hello World </ion-content>
</ion-popover>
</div>
<div class="grid-item">
<h2>Right</h2>
<ion-button id="right-trigger">Click to Open</ion-button>
<ion-popover class="right-popover" trigger="right-trigger" side="right" size="cover">
<ion-popover show-backdrop="false" class="right-popover" trigger="right-trigger" side="right" size="cover">
<ion-content class="ion-padding"> Hello World </ion-content>
</ion-popover>
</div>
<div class="grid-item">
<h2>Bottom</h2>
<ion-button id="bottom-trigger">Click to Open</ion-button>
<ion-popover class="bottom-popover" trigger="bottom-trigger" side="bottom" size="cover">
<ion-popover
show-backdrop="false"
class="bottom-popover"
trigger="bottom-trigger"
side="bottom"
size="cover"
>
<ion-content class="ion-padding"> Hello World </ion-content>
</ion-popover>
</div>
<div class="grid-item">
<h2>Left</h2>
<ion-button id="left-trigger">Click to Open</ion-button>
<ion-popover class="left-popover" trigger="left-trigger" side="left" size="cover">
<ion-popover show-backdrop="false" class="left-popover" trigger="left-trigger" side="left" size="cover">
<ion-content class="ion-padding"> Hello World </ion-content>
</ion-popover>
</div>
<div class="grid-item">
<h2>Start</h2>
<ion-button id="start-trigger">Click to Open</ion-button>
<ion-popover class="start-popover" trigger="start-trigger" side="start" size="cover">
<ion-popover show-backdrop="false" class="start-popover" trigger="start-trigger" side="start" size="cover">
<ion-content class="ion-padding"> Hello World </ion-content>
</ion-popover>
</div>
<div class="grid-item">
<h2>End</h2>
<ion-button id="end-trigger">Click to Open</ion-button>
<ion-popover class="end-popover" trigger="end-trigger" side="end" size="cover">
<ion-popover show-backdrop="false" class="end-popover" trigger="end-trigger" side="end" size="cover">
<ion-content class="ion-padding"> Hello World </ion-content>
</ion-popover>
</div>

View File

@ -0,0 +1,23 @@
import { expect } from '@playwright/test';
import { test, Viewports } from '@utils/test/playwright';
import { openPopover } from '../test.utils';
test.describe('popover: arrow rendering', async () => {
/**
* The popovers have showBackdrop=false so we can open all of them at once
* and massively cut down on screenshots taken. The content has its own
* backdrop so you can still see the popovers.
*/
test('should not have visual regressions', async ({ page }) => {
await page.goto('/src/components/popover/test/arrow');
await page.setViewportSize(Viewports.tablet.portrait); // avoid extra-long viewport screenshots
const sides = ['top', 'right', 'bottom', 'left', 'start', 'end'];
for (const side of sides) {
await openPopover(page, `${side}-trigger`, true);
}
expect(await page.screenshot()).toMatchSnapshot(`popover-arrow-${page.getSnapshotSettings()}.png`);
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -20,52 +20,6 @@
<body>
<ion-app>
<ion-content class="ion-padding" id="content">
<ion-button
id="basic-popover"
expand="block"
onclick="presentPopover({ component: 'profile-page', event: event, htmlAttributes: { 'data-testid': 'basic-popover' } })"
>
Show Popover</ion-button
>
<ion-button
id="translucent-popover"
expand="block"
onclick="presentPopover({ component: 'translucent-page', event: event, translucent: true })"
>Show Translucent Popover</ion-button
>
<ion-button
id="long-list-popover"
expand="block"
color="secondary"
onclick="presentPopover({ component: 'list-page', event: event })"
>Show Long List Popover</ion-button
>
<ion-button
id="no-event-popover"
expand="block"
color="danger"
onclick="presentPopover({ component: 'profile-page' })"
>No Event Popover</ion-button
>
<ion-button
id="custom-class-popover"
expand="block"
color="tertiary"
onclick="presentPopover({ component: 'translucent-page', event: event, cssClass: 'my-custom-class' })"
>Custom Class Popover</ion-button
>
<ion-button id="header-popover" expand="block" onclick="presentPopover({ component: 'header-page' })"
>Popover With Header</ion-button
>
<ion-button
id="translucent-header-popover"
expand="block"
onclick="presentPopover({ component: 'translucent-header-page' })"
>Popover With Translucent Header</ion-button
>
</ion-content>
<ion-content class="ion-padding" id="content">
<ion-button
id="basic-popover"

View File

@ -1,142 +1,65 @@
import type { E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';
import { expect } from '@playwright/test';
import type { E2EPage } from '@utils/test/playwright';
import { test } from '@utils/test/playwright';
import { testPopover } from '../test.utils';
import { openPopover, screenshotPopover } from '../test.utils';
const DIRECTORY = 'basic';
test.describe('popover: rendering', async () => {
test('should not have visual regressions', async ({ page }) => {
const buttonIDs = [
'basic-popover',
'translucent-popover',
'long-list-popover',
'no-event-popover',
'custom-class-popover',
'header-popover',
'translucent-header-popover',
];
/**
* Focusing happens async inside of popover so we need
* to wait for the requestAnimationFrame to fire.
*/
const expectActiveElementTextToEqual = async (page: E2EPage, textValue: string) => {
await page.evaluate((text) => document.activeElement!.textContent === text, textValue);
};
const getActiveElementSelectionStart = (page: E2EPage) => {
return page.evaluate(() =>
document.activeElement instanceof HTMLTextAreaElement ? document.activeElement.selectionStart : null
);
};
const getActiveElementScrollTop = (page: E2EPage) => {
return page.evaluate(() => {
// Returns the closest ion-textarea or active element
const target = document.activeElement!.closest('ion-textarea') ?? document.activeElement;
return target!.scrollTop;
for (const id of buttonIDs) {
await screenshotPopover(page, id, 'basic');
}
});
};
test('popover: basic', async () => {
await testPopover(DIRECTORY, '#basic-popover');
});
test('popover: translucent', async () => {
await testPopover(DIRECTORY, '#translucent-popover');
test.describe('popover: htmlAttributes', async () => {
test('should inherit attributes on host', async ({ page }) => {
await page.goto('/src/components/popover/test/basic');
await openPopover(page, 'basic-popover');
const alert = page.locator('ion-popover');
expect(alert).toHaveAttribute('data-testid', 'basic-popover');
});
});
test('popover: long list', async () => {
await testPopover(DIRECTORY, '#long-list-popover');
});
test.describe('popover: focus trap', async () => {
test.beforeEach(async ({ page }) => {
await page.goto('/src/components/popover/test/basic');
});
test('popover: no event', async () => {
await testPopover(DIRECTORY, '#no-event-popover');
});
test('popover: custom class', async () => {
await testPopover(DIRECTORY, '#custom-class-popover');
});
test('popover: header', async () => {
await testPopover(DIRECTORY, '#header-popover');
});
test('popover: translucent header', async () => {
await testPopover(DIRECTORY, '#translucent-header-popover');
});
/**
* RTL Tests
*/
test('popover:rtl: basic', async () => {
await testPopover(DIRECTORY, '#basic-popover', true, true);
});
test('popover:rtl: translucent', async () => {
await testPopover(DIRECTORY, '#translucent-popover', true, true);
});
test('popover:rtl: long list', async () => {
await testPopover(DIRECTORY, '#long-list-popover', true, true);
});
test('popover:rtl: no event', async () => {
await testPopover(DIRECTORY, '#no-event-popover', true, true);
});
test('popover:rtl: custom class', async () => {
await testPopover(DIRECTORY, '#custom-class-popover', true, true);
});
test('popover:rtl: header', async () => {
await testPopover(DIRECTORY, '#header-popover', true);
});
test('popover:rtl: translucent header', async () => {
await testPopover(DIRECTORY, '#translucent-header-popover', true);
});
test('popover: htmlAttributes', async () => {
const page = await newE2EPage({ url: '/src/components/popover/test/basic?ionic:_testing=true' });
await page.click('#basic-popover');
await page.waitForSelector('#basic-popover');
const alert = await page.find('ion-popover');
expect(alert).not.toBe(null);
await alert.waitForVisible();
const attribute = await page.evaluate(() => document.querySelector('ion-popover')!.getAttribute('data-testid'));
expect(attribute).toEqual('basic-popover');
});
describe('popover: focus trap', () => {
it('should focus the first ion-item on ArrowDown', async () => {
const page = await newE2EPage({ url: '/src/components/popover/test/basic?ionic:_testing=true' });
await page.click('#basic-popover');
const popover = await page.find('ion-popover');
expect(popover).not.toBe(null);
await popover.waitForVisible();
test('should focus the first ion-item on ArrowDown', async ({ page }) => {
await openPopover(page, 'basic-popover');
await page.keyboard.press('ArrowDown');
await expectActiveElementTextToEqual(page, 'Item 0');
});
it('should work with ion-item children', async () => {
const page = await newE2EPage({ url: '/src/components/popover/test/basic?ionic:_testing=true' });
await page.click('#basic-popover');
await page.waitForSelector('#basic-popover');
const popover = await page.find('ion-popover');
expect(popover).not.toBe(null);
await popover.waitForVisible();
test('should trap focus', async ({ page, browserName }) => {
await openPopover(page, 'basic-popover');
await page.keyboard.press('Tab');
await expectActiveElementTextToEqual(page, 'Item 0');
await page.keyboard.down('Shift');
if (browserName === 'webkit') {
await page.keyboard.down('Alt');
}
await page.keyboard.press('Tab');
await page.keyboard.up('Shift');
if (browserName === 'webkit') {
await page.keyboard.up('Alt');
}
await expectActiveElementTextToEqual(page, 'Item 3');
@ -161,19 +84,17 @@ describe('popover: focus trap', () => {
await expectActiveElementTextToEqual(page, 'Item 3');
});
it('should not override keyboard interactions for textarea elements', async () => {
const page = await newE2EPage({ url: '/src/components/popover/test/basic?ionic:_testing=true' });
await page.waitForSelector('#popover-with-textarea');
await page.click('#popover-with-textarea');
const popover = await page.find('ion-popover');
await popover.waitForVisible();
await page.waitForFunction('document.activeElement.tagName === "ION-POPOVER"');
test('should not override keyboard interactions for textarea elements', async ({ page, browserName }) => {
await openPopover(page, 'popover-with-textarea');
await page.waitForFunction(() => document.activeElement?.tagName === 'ION-POPOVER');
await page.keyboard.press('Tab');
// Checking within ion-textarea
// for Firefox, ion-textarea is focused first
// need to tab again to get to native input
if (browserName === 'firefox') {
await page.keyboard.press('Tab');
}
let activeElementTagName = await page.evaluate(() => document.activeElement!.tagName);
let scrollTop = null;
@ -235,3 +156,27 @@ describe('popover: focus trap', () => {
expect(scrollTop).toBeGreaterThanOrEqual(previousScrollTop);
});
});
// TODO(FW-1424): convert these to Playwright assertions where possible
/**
* Focusing happens async inside of popover so we need
* to wait for the requestAnimationFrame to fire.
*/
const expectActiveElementTextToEqual = async (page: E2EPage, textValue: string) => {
await page.evaluate((text) => document.activeElement!.textContent === text, textValue);
};
const getActiveElementSelectionStart = (page: E2EPage) => {
return page.evaluate(() =>
document.activeElement instanceof HTMLTextAreaElement ? document.activeElement.selectionStart : null
);
};
const getActiveElementScrollTop = (page: E2EPage) => {
return page.evaluate(() => {
// Returns the closest ion-textarea or active element
const target = document.activeElement!.closest('ion-textarea') ?? document.activeElement;
return target!.scrollTop;
});
};

Some files were not shown because too many files have changed in this diff Show More