chore(): sync feature-6.2 with main
chore(): sync feature-6.2 with main
31
CHANGELOG.md
@@ -3,6 +3,37 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.1.15](https://github.com/ionic-team/ionic-framework/compare/v6.1.14...v6.1.15) (2022-07-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** use scroll listener to detect month changes ([#25586](https://github.com/ionic-team/ionic-framework/issues/25586)) ([b7afcb0](https://github.com/ionic-team/ionic-framework/commit/b7afcb0f0c36d84f3b4d65844df28e6293bc1ea5)), closes [#25257](https://github.com/ionic-team/ionic-framework/issues/25257) [#25608](https://github.com/ionic-team/ionic-framework/issues/25608) [#24980](https://github.com/ionic-team/ionic-framework/issues/24980)
|
||||
* **fab-button:** aria attributes are inherited ([#25635](https://github.com/ionic-team/ionic-framework/issues/25635)) ([64ae3d2](https://github.com/ionic-team/ionic-framework/commit/64ae3d2b9729c5c6be8644b1df6c8b3d40584d3b)), closes [#25633](https://github.com/ionic-team/ionic-framework/issues/25633)
|
||||
* **modal:** allow for custom dialog implementations ([#25630](https://github.com/ionic-team/ionic-framework/issues/25630)) ([a6f3ae6](https://github.com/ionic-team/ionic-framework/commit/a6f3ae67ab91ab95408ad425156167edc3570978)), closes [#24080](https://github.com/ionic-team/ionic-framework/issues/24080)
|
||||
* **react:** swipe to go back gesture works on ios ([#25563](https://github.com/ionic-team/ionic-framework/issues/25563)) ([7ec3683](https://github.com/ionic-team/ionic-framework/commit/7ec3683e94e5397022560ce8489532ff40d3f40c)), closes [#22342](https://github.com/ionic-team/ionic-framework/issues/22342)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **input:** passive event listener for touch start events ([#25610](https://github.com/ionic-team/ionic-framework/issues/25610)) ([2d1efdb](https://github.com/ionic-team/ionic-framework/commit/2d1efdbe6dd9436badab4684f2a484476489c166)), closes [#25599](https://github.com/ionic-team/ionic-framework/issues/25599)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.1.14](https://github.com/ionic-team/ionic-framework/compare/v6.1.13...v6.1.14) (2022-07-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** datetime works within stencil apps ([#25592](https://github.com/ionic-team/ionic-framework/issues/25592)) ([7b10fa6](https://github.com/ionic-team/ionic-framework/commit/7b10fa6476c2c2896c6810c57b3160f8c8896faa)), closes [#25591](https://github.com/ionic-team/ionic-framework/issues/25591)
|
||||
* **react:** IonNav works with react ([#25565](https://github.com/ionic-team/ionic-framework/issues/25565)) ([420f9bb](https://github.com/ionic-team/ionic-framework/commit/420f9bbebd41f3eab6def795bcdd1933d5c5a47a)), closes [#24002](https://github.com/ionic-team/ionic-framework/issues/24002)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.1.13](https://github.com/ionic-team/ionic-framework/compare/v6.1.12...v6.1.13) (2022-07-06)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.1.15](https://github.com/ionic-team/ionic/compare/v6.1.14...v6.1.15) (2022-07-20)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.1.14](https://github.com/ionic-team/ionic/compare/v6.1.13...v6.1.14) (2022-07-13)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.1.13](https://github.com/ionic-team/ionic/compare/v6.1.12...v6.1.13) (2022-07-06)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
18
angular/package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "6.1.13",
|
||||
"version": "6.1.15",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/angular",
|
||||
"version": "6.1.13",
|
||||
"version": "6.1.15",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ionic/core": "^6.1.13",
|
||||
"@ionic/core": "^6.1.15",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
@@ -1023,9 +1023,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@ionic/core": {
|
||||
"version": "6.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.13.tgz",
|
||||
"integrity": "sha512-CZ5P1El/bk6ZDKqey/67/ZgpUhVQTr+WyhGxFTnPCsIWg+VfOogQ7rHCkEqWfXJqdzNPsvtH5/Lck4qoD0nCkg==",
|
||||
"version": "6.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.15.tgz",
|
||||
"integrity": "sha512-NvBlHR7O4kfp9KYz6oLsghFzGZImA7hM4qS4tFRUI62R+Q5iCJEY4OmXE5bif5K+SQkMhQY+x1l2Nq20waLzLg==",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^2.16.0",
|
||||
"ionicons": "^6.0.2",
|
||||
@@ -7939,9 +7939,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "6.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.13.tgz",
|
||||
"integrity": "sha512-CZ5P1El/bk6ZDKqey/67/ZgpUhVQTr+WyhGxFTnPCsIWg+VfOogQ7rHCkEqWfXJqdzNPsvtH5/Lck4qoD0nCkg==",
|
||||
"version": "6.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.1.15.tgz",
|
||||
"integrity": "sha512-NvBlHR7O4kfp9KYz6oLsghFzGZImA7hM4qS4tFRUI62R+Q5iCJEY4OmXE5bif5K+SQkMhQY+x1l2Nq20waLzLg==",
|
||||
"requires": {
|
||||
"@stencil/core": "^2.16.0",
|
||||
"ionicons": "^6.0.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "6.1.13",
|
||||
"version": "6.1.15",
|
||||
"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.13",
|
||||
"@ionic/core": "^6.1.15",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
|
||||
Cypress.Commands.add('ionSwipeToGoBack', (complete = false, selector = 'ion-router-outlet') => {
|
||||
const increment = (complete) ? 60 : 25;
|
||||
const increment = complete ? 60 : 25;
|
||||
cy.get(selector)
|
||||
.first()
|
||||
.trigger('mousedown', 0, 275, { which: 1, force: true })
|
||||
@@ -37,18 +37,16 @@ Cypress.Commands.add('ionSwipeToGoBack', (complete = false, selector = 'ion-rout
|
||||
.wait(50)
|
||||
.trigger('mousemove', increment * 4, 275, { which: 1, force: true })
|
||||
.wait(50)
|
||||
.trigger('mouseup', increment * 4, 275, { which: 1, force: true })
|
||||
.trigger('mouseup', increment * 4, 275, { which: 1, force: true });
|
||||
cy.wait(150);
|
||||
})
|
||||
});
|
||||
|
||||
Cypress.Commands.add('testStack', (selector, expected) => {
|
||||
cy.document().then((doc) => {
|
||||
const children = Array.from(
|
||||
doc.querySelector(selector).children
|
||||
).map(el => el.tagName.toLowerCase());
|
||||
const children = Array.from(doc.querySelector(selector).children).map((el) => el.tagName.toLowerCase());
|
||||
expect(children).to.deep.equal(expected);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('testLifeCycle', (selector, expected) => {
|
||||
cy.get(`${selector} #ngOnInit`).invoke('text').should('equal', '1');
|
||||
@@ -56,24 +54,26 @@ Cypress.Commands.add('testLifeCycle', (selector, expected) => {
|
||||
cy.get(`${selector} #ionViewDidEnter`).invoke('text').should('equal', expected.ionViewDidEnter.toString());
|
||||
cy.get(`${selector} #ionViewWillLeave`).invoke('text').should('equal', expected.ionViewWillLeave.toString());
|
||||
cy.get(`${selector} #ionViewDidLeave`).invoke('text').should('equal', expected.ionViewDidLeave.toString());
|
||||
})
|
||||
});
|
||||
|
||||
Cypress.Commands.add('ionPageVisible', (selector) => {
|
||||
cy.get(selector)
|
||||
.should('have.class', 'ion-page')
|
||||
.should('not.have.class', 'ion-page-hidden')
|
||||
.should('not.have.class', 'ion-page-invisible')
|
||||
.should('have.length', 1)
|
||||
})
|
||||
.should('have.length', 1);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('ionPageHidden', (selector) => {
|
||||
cy.get(selector)
|
||||
.should('have.class', 'ion-page')
|
||||
.should('have.class', 'ion-page-hidden')
|
||||
.should('have.length', 1)
|
||||
})
|
||||
cy.get(selector).should('have.class', 'ion-page').should('have.class', 'ion-page-hidden').should('have.length', 1);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('ionPageDoesNotExist', (selector) => {
|
||||
cy.get(selector)
|
||||
.should('not.exist')
|
||||
cy.get(selector).should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('ionTabClick', (tabText) => {
|
||||
// TODO: Figure out how to get rid of wait. It's a workaround for flakiness in CI.
|
||||
cy.wait(250);
|
||||
cy.contains('ion-tab-button', tabText).click({ force: true });
|
||||
});
|
||||
|
||||
@@ -63,6 +63,11 @@ declare namespace Cypress {
|
||||
* ```
|
||||
*/
|
||||
ionPageDoesNotExist(selector: string): Chainable<any>
|
||||
|
||||
/**
|
||||
* Clicks on a tab button with the given text.
|
||||
*/
|
||||
ionTabClick(tabText: string): Chainable<any>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,13 +230,13 @@ describe('Tabs', () => {
|
||||
tab.find('#goto-next').click();
|
||||
testTabTitle('Tab 1 - Page 2 (2)');
|
||||
|
||||
cy.get('#tab-button-contact').click();
|
||||
cy.ionTabClick('Tab Two');
|
||||
testTabTitle('Tab 2 - Page 1');
|
||||
|
||||
cy.get('#tab-button-account').click();
|
||||
cy.ionTabClick('Tab One');
|
||||
testTabTitle('Tab 1 - Page 2 (2)');
|
||||
|
||||
cy.get('#tab-button-account').click();
|
||||
cy.ionTabClick('Tab One');
|
||||
testTabTitle('Tab 1 - Page 1');
|
||||
|
||||
testUrlContains(rootUrl);
|
||||
|
||||
@@ -3,6 +3,35 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.1.15](https://github.com/ionic-team/ionic/compare/v6.1.14...v6.1.15) (2022-07-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** use scroll listener to detect month changes ([#25586](https://github.com/ionic-team/ionic/issues/25586)) ([b7afcb0](https://github.com/ionic-team/ionic/commit/b7afcb0f0c36d84f3b4d65844df28e6293bc1ea5)), closes [#25257](https://github.com/ionic-team/ionic/issues/25257) [#25608](https://github.com/ionic-team/ionic/issues/25608) [#24980](https://github.com/ionic-team/ionic/issues/24980)
|
||||
* **fab-button:** aria attributes are inherited ([#25635](https://github.com/ionic-team/ionic/issues/25635)) ([64ae3d2](https://github.com/ionic-team/ionic/commit/64ae3d2b9729c5c6be8644b1df6c8b3d40584d3b)), closes [#25633](https://github.com/ionic-team/ionic/issues/25633)
|
||||
* **modal:** allow for custom dialog implementations ([#25630](https://github.com/ionic-team/ionic/issues/25630)) ([a6f3ae6](https://github.com/ionic-team/ionic/commit/a6f3ae67ab91ab95408ad425156167edc3570978)), closes [#24080](https://github.com/ionic-team/ionic/issues/24080)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **input:** passive event listener for touch start events ([#25610](https://github.com/ionic-team/ionic/issues/25610)) ([2d1efdb](https://github.com/ionic-team/ionic/commit/2d1efdbe6dd9436badab4684f2a484476489c166)), closes [#25599](https://github.com/ionic-team/ionic/issues/25599)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.1.14](https://github.com/ionic-team/ionic/compare/v6.1.13...v6.1.14) (2022-07-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** datetime works within stencil apps ([#25592](https://github.com/ionic-team/ionic/issues/25592)) ([7b10fa6](https://github.com/ionic-team/ionic/commit/7b10fa6476c2c2896c6810c57b3160f8c8896faa)), closes [#25591](https://github.com/ionic-team/ionic/issues/25591)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.1.13](https://github.com/ionic-team/ionic/compare/v6.1.12...v6.1.13) (2022-07-06)
|
||||
|
||||
|
||||
|
||||
1456
core/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "6.1.13",
|
||||
"version": "6.1.15",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -41,7 +41,7 @@
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
"@jest/core": "^26.6.3",
|
||||
"@playwright/test": "^1.20.0",
|
||||
"@playwright/test": "^1.23.3",
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/angular-output-target": "^0.4.0",
|
||||
|
||||
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 117 KiB |
@@ -20,7 +20,7 @@ test.describe('action sheet: basic', () => {
|
||||
await buttonOption.click();
|
||||
|
||||
await ionActionSheetDidDismiss.next();
|
||||
expect(ionActionSheetDidDismiss).toHaveReceivedEventDetail({ data: { type: '1' } });
|
||||
expect(ionActionSheetDidDismiss).toHaveReceivedEventDetail({ data: { type: '1' }, role: undefined });
|
||||
});
|
||||
test('should return cancel button data', async ({ page }, testInfo) => {
|
||||
test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.');
|
||||
|
||||
12
core/src/components/checkbox/test/basic/checkbox.e2e.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: basic', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/basic`);
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
expect(await page.screenshot()).toMatchSnapshot(`checkbox-basic-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 107 KiB |
@@ -1,10 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('checkbox: basic', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/checkbox/test/basic?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -44,7 +44,7 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Secondary</ion-label>
|
||||
<ion-label>Secondary, disabled</ion-label>
|
||||
<ion-checkbox disabled checked color="secondary"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Dark</ion-label>
|
||||
<ion-label>Dark, --size</ion-label>
|
||||
<ion-checkbox checked color="dark" style="--size: 100px"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
@@ -83,11 +83,6 @@
|
||||
<ion-checkbox checked color="medium" slot="start"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Dark</ion-label>
|
||||
<ion-checkbox checked color="dark" slot="start" style="--size: 100px"></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>
|
||||
@@ -109,33 +104,5 @@
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const inputs = document.querySelectorAll('ion-checkbox');
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i];
|
||||
|
||||
input.addEventListener('ionBlur', function () {
|
||||
console.log('Listen ionBlur: fired');
|
||||
});
|
||||
|
||||
input.addEventListener('ionFocus', function () {
|
||||
console.log('Listen ionFocus: fired');
|
||||
});
|
||||
|
||||
input.addEventListener('ionChange', function (ev) {
|
||||
console.log('Listen ionChange: fired', ev.detail);
|
||||
});
|
||||
|
||||
input.addEventListener('click', function () {
|
||||
console.log('Listen click: fired');
|
||||
});
|
||||
}
|
||||
|
||||
const clickItem = () => {
|
||||
console.log('Item click: fired');
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: indeterminate', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/indeterminate`);
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
expect(await page.screenshot()).toMatchSnapshot(`checkbox-indeterminate-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 67 KiB |
@@ -1,10 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('checkbox: indeterminate', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/checkbox/test/indeterminate?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -23,45 +23,6 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<ion-list-header>
|
||||
<ion-label> Native </ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<div class="ion-padding-start">
|
||||
<!-- Default to unchecked -->
|
||||
<label for="unchecked">Unchecked</label>
|
||||
<input name="unchecked" id="unchecked" type="checkbox" />
|
||||
<br />
|
||||
|
||||
<!-- Default to checked -->
|
||||
<label for="checked">Checked</label>
|
||||
<input name="checked" id="checked" type="checkbox" checked />
|
||||
<br />
|
||||
|
||||
<!-- Default to indeterminate -->
|
||||
<label for="indeterminate">Indeterminate</label>
|
||||
<input name="indeterminate" id="indeterminate" type="checkbox" class="indeterminate" />
|
||||
<br />
|
||||
|
||||
<!-- Default to checked / indeterminate -->
|
||||
<label for="both">Checked / Indeterminate</label>
|
||||
<input name="both" id="both" type="checkbox" checked class="indeterminate" />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<ion-list-header>
|
||||
<ion-label> Ionic </ion-label>
|
||||
</ion-list-header>
|
||||
<ion-item>
|
||||
<ion-label>Unchecked</ion-label>
|
||||
<ion-checkbox slot="end"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checked</ion-label>
|
||||
<ion-checkbox slot="end" checked></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Indeterminate</ion-label>
|
||||
<ion-checkbox slot="end" indeterminate></ion-checkbox>
|
||||
|
||||
12
core/src/components/checkbox/test/standalone/checkbox.e2e.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: standalone', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/standalone`);
|
||||
|
||||
expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(
|
||||
`checkbox-standalone-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 85 KiB |
@@ -1,10 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('checkbox: standalone', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/checkbox/test/standalone?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -126,9 +126,8 @@ ion-picker-column-internal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Safari 14 has an issue where Intersection
|
||||
* Observer is incorrectly fired when
|
||||
* unhiding the calendar content.
|
||||
* Safari 14 has an issue where a scroll event
|
||||
* is incorrectly fired when unhiding the calendar content.
|
||||
* To workaround this, we set the opacity
|
||||
* of the content to 0 and hide it offscreen.
|
||||
*
|
||||
|
||||
@@ -68,7 +68,6 @@ export class Datetime implements ComponentInterface {
|
||||
private calendarBodyRef?: HTMLElement;
|
||||
private popoverRef?: HTMLIonPopoverElement;
|
||||
private clearFocusVisible?: () => void;
|
||||
private overlayIsPresenting = false;
|
||||
|
||||
/**
|
||||
* Whether to highlight the active day with a solid circle (as opposed
|
||||
@@ -86,9 +85,8 @@ export class Datetime implements ComponentInterface {
|
||||
private parsedYearValues?: number[];
|
||||
private parsedDayValues?: number[];
|
||||
|
||||
private destroyCalendarIO?: () => void;
|
||||
private destroyCalendarListener?: () => void;
|
||||
private destroyKeyboardMO?: () => void;
|
||||
private destroyOverlayListener?: () => void;
|
||||
|
||||
private minParts?: any;
|
||||
private maxParts?: any;
|
||||
@@ -806,20 +804,17 @@ export class Datetime implements ComponentInterface {
|
||||
};
|
||||
};
|
||||
|
||||
private initializeCalendarIOListeners = () => {
|
||||
private initializeCalendarListener = () => {
|
||||
const calendarBodyRef = this.getCalendarBodyEl();
|
||||
if (!calendarBodyRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = getIonMode(this);
|
||||
|
||||
/**
|
||||
* For performance reasons, we only render 3
|
||||
* months at a time: The current month, the previous
|
||||
* month, and the next month. We have IntersectionObservers
|
||||
* on the previous and next month elements to append/prepend
|
||||
* new months.
|
||||
* month, and the next month. We have a scroll listener
|
||||
* on the calendar body to append/prepend new months.
|
||||
*
|
||||
* We can do this because Stencil is smart enough to not
|
||||
* re-create the .calendar-month containers, but rather
|
||||
@@ -835,43 +830,78 @@ export class Datetime implements ComponentInterface {
|
||||
const startMonth = months[0] as HTMLElement;
|
||||
const workingMonth = months[1] as HTMLElement;
|
||||
const endMonth = months[2] as HTMLElement;
|
||||
const mode = getIonMode(this);
|
||||
const needsiOSRubberBandFix = mode === 'ios' && typeof navigator !== 'undefined' && navigator.maxTouchPoints > 1;
|
||||
|
||||
/**
|
||||
* Before setting up the IntersectionObserver,
|
||||
* Before setting up the scroll listener,
|
||||
* scroll the middle month into view.
|
||||
* scrollIntoView() will scroll entire page
|
||||
* if element is not in viewport. Use scrollLeft instead.
|
||||
*/
|
||||
let endIO: IntersectionObserver | undefined;
|
||||
let startIO: IntersectionObserver | undefined;
|
||||
writeTask(() => {
|
||||
calendarBodyRef.scrollLeft = startMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
const ioCallback = (callbackType: 'start' | 'end', entries: IntersectionObserverEntry[]) => {
|
||||
const refIO = callbackType === 'start' ? startIO : endIO;
|
||||
const refMonth = callbackType === 'start' ? startMonth : endMonth;
|
||||
const refMonthFn = callbackType === 'start' ? getPreviousMonth : getNextMonth;
|
||||
|
||||
const getChangedMonth = (parts: DatetimeParts): DatetimeParts | undefined => {
|
||||
const box = calendarBodyRef.getBoundingClientRect();
|
||||
const root = this.el!.shadowRoot!;
|
||||
|
||||
/**
|
||||
* If the month is not fully in view, do not do anything
|
||||
* Get the element that is in the center of the calendar body.
|
||||
* This will be an element inside of the active month.
|
||||
*/
|
||||
const ev = entries[0];
|
||||
if (!ev.isIntersecting) {
|
||||
const elementAtCenter = root.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2);
|
||||
/**
|
||||
* If there is no element then the
|
||||
* component may be re-rendering on a slow device.
|
||||
*/
|
||||
if (!elementAtCenter) return;
|
||||
|
||||
const month = elementAtCenter.closest('.calendar-month');
|
||||
if (!month) return;
|
||||
|
||||
/**
|
||||
* The edge of the month must be lined up with
|
||||
* the edge of the calendar body in order for
|
||||
* the component to update. Otherwise, it
|
||||
* may be the case that the user has paused their
|
||||
* swipe or the browser has not finished snapping yet.
|
||||
* Rather than check if the x values are equal,
|
||||
* we give it a tolerance of 2px to account for
|
||||
* sub pixel rendering.
|
||||
*/
|
||||
const monthBox = month.getBoundingClientRect();
|
||||
if (Math.abs(monthBox.x - box.x) > 2) return;
|
||||
|
||||
/**
|
||||
* From here, we can determine if the start
|
||||
* month or the end month was scrolled into view.
|
||||
* If no month was changed, then we can return from
|
||||
* the scroll callback early.
|
||||
*/
|
||||
if (month === startMonth) {
|
||||
return getPreviousMonth(parts);
|
||||
} else if (month === endMonth) {
|
||||
return getNextMonth(parts);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const updateActiveMonth = () => {
|
||||
if (needsiOSRubberBandFix) {
|
||||
calendarBodyRef.style.removeProperty('pointer-events');
|
||||
appliediOSRubberBandFix = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* When presenting an inline overlay,
|
||||
* subsequent presentations will cause
|
||||
* the IO to fire again (since the overlay
|
||||
* is now visible and therefore the calendar
|
||||
* months are intersecting).
|
||||
* If the month did not change
|
||||
* then we can return early.
|
||||
*/
|
||||
if (this.overlayIsPresenting) {
|
||||
this.overlayIsPresenting = false;
|
||||
return;
|
||||
}
|
||||
const newDate = getChangedMonth(this.workingParts);
|
||||
if (!newDate) return;
|
||||
|
||||
const { month, year, day } = refMonthFn(this.workingParts);
|
||||
const { month, day, year } = newDate;
|
||||
|
||||
if (
|
||||
isMonthDisabled(
|
||||
@@ -885,25 +915,6 @@ export class Datetime implements ComponentInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* On iOS, we need to set pointer-events: none
|
||||
* when the user is almost done with the gesture
|
||||
* so that they cannot quickly swipe while
|
||||
* the scrollable container is snapping.
|
||||
* Updating the container while snapping
|
||||
* causes WebKit to snap incorrectly.
|
||||
*/
|
||||
if (mode === 'ios') {
|
||||
const ratio = ev.intersectionRatio;
|
||||
// `maxTouchPoints` will be 1 in device preview, but > 1 on device
|
||||
const shouldDisable = Math.abs(ratio - 0.7) <= 0.1 && navigator.maxTouchPoints > 1;
|
||||
|
||||
if (shouldDisable) {
|
||||
calendarBodyRef.style.setProperty('pointer-events', 'none');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent scrolling for other browsers
|
||||
* to give the DOM time to update and the container
|
||||
@@ -911,16 +922,6 @@ export class Datetime implements ComponentInterface {
|
||||
*/
|
||||
calendarBodyRef.style.setProperty('overflow', 'hidden');
|
||||
|
||||
/**
|
||||
* Remove the IO temporarily
|
||||
* otherwise you can sometimes get duplicate
|
||||
* events when rubber banding.
|
||||
*/
|
||||
if (refIO === undefined) {
|
||||
return;
|
||||
}
|
||||
refIO.disconnect();
|
||||
|
||||
/**
|
||||
* Use a writeTask here to ensure
|
||||
* that the state is updated and the
|
||||
@@ -931,85 +932,57 @@ export class Datetime implements ComponentInterface {
|
||||
* if we did not do this.
|
||||
*/
|
||||
writeTask(() => {
|
||||
// Disconnect all active intersection observers
|
||||
// to avoid a re-render causing a duplicate event.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
}
|
||||
|
||||
raf(() => {
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
month,
|
||||
day: day!,
|
||||
year,
|
||||
});
|
||||
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
calendarBodyRef.style.removeProperty('overflow');
|
||||
calendarBodyRef.style.removeProperty('pointer-events');
|
||||
|
||||
endIO?.observe(endMonth);
|
||||
startIO?.observe(startMonth);
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
month,
|
||||
day: day!,
|
||||
year,
|
||||
});
|
||||
|
||||
/**
|
||||
* Now that state has been updated
|
||||
* and the correct month is in view,
|
||||
* we can resume the IO.
|
||||
*/
|
||||
if (refIO === undefined) {
|
||||
return;
|
||||
}
|
||||
refIO.observe(refMonth);
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
calendarBodyRef.style.removeProperty('overflow');
|
||||
});
|
||||
};
|
||||
|
||||
const threshold =
|
||||
mode === 'ios' && typeof navigator !== 'undefined' && navigator.maxTouchPoints > 1 ? [0.7, 1] : 1;
|
||||
|
||||
// Intersection observers cannot accurately detect the
|
||||
// intersection with a threshold of 1, when the observed
|
||||
// element width is a sub-pixel value (i.e. 334.05px).
|
||||
// Setting a root margin to 1px solves the issue.
|
||||
const rootMargin = '1px';
|
||||
/**
|
||||
* When the container finishes scrolling we
|
||||
* need to update the DOM with the selected month.
|
||||
*/
|
||||
let scrollTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
/**
|
||||
* Listen on the first month to
|
||||
* prepend a new month and on the last
|
||||
* month to append a new month.
|
||||
* The 0.7 threshold is required on ios
|
||||
* so that we can remove pointer-events
|
||||
* when adding new months.
|
||||
* Adding to a scroll snapping container
|
||||
* while the container is snapping does not
|
||||
* completely work as expected in WebKit.
|
||||
* Adding pointer-events: none allows us to
|
||||
* avoid these issues.
|
||||
*
|
||||
* This should be fine on Chromium, but
|
||||
* when you set pointer-events: none
|
||||
* it applies to active gestures which is not
|
||||
* something WebKit does.
|
||||
* We do not want to attempt to set pointer-events
|
||||
* multiple times within a single swipe gesture as
|
||||
* that adds unnecessary work to the main thread.
|
||||
*/
|
||||
let appliediOSRubberBandFix = false;
|
||||
const scrollCallback = () => {
|
||||
if (scrollTimeout) {
|
||||
clearTimeout(scrollTimeout);
|
||||
}
|
||||
|
||||
endIO = new IntersectionObserver((ev) => ioCallback('end', ev), {
|
||||
threshold,
|
||||
root: calendarBodyRef,
|
||||
rootMargin,
|
||||
});
|
||||
endIO.observe(endMonth);
|
||||
/**
|
||||
* On iOS it is possible to quickly rubber band
|
||||
* the scroll area before the scroll timeout has fired.
|
||||
* This results in users reaching the end of the scrollable
|
||||
* container before the DOM has updated.
|
||||
* By setting `pointer-events: none` we can ensure that
|
||||
* subsequent swipes do not happen while the container
|
||||
* is snapping.
|
||||
*/
|
||||
if (!appliediOSRubberBandFix && needsiOSRubberBandFix) {
|
||||
calendarBodyRef.style.setProperty('pointer-events', 'none');
|
||||
appliediOSRubberBandFix = true;
|
||||
}
|
||||
|
||||
startIO = new IntersectionObserver((ev) => ioCallback('start', ev), {
|
||||
threshold,
|
||||
root: calendarBodyRef,
|
||||
rootMargin,
|
||||
});
|
||||
startIO.observe(startMonth);
|
||||
// Wait ~3 frames
|
||||
scrollTimeout = setTimeout(updateActiveMonth, 50);
|
||||
};
|
||||
|
||||
this.destroyCalendarIO = () => {
|
||||
endIO?.disconnect();
|
||||
startIO?.disconnect();
|
||||
calendarBodyRef.addEventListener('scroll', scrollCallback);
|
||||
|
||||
this.destroyCalendarListener = () => {
|
||||
calendarBodyRef.removeEventListener('scroll', scrollCallback);
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -1031,10 +1004,10 @@ export class Datetime implements ComponentInterface {
|
||||
* if the datetime has been hidden/presented by a modal or popover.
|
||||
*/
|
||||
private destroyInteractionListeners = () => {
|
||||
const { destroyCalendarIO, destroyKeyboardMO } = this;
|
||||
const { destroyCalendarListener, destroyKeyboardMO } = this;
|
||||
|
||||
if (destroyCalendarIO !== undefined) {
|
||||
destroyCalendarIO();
|
||||
if (destroyCalendarListener !== undefined) {
|
||||
destroyCalendarListener();
|
||||
}
|
||||
|
||||
if (destroyKeyboardMO !== undefined) {
|
||||
@@ -1043,9 +1016,8 @@ export class Datetime implements ComponentInterface {
|
||||
};
|
||||
|
||||
private initializeListeners() {
|
||||
this.initializeCalendarIOListeners();
|
||||
this.initializeCalendarListener();
|
||||
this.initializeKeyboardListeners();
|
||||
this.initializeOverlayListener();
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
@@ -1140,37 +1112,10 @@ export class Datetime implements ComponentInterface {
|
||||
this.prevPresentation = presentation;
|
||||
|
||||
this.destroyInteractionListeners();
|
||||
if (this.destroyOverlayListener !== undefined) {
|
||||
this.destroyOverlayListener();
|
||||
}
|
||||
|
||||
this.initializeListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* When doing subsequent presentations of an inline
|
||||
* overlay, the IO callback will fire again causing
|
||||
* the calendar to go back one month. We need to listen
|
||||
* for the presentation of the overlay so we can properly
|
||||
* cancel that IO callback.
|
||||
*/
|
||||
private initializeOverlayListener = () => {
|
||||
const overlay = this.el.closest('ion-popover, ion-modal');
|
||||
if (overlay === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const overlayListener = () => {
|
||||
this.overlayIsPresenting = true;
|
||||
};
|
||||
|
||||
overlay.addEventListener('willPresent', overlayListener);
|
||||
|
||||
this.destroyOverlayListener = () => {
|
||||
overlay.removeEventListener('willPresent', overlayListener);
|
||||
};
|
||||
};
|
||||
|
||||
private processValue = (value?: string | string[] | null) => {
|
||||
this.highlightActiveParts = !!value;
|
||||
let valueToProcess = parseDate(value || getToday());
|
||||
@@ -1471,11 +1416,12 @@ export class Datetime implements ComponentInterface {
|
||||
items={items}
|
||||
value={todayString}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
// TODO(FW-1823) Remove this when iOS 14 support is dropped.
|
||||
// Due to a Safari 14 issue we need to destroy
|
||||
// the intersection observer before we update state
|
||||
// the scroll listener before we update state
|
||||
// and trigger a re-render.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
if (this.destroyCalendarListener) {
|
||||
this.destroyCalendarListener();
|
||||
}
|
||||
|
||||
const { value } = ev.detail;
|
||||
@@ -1493,9 +1439,9 @@ export class Datetime implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
// We can re-attach the intersection observer after
|
||||
// We can re-attach the scroll listener after
|
||||
// the working parts have been updated.
|
||||
this.initializeCalendarIOListeners();
|
||||
this.initializeCalendarListener();
|
||||
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
@@ -1569,11 +1515,12 @@ export class Datetime implements ComponentInterface {
|
||||
items={days}
|
||||
value={(workingParts.day || this.todayParts.day) ?? undefined}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
// TODO(FW-1823) Remove this when iOS 14 support is dropped.
|
||||
// Due to a Safari 14 issue we need to destroy
|
||||
// the intersection observer before we update state
|
||||
// the scroll listener before we update state
|
||||
// and trigger a re-render.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
if (this.destroyCalendarListener) {
|
||||
this.destroyCalendarListener();
|
||||
}
|
||||
|
||||
this.setWorkingParts({
|
||||
@@ -1588,9 +1535,9 @@ export class Datetime implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
// We can re-attach the intersection observer after
|
||||
// We can re-attach the scroll listener after
|
||||
// the working parts have been updated.
|
||||
this.initializeCalendarIOListeners();
|
||||
this.initializeCalendarListener();
|
||||
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
@@ -1612,11 +1559,12 @@ export class Datetime implements ComponentInterface {
|
||||
items={months}
|
||||
value={workingParts.month}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
// TODO(FW-1823) Remove this when iOS 14 support is dropped.
|
||||
// Due to a Safari 14 issue we need to destroy
|
||||
// the intersection observer before we update state
|
||||
// the scroll listener before we update state
|
||||
// and trigger a re-render.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
if (this.destroyCalendarListener) {
|
||||
this.destroyCalendarListener();
|
||||
}
|
||||
|
||||
this.setWorkingParts({
|
||||
@@ -1631,9 +1579,9 @@ export class Datetime implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
// We can re-attach the intersection observer after
|
||||
// We can re-attach the scroll listener after
|
||||
// the working parts have been updated.
|
||||
this.initializeCalendarIOListeners();
|
||||
this.initializeCalendarListener();
|
||||
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
@@ -1654,11 +1602,12 @@ export class Datetime implements ComponentInterface {
|
||||
items={years}
|
||||
value={workingParts.year}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
// TODO(FW-1823) Remove this when iOS 14 support is dropped.
|
||||
// Due to a Safari 14 issue we need to destroy
|
||||
// the intersection observer before we update state
|
||||
// the scroll listener before we update state
|
||||
// and trigger a re-render.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
if (this.destroyCalendarListener) {
|
||||
this.destroyCalendarListener();
|
||||
}
|
||||
|
||||
this.setWorkingParts({
|
||||
@@ -1673,9 +1622,9 @@ export class Datetime implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
// We can re-attach the intersection observer after
|
||||
// We can re-attach the scroll listener after
|
||||
// the working parts have been updated.
|
||||
this.initializeCalendarIOListeners();
|
||||
this.initializeCalendarListener();
|
||||
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
|
||||
@@ -183,3 +183,69 @@ test.describe('datetime: footer', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('datetime: swiping', () => {
|
||||
// eslint-disable-next-line no-empty-pattern
|
||||
test.beforeEach(({}, testInfo) => {
|
||||
test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs RTL layouts.');
|
||||
test.skip(testInfo.project.metadata.mode === 'ios', 'This does not have mode-specific logic.');
|
||||
});
|
||||
test('should move to prev month by swiping', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-datetime value="2022-05-03"></ion-datetime>
|
||||
`);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const calendarBody = page.locator('ion-datetime .calendar-body');
|
||||
const calendarHeader = page.locator('ion-datetime .calendar-month-year');
|
||||
|
||||
await expect(calendarHeader).toHaveText(/May 2022/);
|
||||
|
||||
await calendarBody.evaluate((el: HTMLElement) => (el.scrollLeft = 0));
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(calendarHeader).toHaveText(/April 2022/);
|
||||
});
|
||||
test('should move to next month by swiping', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-datetime value="2022-05-03"></ion-datetime>
|
||||
`);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const calendarBody = page.locator('ion-datetime .calendar-body');
|
||||
const calendarHeader = page.locator('ion-datetime .calendar-month-year');
|
||||
|
||||
await expect(calendarHeader).toHaveText(/May 2022/);
|
||||
|
||||
await calendarBody.evaluate((el: HTMLElement) => (el.scrollLeft = el.scrollWidth));
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(calendarHeader).toHaveText(/June 2022/);
|
||||
});
|
||||
test('should not re-render if swipe is in progress', async ({ page, browserName }) => {
|
||||
test.skip(browserName === 'webkit', 'Wheel is not available in WebKit');
|
||||
|
||||
await page.setContent(`
|
||||
<ion-datetime value="2022-05-03"></ion-datetime>
|
||||
`);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const calendarBody = page.locator('ion-datetime .calendar-body');
|
||||
const calendarHeader = page.locator('ion-datetime .calendar-month-year');
|
||||
|
||||
await expect(calendarHeader).toHaveText(/May 2022/);
|
||||
|
||||
const box = await calendarBody.boundingBox();
|
||||
|
||||
if (box) {
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||
await page.mouse.wheel(-50, 0);
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(calendarHeader).toHaveText(/May 2022/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,8 +33,8 @@ test.describe('datetime: minmax', () => {
|
||||
const prevButton = page.locator('ion-datetime .calendar-next-prev ion-button:nth-child(1)');
|
||||
const nextButton = page.locator('ion-datetime .calendar-next-prev ion-button:nth-child(2)');
|
||||
|
||||
await expect(nextButton).toBeEnabled();
|
||||
await expect(prevButton).toBeDisabled();
|
||||
await expect(nextButton).toHaveJSProperty('disabled', false);
|
||||
await expect(prevButton).toHaveJSProperty('disabled', true);
|
||||
|
||||
await page.evaluate('initDatetimeChangeEvent()');
|
||||
|
||||
@@ -45,8 +45,8 @@ test.describe('datetime: minmax', () => {
|
||||
|
||||
await monthDidChangeSpy.next();
|
||||
|
||||
await expect(nextButton).toBeDisabled();
|
||||
await expect(prevButton).toBeEnabled();
|
||||
await expect(nextButton).toHaveJSProperty('disabled', true);
|
||||
await expect(prevButton).toHaveJSProperty('disabled', false);
|
||||
});
|
||||
|
||||
test('datetime: minmax months disabled', async ({ page }) => {
|
||||
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@@ -1,45 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('datetime: sub-pixel width', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/datetime/test/sub-pixel-width');
|
||||
});
|
||||
test('should update the month when next button is clicked', async ({ page }) => {
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
const datetimeMonthDidChange = await page.spyOnEvent('datetimeMonthDidChange');
|
||||
|
||||
const openModalBtn = page.locator('#open-modal');
|
||||
|
||||
await openModalBtn.click();
|
||||
await ionModalDidPresent.next();
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const buttons = page.locator('ion-datetime .calendar-next-prev ion-button');
|
||||
await buttons.nth(1).click();
|
||||
|
||||
await datetimeMonthDidChange.next();
|
||||
|
||||
const monthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(monthYear).toHaveText('March 2022');
|
||||
});
|
||||
|
||||
test('should update the month when prev button is clicked', async ({ page }) => {
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
const datetimeMonthDidChange = await page.spyOnEvent('datetimeMonthDidChange');
|
||||
|
||||
const openModalBtn = page.locator('#open-modal');
|
||||
|
||||
await openModalBtn.click();
|
||||
await ionModalDidPresent.next();
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const buttons = page.locator('ion-datetime .calendar-next-prev ion-button');
|
||||
await buttons.nth(0).click();
|
||||
|
||||
await datetimeMonthDidChange.next();
|
||||
|
||||
const monthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(monthYear).toHaveText('January 2022');
|
||||
});
|
||||
});
|
||||
@@ -1,53 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Datetime - Sub Pixel Width</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
ion-datetime {
|
||||
width: 334.05px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#background {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Datetime - Sub Pixel Width</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<h2>Modal</h2>
|
||||
<ion-button id="open-modal">Present Modal</ion-button>
|
||||
<ion-modal trigger="open-modal" id="modal">
|
||||
<div id="background">
|
||||
<ion-datetime id="picker" value="2022-02-01"></ion-datetime>
|
||||
</div>
|
||||
</ion-modal>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script type="module">
|
||||
import { InitMonthDidChangeEvent } from '../test/utils/month-did-change-event.js';
|
||||
|
||||
document.getElementById('open-modal').addEventListener('click', () => {
|
||||
InitMonthDidChangeEvent();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,55 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.use({
|
||||
viewport: {
|
||||
width: 640,
|
||||
height: 480,
|
||||
},
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
|
||||
/**
|
||||
* This test emulates zoom behavior in the browser to make sure
|
||||
* that key functions of the ion-datetime continue to function even
|
||||
* if the page is zoomed in or out.
|
||||
*/
|
||||
test.describe('datetime: zoom in interactivity', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/datetime/test/zoom');
|
||||
});
|
||||
|
||||
test('should update the month when next button is clicked', async ({ page }) => {
|
||||
const openModalBtn = page.locator('#open-modal');
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
const datetimeMonthDidChange = await page.spyOnEvent('datetimeMonthDidChange');
|
||||
|
||||
await openModalBtn.click();
|
||||
await ionModalDidPresent.next();
|
||||
|
||||
const buttons = page.locator('ion-datetime .calendar-next-prev ion-button');
|
||||
|
||||
await buttons.nth(1).click();
|
||||
await datetimeMonthDidChange.next();
|
||||
|
||||
const monthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(monthYear).toHaveText('March 2022');
|
||||
});
|
||||
|
||||
test('should update the month when prev button is clicked', async ({ page }) => {
|
||||
const openModalBtn = page.locator('#open-modal');
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
const datetimeMonthDidChange = await page.spyOnEvent('datetimeMonthDidChange');
|
||||
|
||||
await openModalBtn.click();
|
||||
await ionModalDidPresent.next();
|
||||
|
||||
const buttons = page.locator('ion-datetime .calendar-next-prev ion-button');
|
||||
|
||||
await buttons.nth(0).click();
|
||||
await datetimeMonthDidChange.next();
|
||||
|
||||
const monthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(monthYear).toHaveText('January 2022');
|
||||
});
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.use({
|
||||
viewport: {
|
||||
width: 640,
|
||||
height: 480,
|
||||
},
|
||||
deviceScaleFactor: 0.75,
|
||||
});
|
||||
|
||||
/**
|
||||
* This test emulates zoom behavior in the browser to make sure
|
||||
* that key functions of the ion-datetime continue to function even
|
||||
* if the page is zoomed in or out.
|
||||
*/
|
||||
test.describe('datetime: zoom out interactivity', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/datetime/test/zoom');
|
||||
});
|
||||
|
||||
test('should update the month when next button is clicked', async ({ page }) => {
|
||||
const openModalBtn = page.locator('#open-modal');
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
const datetimeMonthDidChange = await page.spyOnEvent('datetimeMonthDidChange');
|
||||
|
||||
await openModalBtn.click();
|
||||
await ionModalDidPresent.next();
|
||||
|
||||
const buttons = page.locator('ion-datetime .calendar-next-prev ion-button');
|
||||
|
||||
await buttons.nth(1).click();
|
||||
await datetimeMonthDidChange.next();
|
||||
|
||||
const monthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(monthYear).toHaveText('March 2022');
|
||||
});
|
||||
|
||||
test('should update the month when prev button is clicked', async ({ page }) => {
|
||||
const openModalBtn = page.locator('#open-modal');
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
const datetimeMonthDidChange = await page.spyOnEvent('datetimeMonthDidChange');
|
||||
|
||||
await openModalBtn.click();
|
||||
await ionModalDidPresent.next();
|
||||
|
||||
const buttons = page.locator('ion-datetime .calendar-next-prev ion-button');
|
||||
|
||||
await buttons.nth(0).click();
|
||||
await datetimeMonthDidChange.next();
|
||||
|
||||
const monthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(monthYear).toHaveText('January 2022');
|
||||
});
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Datetime - Zoom</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
#background {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Datetime - Zoom</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<h2>Modal</h2>
|
||||
<ion-button id="open-modal">Present Modal</ion-button>
|
||||
<ion-modal trigger="open-modal" id="modal">
|
||||
<div id="background">
|
||||
<ion-datetime id="picker" value="2022-02-01"></ion-datetime>
|
||||
</div>
|
||||
</ion-modal>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script type="module">
|
||||
import { InitMonthDidChangeEvent } from '../test/utils/month-did-change-event.js';
|
||||
|
||||
document.getElementById('open-modal').addEventListener('click', () => {
|
||||
InitMonthDidChangeEvent();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,6 +5,8 @@ import { close } from 'ionicons/icons';
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import type { AnimationBuilder, Color, RouterDirection } from '../../interface';
|
||||
import type { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
|
||||
import { inheritAriaAttributes } from '../../utils/helpers';
|
||||
import type { Attributes } from '../../utils/helpers';
|
||||
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -23,6 +25,7 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
})
|
||||
export class FabButton implements ComponentInterface, AnchorInterface, ButtonInterface {
|
||||
private fab: HTMLIonFabElement | null = null;
|
||||
private inheritedAttributes: Attributes = {};
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@@ -142,8 +145,12 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
|
||||
fab.toggle();
|
||||
};
|
||||
|
||||
componentWillLoad() {
|
||||
this.inheritedAttributes = inheritAriaAttributes(this.el);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { el, disabled, color, href, activated, show, translucent, size } = this;
|
||||
const { el, disabled, color, href, activated, show, translucent, size, inheritedAttributes } = this;
|
||||
const inList = hostContext('ion-fab-list', el);
|
||||
const mode = getIonMode(this);
|
||||
const TagType = href === undefined ? 'button' : ('a' as any);
|
||||
@@ -182,6 +189,7 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onClick={(ev: Event) => openURL(href, ev, this.routerDirection, this.routerAnimation)}
|
||||
{...inheritedAttributes}
|
||||
>
|
||||
<ion-icon icon={this.closeIcon} part="close-icon" class="close-icon" lazy={false}></ion-icon>
|
||||
<span class="button-inner">
|
||||
|
||||
19
core/src/components/fab-button/test/fab-button.spec.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { FabButton } from '../fab-button';
|
||||
|
||||
describe('fab-button: aria attributes', () => {
|
||||
it('should inherit aria attributes to inner <button>', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [FabButton],
|
||||
html: `
|
||||
<ion-fab-button aria-label="Hello World">My Button</ion-fab-button>
|
||||
`,
|
||||
});
|
||||
|
||||
const fabButton = page.body.querySelector('ion-fab-button');
|
||||
const root = fabButton.shadowRoot;
|
||||
const nativeButton = root.querySelector('button');
|
||||
expect(nativeButton.getAttribute('aria-label')).toEqual('Hello World');
|
||||
});
|
||||
});
|
||||
@@ -496,8 +496,7 @@ export class Input implements ComponentInterface {
|
||||
aria-label="reset"
|
||||
type="button"
|
||||
class="input-clear-icon"
|
||||
onTouchStart={this.clearTextInput}
|
||||
onMouseDown={this.clearTextInput}
|
||||
onPointerDown={this.clearTextInput}
|
||||
onKeyDown={this.clearTextOnEnter}
|
||||
/>
|
||||
)}
|
||||
|
||||
38
core/src/components/input/test/a11y/input.e2e.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('input: a11y', () => {
|
||||
test('does not set a default aria-labelledby when there is not a neighboring ion-label', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.');
|
||||
|
||||
await page.setContent('<ion-input></ion-input>');
|
||||
|
||||
const input = page.locator('ion-input > input');
|
||||
const ariaLabelledBy = await input.getAttribute('aria-labelledby');
|
||||
|
||||
await expect(ariaLabelledBy).toBe(null);
|
||||
});
|
||||
|
||||
test('set a default aria-labelledby when a neighboring ion-label exists', async ({ page }, testInfo) => {
|
||||
test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.');
|
||||
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-item>
|
||||
<ion-label>A11y Test</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
`
|
||||
);
|
||||
|
||||
const label = page.locator('ion-label');
|
||||
const input = page.locator('ion-input > input');
|
||||
|
||||
const ariaLabelledBy = await input.getAttribute('aria-labelledby');
|
||||
const labelId = await label.getAttribute('id');
|
||||
|
||||
await expect(ariaLabelledBy).toBe(labelId);
|
||||
});
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Item } from '../../../item/item';
|
||||
import { Label } from '../../../label/label';
|
||||
import { Input } from '../../input';
|
||||
|
||||
describe('Input a11y', () => {
|
||||
it('does not set a default aria-labelledby when there is not a neighboring ion-label', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Input, Item, Label],
|
||||
html: `<ion-input></ion-input>`,
|
||||
});
|
||||
|
||||
const ariaLabelledBy = page.body.querySelector('ion-input > input').getAttribute('aria-labelledby');
|
||||
expect(ariaLabelledBy).toBe(null);
|
||||
});
|
||||
|
||||
it('set a default aria-labelledby when a neighboring ion-label exists', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Input, Item, Label],
|
||||
html: `<ion-item>
|
||||
<ion-label>A11y Test</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>`,
|
||||
});
|
||||
|
||||
const label = page.body.querySelector('ion-label');
|
||||
const ariaLabelledBy = page.body.querySelector('ion-input > input').getAttribute('aria-labelledby');
|
||||
expect(ariaLabelledBy).toBe(label.id);
|
||||
});
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('input: basic', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/input/test/basic?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compares = [];
|
||||
|
||||
compares.push(await page.compareScreenshot());
|
||||
|
||||
const fullInput = await page.find('#fullInput');
|
||||
await fullInput.click();
|
||||
|
||||
const fullItem = await page.find('#fullItem');
|
||||
expect(fullItem).toHaveClass('item-has-focus');
|
||||
|
||||
compares.push(await page.compareScreenshot('full input focused'));
|
||||
|
||||
const insetInput = await page.find('#insetInput');
|
||||
await insetInput.click();
|
||||
|
||||
const insetItem = await page.find('#insetItem');
|
||||
expect(insetItem).toHaveClass('item-has-focus');
|
||||
|
||||
compares.push(await page.compareScreenshot('inset input focused'));
|
||||
|
||||
const noneInput = await page.find('#noneInput');
|
||||
await noneInput.click();
|
||||
|
||||
const noneItem = await page.find('#noneItem');
|
||||
expect(noneItem).toHaveClass('item-has-focus');
|
||||
|
||||
compares.push(await page.compareScreenshot('no lines input focused'));
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
test('input: basic should not error on input', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/input/test/basic?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const errors = [];
|
||||
|
||||
page.on('console', (msg) => {
|
||||
if (msg.type() === 'error') {
|
||||
errors.push(msg.text());
|
||||
}
|
||||
});
|
||||
|
||||
const inputs = await page.findAll('ion-input');
|
||||
|
||||
for (const input of inputs) {
|
||||
await input.click();
|
||||
await input.type('letters and 12345');
|
||||
}
|
||||
|
||||
expect(errors.length).toEqual(0);
|
||||
});
|
||||
159
core/src/components/input/test/basic/input.e2e.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('input: basic', () => {
|
||||
test.describe('input with overflow', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-input value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
// Validates the display of an input where text extends off the edge of the component.
|
||||
expect(await item.screenshot()).toMatchSnapshot(`input-with-text-overflow-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('input with placeholder', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-input placeholder="Placeholder"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
// Validates the display of an input with a placeholder.
|
||||
expect(await item.screenshot()).toMatchSnapshot(`input-with-placeholder-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('input disabled', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-input value="Input disabled" disabled></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
// Validates the display of an input in a disabled state.
|
||||
expect(await item.screenshot()).toMatchSnapshot(`input-disabled-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('input with lines="full"', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item lines="full">
|
||||
<ion-input placeholder="Full"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
const input = page.locator('ion-input');
|
||||
// Validates the display of an input with an ion-item using lines="full".
|
||||
expect(await item.screenshot()).toMatchSnapshot(`input-with-lines-full-${page.getSnapshotSettings()}.png`);
|
||||
|
||||
await input.click();
|
||||
|
||||
// Verifies that the parent item receives .item-has-focus when the input is focused.
|
||||
await expect(item).toHaveClass(/item-has-focus/);
|
||||
// Validates the display of an input with an ion-item using lines="full" when focused.
|
||||
expect(await item.screenshot()).toMatchSnapshot(
|
||||
`input-with-lines-full-focused-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('input with lines="inset"', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item lines="inset">
|
||||
<ion-input placeholder="Inset"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
const input = page.locator('ion-input');
|
||||
// Validates the display of an input with an ion-item using lines="inset".
|
||||
expect(await item.screenshot()).toMatchSnapshot(`input-with-lines-inset-${page.getSnapshotSettings()}.png`);
|
||||
|
||||
await input.click();
|
||||
|
||||
// Verifies that the parent item receives .item-has-focus when the input is focused.
|
||||
await expect(item).toHaveClass(/item-has-focus/);
|
||||
|
||||
// Validates the display of an input with an ion-item using lines="inset" when focused.
|
||||
expect(await item.screenshot()).toMatchSnapshot(
|
||||
`input-with-lines-inset-focused-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('input with lines="none"', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item lines="none">
|
||||
<ion-input placeholder="None"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
const input = page.locator('ion-input');
|
||||
// Validates the display of an input with an ion-item using lines="none".
|
||||
expect(await item.screenshot()).toMatchSnapshot(`input-with-lines-none-${page.getSnapshotSettings()}.png`);
|
||||
|
||||
await input.click();
|
||||
|
||||
// Verifies that the parent item receives .item-has-focus when the input is focused.
|
||||
await expect(item).toHaveClass(/item-has-focus/);
|
||||
|
||||
// Validates the display of an input with an ion-item using lines="none" when focused.
|
||||
expect(await item.screenshot()).toMatchSnapshot(
|
||||
`input-with-lines-none-focused-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('input with clear button', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label>Clear Input</ion-label>
|
||||
<ion-input
|
||||
clear-input
|
||||
value="reallylonglonglonginputtoseetheedgesreallylonglonglonginputtoseetheedges"
|
||||
></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
// Validates the display of an input with a clear button.
|
||||
expect(await item.screenshot()).toMatchSnapshot(`input-with-clear-button-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 10 KiB |