mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
918edf2f72 | ||
|
|
ba894d05a8 | ||
|
|
9313a914b7 | ||
|
|
27a9aaaedc | ||
|
|
35256d70ec | ||
|
|
ce0767bbb0 | ||
|
|
b1369a94ae | ||
|
|
c98ad6f16a | ||
|
|
f27c899d13 | ||
|
|
16ee234258 | ||
|
|
4804b67785 | ||
|
|
55a3d0fd14 | ||
|
|
df3aaff7c3 | ||
|
|
19c53c471b | ||
|
|
ce863354e4 | ||
|
|
557f6ea97f | ||
|
|
aae9b61f76 | ||
|
|
02d722e9e1 | ||
|
|
0bb84bb1c0 | ||
|
|
fe86d9a586 | ||
|
|
f5e668c390 | ||
|
|
bfe7b38831 | ||
|
|
f62ba3e8a6 | ||
|
|
c3a3961478 | ||
|
|
32ab63ac7d | ||
|
|
dc4165874a | ||
|
|
89f710c6c9 | ||
|
|
9aea956e5b | ||
|
|
ae554165fe | ||
|
|
225fd2ae0e | ||
|
|
d4dc29b1bc | ||
|
|
17b43293bb | ||
|
|
bc05c57c3c | ||
|
|
dbb139afa0 | ||
|
|
e3a0935c95 | ||
|
|
43a61b0e46 | ||
|
|
a6bf51fe2f | ||
|
|
4a25913669 | ||
|
|
945624c0db | ||
|
|
293f128c14 | ||
|
|
87b2aedc94 | ||
|
|
dc0f701c39 | ||
|
|
69ed6d9099 | ||
|
|
a1fca33f2d | ||
|
|
b44ad49a4a | ||
|
|
20d9d97dcf | ||
|
|
642faed272 | ||
|
|
1af71522f3 | ||
|
|
667eb20b4d | ||
|
|
ab992b02c6 | ||
|
|
a667b3e5b3 | ||
|
|
37aa7e9bdb | ||
|
|
ff7e3740ad | ||
|
|
1439ff9c43 | ||
|
|
ea4c24a8af | ||
|
|
4826a3d9f5 | ||
|
|
eed7f50b30 | ||
|
|
db121ac473 | ||
|
|
7e1f996dc6 | ||
|
|
65245826e3 | ||
|
|
8ee16f2a6b | ||
|
|
c9dbbc3ca5 | ||
|
|
d9d11ede0a | ||
|
|
c569761c89 | ||
|
|
e501ac001c | ||
|
|
01e028b789 | ||
|
|
0ac451998c | ||
|
|
c267b43396 | ||
|
|
d425e6d4f3 | ||
|
|
ad02058ef3 | ||
|
|
5a2b87cbcc | ||
|
|
a8749929e0 | ||
|
|
550e166d9c | ||
|
|
beb46bf9de | ||
|
|
6f910576e2 | ||
|
|
678da3db36 | ||
|
|
29bef31e0f | ||
|
|
3dc319e6e9 |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -270,6 +270,8 @@ npm install file:/~/ionic-vue-router-7.0.1.tgz
|
||||
3. If a new test is needed, the easiest way is to copy the `basic/` directory from the component's `test/` directory, rename it, and edit the content in both the `index.html` and `e2e.ts` file (see [Screenshot Tests](#screenshot-tests) for more information on this file).
|
||||
4. The `preview/` directory is used in the documentation as a demo. Only update this test if there is a bug in the test or if the API has a change that hasn't been updated in the test.
|
||||
|
||||
See [Ionic's E2E testing guide](../core/src/utils/test/playwright/docs/README.md) for information regarding the tools you can use to test Ionic.
|
||||
|
||||
##### Screenshot Tests
|
||||
|
||||
1. If the test exists in screenshot, there will be a file named `e2e.ts` in the directory of the test.
|
||||
|
||||
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or https://ionic.link/discord/ -->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[ ] bug report
|
||||
[ ] feature request
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or https://ionic.link/discord -->
|
||||
|
||||
**Current behavior:**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,13 +2,9 @@ Issue number: #
|
||||
|
||||
---------
|
||||
|
||||
<!-- Please refer to our contributing documentation for any questions on submitting a pull request, or let us know here if you need any help: https://ionicframework.com/docs/building/contributing -->
|
||||
<!-- Please do not submit updates to dependencies unless it fixes an issue. -->
|
||||
|
||||
<!-- Some docs updates need to be made in the `ionic-docs` repo, in a separate PR. See https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation for details. -->
|
||||
|
||||
<!-- Please do not submit updates to dependencies unless it fixes an issue. -->
|
||||
|
||||
<!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. -->
|
||||
<!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. -->
|
||||
|
||||
## What is the current behavior?
|
||||
<!-- Please describe the current behavior that you are modifying. -->
|
||||
|
||||
@@ -22,6 +22,9 @@ runs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
- name: Install latest npm
|
||||
run: npm install -g npm@latest
|
||||
shell: bash
|
||||
- name: Install Dependencies
|
||||
run: lerna bootstrap --include-dependencies --scope ${{ inputs.scope }} --ignore-scripts -- --legacy-peer-deps
|
||||
shell: bash
|
||||
@@ -41,6 +44,6 @@ runs:
|
||||
env:
|
||||
NPM_TOKEN: ${{ inputs.token }}
|
||||
- name: Publish to NPM
|
||||
run: npm publish ${{ inputs.folder }} --tag ${{ inputs.tag }}
|
||||
run: npm publish ${{ inputs.folder }} --tag ${{ inputs.tag }} --provenance
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
@@ -29,7 +29,7 @@ runs:
|
||||
shell: bash
|
||||
working-directory: ./angular/test
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
run: npm install --legacy-peer-deps
|
||||
shell: bash
|
||||
working-directory: ./angular/test/build/${{ inputs.app }}
|
||||
- name: Sync Built Changes
|
||||
|
||||
@@ -7,6 +7,9 @@ inputs:
|
||||
description: 'Playwright total number of test shards (ex: 4)'
|
||||
update:
|
||||
description: 'Whether or not to update the reference snapshots'
|
||||
commandModifier:
|
||||
description: 'Command modifier to run. This is should only be used to temporarily run legacy E2E tests as we migrate to the generator infrastructure.'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -24,7 +27,7 @@ runs:
|
||||
working-directory: ./core
|
||||
- name: Test
|
||||
if: inputs.update != 'true'
|
||||
run: npm run test.e2e -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }}
|
||||
run: npm run test.e2e${{ inputs.commandModifier }} -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }}
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
- name: Test and Update
|
||||
@@ -46,14 +49,14 @@ runs:
|
||||
# which is why we not using the upload-archive
|
||||
# composite step here.
|
||||
run: |
|
||||
npm run test.e2e -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }} --update-snapshots
|
||||
npm run test.e2e${{ inputs.commandModifier }} -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }} --update-snapshots
|
||||
git add src/\*.png --force
|
||||
mkdir updated-screenshots
|
||||
cd ../ && rsync -R --progress $(git diff --name-only --cached) core/updated-screenshots
|
||||
if [ -d core/updated-screenshots/core ]; then
|
||||
echo "::set-output name=hasUpdatedScreenshots::$(echo 'true')"
|
||||
echo "hasUpdatedScreenshots=$(echo 'true')" >> $GITHUB_OUTPUT
|
||||
cd core/updated-screenshots
|
||||
zip -q -r ../../UpdatedScreenshots-${{ inputs.shard }}-${{ inputs.totalShards }}.zip core
|
||||
zip -q -r ../../UpdatedScreenshots${{ inputs.commandModifier }}-${{ inputs.shard }}-${{ inputs.totalShards }}.zip core
|
||||
fi
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
@@ -61,8 +64,8 @@ runs:
|
||||
if: inputs.update == 'true' && steps.test-and-update.outputs.hasUpdatedScreenshots == 'true'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: updated-screenshots-${{ inputs.shard }}-${{ inputs.totalShards }}
|
||||
path: UpdatedScreenshots-${{ inputs.shard }}-${{ inputs.totalShards }}.zip
|
||||
name: updated-screenshots${{ inputs.commandModifier }}-${{ inputs.shard }}-${{ inputs.totalShards }}
|
||||
path: UpdatedScreenshots${{ inputs.commandModifier }}-${{ inputs.shard }}-${{ inputs.totalShards }}.zip
|
||||
- name: Archive Test Results
|
||||
# The always() ensures that this step
|
||||
# runs even if the previous step fails.
|
||||
@@ -73,6 +76,6 @@ runs:
|
||||
if: always()
|
||||
uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: test-results-${{ inputs.shard }}-${{ inputs.totalShards }}
|
||||
output: core/TestResults-${{ inputs.shard }}-${{ inputs.totalShards }}.zip
|
||||
name: test-results${{ inputs.commandModifier }}-${{ inputs.shard }}-${{ inputs.totalShards }}
|
||||
output: core/TestResults${{ inputs.commandModifier }}-${{ inputs.shard }}-${{ inputs.totalShards }}.zip
|
||||
paths: core/playwright-report
|
||||
|
||||
41
.github/workflows/build.yml
vendored
41
.github/workflows/build.yml
vendored
@@ -81,6 +81,45 @@ jobs:
|
||||
if: ${{ needs.test-core-screenshot.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
test-core-screenshot-legacy:
|
||||
strategy:
|
||||
matrix:
|
||||
# Divide the tests into n buckets
|
||||
# and run those buckets in parallel.
|
||||
# To increase the number of shards,
|
||||
# add new items to the shard array
|
||||
# and change the value of totalShards
|
||||
# to be the length of the shard array.
|
||||
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||
totalShards: [20]
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-core-screenshot
|
||||
with:
|
||||
shard: ${{ matrix.shard }}
|
||||
totalShards: ${{ matrix.totalShards }}
|
||||
commandModifier: '.legacy'
|
||||
|
||||
# Screenshots are required to pass
|
||||
# in order for the branch to be merge
|
||||
# eligible. However, the screenshot tests
|
||||
# are run on n runners where n can change
|
||||
# over time. The verify-screenshots step allows
|
||||
# us to have a required status check for screenshot
|
||||
# results without having to manually add each
|
||||
# matrix run in the branch protection rules
|
||||
# Source: https://github.community/t/status-check-for-a-matrix-jobs/127354
|
||||
verify-screenshots-legacy:
|
||||
if: ${{ always() }}
|
||||
needs: test-core-screenshot-legacy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check build matrix status
|
||||
if: ${{ needs.test-core-screenshot-legacy.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
build-vue:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
@@ -135,7 +174,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
apps: [ng14, ng15]
|
||||
apps: [ng14, ng15, ng16]
|
||||
needs: [build-angular, build-angular-server]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
4
.github/workflows/dev-build.yml
vendored
4
.github/workflows/dev-build.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
# as lerna will fail when there is a leading 0
|
||||
# See https://github.com/lerna/lerna/issues/2840
|
||||
- name: Install Dependencies
|
||||
run: npm ci --no-package-lock
|
||||
run: npm ci
|
||||
shell: bash
|
||||
- id: create-dev-hash
|
||||
name: Create Dev Hash
|
||||
@@ -24,6 +24,8 @@ jobs:
|
||||
|
||||
release-ionic:
|
||||
needs: [create-dev-hash]
|
||||
permissions:
|
||||
id-token: write
|
||||
uses: ./.github/workflows/release-ionic.yml
|
||||
with:
|
||||
tag: dev
|
||||
|
||||
4
.github/workflows/nightly.yml
vendored
4
.github/workflows/nightly.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
# as lerna will fail when there is a leading 0
|
||||
# See https://github.com/lerna/lerna/issues/2840
|
||||
- name: Install Dependencies
|
||||
run: npm ci --no-package-lock
|
||||
run: npm ci
|
||||
shell: bash
|
||||
- id: create-nightly-hash
|
||||
name: Create Nightly Hash
|
||||
@@ -29,6 +29,8 @@ jobs:
|
||||
|
||||
release-ionic:
|
||||
needs: [create-nightly-hash]
|
||||
permissions:
|
||||
id-token: write
|
||||
uses: ./.github/workflows/release-ionic.yml
|
||||
with:
|
||||
tag: nightly
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -36,6 +36,8 @@ on:
|
||||
|
||||
jobs:
|
||||
release-ionic:
|
||||
permissions:
|
||||
id-token: write
|
||||
uses: ./.github/workflows/release-ionic.yml
|
||||
with:
|
||||
tag: ${{ inputs.tag }}
|
||||
|
||||
39
.github/workflows/stencil-nightly.yml
vendored
39
.github/workflows/stencil-nightly.yml
vendored
@@ -87,6 +87,45 @@ jobs:
|
||||
if: ${{ needs.test-core-screenshot.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
test-core-screenshot-legacy:
|
||||
strategy:
|
||||
matrix:
|
||||
# Divide the tests into n buckets
|
||||
# and run those buckets in parallel.
|
||||
# To increase the number of shards,
|
||||
# add new items to the shard array
|
||||
# and change the value of totalShards
|
||||
# to be the length of the shard array.
|
||||
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||
totalShards: [20]
|
||||
needs: [build-core-with-stencil-nightly]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-core-screenshot
|
||||
with:
|
||||
shard: ${{ matrix.shard }}
|
||||
totalShards: ${{ matrix.totalShards }}
|
||||
commandModifier: '.legacy'
|
||||
|
||||
# Screenshots are required to pass
|
||||
# in order for the branch to be merge
|
||||
# eligible. However, the screenshot tests
|
||||
# are run on n runners where n can change
|
||||
# over time. The verify-screenshots step allows
|
||||
# us to have a required status check for screenshot
|
||||
# results without having to manually add each
|
||||
# matrix run in the branch protection rules
|
||||
# Source: https://github.community/t/status-check-for-a-matrix-jobs/127354
|
||||
verify-screenshots-legacy:
|
||||
if: ${{ always() }}
|
||||
needs: test-core-screenshot-legacy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check build matrix status
|
||||
if: ${{ needs.test-core-screenshot-legacy.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
build-vue:
|
||||
needs: [build-core-with-stencil-nightly]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -3,7 +3,35 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.0.3](https://github.com/ionic-team/ionic-framework/compare/v7.0.1...v7.0.3) (2023-04-19)
|
||||
## [7.0.5](https://github.com/ionic-team/ionic-framework/compare/v7.0.4...v7.0.5) (2023-05-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **many:** form components do not take up full width in slot ([#27306](https://github.com/ionic-team/ionic-framework/issues/27306)) ([bfe7b38](https://github.com/ionic-team/ionic-framework/commit/bfe7b388318aca98014a0748f678e41a0f3910ae)), closes [#27305](https://github.com/ionic-team/ionic-framework/issues/27305)
|
||||
* **scroll-assist:** set correct scroll padding ([#27261](https://github.com/ionic-team/ionic-framework/issues/27261)) ([7e1f996](https://github.com/ionic-team/ionic-framework/commit/7e1f996dc63cd414b30b22aebbfc09b0b6b4f6fc)), closes [#27257](https://github.com/ionic-team/ionic-framework/issues/27257)
|
||||
* **toggle:** swipe gesture applies to knob ([#27255](https://github.com/ionic-team/ionic-framework/issues/27255)) ([6524582](https://github.com/ionic-team/ionic-framework/commit/65245826e3a775bcb8a5d6cfd05230f53470fc66)), closes [#27254](https://github.com/ionic-team/ionic-framework/issues/27254)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.4](https://github.com/ionic-team/ionic-framework/compare/v7.0.3...v7.0.4) (2023-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **breadcumb:** set background when focused on md ([#27274](https://github.com/ionic-team/ionic-framework/issues/27274)) ([01e028b](https://github.com/ionic-team/ionic-framework/commit/01e028b789f84e80f20ce2be7be7f8519f925211)), closes [#27273](https://github.com/ionic-team/ionic-framework/issues/27273)
|
||||
* **ios:** scroll assist sizes input correctly ([#27253](https://github.com/ionic-team/ionic-framework/issues/27253)) ([a874992](https://github.com/ionic-team/ionic-framework/commit/a8749929e01b07043631fbc8c522d39cbc3ae798)), closes [#27249](https://github.com/ionic-team/ionic-framework/issues/27249)
|
||||
* **modal:** set default text color ([#27207](https://github.com/ionic-team/ionic-framework/issues/27207)) ([c267b43](https://github.com/ionic-team/ionic-framework/commit/c267b43396057d9fab344a30bd83d00523911dc1)), closes [#26060](https://github.com/ionic-team/ionic-framework/issues/26060) [/github.com/ionic-team/ionic-framework/blob/main/core/src/components/popover/popover.scss#L42](https://github.com//github.com/ionic-team/ionic-framework/blob/main/core/src/components/popover/popover.scss/issues/L42)
|
||||
* **react:** nav will remove components from the DOM ([#25763](https://github.com/ionic-team/ionic-framework/issues/25763)) ([beb46bf](https://github.com/ionic-team/ionic-framework/commit/beb46bf9def466c4bf54aeed2b5ccdcfcf5cd579)), closes [#25753](https://github.com/ionic-team/ionic-framework/issues/25753)
|
||||
* **select:** adjust label alignment when in a card ([#27202](https://github.com/ionic-team/ionic-framework/issues/27202)) ([5a2b87c](https://github.com/ionic-team/ionic-framework/commit/5a2b87cbcc5c789d02b29e776e2b9768d7ad5631)), closes [#27086](https://github.com/ionic-team/ionic-framework/issues/27086)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.3](https://github.com/ionic-team/ionic-framework/compare/v7.0.2...v7.0.3) (2023-04-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -11,13 +39,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
||||
* **accordion:** inset style respects animated property ([#27173](https://github.com/ionic-team/ionic-framework/issues/27173)) ([114fe28](https://github.com/ionic-team/ionic-framework/commit/114fe28f3cc9ee52bc5eefa569353f490ab01023)), closes [#27047](https://github.com/ionic-team/ionic-framework/issues/27047)
|
||||
* **datetime:** clamp date between min and max when using month picker ([#27185](https://github.com/ionic-team/ionic-framework/issues/27185)) ([0385c08](https://github.com/ionic-team/ionic-framework/commit/0385c0862c98c9387b38d3a4416d74a2cc132ddd)), closes [#27027](https://github.com/ionic-team/ionic-framework/issues/27027)
|
||||
* **input:** string values are emitted ([#27226](https://github.com/ionic-team/ionic-framework/issues/27226)) ([cdb0627](https://github.com/ionic-team/ionic-framework/commit/cdb0627c87299ba36da670c81f9d4e3446bb500d))
|
||||
* **item:** ios active state has correct contrast ([#27134](https://github.com/ionic-team/ionic-framework/issues/27134)) ([bbdb0ca](https://github.com/ionic-team/ionic-framework/commit/bbdb0ca480d7cd46c030d1947ced712653cf122b)), closes [#000](https://github.com/ionic-team/ionic-framework/issues/000) [#000](https://github.com/ionic-team/ionic-framework/issues/000)
|
||||
* **many:** dynamic label support for modern form controls ([#27156](https://github.com/ionic-team/ionic-framework/issues/27156)) ([30b548b](https://github.com/ionic-team/ionic-framework/commit/30b548b167883f0a657b0410d3bcf76dbb6895e0)), closes [#27085](https://github.com/ionic-team/ionic-framework/issues/27085)
|
||||
* **menu:** export menu interfaces ([#27227](https://github.com/ionic-team/ionic-framework/issues/27227)) ([80d8c66](https://github.com/ionic-team/ionic-framework/commit/80d8c667666ffdb6b8e668ef94cc58a93045bd0e))
|
||||
* **menu:** refs are not destroyed on unmount ([#27141](https://github.com/ionic-team/ionic-framework/issues/27141)) ([b81b0d1](https://github.com/ionic-team/ionic-framework/commit/b81b0d14258d7b8caf028e6cfe81772ed2f5f119)), closes [/github.com/ionic-team/ionic-framework/blob/687b37ad3e3237b874473817bb7b59143ac113ce/packages/core/src/components/menu/menu.tsx#L136-L137](https://github.com//github.com/ionic-team/ionic-framework/blob/687b37ad3e3237b874473817bb7b59143ac113ce/packages/core/src/components/menu/menu.tsx/issues/L136-L137)
|
||||
* **radio:** takes up full height in item ([#27168](https://github.com/ionic-team/ionic-framework/issues/27168)) ([987c79f](https://github.com/ionic-team/ionic-framework/commit/987c79f05b6791084c4526d80c8c28a28047dd58)), closes [/github.com/ionic-team/ionic-framework/blob/cb8f07c5530ffc222580a3e3bae8dc85f62c73e5/core/src/components/checkbox/checkbox.scss#L42](https://github.com//github.com/ionic-team/ionic-framework/blob/cb8f07c5530ffc222580a3e3bae8dc85f62c73e5/core/src/components/checkbox/checkbox.scss/issues/L42) [/github.com/ionic-team/ionic-framework/blob/cb8f07c5530ffc222580a3e3bae8dc85f62c73e5/core/src/components/toggle/toggle.scss#L43](https://github.com//github.com/ionic-team/ionic-framework/blob/cb8f07c5530ffc222580a3e3bae8dc85f62c73e5/core/src/components/toggle/toggle.scss/issues/L43)
|
||||
* **segment-button:** update checked state on render ([#26970](https://github.com/ionic-team/ionic-framework/issues/26970)) ([16aa977](https://github.com/ionic-team/ionic-framework/commit/16aa9770bba983705d807ad363498693a0e7969b)), closes [#26830](https://github.com/ionic-team/ionic-framework/issues/26830)
|
||||
* **segment:** segment disables segment buttons created asyncronously ([#27155](https://github.com/ionic-team/ionic-framework/issues/27155)) ([ad6b130](https://github.com/ionic-team/ionic-framework/commit/ad6b1301cf8528f7c9ad3c52730f01861117b380))
|
||||
* **select:** respect --border-radius with modern template ([#27213](https://github.com/ionic-team/ionic-framework/issues/27213)) ([6ffbdbb](https://github.com/ionic-team/ionic-framework/commit/6ffbdbb3b2b69290cf25753d535bc7483bd7c6e8)), closes [#27208](https://github.com/ionic-team/ionic-framework/issues/27208)
|
||||
* **select:** text does not overlap icon ([#27125](https://github.com/ionic-team/ionic-framework/issues/27125)) ([6fc0024](https://github.com/ionic-team/ionic-framework/commit/6fc002458ad23b129a214fd34d3a2fdc33800373)), closes [#27081](https://github.com/ionic-team/ionic-framework/issues/27081)
|
||||
* **textarea:** legacy textarea respects padding ([#27219](https://github.com/ionic-team/ionic-framework/issues/27219)) ([742d429](https://github.com/ionic-team/ionic-framework/commit/742d4295ddfe40c643d9dd21ffc6d9fb3eb6f717)), closes [#27218](https://github.com/ionic-team/ionic-framework/issues/27218)
|
||||
|
||||
@@ -3,7 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.0.3](https://github.com/ionic-team/ionic/compare/v7.0.1...v7.0.3) (2023-04-19)
|
||||
## [7.0.5](https://github.com/ionic-team/ionic-framework/compare/v7.0.4...v7.0.5) (2023-05-03)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.4](https://github.com/ionic-team/ionic-framework/compare/v7.0.3...v7.0.4) (2023-04-26)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.3](https://github.com/ionic-team/ionic/compare/v7.0.2...v7.0.3) (2023-04-19)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Ionic Angular specific building blocks on top of [@ionic/core](https://www.npmjs
|
||||
* [Ionic Forum](https://forum.ionicframework.com/)
|
||||
* [Ionicons](http://ionicons.com/)
|
||||
* [Stencil](https://stenciljs.com/)
|
||||
* [Stencil Worldwide Slack](https://stencil-worldwide.herokuapp.com/)
|
||||
* [Stencil Worldwide Slack](https://stencil-worldwide.slack.com/)
|
||||
* [Capacitor](https://capacitor.ionicframework.com/)
|
||||
|
||||
|
||||
|
||||
34
angular/package-lock.json
generated
34
angular/package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/angular",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ionic/core": "^7.0.3",
|
||||
"@ionic/core": "^7.0.5",
|
||||
"ionicons": "^7.0.0",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
@@ -1227,19 +1227,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@ionic/core": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.2.tgz",
|
||||
"integrity": "sha512-GxQsMmsyBGeK9khIPNR3zw3WvDZj3I0inKtPcd/s9hEnfndAlFFI9gckB7/zrp55xoknCRwqAYT8sGWze3sn2w==",
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.5.tgz",
|
||||
"integrity": "sha512-dcuE/PEF+GHsPEsLppUASSwWnzVcxFZE7uPMDzTwUPMOFTTaRgWbPxIly/4/VRbV6GSL6MEcuVVxhEdWjSODTg==",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^3.2.1",
|
||||
"@stencil/core": "^3.2.2",
|
||||
"ionicons": "^7.1.0",
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ionic/core/node_modules/@stencil/core": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.1.tgz",
|
||||
"integrity": "sha512-Ybm4NteQBScLq3H0JML/uqo4nWjNpZw1HAAURtR5LlRm7ptzNKO5S8EnHp3m05/uyTzeh9yLpUFHY7bxGNdYLg==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.2.tgz",
|
||||
"integrity": "sha512-wXb9cVWL0T3cTwYLveekdTFCRGx6+9hpVDEXna+N8K8OPoW6xtFAHRLv+LjOM7k59PkA8MG3IinAfV7Y+xa0Hw==",
|
||||
"bin": {
|
||||
"stencil": "bin/stencil"
|
||||
},
|
||||
@@ -8104,19 +8104,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.2.tgz",
|
||||
"integrity": "sha512-GxQsMmsyBGeK9khIPNR3zw3WvDZj3I0inKtPcd/s9hEnfndAlFFI9gckB7/zrp55xoknCRwqAYT8sGWze3sn2w==",
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.5.tgz",
|
||||
"integrity": "sha512-dcuE/PEF+GHsPEsLppUASSwWnzVcxFZE7uPMDzTwUPMOFTTaRgWbPxIly/4/VRbV6GSL6MEcuVVxhEdWjSODTg==",
|
||||
"requires": {
|
||||
"@stencil/core": "^3.2.1",
|
||||
"@stencil/core": "^3.2.2",
|
||||
"ionicons": "^7.1.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stencil/core": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.1.tgz",
|
||||
"integrity": "sha512-Ybm4NteQBScLq3H0JML/uqo4nWjNpZw1HAAURtR5LlRm7ptzNKO5S8EnHp3m05/uyTzeh9yLpUFHY7bxGNdYLg=="
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.2.tgz",
|
||||
"integrity": "sha512-wXb9cVWL0T3cTwYLveekdTFCRGx6+9hpVDEXna+N8K8OPoW6xtFAHRLv+LjOM7k59PkA8MG3IinAfV7Y+xa0Hw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.5",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -17,7 +17,7 @@
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ionic-team/ionic.git"
|
||||
"url": "git+https://github.com/ionic-team/ionic-framework.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ionic-team/ionic/issues"
|
||||
@@ -47,7 +47,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/core": "^7.0.3",
|
||||
"@ionic/core": "^7.0.5",
|
||||
"ionicons": "^7.0.0",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"keywords": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ionic-team/ionic.git"
|
||||
"url": "git+https://github.com/ionic-team/ionic-framework.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "umd/index.js",
|
||||
|
||||
@@ -116,7 +116,7 @@ export class IonModal {
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
this.el = r.nativeElement;
|
||||
|
||||
this.el.addEventListener('willPresent', () => {
|
||||
this.el.addEventListener('ionMount', () => {
|
||||
this.isCmpOpen = true;
|
||||
c.detectChanges();
|
||||
});
|
||||
@@ -124,7 +124,6 @@ export class IonModal {
|
||||
this.isCmpOpen = false;
|
||||
c.detectChanges();
|
||||
});
|
||||
|
||||
proxyOutputs(this, this.el, [
|
||||
'ionModalDidPresent',
|
||||
'ionModalWillPresent',
|
||||
|
||||
@@ -977,7 +977,7 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'legacy', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value'],
|
||||
inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'legacy', 'mask', 'maskPlaceholder', 'maskVisibility', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value'],
|
||||
methods: ['setFocus', 'getInputElement']
|
||||
})
|
||||
@Component({
|
||||
@@ -985,7 +985,7 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'legacy', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value'],
|
||||
inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'legacy', 'mask', 'maskPlaceholder', 'maskVisibility', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value'],
|
||||
})
|
||||
export class IonInput {
|
||||
protected el: HTMLElement;
|
||||
|
||||
5
angular/test/apps/ng14/e2e/src/angular-version.spec.ts
Normal file
5
angular/test/apps/ng14/e2e/src/angular-version.spec.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
it("should be on Angular 14", () => {
|
||||
cy.visit('/');
|
||||
|
||||
cy.get('ion-title').contains('Angular 14');
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
<ion-app>
|
||||
<ion-router-outlet></ion-router-outlet>
|
||||
</ion-app>
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent { }
|
||||
5
angular/test/apps/ng15/e2e/src/angular-version.spec.ts
Normal file
5
angular/test/apps/ng15/e2e/src/angular-version.spec.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
it("should be on Angular 15", () => {
|
||||
cy.visit('/');
|
||||
|
||||
cy.get('ion-title').contains('Angular 15');
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
<ion-app>
|
||||
<ion-router-outlet></ion-router-outlet>
|
||||
</ion-app>
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent { }
|
||||
0
angular/test/apps/ng16/e2e/src/.gitkeep
Normal file
0
angular/test/apps/ng16/e2e/src/.gitkeep
Normal file
5
angular/test/apps/ng16/e2e/src/angular-version.spec.ts
Normal file
5
angular/test/apps/ng16/e2e/src/angular-version.spec.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
it("should be on Angular 16", () => {
|
||||
cy.visit('/');
|
||||
|
||||
cy.get('ion-title').contains('Angular 16');
|
||||
});
|
||||
28260
angular/test/apps/ng16/package-lock.json
generated
Normal file
28260
angular/test/apps/ng16/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
70
angular/test/apps/ng16/package.json
Normal file
70
angular/test/apps/ng16/package.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "ionic-angular-test-app",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"sync:build": "sh scripts/build-ionic.sh",
|
||||
"sync": "sh scripts/sync.sh",
|
||||
"build": "ng build --configuration production --no-progress",
|
||||
"lint": "ng lint",
|
||||
"serve:ssr": "node dist/test-app/server/main.js",
|
||||
"build:ssr": "ng build --prod && ng run test-app:server:production",
|
||||
"dev:ssr": "ng run test-app:serve-ssr",
|
||||
"prerender": "ng run test-app:prerender",
|
||||
"cy.open": "cypress open",
|
||||
"cy.run": "cypress run",
|
||||
"test": "concurrently \"npm run start -- --configuration production\" \"wait-on http-get://localhost:4200 && npm run cy.run\" --kill-others --success first",
|
||||
"test.watch": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.open\" --kill-others --success first"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^16.0.0-rc.0",
|
||||
"@angular/common": "^16.0.0-rc.0",
|
||||
"@angular/compiler": "^16.0.0-rc.0",
|
||||
"@angular/core": "^16.0.0-rc.0",
|
||||
"@angular/forms": "^16.0.0-rc.0",
|
||||
"@angular/platform-browser": "^16.0.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^16.0.0-rc.0",
|
||||
"@angular/platform-server": "^16.0.0-rc.0",
|
||||
"@angular/router": "^16.0.0-rc.0",
|
||||
"@ionic/angular": "^7.0.0",
|
||||
"@ionic/angular-server": "^7.0.0",
|
||||
"@nguniversal/express-engine": "^15.0.0",
|
||||
"core-js": "^2.6.11",
|
||||
"express": "^4.15.2",
|
||||
"ionicons": "^6.0.4",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"typescript-eslint-language-service": "^4.1.5",
|
||||
"zone.js": "~0.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^16.0.0-rc.0",
|
||||
"@angular-eslint/builder": "^15.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^15.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^15.0.0",
|
||||
"@angular-eslint/schematics": "^15.0.0",
|
||||
"@angular-eslint/template-parser": "^15.0.0",
|
||||
"@angular/cli": "^16.0.0-rc.0",
|
||||
"@angular/compiler-cli": "^16.0.0-rc.0",
|
||||
"@angular/language-service": "^16.0.0-rc.0",
|
||||
"@nguniversal/builders": "^15.0.0",
|
||||
"@types/express": "^4.17.7",
|
||||
"@types/node": "^12.12.54",
|
||||
"@typescript-eslint/eslint-plugin": "4.28.2",
|
||||
"@typescript-eslint/parser": "4.28.2",
|
||||
"concurrently": "^6.0.0",
|
||||
"cypress": "^10.2.0",
|
||||
"eslint": "^7.26.0",
|
||||
"ts-loader": "^6.2.2",
|
||||
"ts-node": "^8.3.0",
|
||||
"typescript": "~5.0.2",
|
||||
"wait-on": "^5.2.1",
|
||||
"webpack": "^5.61.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([])
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class VersionTestRoutingModule { }
|
||||
35
angular/test/apps/ng16/tsconfig.json
Normal file
35
angular/test/apps/ng16/tsconfig.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": false,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"emitDecoratorMetadata": true,
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"lib": ["ES2022", "dom"],
|
||||
"plugins": [
|
||||
{
|
||||
"name": "typescript-eslint-language-service"
|
||||
}
|
||||
],
|
||||
"useDefineForClassFields": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"aot": true,
|
||||
"outputPath": "dist/test-app/browser",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
@@ -64,8 +63,17 @@
|
||||
"maximumWarning": "6kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
@@ -75,8 +83,12 @@
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "test-app:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "test-app:build:development"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
@@ -136,9 +148,7 @@
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"schematicCollections": [
|
||||
"@angular-eslint/schematics"
|
||||
],
|
||||
"schematicCollections": ["@angular-eslint/schematics"],
|
||||
"cache": {
|
||||
"enabled": false
|
||||
}
|
||||
|
||||
@@ -121,4 +121,4 @@ describe('when in a modal', () => {
|
||||
cy.get('#set-to-null').click();
|
||||
cy.get('#inputWithFloatingLabel').should('not.have.class', 'item-has-value');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,7 +15,7 @@ npm pack ../../../dist
|
||||
npm pack ../../../../packages/angular-server/dist
|
||||
|
||||
# Install Dependencies
|
||||
npm install *.tgz --no-save
|
||||
npm install *.tgz --no-save --legacy-peer-deps
|
||||
|
||||
# Delete Angular cache directory
|
||||
rm -rf .angular/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Test App
|
||||
Test App - Angular {{ angularVersion.major }}
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, VERSION } from '@angular/core';
|
||||
import { AnimationBuilder, AnimationController } from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
@@ -6,6 +6,9 @@ import { AnimationBuilder, AnimationController } from '@ionic/angular';
|
||||
templateUrl: './home-page.component.html',
|
||||
})
|
||||
export class HomePageComponent {
|
||||
|
||||
angularVersion = VERSION;
|
||||
|
||||
routerAnimation: AnimationBuilder = (_, opts) => {
|
||||
const { direction, enteringEl, leavingEl } = opts;
|
||||
const animation = this.animationCtrl.create().duration(500).easing('ease-out');
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
</ion-modal>
|
||||
@@ -24,4 +24,5 @@ export class ModalInlineComponent implements AfterViewInit {
|
||||
onBreakpointDidChange() {
|
||||
this.breakpointDidChangeCounter++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags.ts';
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
@@ -42,7 +42,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import './zone-flags.ts';
|
||||
import './zone-flags';
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
|
||||
@@ -3,6 +3,33 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.0.5](https://github.com/ionic-team/ionic-framework/compare/v7.0.4...v7.0.5) (2023-05-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **many:** form components do not take up full width in slot ([#27306](https://github.com/ionic-team/ionic-framework/issues/27306)) ([bfe7b38](https://github.com/ionic-team/ionic-framework/commit/bfe7b388318aca98014a0748f678e41a0f3910ae)), closes [#27305](https://github.com/ionic-team/ionic-framework/issues/27305)
|
||||
* **scroll-assist:** set correct scroll padding ([#27261](https://github.com/ionic-team/ionic-framework/issues/27261)) ([7e1f996](https://github.com/ionic-team/ionic-framework/commit/7e1f996dc63cd414b30b22aebbfc09b0b6b4f6fc)), closes [#27257](https://github.com/ionic-team/ionic-framework/issues/27257)
|
||||
* **toggle:** swipe gesture applies to knob ([#27255](https://github.com/ionic-team/ionic-framework/issues/27255)) ([6524582](https://github.com/ionic-team/ionic-framework/commit/65245826e3a775bcb8a5d6cfd05230f53470fc66)), closes [#27254](https://github.com/ionic-team/ionic-framework/issues/27254)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.4](https://github.com/ionic-team/ionic-framework/compare/v7.0.3...v7.0.4) (2023-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **breadcumb:** set background when focused on md ([#27274](https://github.com/ionic-team/ionic-framework/issues/27274)) ([01e028b](https://github.com/ionic-team/ionic-framework/commit/01e028b789f84e80f20ce2be7be7f8519f925211)), closes [#27273](https://github.com/ionic-team/ionic-framework/issues/27273)
|
||||
* **ios:** scroll assist sizes input correctly ([#27253](https://github.com/ionic-team/ionic-framework/issues/27253)) ([a874992](https://github.com/ionic-team/ionic-framework/commit/a8749929e01b07043631fbc8c522d39cbc3ae798)), closes [#27249](https://github.com/ionic-team/ionic-framework/issues/27249)
|
||||
* **modal:** set default text color ([#27207](https://github.com/ionic-team/ionic-framework/issues/27207)) ([c267b43](https://github.com/ionic-team/ionic-framework/commit/c267b43396057d9fab344a30bd83d00523911dc1)), closes [#26060](https://github.com/ionic-team/ionic-framework/issues/26060) [/github.com/ionic-team/ionic-framework/blob/main/core/src/components/popover/popover.scss#L42](https://github.com//github.com/ionic-team/ionic-framework/blob/main/core/src/components/popover/popover.scss/issues/L42)
|
||||
* **select:** adjust label alignment when in a card ([#27202](https://github.com/ionic-team/ionic-framework/issues/27202)) ([5a2b87c](https://github.com/ionic-team/ionic-framework/commit/5a2b87cbcc5c789d02b29e776e2b9768d7ad5631)), closes [#27086](https://github.com/ionic-team/ionic-framework/issues/27086)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.3](https://github.com/ionic-team/ionic/compare/v7.0.1...v7.0.3) (2023-04-19)
|
||||
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ const showModal = async () => {
|
||||
* [Ionic Forum](https://forum.ionicframework.com/)
|
||||
* [Ionicons](http://ionicons.com/)
|
||||
* [Stencil](https://stenciljs.com/)
|
||||
* [Stencil Worldwide Slack](https://stencil-worldwide.herokuapp.com/)
|
||||
* [Stencil Worldwide Slack](https://stencil-worldwide.slack.com/)
|
||||
* [Capacitor](https://capacitor.ionicframework.com/)
|
||||
|
||||
|
||||
|
||||
@@ -553,6 +553,9 @@ ion-input,prop,inputmode,"decimal" | "email" | "none" | "numeric" | "search" | "
|
||||
ion-input,prop,label,string | undefined,undefined,false,false
|
||||
ion-input,prop,labelPlacement,"end" | "fixed" | "floating" | "stacked" | "start",'start',false,false
|
||||
ion-input,prop,legacy,boolean | undefined,undefined,false,false
|
||||
ion-input,prop,mask,(string | RegExp)[] | RegExp | undefined,undefined,false,false
|
||||
ion-input,prop,maskPlaceholder,null | string | undefined,'_',false,false
|
||||
ion-input,prop,maskVisibility,"always" | "focus" | "never" | undefined,'always',false,false
|
||||
ion-input,prop,max,number | string | undefined,undefined,false,false
|
||||
ion-input,prop,maxlength,number | undefined,undefined,false,false
|
||||
ion-input,prop,min,number | string | undefined,undefined,false,false
|
||||
|
||||
18
core/package-lock.json
generated
18
core/package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/core",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^3.2.1",
|
||||
"@stencil/core": "^3.2.2",
|
||||
"ionicons": "^7.1.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -1594,9 +1594,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@stencil/core": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.1.tgz",
|
||||
"integrity": "sha512-Ybm4NteQBScLq3H0JML/uqo4nWjNpZw1HAAURtR5LlRm7ptzNKO5S8EnHp3m05/uyTzeh9yLpUFHY7bxGNdYLg==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.2.tgz",
|
||||
"integrity": "sha512-wXb9cVWL0T3cTwYLveekdTFCRGx6+9hpVDEXna+N8K8OPoW6xtFAHRLv+LjOM7k59PkA8MG3IinAfV7Y+xa0Hw==",
|
||||
"bin": {
|
||||
"stencil": "bin/stencil"
|
||||
},
|
||||
@@ -11467,9 +11467,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"@stencil/core": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.1.tgz",
|
||||
"integrity": "sha512-Ybm4NteQBScLq3H0JML/uqo4nWjNpZw1HAAURtR5LlRm7ptzNKO5S8EnHp3m05/uyTzeh9yLpUFHY7bxGNdYLg=="
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.2.2.tgz",
|
||||
"integrity": "sha512-wXb9cVWL0T3cTwYLveekdTFCRGx6+9hpVDEXna+N8K8OPoW6xtFAHRLv+LjOM7k59PkA8MG3IinAfV7Y+xa0Hw=="
|
||||
},
|
||||
"@stencil/react-output-target": {
|
||||
"version": "0.5.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.5",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -31,7 +31,7 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@stencil/core": "^3.2.1",
|
||||
"@stencil/core": "^3.2.2",
|
||||
"ionicons": "^7.1.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -90,7 +90,8 @@
|
||||
"test": "npm run test.spec && npm run test.e2e",
|
||||
"test.spec": "stencil test --spec --max-workers=2",
|
||||
"test.spec.debug": "npx --node-arg=\"--inspect-brk\" stencil test --spec",
|
||||
"test.e2e": "npx playwright test",
|
||||
"test.e2e": "npx playwright test --config playwright.config.ts",
|
||||
"test.e2e.legacy": "npx playwright test --config playwright.config-legacy.ts",
|
||||
"test.watch": "jest --watch --no-cache",
|
||||
"test.treeshake": "node scripts/treeshaking.js dist/index.js",
|
||||
"validate": "npm run lint && npm run test && npm run build && npm run test.treeshake"
|
||||
@@ -99,7 +100,7 @@
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ionic-team/ionic.git"
|
||||
"url": "git+https://github.com/ionic-team/ionic-framework.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ionic-team/ionic/issues"
|
||||
|
||||
119
core/playwright.config-legacy.ts
Normal file
119
core/playwright.config-legacy.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { PlaywrightTestConfig, PlaywrightTestOptions, PlaywrightWorkerOptions, Project } from '@playwright/test';
|
||||
import { devices, expect } from '@playwright/test';
|
||||
|
||||
import { matchers } from './src/utils/test/playwright';
|
||||
|
||||
expect.extend(matchers);
|
||||
|
||||
const projects: Project<PlaywrightTestOptions, PlaywrightWorkerOptions>[] = [
|
||||
{
|
||||
/**
|
||||
* This is really just desktop Firefox
|
||||
* but with a mobile viewport.
|
||||
*/
|
||||
name: 'Mobile Firefox',
|
||||
use: {
|
||||
browserName: 'firefox',
|
||||
/**
|
||||
* This is the Pixel 5 configuration.
|
||||
* We can't use devices['Pixel 5']
|
||||
* because the "isMobile" option is
|
||||
* not supported on Firefox.
|
||||
*/
|
||||
viewport: {
|
||||
width: 393,
|
||||
height: 727
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Mobile Chrome',
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
...devices['Pixel 5']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Mobile Safari',
|
||||
use: {
|
||||
browserName: 'webkit',
|
||||
...devices['iPhone 12']
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const modes = ['ios', 'md'];
|
||||
|
||||
const generateProjects = () => {
|
||||
const projectsWithMetadata: Project<PlaywrightTestOptions, PlaywrightWorkerOptions>[] = [];
|
||||
|
||||
modes.forEach(mode => {
|
||||
projects.forEach(project => {
|
||||
projectsWithMetadata.push({
|
||||
...project,
|
||||
metadata: {
|
||||
mode,
|
||||
rtl: false,
|
||||
_testing: true
|
||||
}
|
||||
});
|
||||
projectsWithMetadata.push({
|
||||
...project,
|
||||
metadata: {
|
||||
mode,
|
||||
rtl: true,
|
||||
_testing: true
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return projectsWithMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
const config: PlaywrightTestConfig = {
|
||||
testMatch: '*.e2e-legacy.ts',
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000
|
||||
},
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Fail fast on CI */
|
||||
maxFailures: process.env.CI ? 1 : 0,
|
||||
/* Flaky test should be either addressed or disabled until we can address them */
|
||||
retries: 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/**
|
||||
* All failed tests should create
|
||||
* a trace file for easier debugging.
|
||||
*
|
||||
* See https://playwright.dev/docs/trace-viewer
|
||||
*/
|
||||
trace: 'retain-on-failure',
|
||||
baseURL: 'http://localhost:3333',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: generateProjects(),
|
||||
webServer: {
|
||||
command: 'serve -p 3333',
|
||||
port: 3333,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -42,35 +42,6 @@ const projects: Project<PlaywrightTestOptions, PlaywrightWorkerOptions>[] = [
|
||||
}
|
||||
];
|
||||
|
||||
const modes = ['ios', 'md'];
|
||||
|
||||
const generateProjects = () => {
|
||||
const projectsWithMetadata: Project<PlaywrightTestOptions, PlaywrightWorkerOptions>[] = [];
|
||||
|
||||
modes.forEach(mode => {
|
||||
projects.forEach(project => {
|
||||
projectsWithMetadata.push({
|
||||
...project,
|
||||
metadata: {
|
||||
mode,
|
||||
rtl: false,
|
||||
_testing: true
|
||||
}
|
||||
});
|
||||
projectsWithMetadata.push({
|
||||
...project,
|
||||
metadata: {
|
||||
mode,
|
||||
rtl: true,
|
||||
_testing: true
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return projectsWithMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
@@ -108,7 +79,7 @@ const config: PlaywrightTestConfig = {
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: generateProjects(),
|
||||
projects,
|
||||
webServer: {
|
||||
command: 'serve -p 3333',
|
||||
port: 3333,
|
||||
|
||||
36
core/src/components.d.ts
vendored
36
core/src/components.d.ts
vendored
@@ -17,6 +17,7 @@ import { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interf
|
||||
import { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface";
|
||||
import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimePresentation, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
|
||||
import { SpinnerTypes } from "./components/spinner/spinner-configs";
|
||||
import { MaskExpression, MaskPlaceholder, MaskVisibility } from "./utils/input-masking/public-api";
|
||||
import { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
|
||||
import { CounterFormatter } from "./components/item/item-interface";
|
||||
import { MenuChangeEventDetail, Side } from "./components/menu/menu-interface";
|
||||
@@ -53,6 +54,7 @@ export { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interf
|
||||
export { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface";
|
||||
export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimePresentation, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
|
||||
export { SpinnerTypes } from "./components/spinner/spinner-configs";
|
||||
export { MaskExpression, MaskPlaceholder, MaskVisibility } from "./utils/input-masking/public-api";
|
||||
export { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
|
||||
export { CounterFormatter } from "./components/item/item-interface";
|
||||
export { MenuChangeEventDetail, Side } from "./components/menu/menu-interface";
|
||||
@@ -1225,6 +1227,18 @@ export namespace Components {
|
||||
* Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt components in to the modern form markup when they are using either the `aria-label` attribute or the `label` property. As a result, the `legacy` property should only be used as an escape hatch when you want to avoid this automatic opt-in behavior. Note that this property will be removed in an upcoming major release of Ionic, and all form components will be opted-in to using the modern form markup.
|
||||
*/
|
||||
"legacy"?: boolean;
|
||||
/**
|
||||
* The predefined format of the user's input. For example, you can set a mask that only accepts digits, or you can configure a more complex pattern like a phone number or credit card number. The mask supports two formats: 1. A valid [regular expression pattern](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) 2. An array containing regular expression and fixed character patterns The fixed characters in the mask cannot be erased or replaced by the user. For example in a phone number mask, the `(`, `)` and `-` are examples of fixed characters.
|
||||
*/
|
||||
"mask"?: MaskExpression;
|
||||
/**
|
||||
* Character or string to cover unfilled parts of the mask. The default character is `_`. If set to `null`, `undefined` or an empty string, unfilled parts will be empty as in a regular input.
|
||||
*/
|
||||
"maskPlaceholder"?: MaskPlaceholder;
|
||||
/**
|
||||
* The visibility of the mask placeholder. With `always`, the placeholder will be visible even when the control does not have focus. With `focus`, the placeholder will only be visible when the control has focus. With `never`, the placeholder will never be visibly displayed.
|
||||
*/
|
||||
"maskVisibility"?: MaskVisibility;
|
||||
/**
|
||||
* The maximum value, which must not be less than its minimum (min attribute) value.
|
||||
*/
|
||||
@@ -2888,16 +2902,16 @@ export namespace Components {
|
||||
interface IonTabs {
|
||||
"getRouteId": () => Promise<RouteID | undefined>;
|
||||
/**
|
||||
* Get the currently selected tab.
|
||||
* Get the currently selected tab. This method is only available for vanilla JavaScript projects. The Angular, React, and Vue implementations of tabs are coupled to each framework's router.
|
||||
*/
|
||||
"getSelected": () => Promise<string | undefined>;
|
||||
/**
|
||||
* Get a specific tab by the value of its `tab` property or an element reference.
|
||||
* Get a specific tab by the value of its `tab` property or an element reference. This method is only available for vanilla JavaScript projects. The Angular, React, and Vue implementations of tabs are coupled to each framework's router.
|
||||
* @param tab The tab instance to select. If passed a string, it should be the value of the tab's `tab` property.
|
||||
*/
|
||||
"getTab": (tab: string | HTMLIonTabElement) => Promise<HTMLIonTabElement | undefined>;
|
||||
/**
|
||||
* Select a tab by the value of its `tab` property or an element reference.
|
||||
* Select a tab by the value of its `tab` property or an element reference. This method is only available for vanilla JavaScript projects. The Angular, React, and Vue implementations of tabs are coupled to each framework's router.
|
||||
* @param tab The tab instance to select. If passed a string, it should be the value of the tab's `tab` property.
|
||||
*/
|
||||
"select": (tab: string | HTMLIonTabElement) => Promise<boolean>;
|
||||
@@ -5255,6 +5269,18 @@ declare namespace LocalJSX {
|
||||
* Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt components in to the modern form markup when they are using either the `aria-label` attribute or the `label` property. As a result, the `legacy` property should only be used as an escape hatch when you want to avoid this automatic opt-in behavior. Note that this property will be removed in an upcoming major release of Ionic, and all form components will be opted-in to using the modern form markup.
|
||||
*/
|
||||
"legacy"?: boolean;
|
||||
/**
|
||||
* The predefined format of the user's input. For example, you can set a mask that only accepts digits, or you can configure a more complex pattern like a phone number or credit card number. The mask supports two formats: 1. A valid [regular expression pattern](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) 2. An array containing regular expression and fixed character patterns The fixed characters in the mask cannot be erased or replaced by the user. For example in a phone number mask, the `(`, `)` and `-` are examples of fixed characters.
|
||||
*/
|
||||
"mask"?: MaskExpression;
|
||||
/**
|
||||
* Character or string to cover unfilled parts of the mask. The default character is `_`. If set to `null`, `undefined` or an empty string, unfilled parts will be empty as in a regular input.
|
||||
*/
|
||||
"maskPlaceholder"?: MaskPlaceholder;
|
||||
/**
|
||||
* The visibility of the mask placeholder. With `always`, the placeholder will be visible even when the control does not have focus. With `focus`, the placeholder will only be visible when the control has focus. With `never`, the placeholder will never be visibly displayed.
|
||||
*/
|
||||
"maskVisibility"?: MaskVisibility;
|
||||
/**
|
||||
* The maximum value, which must not be less than its minimum (min attribute) value.
|
||||
*/
|
||||
@@ -5830,6 +5856,10 @@ declare namespace LocalJSX {
|
||||
* Emitted before the modal has presented.
|
||||
*/
|
||||
"onIonModalWillPresent"?: (event: IonModalCustomEvent<void>) => void;
|
||||
/**
|
||||
* Emitted before the modal has presented, but after the component has been mounted in the DOM. This event exists so iOS can run the entering transition properly
|
||||
*/
|
||||
"onIonMount"?: (event: IonModalCustomEvent<void>) => void;
|
||||
/**
|
||||
* Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss.
|
||||
*/
|
||||
|
||||
@@ -1,44 +1,46 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('accordion: a11y', () => {
|
||||
test('accordions should be keyboard navigable', async ({ page, skip, browserName }) => {
|
||||
// TODO(FW-1764): remove skip once issue is resolved
|
||||
skip.browser('firefox', 'https://github.com/ionic-team/ionic-framework/issues/25070');
|
||||
// TODO (FW-2979)
|
||||
skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.');
|
||||
configs().forEach(({ config, title }) => {
|
||||
test.describe(title('accordion: a11y'), () => {
|
||||
test('accordions should be keyboard navigable', async ({ page, skip, browserName }) => {
|
||||
// TODO(FW-1764): remove skip once issue is resolved
|
||||
skip.browser('firefox', 'https://github.com/ionic-team/ionic-framework/issues/25070');
|
||||
// TODO (FW-2979)
|
||||
skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.');
|
||||
|
||||
await page.goto(`/src/components/accordion/test/a11y`);
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
await page.goto(`/src/components/accordion/test/a11y`, config);
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
const personalInfoHeader = page.locator('ion-accordion:first-child > ion-item');
|
||||
const billingAddressHeader = page.locator('ion-accordion:nth-child(2) > ion-item');
|
||||
const shippingAddressHeader = page.locator('ion-accordion:nth-child(3) > ion-item');
|
||||
const addressInput = page.locator('#address1 input');
|
||||
const personalInfoHeader = page.locator('ion-accordion:first-child > ion-item');
|
||||
const billingAddressHeader = page.locator('ion-accordion:nth-child(2) > ion-item');
|
||||
const shippingAddressHeader = page.locator('ion-accordion:nth-child(3) > ion-item');
|
||||
const addressInput = page.locator('#address1 input');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(personalInfoHeader).toBeFocused();
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(personalInfoHeader).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(billingAddressHeader).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(billingAddressHeader).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(shippingAddressHeader).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(shippingAddressHeader).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(personalInfoHeader).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(personalInfoHeader).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(shippingAddressHeader).toBeFocused();
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(shippingAddressHeader).toBeFocused();
|
||||
|
||||
// open Shipping Address accordion and move focus to the input inside it
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForChanges();
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(addressInput).toBeFocused();
|
||||
// open Shipping Address accordion and move focus to the input inside it
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForChanges();
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(addressInput).toBeFocused();
|
||||
|
||||
// ensure keyboard interaction doesn't move focus from body
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(addressInput).toBeFocused();
|
||||
// ensure keyboard interaction doesn't move focus from body
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(addressInput).toBeFocused();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,56 +1,49 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('accordion: states', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('ios');
|
||||
});
|
||||
test('should properly set readonly on child accordions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('accordion: states'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-accordion-group animated="false">
|
||||
<ion-accordion>
|
||||
<ion-item slot="header">Label</ion-item>
|
||||
<div slot="content">Content</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
});
|
||||
test('should properly set readonly on child accordions', async ({ page }) => {
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
const accordion = page.locator('ion-accordion');
|
||||
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
const accordion = page.locator('ion-accordion');
|
||||
await expect(accordion).toHaveJSProperty('readonly', false);
|
||||
|
||||
await expect(accordion).toHaveJSProperty('readonly', false);
|
||||
await accordionGroup.evaluate((el: HTMLIonAccordionGroupElement) => {
|
||||
el.readonly = true;
|
||||
});
|
||||
|
||||
await accordionGroup.evaluate((el: HTMLIonAccordionGroupElement) => {
|
||||
el.readonly = true;
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(accordion).toHaveJSProperty('readonly', true);
|
||||
});
|
||||
|
||||
await page.waitForChanges();
|
||||
test('should properly set disabled on child accordions', async ({ page }) => {
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
const accordion = page.locator('ion-accordion');
|
||||
|
||||
await expect(accordion).toHaveJSProperty('readonly', true);
|
||||
});
|
||||
await expect(accordion).toHaveJSProperty('disabled', false);
|
||||
|
||||
test('should properly set disabled on child accordions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-accordion-group animated="false">
|
||||
<ion-accordion>
|
||||
<ion-item slot="header">Label</ion-item>
|
||||
<div slot="content">Content</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
`);
|
||||
await accordionGroup.evaluate((el: HTMLIonAccordionGroupElement) => {
|
||||
el.disabled = true;
|
||||
});
|
||||
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
const accordion = page.locator('ion-accordion');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(accordion).toHaveJSProperty('disabled', false);
|
||||
|
||||
await accordionGroup.evaluate((el: HTMLIonAccordionGroupElement) => {
|
||||
el.disabled = true;
|
||||
await expect(accordion).toHaveJSProperty('disabled', true);
|
||||
});
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(accordion).toHaveJSProperty('disabled', true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,53 +1,22 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('accordion: basic', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/basic`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('accordion: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/basic`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`accordion-basic-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot('accordion-basic'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('accordion: ionChange', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
test('should fire ionChange when interacting with accordions', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-accordion-group value="second">
|
||||
<ion-accordion value="first">
|
||||
<button slot="header">Header</button>
|
||||
<div slot="content">First Content</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="second">
|
||||
<button slot="header">Header</button>
|
||||
<div slot="content">Second Content</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="third">
|
||||
<button slot="header">Header</button>
|
||||
<div slot="content">Third Content</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
`);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const accordionHeaders = page.locator('button[slot="header"]');
|
||||
|
||||
await accordionHeaders.nth(0).click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'first' });
|
||||
|
||||
await accordionHeaders.nth(1).click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'second' });
|
||||
|
||||
await accordionHeaders.nth(2).click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'third' });
|
||||
});
|
||||
|
||||
test('should not fire when programmatically setting a valid value', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('accordion: ionChange'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-accordion-group>
|
||||
<ion-accordion value="first">
|
||||
<button slot="header">Header</button>
|
||||
@@ -62,13 +31,30 @@ test.describe('accordion: ionChange', () => {
|
||||
<div slot="content">Third Content</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
});
|
||||
test('should fire ionChange when interacting with accordions', async ({ page }) => {
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const accordionHeaders = page.locator('button[slot="header"]');
|
||||
|
||||
`);
|
||||
await accordionHeaders.nth(0).click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'first' });
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
await accordionHeaders.nth(1).click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'second' });
|
||||
|
||||
await accordionGroup.evaluate((el: HTMLIonAccordionGroupElement) => (el.value = 'second'));
|
||||
await expect(ionChange).not.toHaveReceivedEvent();
|
||||
await accordionHeaders.nth(2).click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'third' });
|
||||
});
|
||||
|
||||
test('should not fire when programmatically setting a valid value', async ({ page }) => {
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
|
||||
await accordionGroup.evaluate((el: HTMLIonAccordionGroupElement) => (el.value = 'second'));
|
||||
await expect(ionChange).not.toHaveReceivedEvent();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('accordion: multiple', () => {
|
||||
test('should update value and visually expand items', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('accordion: multiple'), () => {
|
||||
test('should update value and visually expand items', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/multiple`, config);
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
const diningHeader = page.locator('ion-accordion[value="dining"] ion-item[slot="header"]');
|
||||
const attractionsHeader = page.locator('ion-accordion[value="attractions"] ion-item[slot="header"]');
|
||||
|
||||
await page.goto(`/src/components/accordion/test/multiple`);
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
const diningHeader = page.locator('ion-accordion[value="dining"] ion-item[slot="header"]');
|
||||
const attractionsHeader = page.locator('ion-accordion[value="attractions"] ion-item[slot="header"]');
|
||||
await expect(accordionGroup).toHaveJSProperty('value', 'attractions');
|
||||
|
||||
await expect(accordionGroup).toHaveJSProperty('value', 'attractions');
|
||||
await expect(accordionGroup).toHaveScreenshot(screenshot('accordion-one-open'));
|
||||
|
||||
await expect(accordionGroup).toHaveScreenshot(`accordion-one-open-${page.getSnapshotSettings()}.png`);
|
||||
await diningHeader.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await diningHeader.click();
|
||||
await page.waitForChanges();
|
||||
await expect(accordionGroup).toHaveJSProperty('value', ['attractions', 'dining']);
|
||||
|
||||
await expect(accordionGroup).toHaveJSProperty('value', ['attractions', 'dining']);
|
||||
await expect(accordionGroup).toHaveScreenshot(screenshot('accordion-two-open'));
|
||||
|
||||
await expect(accordionGroup).toHaveScreenshot(`accordion-two-open-${page.getSnapshotSettings()}.png`);
|
||||
await diningHeader.click();
|
||||
await attractionsHeader.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await diningHeader.click();
|
||||
await attractionsHeader.click();
|
||||
await page.waitForChanges();
|
||||
await expect(accordionGroup).toHaveJSProperty('value', []);
|
||||
|
||||
await expect(accordionGroup).toHaveJSProperty('value', []);
|
||||
|
||||
await expect(accordionGroup).toHaveScreenshot(`accordion-zero-open-${page.getSnapshotSettings()}.png`);
|
||||
await expect(accordionGroup).toHaveScreenshot(screenshot('accordion-zero-open'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('accordion: nested', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('accordion: nested'), () => {
|
||||
test('parent and child should not be disabled', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/nested`, config);
|
||||
|
||||
await page.goto(`/src/components/accordion/test/nested`);
|
||||
});
|
||||
const enabledGroup = page.locator('ion-accordion-group#enabled');
|
||||
|
||||
test('parent and child should not be disabled', async ({ page }) => {
|
||||
const enabledGroup = page.locator('ion-accordion-group#enabled');
|
||||
await expect(enabledGroup).toHaveScreenshot(screenshot('accordion-nested-enabled'));
|
||||
});
|
||||
|
||||
await expect(enabledGroup).toHaveScreenshot(`accordion-nested-enabled-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('parent should not be disabled when only child is disabled', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/nested`, config);
|
||||
|
||||
test('parent should not be disabled when only child is disabled', async ({ page }) => {
|
||||
const nestedDisabledGroup = page.locator('ion-accordion-group#nested-disabled');
|
||||
const nestedDisabledGroup = page.locator('ion-accordion-group#nested-disabled');
|
||||
|
||||
await expect(nestedDisabledGroup).toHaveScreenshot(`accordion-child-disabled-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
await expect(nestedDisabledGroup).toHaveScreenshot(screenshot('accordion-child-disabled'));
|
||||
});
|
||||
|
||||
test('parent and child should be disabled when parent is disabled', async ({ page }) => {
|
||||
const parentDisabledGroup = page.locator('ion-accordion-group#parent-disabled');
|
||||
test('parent and child should be disabled when parent is disabled', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/nested`, config);
|
||||
|
||||
await expect(parentDisabledGroup).toHaveScreenshot(`accordion-parent-disabled-${page.getSnapshotSettings()}.png`);
|
||||
const parentDisabledGroup = page.locator('ion-accordion-group#parent-disabled');
|
||||
|
||||
await expect(parentDisabledGroup).toHaveScreenshot(screenshot('accordion-parent-disabled'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('accordion: standalone', () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/standalone`);
|
||||
configs().forEach(({ config, title }) => {
|
||||
test.describe(title('accordion: standalone'), () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/accordion/test/standalone`, config);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
|
||||
expect(results.violations).toEqual([]);
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
prepareOverlay,
|
||||
present,
|
||||
safeCall,
|
||||
setOverlayId,
|
||||
} from '../../utils/overlays';
|
||||
import type { OverlayEventDetail } from '../../utils/overlays-interface';
|
||||
import { getClassMap } from '../../utils/theme';
|
||||
@@ -311,6 +312,10 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
|
||||
this.triggerController.removeClickListener();
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
setOverlayId(this.el);
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
/**
|
||||
* Do not create gesture if:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
const testAria = async (page: E2EPage, buttonID: string, expectedAriaLabelledBy: string | null) => {
|
||||
const didPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
@@ -20,38 +20,37 @@ const testAria = async (page: E2EPage, buttonID: string, expectedAriaLabelledBy:
|
||||
|
||||
expect(ariaLabelledBy).toBe(expectedAriaLabelledBy);
|
||||
};
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('action-sheet: a11y'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/action-sheet/test/a11y`, config);
|
||||
});
|
||||
test('should not have accessibility violations when header is defined', async ({ page }) => {
|
||||
const button = page.locator('#bothHeaders');
|
||||
const didPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
|
||||
test.describe('action-sheet: a11y', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/action-sheet/test/a11y`);
|
||||
});
|
||||
await button.click();
|
||||
await didPresent.next();
|
||||
|
||||
test('should not have accessibility violations when header is defined', async ({ page }) => {
|
||||
const button = page.locator('#bothHeaders');
|
||||
const didPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
/**
|
||||
* action-sheet overlays the entire screen, so
|
||||
* Axe will be unable to verify color contrast
|
||||
* on elements under the backdrop.
|
||||
*/
|
||||
const results = await new AxeBuilder({ page }).disableRules('color-contrast').analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
|
||||
await button.click();
|
||||
await didPresent.next();
|
||||
test('should have aria-labelledby when header is set', async ({ page }) => {
|
||||
await testAria(page, 'bothHeaders', 'action-sheet-1-header');
|
||||
});
|
||||
|
||||
/**
|
||||
* action-sheet overlays the entire screen, so
|
||||
* Axe will be unable to verify color contrast
|
||||
* on elements under the backdrop.
|
||||
*/
|
||||
const results = await new AxeBuilder({ page }).disableRules('color-contrast').analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
test('should not have aria-labelledby when header is not set', async ({ page }) => {
|
||||
await testAria(page, 'noHeaders', null);
|
||||
});
|
||||
|
||||
test('should have aria-labelledby when header is set', async ({ page }) => {
|
||||
await testAria(page, 'bothHeaders', 'action-sheet-1-header');
|
||||
});
|
||||
|
||||
test('should not have aria-labelledby when header is not set', async ({ page }) => {
|
||||
await testAria(page, 'noHeaders', null);
|
||||
});
|
||||
|
||||
test('should allow for manually specifying aria attributes', async ({ page }) => {
|
||||
await testAria(page, 'customAria', 'Custom title');
|
||||
test('should allow for manually specifying aria attributes', async ({ page }) => {
|
||||
await testAria(page, 'customAria', 'Custom title');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { ActionSheet } from '../action-sheet';
|
||||
|
||||
it('action sheet should be assigned an incrementing id', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [ActionSheet],
|
||||
html: `<ion-action-sheet is-open="true"></ion-action-sheet>`,
|
||||
});
|
||||
let actionSheet: HTMLIonActionSheetElement;
|
||||
|
||||
actionSheet = page.body.querySelector('ion-action-sheet')!;
|
||||
|
||||
expect(actionSheet).not.toBe(null);
|
||||
expect(actionSheet.getAttribute('id')).toBe('ion-overlay-1');
|
||||
|
||||
// Remove the action sheet from the DOM
|
||||
actionSheet.remove();
|
||||
await page.waitForChanges();
|
||||
|
||||
// Create a new action sheet to verify the id is incremented
|
||||
actionSheet = document.createElement('ion-action-sheet');
|
||||
actionSheet.isOpen = true;
|
||||
page.body.appendChild(actionSheet);
|
||||
await page.waitForChanges();
|
||||
|
||||
actionSheet = page.body.querySelector('ion-action-sheet')!;
|
||||
|
||||
expect(actionSheet.getAttribute('id')).toBe('ion-overlay-2');
|
||||
|
||||
// Presenting the same action sheet again should reuse the existing id
|
||||
|
||||
actionSheet.isOpen = false;
|
||||
await page.waitForChanges();
|
||||
actionSheet.isOpen = true;
|
||||
await page.waitForChanges();
|
||||
|
||||
actionSheet = page.body.querySelector('ion-action-sheet')!;
|
||||
|
||||
expect(actionSheet.getAttribute('id')).toBe('ion-overlay-2');
|
||||
});
|
||||
@@ -1,17 +1,17 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import type { Locator } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
|
||||
test.describe('action sheet: basic', () => {
|
||||
let actionSheetFixture: ActionSheetFixture;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/action-sheet/test/basic`);
|
||||
actionSheetFixture = new ActionSheetFixture(page);
|
||||
});
|
||||
test.describe('action sheet: data', () => {
|
||||
test('should return data', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('action sheet: data'), () => {
|
||||
let actionSheetFixture!: ActionSheetFixture;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
actionSheetFixture = new ActionSheetFixture(page);
|
||||
|
||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
||||
});
|
||||
test('should return data', async ({ page }) => {
|
||||
const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');
|
||||
|
||||
await actionSheetFixture.open('#buttonData');
|
||||
@@ -22,8 +22,7 @@ test.describe('action sheet: basic', () => {
|
||||
await ionActionSheetDidDismiss.next();
|
||||
expect(ionActionSheetDidDismiss).toHaveReceivedEventDetail({ data: { type: '1' }, role: undefined });
|
||||
});
|
||||
test('should return cancel button data', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
test('should return cancel button data', async ({ page }) => {
|
||||
const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');
|
||||
|
||||
await actionSheetFixture.open('#buttonData');
|
||||
@@ -35,16 +34,28 @@ test.describe('action sheet: basic', () => {
|
||||
expect(ionActionSheetDidDismiss).toHaveReceivedEventDetail({ data: { type: 'cancel' }, role: 'cancel' });
|
||||
});
|
||||
});
|
||||
test.describe('action sheet: attributes', () => {
|
||||
test('should set htmlAttributes', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('action sheet: attributes'), () => {
|
||||
test('should set htmlAttributes', async ({ page }) => {
|
||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
||||
const actionSheetFixture = new ActionSheetFixture(page);
|
||||
|
||||
await actionSheetFixture.open('#basic');
|
||||
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
await expect(actionSheet).toHaveAttribute('data-testid', 'basic-action-sheet');
|
||||
});
|
||||
});
|
||||
test.describe('action sheet: variants', () => {
|
||||
});
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('action sheet: variant rendering'), () => {
|
||||
let actionSheetFixture!: ActionSheetFixture;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
actionSheetFixture = new ActionSheetFixture(page, screenshot);
|
||||
|
||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
||||
});
|
||||
test('should open basic action sheet', async () => {
|
||||
await actionSheetFixture.open('#basic');
|
||||
await actionSheetFixture.screenshot('basic');
|
||||
@@ -73,24 +84,31 @@ test.describe('action sheet: basic', () => {
|
||||
await actionSheetFixture.open('#scrollWithoutCancel');
|
||||
await actionSheetFixture.screenshot('scroll-without-cancel');
|
||||
});
|
||||
test('should open custom backdrop action sheet', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
});
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('action sheet: variant functionality'), () => {
|
||||
let actionSheetFixture!: ActionSheetFixture;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
actionSheetFixture = new ActionSheetFixture(page);
|
||||
|
||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
||||
});
|
||||
test('should open custom backdrop action sheet', async ({ page }) => {
|
||||
await actionSheetFixture.open('#customBackdrop');
|
||||
|
||||
const backdrop = page.locator('ion-action-sheet ion-backdrop');
|
||||
await expect(backdrop).toHaveCSS('opacity', '1');
|
||||
});
|
||||
test('should open alert from action sheet', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
test('should open alert from action sheet', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
await actionSheetFixture.open('#alertFromActionSheet');
|
||||
|
||||
await page.locator('#open-alert').click();
|
||||
await page.click('#open-alert');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
});
|
||||
test('should not dismiss action sheet when backdropDismiss: false', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
test('should not dismiss action sheet when backdropDismiss: false', async ({ page }) => {
|
||||
await actionSheetFixture.open('#noBackdropDismiss');
|
||||
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
@@ -99,9 +117,13 @@ test.describe('action sheet: basic', () => {
|
||||
await expect(actionSheet).toBeVisible();
|
||||
});
|
||||
});
|
||||
test.describe('action sheet: focus trap', () => {
|
||||
test('it should trap focus in action sheet', async ({ page, skip, browserName }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('action sheet: focus trap'), () => {
|
||||
test('it should trap focus in action sheet', async ({ page, browserName }) => {
|
||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
||||
const actionSheetFixture = new ActionSheetFixture(page);
|
||||
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
await actionSheetFixture.open('#basic');
|
||||
@@ -121,11 +143,13 @@ test.describe('action sheet: basic', () => {
|
||||
|
||||
class ActionSheetFixture {
|
||||
readonly page: E2EPage;
|
||||
readonly screenshotFn?: (file: string) => string;
|
||||
|
||||
private actionSheet!: Locator;
|
||||
|
||||
constructor(page: E2EPage) {
|
||||
constructor(page: E2EPage, screenshot?: (file: string) => string) {
|
||||
this.page = page;
|
||||
this.screenshotFn = screenshot;
|
||||
}
|
||||
|
||||
async open(selector: string) {
|
||||
@@ -144,8 +168,14 @@ class ActionSheetFixture {
|
||||
}
|
||||
|
||||
async screenshot(modifier: string) {
|
||||
await expect(this.actionSheet).toHaveScreenshot(
|
||||
`action-sheet-${modifier}-diff-${this.page.getSnapshotSettings()}.png`
|
||||
);
|
||||
const { screenshotFn } = this;
|
||||
|
||||
if (!screenshotFn) {
|
||||
throw new Error(
|
||||
'A screenshot function is required to take a screenshot. Pass one in when creating ActionSheetFixture.'
|
||||
);
|
||||
}
|
||||
|
||||
await expect(this.actionSheet).toHaveScreenshot(screenshotFn(`action-sheet-${modifier}-diff`));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('action sheet: isOpen', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl('isOpen does not behave differently in RTL');
|
||||
skip.mode('md', 'isOpen does not behave differently in MD');
|
||||
await page.goto('/src/components/action-sheet/test/is-open');
|
||||
});
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('action sheet: isOpen'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/action-sheet/test/is-open', config);
|
||||
});
|
||||
test('should open the action sheet', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
|
||||
test('should open the action sheet', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
await page.click('#default');
|
||||
|
||||
await page.click('#default');
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
});
|
||||
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
});
|
||||
test('should open the action sheet then close after a timeout', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
|
||||
test('should open the action sheet then close after a timeout', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
await page.click('#timeout');
|
||||
|
||||
await page.click('#timeout');
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
await ionActionSheetDidDismiss.next();
|
||||
|
||||
await ionActionSheetDidDismiss.next();
|
||||
|
||||
await expect(actionSheet).toBeHidden();
|
||||
await expect(actionSheet).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('action sheet: translucent', () => {
|
||||
test('should not have visual regressions', async ({ page, skip }) => {
|
||||
skip.mode('md', 'Translucent effect is only active on iOS mode');
|
||||
skip.rtl('This tests how the component is painted, not layout. RTL tests are not needed here');
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('action sheet: translucent'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/action-sheet/test/translucent`, config);
|
||||
|
||||
await page.goto(`/src/components/action-sheet/test/translucent`);
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const basic = page.locator('#basic');
|
||||
await basic.click();
|
||||
|
||||
const basic = page.locator('#basic');
|
||||
await basic.click();
|
||||
await ionActionSheetDidPresent.next();
|
||||
|
||||
await ionActionSheetDidPresent.next();
|
||||
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
await expect(actionSheet).toHaveScreenshot(`action-sheet-translucent-${page.getSnapshotSettings()}.png`);
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
await expect(actionSheet).toHaveScreenshot(screenshot('action-sheet-translucent'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('action sheet: trigger', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl('trigger does not behave differently in RTL');
|
||||
skip.mode('md');
|
||||
await page.goto('/src/components/action-sheet/test/trigger');
|
||||
});
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('action sheet: trigger'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/action-sheet/test/trigger', config);
|
||||
});
|
||||
test('should open the action sheet', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const actionSheet = page.locator('#default-action-sheet');
|
||||
|
||||
test('should open the action sheet', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const actionSheet = page.locator('#default-action-sheet');
|
||||
await page.click('#default');
|
||||
|
||||
await page.click('#default');
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
});
|
||||
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
});
|
||||
test('should present a previously presented action sheet', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');
|
||||
const actionSheet = page.locator('#timeout-action-sheet');
|
||||
|
||||
test('should present a previously presented action sheet', async ({ page }) => {
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss');
|
||||
const actionSheet = page.locator('#timeout-action-sheet');
|
||||
await page.click('#timeout');
|
||||
|
||||
await page.click('#timeout');
|
||||
await ionActionSheetDidDismiss.next();
|
||||
|
||||
await ionActionSheetDidDismiss.next();
|
||||
await page.click('#timeout');
|
||||
|
||||
await page.click('#timeout');
|
||||
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
await ionActionSheetDidPresent.next();
|
||||
await expect(actionSheet).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
prepareOverlay,
|
||||
present,
|
||||
safeCall,
|
||||
setOverlayId,
|
||||
} from '../../utils/overlays';
|
||||
import type { OverlayEventDetail } from '../../utils/overlays-interface';
|
||||
import type { IonicSafeString } from '../../utils/sanitization';
|
||||
@@ -329,6 +330,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
setOverlayId(this.el);
|
||||
this.inputsChanged();
|
||||
this.buttonsChanged();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
const testAria = async (
|
||||
page: E2EPage,
|
||||
@@ -28,36 +28,36 @@ const testAria = async (
|
||||
expect(ariaDescribedBy).toBe(expectedAriaDescribedBy);
|
||||
};
|
||||
|
||||
test.describe('alert: a11y', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/a11y`);
|
||||
});
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('alert: a11y'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/alert/test/a11y`, config);
|
||||
});
|
||||
test('should not have accessibility violations when header and message are defined', async ({ page }) => {
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#bothHeaders');
|
||||
await button.click();
|
||||
|
||||
test('should not have accessibility violations when header and message are defined', async ({ page }) => {
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#bothHeaders');
|
||||
await button.click();
|
||||
await didPresent.next();
|
||||
|
||||
await didPresent.next();
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
test('should have aria-labelledby when header is set', async ({ page }) => {
|
||||
await testAria(page, 'noMessage', 'alert-1-hdr', null);
|
||||
});
|
||||
|
||||
test('should have aria-labelledby when header is set', async ({ page }) => {
|
||||
await testAria(page, 'noMessage', 'alert-1-hdr', null);
|
||||
});
|
||||
test('should have aria-describedby when message is set', async ({ page }) => {
|
||||
await testAria(page, 'noHeaders', null, 'alert-1-msg');
|
||||
});
|
||||
|
||||
test('should have aria-describedby when message is set', async ({ page }) => {
|
||||
await testAria(page, 'noHeaders', null, 'alert-1-msg');
|
||||
});
|
||||
test('should fall back to subHeader for aria-labelledby if header is not defined', async ({ page }) => {
|
||||
await testAria(page, 'subHeaderOnly', 'alert-1-sub-hdr', 'alert-1-msg');
|
||||
});
|
||||
|
||||
test('should fall back to subHeader for aria-labelledby if header is not defined', async ({ page }) => {
|
||||
await testAria(page, 'subHeaderOnly', 'alert-1-sub-hdr', 'alert-1-msg');
|
||||
});
|
||||
|
||||
test('should allow for manually specifying aria attributes', async ({ page }) => {
|
||||
await testAria(page, 'customAria', 'Custom title', 'Custom description');
|
||||
test('should allow for manually specifying aria attributes', async ({ page }) => {
|
||||
await testAria(page, 'customAria', 'Custom title', 'Custom description');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
41
core/src/components/alert/test/alert-id.spec.ts
Normal file
41
core/src/components/alert/test/alert-id.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Alert } from '../alert';
|
||||
|
||||
it('alert should be assigned an incrementing id', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Alert],
|
||||
html: `<ion-alert is-open="true"></ion-alert>`,
|
||||
});
|
||||
let alert: HTMLIonAlertElement;
|
||||
|
||||
alert = page.body.querySelector('ion-alert')!;
|
||||
|
||||
expect(alert).not.toBe(null);
|
||||
expect(alert.getAttribute('id')).toBe('ion-overlay-1');
|
||||
|
||||
// Remove the alert from the DOM
|
||||
alert.remove();
|
||||
await page.waitForChanges();
|
||||
|
||||
// Create a new alert to verify the id is incremented
|
||||
alert = document.createElement('ion-alert');
|
||||
alert.isOpen = true;
|
||||
page.body.appendChild(alert);
|
||||
await page.waitForChanges();
|
||||
|
||||
alert = page.body.querySelector('ion-alert')!;
|
||||
|
||||
expect(alert.getAttribute('id')).toBe('ion-overlay-2');
|
||||
|
||||
// Presenting the same alert again should reuse the existing id
|
||||
|
||||
alert.isOpen = false;
|
||||
await page.waitForChanges();
|
||||
alert.isOpen = true;
|
||||
await page.waitForChanges();
|
||||
|
||||
alert = page.body.querySelector('ion-alert')!;
|
||||
|
||||
expect(alert.getAttribute('id')).toBe('ion-overlay-2');
|
||||
});
|
||||
@@ -1,113 +1,147 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import type { Locator } from '@playwright/test';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
const openAlert = async (page: E2EPage, buttonID: string) => {
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('alert: basic'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/basic', config);
|
||||
});
|
||||
test('focus trap should work correctly', async ({ page, browserName }) => {
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
await page.click(`#${buttonID}`);
|
||||
await didPresent.next();
|
||||
const alertFixture = new AlertFixture(page, screenshot);
|
||||
|
||||
return page.locator('ion-alert');
|
||||
};
|
||||
const alert = await alertFixture.open('#multipleButtons');
|
||||
const alertBtns = alert.locator('button');
|
||||
|
||||
const testAlert = async (page: E2EPage, buttonID: string) => {
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
|
||||
const didDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = await openAlert(page, buttonID);
|
||||
await page.keyboard.press(`Shift+${tabKey}`);
|
||||
await expect(alertBtns.nth(2)).toBeFocused();
|
||||
|
||||
await expect(alert).toBeVisible();
|
||||
await expect(alert).toHaveScreenshot(`alert-${buttonID}-${page.getSnapshotSettings()}.png`);
|
||||
|
||||
await alert.evaluate((el: HTMLIonAlertElement) => el.dismiss());
|
||||
await didDismiss.next();
|
||||
|
||||
await expect(alert).toHaveCount(0);
|
||||
};
|
||||
|
||||
test.describe('alert: basic', () => {
|
||||
test('focus trap should work correctly', async ({ page, skip, browserName }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
const alert = await openAlert(page, 'multipleButtons');
|
||||
const alertBtns = alert.locator('button');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
|
||||
await page.keyboard.press(`Shift+${tabKey}`);
|
||||
await expect(alertBtns.nth(2)).toBeFocused();
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
});
|
||||
|
||||
test('should set custom attributes', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
|
||||
const alert = await openAlert(page, 'basic');
|
||||
await expect(alert).toHaveAttribute('data-testid', 'basic-alert');
|
||||
});
|
||||
|
||||
test('should dismiss when async handler resolves', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const ionLoadingDidDismiss = await page.spyOnEvent('ionLoadingDidDismiss');
|
||||
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await page.click('#asyncHandler');
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await page.click('.alert-button');
|
||||
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
await ionLoadingDidDismiss.next();
|
||||
await ionAlertDidDismiss.next();
|
||||
|
||||
await expect(alert).toBeHidden();
|
||||
});
|
||||
|
||||
test.describe('should not have visual regressions', () => {
|
||||
test('header, subheader, message', async ({ page }) => {
|
||||
await testAlert(page, 'basic');
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
});
|
||||
|
||||
test('long message', async ({ page }) => {
|
||||
await testAlert(page, 'longMessage');
|
||||
test('should set custom attributes', async ({ page }) => {
|
||||
const alertFixture = new AlertFixture(page, screenshot);
|
||||
|
||||
const alert = await alertFixture.open('#basic');
|
||||
await expect(alert).toHaveAttribute('data-testid', 'basic-alert');
|
||||
});
|
||||
|
||||
test('more than two buttons', async ({ page }) => {
|
||||
await testAlert(page, 'multipleButtons');
|
||||
});
|
||||
test('should dismiss when async handler resolves', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const ionLoadingDidDismiss = await page.spyOnEvent('ionLoadingDidDismiss');
|
||||
|
||||
test('no message', async ({ page }) => {
|
||||
await testAlert(page, 'noMessage');
|
||||
});
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
test('two buttons', async ({ page }) => {
|
||||
await testAlert(page, 'confirm');
|
||||
});
|
||||
await page.click('#asyncHandler');
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
test('form prompt', async ({ page }) => {
|
||||
await testAlert(page, 'prompt');
|
||||
});
|
||||
await page.click('.alert-button');
|
||||
|
||||
test('radios', async ({ page }) => {
|
||||
await testAlert(page, 'radio');
|
||||
});
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
test('checkboxes', async ({ page }) => {
|
||||
await testAlert(page, 'checkbox');
|
||||
await ionLoadingDidDismiss.next();
|
||||
await ionAlertDidDismiss.next();
|
||||
|
||||
await expect(alert).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('should not have visual regressions'), () => {
|
||||
let alertFixture!: AlertFixture;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/basic', config);
|
||||
alertFixture = new AlertFixture(page, screenshot);
|
||||
});
|
||||
test('header, subheader, message', async () => {
|
||||
await alertFixture.open('#basic');
|
||||
await alertFixture.screenshot('basic');
|
||||
});
|
||||
|
||||
test('long message', async () => {
|
||||
await alertFixture.open('#longMessage');
|
||||
await alertFixture.screenshot('longMessage');
|
||||
});
|
||||
|
||||
test('more than two buttons', async () => {
|
||||
await alertFixture.open('#multipleButtons');
|
||||
await alertFixture.screenshot('multipleButtons');
|
||||
});
|
||||
|
||||
test('no message', async () => {
|
||||
await alertFixture.open('#noMessage');
|
||||
await alertFixture.screenshot('noMessage');
|
||||
});
|
||||
|
||||
test('two buttons', async () => {
|
||||
await alertFixture.open('#confirm');
|
||||
await alertFixture.screenshot('confirm');
|
||||
});
|
||||
|
||||
test('form prompt', async () => {
|
||||
await alertFixture.open('#prompt');
|
||||
await alertFixture.screenshot('prompt');
|
||||
});
|
||||
|
||||
test('radios', async () => {
|
||||
await alertFixture.open('#radio');
|
||||
await alertFixture.screenshot('radio');
|
||||
});
|
||||
|
||||
test('checkboxes', async () => {
|
||||
await alertFixture.open('#checkbox');
|
||||
await alertFixture.screenshot('checkbox');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class AlertFixture {
|
||||
readonly page: E2EPage;
|
||||
readonly screenshotFn?: (file: string) => string;
|
||||
|
||||
private alert!: Locator;
|
||||
|
||||
constructor(page: E2EPage, screenshot?: (file: string) => string) {
|
||||
this.page = page;
|
||||
this.screenshotFn = screenshot;
|
||||
}
|
||||
|
||||
async open(selector: string) {
|
||||
const ionAlertDidPresent = await this.page.spyOnEvent('ionAlertDidPresent');
|
||||
await this.page.locator(selector).click();
|
||||
await ionAlertDidPresent.next();
|
||||
this.alert = this.page.locator('ion-alert');
|
||||
await expect(this.alert).toBeVisible();
|
||||
|
||||
return this.alert;
|
||||
}
|
||||
|
||||
async dismiss() {
|
||||
const ionAlertDidDismiss = await this.page.spyOnEvent('ionAlertDidDismiss');
|
||||
await this.alert.evaluate((el: HTMLIonAlertElement) => el.dismiss());
|
||||
await ionAlertDidDismiss.next();
|
||||
await expect(this.alert).not.toBeVisible();
|
||||
}
|
||||
|
||||
async screenshot(modifier: string) {
|
||||
const { screenshotFn } = this;
|
||||
|
||||
if (!screenshotFn) {
|
||||
throw new Error(
|
||||
'A screenshot function is required to take a screenshot. Pass one in when creating ActionSheetFixture.'
|
||||
);
|
||||
}
|
||||
|
||||
await expect(this.alert).toHaveScreenshot(screenshotFn(`alert-${modifier}`));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('alert: isOpen', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl('isOpen does not behave differently in RTL');
|
||||
skip.mode('md', 'isOpen does not behave differently in MD');
|
||||
await page.goto('/src/components/alert/test/is-open');
|
||||
});
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('alert: isOpen'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/is-open', config);
|
||||
});
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('ion-alert');
|
||||
await page.click('#default');
|
||||
|
||||
await page.click('#default');
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
test('should open the alert then close after a timeout', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
test('should open the alert then close after a timeout', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('ion-alert');
|
||||
await page.click('#timeout');
|
||||
|
||||
await page.click('#timeout');
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
await ionAlertDidDismiss.next();
|
||||
await expect(alert).toBeHidden();
|
||||
await ionAlertDidDismiss.next();
|
||||
await expect(alert).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('alert: standalone', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/alert/test/standalone`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('alert: standalone'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/alert/test/standalone`, config);
|
||||
|
||||
const alert = page.locator('ion-alert');
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('ion-alert');
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
|
||||
await page.click('#basic');
|
||||
await didPresent.next();
|
||||
await page.click('#basic');
|
||||
await didPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(`alert-standalone-${page.getSnapshotSettings()}.png`);
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-standalone'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('alert: trigger', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl('trigger does not behave differently in RTL');
|
||||
skip.mode('md');
|
||||
await page.goto('/src/components/alert/test/trigger');
|
||||
});
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('alert: trigger'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/trigger', config);
|
||||
});
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('#default-alert');
|
||||
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('#default-alert');
|
||||
await page.click('#default');
|
||||
|
||||
await page.click('#default');
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
test('should present a previously presented alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('#timeout-alert');
|
||||
|
||||
test('should present a previously presented alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('#timeout-alert');
|
||||
await page.click('#timeout');
|
||||
|
||||
await page.click('#timeout');
|
||||
await ionAlertDidDismiss.next();
|
||||
|
||||
await ionAlertDidDismiss.next();
|
||||
await page.click('#timeout');
|
||||
|
||||
await page.click('#timeout');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
|
||||
test.describe('app: safe-area', () => {
|
||||
const testOverlay = async (page: E2EPage, trigger: string, event: string, screenshotModifier: string) => {
|
||||
const presentEvent = await page.spyOnEvent(event);
|
||||
/**
|
||||
* Safe area tests only check top and bottom edges. RTL checks are not required here.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title, screenshot }) => {
|
||||
test.describe(title('app: safe-area'), () => {
|
||||
const testOverlay = async (page: E2EPage, trigger: string, event: string, screenshotModifier: string) => {
|
||||
const presentEvent = await page.spyOnEvent(event);
|
||||
|
||||
await page.click(trigger);
|
||||
await presentEvent.next();
|
||||
await page.click(trigger);
|
||||
await presentEvent.next();
|
||||
|
||||
// Sometimes the inner content takes a frame or two to render
|
||||
await page.waitForChanges();
|
||||
// Sometimes the inner content takes a frame or two to render
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(page).toHaveScreenshot(`app-${screenshotModifier}-diff-${page.getSnapshotSettings()}.png`);
|
||||
};
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl('Safe area tests only check top and bottom edges. RTL checks are not required here.');
|
||||
|
||||
await page.goto(`/src/components/app/test/safe-area`);
|
||||
});
|
||||
test('should not have visual regressions with action sheet', async ({ page }) => {
|
||||
await testOverlay(page, '#show-action-sheet', 'ionActionSheetDidPresent', 'action-sheet');
|
||||
});
|
||||
test('should not have visual regressions with menu', async ({ page }) => {
|
||||
await testOverlay(page, '#show-menu', 'ionDidOpen', 'menu');
|
||||
});
|
||||
test('should not have visual regressions with picker', async ({ page }) => {
|
||||
await testOverlay(page, '#show-picker', 'ionPickerDidPresent', 'picker');
|
||||
});
|
||||
test('should not have visual regressions with toast', async ({ page }) => {
|
||||
await testOverlay(page, '#show-toast', 'ionToastDidPresent', 'toast');
|
||||
await expect(page).toHaveScreenshot(screenshot(`app-${screenshotModifier}-diff`));
|
||||
};
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/app/test/safe-area`, config);
|
||||
});
|
||||
test('should not have visual regressions with action sheet', async ({ page }) => {
|
||||
await testOverlay(page, '#show-action-sheet', 'ionActionSheetDidPresent', 'action-sheet');
|
||||
});
|
||||
test('should not have visual regressions with menu', async ({ page }) => {
|
||||
await testOverlay(page, '#show-menu', 'ionDidOpen', 'menu');
|
||||
});
|
||||
test('should not have visual regressions with picker', async ({ page }) => {
|
||||
await testOverlay(page, '#show-picker', 'ionPickerDidPresent', 'picker');
|
||||
});
|
||||
test('should not have visual regressions with toast', async ({ page }) => {
|
||||
await testOverlay(page, '#show-toast', 'ionToastDidPresent', 'toast');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('avatar: basic', () => {
|
||||
test('should not have visual regressions', async ({ page, skip }) => {
|
||||
skip.rtl('Avatar does not test RTL behaviors. Usages of Avatar in slots are tested in components that use Avatar.');
|
||||
/**
|
||||
* Avatar does not test RTL behaviors.
|
||||
* Usages of Avatar in slots are tested in components that use Avatar.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('avatar: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/avatar/test/basic`, config);
|
||||
|
||||
await page.goto(`/src/components/avatar/test/basic`);
|
||||
const avatar = page.locator('#avatar');
|
||||
const avatarChip = page.locator('#avatar-chip');
|
||||
const avatarItemStart = page.locator('#avatar-item-start');
|
||||
const avatarItemEnd = page.locator('#avatar-item-end');
|
||||
|
||||
const avatar = page.locator('#avatar');
|
||||
const avatarChip = page.locator('#avatar-chip');
|
||||
const avatarItemStart = page.locator('#avatar-item-start');
|
||||
const avatarItemEnd = page.locator('#avatar-item-end');
|
||||
|
||||
await expect(avatar).toHaveScreenshot(`avatar-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(avatarChip).toHaveScreenshot(`avatar-chip-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(avatarItemStart).toHaveScreenshot(`avatar-item-start-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(avatarItemEnd).toHaveScreenshot(`avatar-item-end-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(avatar).toHaveScreenshot(screenshot(`avatar-diff`));
|
||||
await expect(avatarChip).toHaveScreenshot(screenshot(`avatar-chip-diff`));
|
||||
await expect(avatarItemStart).toHaveScreenshot(screenshot(`avatar-item-start-diff`));
|
||||
await expect(avatarItemEnd).toHaveScreenshot(screenshot(`avatar-item-end-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('back-button: basic', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/back-button/test/basic`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('back-button: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/back-button/test/basic`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`back-button-basic-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`back-button-basic`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('back-button: toolbar', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/back-button/test/toolbar`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('back-button: toolbar'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/back-button/test/toolbar`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`back-button-toolbar-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`back-button-toolbar`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('badge: rendering', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto('/src/components/badge/test/basic');
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('badge: rendering'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto('/src/components/badge/test/basic', config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`badge-basic-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`badge-basic`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
--color-active: #{$breadcrumb-md-color-active};
|
||||
--color-hover: #{$breadcrumb-md-color-active};
|
||||
--color-focused: #{$breadcrumb-md-color-focused};
|
||||
--background-focused: $breadcrumb-md-background-focused;
|
||||
--background-focused: #{$breadcrumb-md-background-focused};
|
||||
}
|
||||
|
||||
:host(.breadcrumb-active) {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('breadcrumbs: axe', () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/breadcrumbs/test/a11y`);
|
||||
configs().forEach(({ config, title }) => {
|
||||
test.describe(title('breadcrumbs: axe'), () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/breadcrumbs/test/a11y`, config);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, 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`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('breadcrumbs: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/breadcrumbs/test/basic`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`breadcrumb-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`breadcrumb-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, 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`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('breadcrumbs: collapsed'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/breadcrumbs/test/collapsed`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`breadcrumb-collapsed-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`breadcrumb-collapsed-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,71 +1,70 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('breadcrumbs: reactive', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('ios');
|
||||
|
||||
await page.goto(`/src/components/breadcrumbs/test/reactive`);
|
||||
});
|
||||
|
||||
test.describe('adding a breadcrumb item', () => {
|
||||
test('should update the active item', async ({ page }) => {
|
||||
const breadcrumbItems = page.locator('ion-breadcrumb');
|
||||
|
||||
const addItemButton = page.locator('ion-button#add-btn');
|
||||
|
||||
await expect(breadcrumbItems).toHaveCount(4);
|
||||
|
||||
await addItemButton.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(breadcrumbItems).toHaveCount(5);
|
||||
|
||||
const previousActiveItem = breadcrumbItems.nth(3);
|
||||
const lastBreadcrumbItem = breadcrumbItems.nth(4);
|
||||
|
||||
await expect(previousActiveItem).not.toHaveClass(/breadcrumb-active/);
|
||||
await expect(lastBreadcrumbItem).toHaveClass(/breadcrumb-active/);
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('breadcrumbs: reactive'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/breadcrumbs/test/reactive`, config);
|
||||
});
|
||||
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setIonViewport();
|
||||
test.describe('adding a breadcrumb item', () => {
|
||||
test('should update the active item', async ({ page }) => {
|
||||
const breadcrumbItems = page.locator('ion-breadcrumb');
|
||||
|
||||
const breadcrumbs = page.locator('ion-breadcrumbs');
|
||||
const addItemButton = page.locator('ion-button#add-btn');
|
||||
|
||||
await page.click('#add-btn');
|
||||
await page.waitForChanges();
|
||||
await expect(breadcrumbItems).toHaveCount(4);
|
||||
|
||||
await expect(breadcrumbs).toHaveScreenshot(`breadcrumbs-reactive-add-diff-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
await addItemButton.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
test.describe('removing a breadcrumb item', () => {
|
||||
test('should update the active item', async ({ page }) => {
|
||||
const breadcrumbItems = page.locator('ion-breadcrumb');
|
||||
await expect(breadcrumbItems).toHaveCount(5);
|
||||
|
||||
await expect(breadcrumbItems).toHaveCount(4);
|
||||
const previousActiveItem = breadcrumbItems.nth(3);
|
||||
const lastBreadcrumbItem = breadcrumbItems.nth(4);
|
||||
|
||||
await page.click('#remove-btn');
|
||||
await page.waitForChanges();
|
||||
await expect(previousActiveItem).not.toHaveClass(/breadcrumb-active/);
|
||||
await expect(lastBreadcrumbItem).toHaveClass(/breadcrumb-active/);
|
||||
});
|
||||
|
||||
await expect(breadcrumbItems).toHaveCount(3);
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setIonViewport();
|
||||
|
||||
const lastBreadcrumbItem = breadcrumbItems.nth(2);
|
||||
const breadcrumbs = page.locator('ion-breadcrumbs');
|
||||
|
||||
await expect(lastBreadcrumbItem).toHaveClass(/breadcrumb-active/);
|
||||
await page.click('#add-btn');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(breadcrumbs).toHaveScreenshot(screenshot(`breadcrumbs-reactive-add-diff`));
|
||||
});
|
||||
});
|
||||
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setIonViewport();
|
||||
test.describe('removing a breadcrumb item', () => {
|
||||
test('should update the active item', async ({ page }) => {
|
||||
const breadcrumbItems = page.locator('ion-breadcrumb');
|
||||
|
||||
const breadcrumbs = page.locator('ion-breadcrumbs');
|
||||
await expect(breadcrumbItems).toHaveCount(4);
|
||||
|
||||
await page.click('#remove-btn');
|
||||
await page.waitForChanges();
|
||||
await page.click('#remove-btn');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(breadcrumbs).toHaveScreenshot(`breadcrumbs-reactive-remove-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(breadcrumbItems).toHaveCount(3);
|
||||
|
||||
const lastBreadcrumbItem = breadcrumbItems.nth(2);
|
||||
|
||||
await expect(lastBreadcrumbItem).toHaveClass(/breadcrumb-active/);
|
||||
});
|
||||
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setIonViewport();
|
||||
|
||||
const breadcrumbs = page.locator('ion-breadcrumbs');
|
||||
|
||||
await page.click('#remove-btn');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(breadcrumbs).toHaveScreenshot(screenshot(`breadcrumbs-reactive-remove-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,56 +1,65 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: basic', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/basic`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('button: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/basic`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`button-diff-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should correctly set fill to undefined', async ({ page, skip }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/25886',
|
||||
});
|
||||
skip.rtl();
|
||||
skip.mode('ios', 'This behavior does not differ across modes');
|
||||
await page.setContent(`
|
||||
<ion-button fill="outline"></ion-button>
|
||||
`);
|
||||
|
||||
const button = page.locator('ion-button');
|
||||
await expect(button).toHaveClass(/button-outline/);
|
||||
|
||||
await button.evaluate((el: HTMLIonButtonElement) => (el.fill = undefined));
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(button).toHaveClass(/button-solid/);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('button: ripple effect', () => {
|
||||
test('should not have visual regressions', async ({ page, skip }) => {
|
||||
skip.mode('ios', 'Ripple effect is only available in MD mode.');
|
||||
|
||||
await page.goto(`/src/components/button/test/basic?ionic:_testing=false`);
|
||||
|
||||
const button = page.locator('#default');
|
||||
|
||||
await button.scrollIntoViewIfNeeded();
|
||||
|
||||
const boundingBox = await button.boundingBox();
|
||||
|
||||
if (boundingBox) {
|
||||
await page.mouse.move(boundingBox.x + boundingBox.width / 2, boundingBox.y + boundingBox.height / 2);
|
||||
await page.mouse.down();
|
||||
}
|
||||
|
||||
await page.waitForSelector('#default.ion-activated');
|
||||
|
||||
await expect(button).toHaveScreenshot(`button-ripple-effect-${page.getSnapshotSettings()}.png`, {
|
||||
animations: 'disabled',
|
||||
await expect(page).toHaveScreenshot(screenshot(`button-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('button: basic'), () => {
|
||||
test('should correctly set fill to undefined', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/25886',
|
||||
});
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-button fill="outline"></ion-button>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const button = page.locator('ion-button');
|
||||
await expect(button).toHaveClass(/button-outline/);
|
||||
|
||||
await button.evaluate((el: HTMLIonButtonElement) => (el.fill = undefined));
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(button).toHaveClass(/button-solid/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Ripple effect is only available in MD mode.
|
||||
*/
|
||||
configs({ modes: ['md'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('button: ripple effect'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/basic?ionic:_testing=false`, config);
|
||||
|
||||
const button = page.locator('#default');
|
||||
|
||||
await button.scrollIntoViewIfNeeded();
|
||||
|
||||
const boundingBox = await button.boundingBox();
|
||||
|
||||
if (boundingBox) {
|
||||
await page.mouse.move(boundingBox.x + boundingBox.width / 2, boundingBox.y + boundingBox.height / 2);
|
||||
await page.mouse.down();
|
||||
}
|
||||
|
||||
await page.waitForSelector('#default.ion-activated');
|
||||
|
||||
await expect(button).toHaveScreenshot(screenshot(`button-ripple-effect`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: clear', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/clear`);
|
||||
configs().forEach(({ title, config, screenshot }) => {
|
||||
test.describe(title('button: clear'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/clear`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`button-clear-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`button-clear`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: expand', () => {
|
||||
test('should not have visual regressions', async ({ page, skip }) => {
|
||||
skip.rtl('All content takes up the full width, so RTL has no effect.');
|
||||
await page.goto(`/src/components/button/test/expand`);
|
||||
/**
|
||||
* All content takes up the full width, so RTL has no effect.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: expand'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/expand`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`button-expand-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`button-expand`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,55 +1,66 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: form', () => {
|
||||
test('should submit the form by id', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<form id="myForm"></form>
|
||||
<ion-button form="myForm" type="submit">Submit</ion-button>
|
||||
`);
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('button: form'), () => {
|
||||
test('should submit the form by id', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<form id="myForm"></form>
|
||||
<ion-button form="myForm" type="submit">Submit</ion-button>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const submitEvent = await page.spyOnEvent('submit');
|
||||
const submitEvent = await page.spyOnEvent('submit');
|
||||
|
||||
await page.click('ion-button');
|
||||
await page.click('ion-button');
|
||||
|
||||
expect(submitEvent).toHaveReceivedEvent();
|
||||
expect(submitEvent).toHaveReceivedEvent();
|
||||
});
|
||||
|
||||
test('should submit the form by reference', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<form></form>
|
||||
<ion-button type="submit">Submit</ion-button>
|
||||
<script>
|
||||
const form = document.querySelector('form');
|
||||
const button = document.querySelector('ion-button');
|
||||
button.form = form;
|
||||
</script>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const submitEvent = await page.spyOnEvent('submit');
|
||||
|
||||
await page.click('ion-button');
|
||||
|
||||
expect(submitEvent).toHaveReceivedEvent();
|
||||
});
|
||||
|
||||
test('should submit the closest form', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<form>
|
||||
<ion-button type="submit">Submit</ion-button>
|
||||
</form>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const submitEvent = await page.spyOnEvent('submit');
|
||||
|
||||
await page.click('ion-button');
|
||||
|
||||
expect(submitEvent).toHaveReceivedEvent();
|
||||
});
|
||||
});
|
||||
|
||||
test('should submit the form by reference', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<form></form>
|
||||
<ion-button type="submit">Submit</ion-button>
|
||||
<script>
|
||||
const form = document.querySelector('form');
|
||||
const button = document.querySelector('ion-button');
|
||||
button.form = form;
|
||||
</script>
|
||||
`);
|
||||
|
||||
const submitEvent = await page.spyOnEvent('submit');
|
||||
|
||||
await page.click('ion-button');
|
||||
|
||||
expect(submitEvent).toHaveReceivedEvent();
|
||||
});
|
||||
|
||||
test('should submit the closest form', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<form>
|
||||
<ion-button type="submit">Submit</ion-button>
|
||||
</form>
|
||||
`);
|
||||
|
||||
const submitEvent = await page.spyOnEvent('submit');
|
||||
|
||||
await page.click('ion-button');
|
||||
|
||||
expect(submitEvent).toHaveReceivedEvent();
|
||||
});
|
||||
|
||||
test.describe('should throw a warning if the form cannot be found', () => {
|
||||
test.describe(title('should throw a warning if the form cannot be found'), () => {
|
||||
test('form is a string selector', async ({ page }) => {
|
||||
await page.setContent(`<ion-button type="submit" form="missingForm">Submit</ion-button>`);
|
||||
await page.setContent(`<ion-button type="submit" form="missingForm">Submit</ion-button>`, config);
|
||||
|
||||
const logs: string[] = [];
|
||||
|
||||
@@ -66,7 +77,8 @@ test.describe('button: form', () => {
|
||||
});
|
||||
|
||||
test('form is an element reference', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-button type="submit">Submit</ion-button>
|
||||
<script>
|
||||
const form = document.querySelector('form');
|
||||
@@ -74,7 +86,9 @@ test.describe('button: form', () => {
|
||||
|
||||
button.form = form;
|
||||
</script>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const logs: string[] = [];
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: icon', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/icon`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: icon'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/icon`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`button-icon-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`button-icon`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: outline', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/outline`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: outline'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/outline`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`button-outline-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`button-outline`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: round', () => {
|
||||
test('should not have visual regressions', async ({ page, skip }) => {
|
||||
skip.rtl('All content takes up the full width, so RTL has no effect.');
|
||||
await page.goto(`/src/components/button/test/round`);
|
||||
/**
|
||||
* All content takes up the full width, so RTL has no effect.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: round'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/round`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`button-round-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`button-round`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,50 +1,62 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('button: size', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
test('should render small buttons', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-button size="small" fill="solid">Small Button</ion-button>
|
||||
`);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-size-small-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should render large buttons', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-button size="large" fill="solid">Large Button</ion-button>
|
||||
`);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-size-large-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test.describe('in ion-buttons', () => {
|
||||
test('should render small button', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-buttons>
|
||||
<ion-button size="small" fill="solid">Small Button</ion-button>
|
||||
</ion-buttons>
|
||||
`);
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: size'), () => {
|
||||
test('should render small buttons', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-button size="small" fill="solid">Small Button</ion-button>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-size-small-in-buttons-${page.getSnapshotSettings()}.png`);
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-size-small`));
|
||||
});
|
||||
test('should render large buttons', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-button size="large" fill="solid">Large Button</ion-button>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-size-large`));
|
||||
});
|
||||
});
|
||||
|
||||
test.describe(title('in ion-buttons'), () => {
|
||||
test('should render small button', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-buttons>
|
||||
<ion-button size="small" fill="solid">Small Button</ion-button>
|
||||
</ion-buttons>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-size-small-in-buttons`));
|
||||
});
|
||||
test('should render large button', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-buttons>
|
||||
<ion-button size="large" fill="solid">Large Button</ion-button>
|
||||
</ion-buttons>
|
||||
`);
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-buttons>
|
||||
<ion-button size="large" fill="solid">Large Button</ion-button>
|
||||
</ion-buttons>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-size-large-in-buttons-${page.getSnapshotSettings()}.png`);
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-size-large-in-buttons`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,55 +1,66 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* Clear buttons have special font-weight
|
||||
* styles which is why we make sure
|
||||
* to capture the clear button here.
|
||||
*/
|
||||
test.describe('button: strong', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
test('should render strong button', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-button fill="solid" strong="true">Button</ion-button>
|
||||
`);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-strong-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should render strong clear button', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-button fill="clear" strong="true">Button</ion-button>
|
||||
`);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-clear-strong-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test.describe('in ion-buttons', () => {
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: strong'), () => {
|
||||
test('should render strong button', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-buttons>
|
||||
<ion-button strong="true" fill="solid">Button</ion-button>
|
||||
</ion-buttons>
|
||||
`);
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-button fill="solid" strong="true">Button</ion-button>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-strong-in-buttons-${page.getSnapshotSettings()}.png`);
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-strong`));
|
||||
});
|
||||
test('should render strong clear button', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-buttons>
|
||||
<ion-button strong="true" fill="clear">Button</ion-button>
|
||||
</ion-buttons>
|
||||
`);
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-button fill="clear" strong="true">Button</ion-button>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(`button-clear-strong-in-buttons-${page.getSnapshotSettings()}.png`);
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-clear-strong`));
|
||||
});
|
||||
});
|
||||
test.describe(title('in ion-buttons'), () => {
|
||||
test('should render strong button', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-buttons>
|
||||
<ion-button strong="true" fill="solid">Button</ion-button>
|
||||
</ion-buttons>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-strong-in-buttons`));
|
||||
});
|
||||
test('should render strong clear button', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-buttons>
|
||||
<ion-button strong="true" fill="clear">Button</ion-button>
|
||||
</ion-buttons>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const wrapper = page.locator('ion-button');
|
||||
|
||||
await expect(wrapper).toHaveScreenshot(screenshot(`button-clear-strong-in-buttons`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('card-header: basic', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/card-header/test/basic`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('card-header: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/card-header/test/basic`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`card-header-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`card-header-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('card: basic', () => {
|
||||
test.describe('card: rendering', () => {
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('card: rendering'), () => {
|
||||
test('should not have visual regressions with basic card', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<ion-card-title>Card Title</ion-card-title>
|
||||
@@ -16,20 +17,20 @@ test.describe('card: basic', () => {
|
||||
in the woods. Wash your spirit clean.
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(`card-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-diff`));
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('card: feature rendering', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
|
||||
});
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('card: feature rendering'), () => {
|
||||
test('should not have visual regressions with button cards', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card button="true">
|
||||
<ion-card-header>
|
||||
<ion-card-title>Card Title</ion-card-title>
|
||||
@@ -40,35 +41,17 @@ test.describe('card: basic', () => {
|
||||
in the woods. Wash your spirit clean.
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(`card-button-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
|
||||
test('should not have visual regressions with translucent cards', async ({ page, skip }) => {
|
||||
skip.mode('md', 'Translucent effect is only available in iOS mode.');
|
||||
|
||||
await page.setContent(`
|
||||
<ion-card>
|
||||
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0">
|
||||
<img style="transform: rotate(145deg) scale(1.5)" src="/src/components/card/test/img.jpg" />
|
||||
</div>
|
||||
<ion-card-header translucent="true">
|
||||
<ion-card-title> Title </ion-card-title>
|
||||
<ion-card-subtitle> Subtitle </ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
|
||||
<ion-card-content style="min-height: 20px"></ion-card-content>
|
||||
</ion-card>
|
||||
`);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(`card-translucent-${page.getSnapshotSettings()}.png`);
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-button`));
|
||||
});
|
||||
|
||||
test('should not have visual regressions with disabled card', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card disabled="true">
|
||||
<ion-card-header>
|
||||
<ion-card-title>Card Title</ion-card-title>
|
||||
@@ -80,13 +63,16 @@ test.describe('card: basic', () => {
|
||||
in the woods. Wash your spirit clean.
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(`card-disabled-${page.getSnapshotSettings()}.png`);
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-disabled`));
|
||||
});
|
||||
test('should not have visual regressions with color', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card color="danger">
|
||||
<ion-card-header>
|
||||
<ion-card-title>Card Title</ion-card-title>
|
||||
@@ -98,13 +84,16 @@ test.describe('card: basic', () => {
|
||||
in the woods. Wash your spirit clean.
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(`card-color-${page.getSnapshotSettings()}.png`);
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-color`));
|
||||
});
|
||||
test('headings should have correct size in card', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card>
|
||||
<ion-card-content>
|
||||
<h1>Heading 1</h1>
|
||||
@@ -116,13 +105,16 @@ test.describe('card: basic', () => {
|
||||
<p>Paragraph</p>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(`card-headings-${page.getSnapshotSettings()}.png`);
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-headings`));
|
||||
});
|
||||
test('should render even without header or content elements', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card>
|
||||
<ion-list lines="none">
|
||||
<ion-item href="#" class="ion-activated">
|
||||
@@ -150,10 +142,38 @@ test.describe('card: basic', () => {
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-card>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(`card-no-content-or-header-${page.getSnapshotSettings()}.png`);
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-no-content-or-header`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('card: translucent'), () => {
|
||||
test('should not have visual regressions with translucent cards', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card>
|
||||
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0">
|
||||
<img style="transform: rotate(145deg) scale(1.5)" src="/src/components/card/test/img.jpg" />
|
||||
</div>
|
||||
<ion-card-header translucent="true">
|
||||
<ion-card-title> Title </ion-card-title>
|
||||
<ion-card-subtitle> Subtitle </ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
|
||||
<ion-card-content style="min-height: 20px"></ion-card-content>
|
||||
</ion-card>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-translucent`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,8 +42,14 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:host(.in-item[slot="start"]:not(.legacy-checkbox)),
|
||||
:host(.in-item[slot="end"]:not(.legacy-checkbox)) {
|
||||
/**
|
||||
* Checkbox can be slotted
|
||||
* in components such as item and
|
||||
* toolbar which is why we do not
|
||||
* limit the below behavior to just ion-item.
|
||||
*/
|
||||
:host([slot="start"]:not(.legacy-checkbox)),
|
||||
:host([slot="end"]:not(.legacy-checkbox)) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: a11y', () => {
|
||||
test.beforeEach(async ({ skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('md');
|
||||
});
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('checkbox: a11y'), () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/a11y`, config);
|
||||
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/a11y`);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,100 +1,123 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: basic visual tests', () => {
|
||||
test('should render unchecked checkbox correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox>Unchecked</ion-checkbox>
|
||||
`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: basic visual tests'), () => {
|
||||
test('should render unchecked checkbox correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox>Unchecked</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(`checkbox-unchecked-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-unchecked`));
|
||||
});
|
||||
|
||||
test('should render checked checkbox correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox checked>Checked</ion-checkbox>
|
||||
`);
|
||||
test('should render checked checkbox correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox checked>Checked</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(`checkbox-checked-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-checked`));
|
||||
});
|
||||
|
||||
test('should render disabled checkbox correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox checked disabled>Disabled</ion-checkbox>
|
||||
`);
|
||||
test('should render disabled checkbox correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox checked disabled>Disabled</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(`checkbox-disabled-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-disabled`));
|
||||
});
|
||||
|
||||
test('should render custom checkmark-width correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox checked style="--checkmark-width: 7">Checkmark Width</ion-checkbox>
|
||||
`);
|
||||
test('should render custom checkmark-width correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox checked style="--checkmark-width: 7">Checkmark Width</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(`checkbox-checkmark-width-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-checkmark-width`));
|
||||
});
|
||||
|
||||
test('should render custom size correctly', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox checked style="--size: 100px">Size</ion-checkbox>
|
||||
`);
|
||||
test('should render custom size correctly', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox checked style="--size: 100px">Size</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(`checkbox-size-${page.getSnapshotSettings()}.png`);
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-size`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('checkbox: ionChange', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('ios');
|
||||
});
|
||||
|
||||
test('should fire ionChange when interacting with checkbox', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox aria-label="checkbox" value="my-checkbox"></ion-checkbox>
|
||||
`);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await checkbox.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await checkbox.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should fire ionChange when interacting with checkbox in item', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-item>
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('checkbox: ionChange'), () => {
|
||||
test('should fire ionChange when interacting with checkbox', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox aria-label="checkbox" value="my-checkbox"></ion-checkbox>
|
||||
</ion-item>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const item = page.locator('ion-item');
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await item.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
await checkbox.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await item.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
await checkbox.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should not fire when programmatically setting a value', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox aria-label="checkbox" value="my-checkbox"></ion-checkbox>
|
||||
`);
|
||||
test('should fire ionChange when interacting with checkbox in item', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-item>
|
||||
<ion-checkbox aria-label="checkbox" value="my-checkbox"></ion-checkbox>
|
||||
</ion-item>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const item = page.locator('ion-item');
|
||||
|
||||
await checkbox.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
|
||||
expect(ionChange).not.toHaveReceivedEvent();
|
||||
await item.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await item.click();
|
||||
expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should not fire when programmatically setting a value', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox aria-label="checkbox" value="my-checkbox"></ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await checkbox.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
|
||||
expect(ionChange).not.toHaveReceivedEvent();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: color', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: color'), () => {
|
||||
test('should apply color when checked', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox color="danger" checked="true">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
test('should apply color when checked', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox color="danger" checked="true">Label</ion-checkbox>
|
||||
`);
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-color-checked`));
|
||||
});
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(`checkbox-color-checked-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should not apply color when unchecked', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox color="danger">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
test('should not apply color when unchecked', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox color="danger">Label</ion-checkbox>
|
||||
`);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(`checkbox-color-unchecked-${page.getSnapshotSettings()}.png`);
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-color-unchecked`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: indeterminate', () => {
|
||||
test('should not have visual regressions', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: indeterminate'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/indeterminate`, config);
|
||||
|
||||
await page.goto(`/src/components/checkbox/test/indeterminate`);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox:first-child');
|
||||
await expect(checkbox).toHaveScreenshot(`checkbox-indeterminate-${page.getSnapshotSettings()}.png`);
|
||||
const checkbox = page.locator('ion-checkbox:first-child');
|
||||
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-indeterminate`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,37 +1,52 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: item', () => {
|
||||
test('should render correctly in list', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-checkbox>Enable Notifications</ion-checkbox>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
`);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(`checkbox-list-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('should render correctly in inset list', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-list inset="true">
|
||||
<ion-item>
|
||||
<ion-checkbox>Enable Notifications</ion-checkbox>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
`);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(`checkbox-inset-list-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
test('label should have correct contrast when used in an item', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.setContent(`
|
||||
<ion-item color="primary">
|
||||
<ion-checkbox>Enable Notifications</ion-checkbox>
|
||||
</ion-item>
|
||||
`);
|
||||
const item = page.locator('ion-item');
|
||||
expect(await item.screenshot()).toMatchSnapshot(`checkbox-item-color-${page.getSnapshotSettings()}.png`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: item with list'), () => {
|
||||
test('should render correctly in list', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-checkbox>Enable Notifications</ion-checkbox>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(screenshot(`checkbox-list`));
|
||||
});
|
||||
test('should render correctly in inset list', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-list inset="true">
|
||||
<ion-item>
|
||||
<ion-checkbox>Enable Notifications</ion-checkbox>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const list = page.locator('ion-list');
|
||||
expect(await list.screenshot()).toMatchSnapshot(screenshot(`checkbox-inset-list`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: label in item'), () => {
|
||||
test('label should have correct contrast when used in an item', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-item color="primary">
|
||||
<ion-checkbox>Enable Notifications</ion-checkbox>
|
||||
</ion-item>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const item = page.locator('ion-item');
|
||||
expect(await item.screenshot()).toMatchSnapshot(screenshot(`checkbox-item-color`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* By default ion-checkbox only takes up
|
||||
@@ -9,120 +9,134 @@ import { test } from '@utils/test/playwright';
|
||||
* we set the width of the checkbox so we can
|
||||
* see the justification results.
|
||||
*/
|
||||
test.describe('checkbox: label', () => {
|
||||
test.describe('checkbox: start placement', () => {
|
||||
test('should render a start justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="start" justify="start" style="width: 200px">Label</ion-checkbox>
|
||||
`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: label'), () => {
|
||||
test.describe('checkbox: start placement', () => {
|
||||
test('should render a start justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="start" justify="start" style="width: 200px">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-start-justify-start-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-start-justify-start`));
|
||||
});
|
||||
|
||||
test('should render an end justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="start" justify="end" style="width: 200px">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-start-justify-end`));
|
||||
});
|
||||
|
||||
test('should render a space between justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="start" justify="space-between" style="width: 200px">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-start-justify-space-between`));
|
||||
});
|
||||
|
||||
test('should truncate long labels with ellipses', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="start" justify="start" style="width: 200px">
|
||||
Long Label Long Label Long Label Long Label Long Label Long Label
|
||||
</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-long-label`));
|
||||
});
|
||||
});
|
||||
|
||||
test('should render an end justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="start" justify="end" style="width: 200px">Label</ion-checkbox>
|
||||
`);
|
||||
test.describe('checkbox: end placement', () => {
|
||||
test('should render a start justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="end" justify="start" style="width: 200px">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-start-justify-end-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-end-justify-start`));
|
||||
});
|
||||
|
||||
test('should render an end justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="end" justify="end" style="width: 200px">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-end-justify-end`));
|
||||
});
|
||||
|
||||
test('should render a space between justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="end" justify="space-between" style="width: 200px">Label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-end-justify-space-between`));
|
||||
});
|
||||
});
|
||||
|
||||
test('should render a space between justification with label in the start position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="start" justify="space-between" style="width: 200px">Label</ion-checkbox>
|
||||
`);
|
||||
test.describe('checkbox: fixed placement', () => {
|
||||
test('should render a start justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="fixed" justify="start" style="width: 200px">This is a long label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-start-justify-space-between-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-fixed-justify-start`));
|
||||
});
|
||||
|
||||
test('should truncate long labels with ellipses', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="start" justify="start" style="width: 200px">
|
||||
Long Label Long Label Long Label Long Label Long Label Long Label
|
||||
</ion-checkbox>
|
||||
`);
|
||||
test('should render an end justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="fixed" justify="end" style="width: 200px">This is a long label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(`checkbox-long-label-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-fixed-justify-end`));
|
||||
});
|
||||
|
||||
test.describe('checkbox: end placement', () => {
|
||||
test('should render a start justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="end" justify="start" style="width: 200px">Label</ion-checkbox>
|
||||
`);
|
||||
test('should render a space between justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox label-placement="fixed" justify="space-between" style="width: 200px">This is a long label</ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-end-justify-start-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
|
||||
test('should render an end justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="end" justify="end" style="width: 200px">Label</ion-checkbox>
|
||||
`);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-end-justify-end-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
|
||||
test('should render a space between justification with label in the end position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="end" justify="space-between" style="width: 200px">Label</ion-checkbox>
|
||||
`);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-end-justify-space-between-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('checkbox: fixed placement', () => {
|
||||
test('should render a start justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="fixed" justify="start" style="width: 200px">This is a long label</ion-checkbox>
|
||||
`);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-fixed-justify-start-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
|
||||
test('should render an end justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="fixed" justify="end" style="width: 200px">This is a long label</ion-checkbox>
|
||||
`);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-fixed-justify-end-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
});
|
||||
|
||||
test('should render a space between justification with label in the fixed position', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox label-placement="fixed" justify="space-between" style="width: 200px">This is a long label</ion-checkbox>
|
||||
`);
|
||||
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(
|
||||
`checkbox-label-fixed-justify-space-between-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-fixed-justify-space-between`));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,61 +1,71 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: basic (legacy)', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/legacy/basic`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: basic (legacy)'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/legacy/basic`, config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
expect(await page.screenshot()).toMatchSnapshot(`checkbox-legacy-basic-${page.getSnapshotSettings()}.png`);
|
||||
expect(await page.screenshot()).toMatchSnapshot(screenshot(`checkbox-legacy-basic`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('checkbox: ionChange', () => {
|
||||
test.beforeEach(({ skip }) => {
|
||||
skip.rtl();
|
||||
});
|
||||
test('should fire ionChange when interacting with checkbox', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
`);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await checkbox.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await checkbox.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should fire ionChange when interacting with checkbox in item', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-item>
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('checkbox: ionChange'), () => {
|
||||
test('should fire ionChange when interacting with checkbox', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
</ion-item>
|
||||
`);
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const item = page.locator('ion-item');
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await item.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
await checkbox.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await item.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
await checkbox.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should not fire when programmatically setting a value', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
`);
|
||||
test('should fire ionChange when interacting with checkbox in item', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-item>
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
</ion-item>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const item = page.locator('ion-item');
|
||||
|
||||
await checkbox.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
|
||||
await expect(ionChange).not.toHaveReceivedEvent();
|
||||
await item.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
|
||||
|
||||
await item.click();
|
||||
await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
|
||||
});
|
||||
|
||||
test('should not fire when programmatically setting a value', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-checkbox value="my-checkbox"></ion-checkbox>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
const checkbox = page.locator('ion-checkbox');
|
||||
|
||||
await checkbox.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
|
||||
await expect(ionChange).not.toHaveReceivedEvent();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('checkbox: indeterminate (legacy)', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/legacy/indeterminate`);
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('checkbox: indeterminate (legacy)'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/checkbox/test/legacy/indeterminate`, config);
|
||||
|
||||
const content = page.locator('#checkboxes');
|
||||
expect(await content.screenshot()).toMatchSnapshot(
|
||||
`checkbox-legacy-indeterminate-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
const content = page.locator('#checkboxes');
|
||||
expect(await content.screenshot()).toMatchSnapshot(screenshot(`checkbox-legacy-indeterminate`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,42 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('chip: rendering', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto('/src/components/chip/test/basic');
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('chip: rendering'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto('/src/components/chip/test/basic', config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`chip-basic-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
|
||||
test('should not clip descenders in item', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('md');
|
||||
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/18313',
|
||||
await expect(page).toHaveScreenshot(screenshot(`chip-basic`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('chip: descenders'), () => {
|
||||
test('should not clip descenders in item', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/18313',
|
||||
});
|
||||
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-chip>
|
||||
<ion-label>Agreements</ion-label>
|
||||
</ion-chip>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const chip = page.locator('ion-chip');
|
||||
|
||||
await expect(chip).toHaveScreenshot(screenshot(`chip-descender`));
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-chip>
|
||||
<ion-label>Agreements</ion-label>
|
||||
</ion-chip>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
`);
|
||||
|
||||
const chip = page.locator('ion-chip');
|
||||
|
||||
await expect(chip).toHaveScreenshot(`chip-descender-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('chip: states', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto('/src/components/chip/test/states');
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('chip: states'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto('/src/components/chip/test/states', config);
|
||||
|
||||
await page.setIonViewport();
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`chip-states-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`chip-states`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('content: basic', () => {
|
||||
test('should not have visual regressions', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('ios', 'ion-content does not have mode-specific styling');
|
||||
/**
|
||||
* ion-content does not have mode-specific styling
|
||||
*/
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('content: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/content/test/basic`, config);
|
||||
|
||||
await page.goto(`/src/components/content/test/basic`);
|
||||
await page.setIonViewport();
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
await expect(page).toHaveScreenshot(`content-diff-${page.getSnapshotSettings()}.png`);
|
||||
await expect(page).toHaveScreenshot(screenshot(`content-diff`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user