merge release-6.3.8

Release 6.3.8
This commit is contained in:
Liam DeBeasi
2022-11-22 10:44:15 -05:00
committed by GitHub
118 changed files with 4983 additions and 15938 deletions

View File

@ -29,7 +29,7 @@ runs:
shell: bash
working-directory: ./angular/test
- name: Install Dependencies
run: npm install --legacy-peer-deps
run: npm install
shell: bash
working-directory: ./angular/test/build/${{ inputs.app }}
- name: Sync Built Changes

View File

@ -4,42 +4,37 @@ on:
workflow_dispatch:
jobs:
dev-build:
create-dev-hash:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.dev-build.outputs.version }}
dev-hash: ${{ steps.create-dev-hash.outputs.DEV_HASH }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Dependencies
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
shell: bash
- name: Prepare NPM Token
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
shell: bash
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# A 1 is required before the timestamp
# as lerna will fail when there is a leading 0
# See https://github.com/lerna/lerna/issues/2840
- name: Create Dev Hash
run: |
echo "HASH=1$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_ENV
echo "TIMESTAMP=1$(date +%s)" >> $GITHUB_ENV
echo "CURRENT_VERSION=$(node ./.scripts/bump-version.js)" >> $GITHUB_ENV
- name: Install Dependencies
run: npm ci --no-package-lock
shell: bash
- name: Create Dev Build
- id: create-dev-hash
name: Create Dev Hash
run: |
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}") --no-verify-access --yes --force-publish='*' --dist-tag dev --no-git-tag-version --no-push --exact
echo "DEV_HASH=$(node ./.scripts/bump-version.js)-dev.1$(date +%s).1$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_OUTPUT
shell: bash
- id: dev-build
run: echo "::set-output name=version::$(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}")"
release-ionic:
needs: [create-dev-hash]
uses: ./.github/workflows/release-ionic.yml
with:
tag: dev
version: ${{ needs.create-dev-hash.outputs.dev-hash }}
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
get-build:
name: Get your dev build!
runs-on: ubuntu-latest
needs: dev-build
needs: [create-dev-hash, release-ionic]
steps:
- run: echo ${{ needs.dev-build.outputs.version }}
- run: echo ${{ needs.create-dev-hash.outputs.dev-hash }}

View File

@ -7,45 +7,64 @@ on:
- cron: '00 06 * * 1-5'
jobs:
nightly-build:
create-nightly-hash:
runs-on: ubuntu-latest
outputs:
nightly-hash: ${{ steps.create-nightly-hash.outputs.NIGHTLY_HASH }}
steps:
- uses: actions/checkout@v3
# A 1 is required before the timestamp
# 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
shell: bash
- id: create-nightly-hash
name: Create Nightly Hash
# The date should output YYYYMMDD
# so that it is human readable
run: |
echo "NIGHTLY_HASH=$(node ./.scripts/bump-version.js)-nightly.$(date +%Y%m%d)" >> $GITHUB_OUTPUT
shell: bash
release-ionic:
needs: [create-nightly-hash]
uses: ./.github/workflows/release-ionic.yml
with:
tag: nightly
version: ${{ needs.create-nightly-hash.outputs.nightly-hash }}
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
finalize-release:
needs: [create-nightly-hash, release-ionic]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
token: ${{ secrets.IONITRON_TOKEN }}
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Dependencies
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
shell: bash
- name: Prepare NPM Token
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
shell: bash
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Configure Identity
# Commits from github-actions do not
# trigger other GitHub Actions. As a result,
# we publish releases from Ionitron instead
# so actions run when merging the release branch
# back into main.
run: |
git config user.name github-actions
git config user.email github-actions@github.com
# A 1 is required before the timestamp
# as lerna will fail when there is a leading 0
# See https://github.com/lerna/lerna/issues/2840
- name: Create Nightly Hash
# The date should output YYYYMMDD
# so that it is human readable
run: |
echo "DATE=$(date +%Y%m%d)" >> $GITHUB_ENV
echo "CURRENT_VERSION=$(node ./.scripts/bump-version.js)" >> $GITHUB_ENV
git config user.name ionitron
git config user.email hi@ionicframework.com
shell: bash
- name: Checkout Nightly Branch
# There are branch protection rules for our version
# branches (i.e. "6.2.x"), so we cannot name the branch
# the nightly hash as it would fall under the protection
# rule. As a result, we prefix "tmp-" to the branch.
run: |
git checkout -b nightly-$(echo "${{ env.DATE }}")
git push origin nightly-$(echo "${{ env.DATE }}")
git checkout -b tmp-${{ needs.create-nightly-hash.outputs.nightly-hash }}
git push origin tmp-${{ needs.create-nightly-hash.outputs.nightly-hash }}
shell: bash
- name: Create Nightly Build
run: |
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ env.CURRENT_VERSION }}")-nightly.$(echo "${{ env.DATE }}") --no-verify-access --yes --force-publish='*' --dist-tag nightly --conventional-commits --conventional-prerelease --exact --create-release github
- name: Create GitHub Release
run: lerna version ${{ needs.create-nightly-hash.outputs.nightly-hash }} --yes --force-publish='*' --conventional-commits --conventional-prerelease --create-release github
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
@ -55,6 +74,6 @@ jobs:
- name: Delete Nightly Branch
run: |
git checkout main
git branch -D nightly-$(echo "${{ env.DATE }}")
git push origin --delete nightly-$(echo "${{ env.DATE }}")
git branch -D tmp-${{ needs.create-nightly-hash.outputs.nightly-hash }}
git push origin --delete tmp-${{ needs.create-nightly-hash.outputs.nightly-hash }}
shell: bash

194
.github/workflows/release-ionic.yml vendored Normal file
View File

@ -0,0 +1,194 @@
name: 'Release Ionic'
on:
workflow_call:
inputs:
version:
description: 'The type of version to release.'
type: string
required: true
tag:
description: 'The tag to publish to on NPM.'
type: string
required: true
secrets:
NPM_TOKEN:
required: true
jobs:
release-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/core'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'core'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/core
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-core
output: core/CoreBuild.zip
paths: core/dist core/components core/css core/hydrate core/loader core/src/components.d.ts
- name: Cache Built @ionic/docs
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-docs
output: docs/DocsBuild.zip
paths: docs/core.json docs/core.d.ts
release-docs:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/docs built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-docs
path: ./docs
filename: DocsBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/docs'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'docs'
token: ${{ secrets.NPM_TOKEN }}
release-angular:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/angular'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'angular'
folder: './dist'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/angular
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-angular
output: ./angular/AngularBuild.zip
paths: ./angular/dist
release-react:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/react'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/react'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/react
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-react
output: packages/react/ReactBuild.zip
paths: packages/react/dist packages/react/css
release-vue:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/vue'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/vue'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/vue
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-vue
output: packages/vue/VueBuild.zip
paths: packages/vue/dist packages/vue/css
release-angular-server:
needs: [release-angular]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/angular built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-angular
path: ./angular
filename: AngularBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/angular-server'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/angular-server'
folder: './dist'
token: ${{ secrets.NPM_TOKEN }}
release-react-router:
needs: [release-react]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/react built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react
path: ./packages/react
filename: ReactBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/react-router'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/react-router'
token: ${{ secrets.NPM_TOKEN }}
release-vue-router:
needs: [release-vue]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/vue built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-vue
path: ./packages/vue
filename: VueBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
scope: '@ionic/vue-router'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/vue-router'
token: ${{ secrets.NPM_TOKEN }}

View File

@ -21,185 +21,16 @@ on:
- v4-lts
jobs:
release-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/release
release-ionic:
uses: ./.github/workflows/release-ionic.yml
with:
scope: '@ionic/core'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'core'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/core
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-core
output: core/CoreBuild.zip
paths: core/dist core/components core/css core/hydrate core/loader core/src/components.d.ts
- name: Cache Built @ionic/docs
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-docs
output: docs/DocsBuild.zip
paths: docs/core.json docs/core.d.ts
release-docs:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/docs built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-docs
path: ./docs
filename: DocsBuild.zip
- uses: ./.github/workflows/actions/release
with:
scope: '@ionic/docs'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'docs'
token: ${{ secrets.NPM_TOKEN }}
release-angular:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/release
with:
scope: '@ionic/angular'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'angular'
folder: './dist'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/angular
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-angular
output: ./angular/AngularBuild.zip
paths: ./angular/dist
release-react:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/release
with:
scope: '@ionic/react'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/react'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/react
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-react
output: packages/react/ReactBuild.zip
paths: packages/react/dist packages/react/css
release-vue:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- uses: ./.github/workflows/actions/release
with:
scope: '@ionic/vue'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/vue'
token: ${{ secrets.NPM_TOKEN }}
- name: Cache Built @ionic/vue
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-vue
output: packages/vue/VueBuild.zip
paths: packages/vue/dist packages/vue/css
release-angular-server:
needs: [release-angular]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/angular built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-angular
path: ./angular
filename: AngularBuild.zip
- uses: ./.github/workflows/actions/release
with:
scope: '@ionic/angular-server'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/angular-server'
folder: './dist'
token: ${{ secrets.NPM_TOKEN }}
release-react-router:
needs: [release-react]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/react built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react
path: ./packages/react
filename: ReactBuild.zip
- uses: ./.github/workflows/actions/release
with:
scope: '@ionic/react-router'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/react-router'
token: ${{ secrets.NPM_TOKEN }}
release-vue-router:
needs: [release-vue]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore @ionic/vue built cache
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-vue
path: ./packages/vue
filename: VueBuild.zip
- uses: ./.github/workflows/actions/release
with:
scope: '@ionic/vue-router'
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
working-directory: 'packages/vue-router'
token: ${{ secrets.NPM_TOKEN }}
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
finalize-release:
needs: [release-react-router, release-angular-server, release-vue-router]
needs: [release-ionic]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -233,7 +64,7 @@ jobs:
shell: bash
purge-cdn-cache:
needs: [release-react-router, release-angular-server, release-vue-router]
needs: [release-ionic]
runs-on: ubuntu-latest
steps:
- name: Purge JSDelivr Cache

View File

@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.8](https://github.com/ionic-team/ionic-framework/compare/v6.3.7...v6.3.8) (2022-11-22)
### Bug Fixes
* **modal:** status bar style defaults to app settings ([#26291](https://github.com/ionic-team/ionic-framework/issues/26291)) ([a6c9e55](https://github.com/ionic-team/ionic-framework/commit/a6c9e55adcaa963f4829d6963b9b1a7b246cef4e)), closes [#26173](https://github.com/ionic-team/ionic-framework/issues/26173)
* **normalize:** normalize css resets button padding ([#26214](https://github.com/ionic-team/ionic-framework/issues/26214)) ([e14c947](https://github.com/ionic-team/ionic-framework/commit/e14c94722c2c8ec145d680f911b708a34f095cd3)), closes [#23928](https://github.com/ionic-team/ionic-framework/issues/23928)
* **popover:** popover positions correctly on all frameworks ([#26306](https://github.com/ionic-team/ionic-framework/issues/26306)) ([be9a399](https://github.com/ionic-team/ionic-framework/commit/be9a399eeed37ae4a67add78ac1283ba0c5e4b14)), closes [#25337](https://github.com/ionic-team/ionic-framework/issues/25337)
* **react:** useIonRouter hook has stable router reference ([#25000](https://github.com/ionic-team/ionic-framework/issues/25000)) ([89e3cd6](https://github.com/ionic-team/ionic-framework/commit/89e3cd67ce6d9cfc0607d6a89362483878a1820b)), closes [#24987](https://github.com/ionic-team/ionic-framework/issues/24987)
* **reorder-group:** support custom components ([#26289](https://github.com/ionic-team/ionic-framework/issues/26289)) ([8425734](https://github.com/ionic-team/ionic-framework/commit/842573477b1b498f2280badc8c7411832c1650a5)), closes [#19447](https://github.com/ionic-team/ionic-framework/issues/19447)
* **segment:** scrollable segments center button on click ([#26285](https://github.com/ionic-team/ionic-framework/issues/26285)) ([73ea64c](https://github.com/ionic-team/ionic-framework/commit/73ea64c02fff1d63651f6c98f03b43265ba5227a)), closes [#25367](https://github.com/ionic-team/ionic-framework/issues/25367)
* **toggle:** rtl layout renders correctly in safari ([#26315](https://github.com/ionic-team/ionic-framework/issues/26315)) ([0932f89](https://github.com/ionic-team/ionic-framework/commit/0932f89f5db63a1e6149f2f45de798d7485d72ee))
## [6.3.7](https://github.com/ionic-team/ionic-framework/compare/v6.3.6...v6.3.7) (2022-11-16)

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.8](https://github.com/ionic-team/ionic/compare/v6.3.7...v6.3.8) (2022-11-22)
**Note:** Version bump only for package @ionic/angular
## [6.3.7](https://github.com/ionic-team/ionic/compare/v6.3.6...v6.3.7) (2022-11-16)

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "6.3.7",
"version": "6.3.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "6.3.7",
"version": "6.3.8",
"license": "MIT",
"dependencies": {
"@ionic/core": "^6.3.7",
"@ionic/core": "^6.3.8",
"ionicons": "^6.0.4",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
@ -1024,9 +1024,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "6.3.7",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.7.tgz",
"integrity": "sha512-HWntdPsc4lVHHwz7kY2BSQteafwXsPxc8y5wAdfeA8woRLGse+1Vq00CDUihf7tTUflM1CdmVgGtyYlDAnO2fA==",
"version": "6.3.8",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.8.tgz",
"integrity": "sha512-mpawxkbjx/lBaWMYexvEHzKP9+03gKKe/CXHGnBh7z9WHpDuR+H9jdojjiHkakoN79TcdM3G+BYMZ0tVGeeJhw==",
"dependencies": {
"@stencil/core": "^2.18.0",
"ionicons": "^6.0.4",
@ -7940,9 +7940,9 @@
"dev": true
},
"@ionic/core": {
"version": "6.3.7",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.7.tgz",
"integrity": "sha512-HWntdPsc4lVHHwz7kY2BSQteafwXsPxc8y5wAdfeA8woRLGse+1Vq00CDUihf7tTUflM1CdmVgGtyYlDAnO2fA==",
"version": "6.3.8",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.8.tgz",
"integrity": "sha512-mpawxkbjx/lBaWMYexvEHzKP9+03gKKe/CXHGnBh7z9WHpDuR+H9jdojjiHkakoN79TcdM3G+BYMZ0tVGeeJhw==",
"requires": {
"@stencil/core": "^2.18.0",
"ionicons": "^6.0.4",

View File

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

View File

@ -21,7 +21,6 @@
"@ionic/angular": "^6.1.15",
"@ionic/angular-server": "^6.1.15",
"@nguniversal/express-engine": "^12.1.3",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"express": "^4.15.2",
"ionicons": "^6.0.4",
@ -4503,16 +4502,6 @@
"ajv": "^6.9.1"
}
},
"node_modules/angular-in-memory-web-api": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.11.0.tgz",
"integrity": "sha512-QV1qYHm+Zd+wrvlcPLnAcqqGpOmCN1EUj4rRuYHpek8+QqFFdxBNuPZOJCKvU7I97z5QSKHsdc6PNKlpUQr3UA==",
"peerDependencies": {
"@angular/common": ">=8.0.0",
"@angular/core": ">=8.0.0",
"rxjs": "^6.0.0"
}
},
"node_modules/ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@ -23204,11 +23193,6 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true
},
"angular-in-memory-web-api": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.11.0.tgz",
"integrity": "sha512-QV1qYHm+Zd+wrvlcPLnAcqqGpOmCN1EUj4rRuYHpek8+QqFFdxBNuPZOJCKvU7I97z5QSKHsdc6PNKlpUQr3UA=="
},
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",

View File

@ -32,7 +32,6 @@
"@ionic/angular": "^6.1.15",
"@ionic/angular-server": "^6.1.15",
"@nguniversal/express-engine": "^12.1.3",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"express": "^4.15.2",
"ionicons": "^6.0.4",

View File

@ -21,7 +21,6 @@
"@ionic/angular": "^6.1.15",
"@ionic/angular-server": "^6.1.15",
"@nguniversal/express-engine": "^13.1.1",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"express": "^4.15.2",
"ionicons": "^6.0.4",
@ -4766,16 +4765,6 @@
"ajv": "^6.9.1"
}
},
"node_modules/angular-in-memory-web-api": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.11.0.tgz",
"integrity": "sha512-QV1qYHm+Zd+wrvlcPLnAcqqGpOmCN1EUj4rRuYHpek8+QqFFdxBNuPZOJCKvU7I97z5QSKHsdc6PNKlpUQr3UA==",
"peerDependencies": {
"@angular/common": ">=8.0.0",
"@angular/core": ">=8.0.0",
"rxjs": "^6.0.0"
}
},
"node_modules/ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@ -19780,11 +19769,6 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true
},
"angular-in-memory-web-api": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.11.0.tgz",
"integrity": "sha512-QV1qYHm+Zd+wrvlcPLnAcqqGpOmCN1EUj4rRuYHpek8+QqFFdxBNuPZOJCKvU7I97z5QSKHsdc6PNKlpUQr3UA=="
},
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",

View File

@ -32,7 +32,6 @@
"@ionic/angular": "^6.1.15",
"@ionic/angular-server": "^6.1.15",
"@nguniversal/express-engine": "^13.1.1",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"express": "^4.15.2",
"ionicons": "^6.0.4",

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,6 @@
"@angular/router": "^14.1.0",
"@ionic/angular": "^6.1.15",
"@ionic/angular-server": "^6.1.15",
"@nguniversal/express-engine": "^14.0.3",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"express": "^4.15.2",

File diff suppressed because it is too large Load Diff

View File

@ -20,19 +20,18 @@
"test.watch": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.open\" --kill-others --success first"
},
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/platform-server": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/animations": "^15.0.0",
"@angular/common": "^15.0.0",
"@angular/compiler": "^15.0.0",
"@angular/core": "^15.0.0",
"@angular/forms": "^15.0.0",
"@angular/platform-browser": "^15.0.0",
"@angular/platform-browser-dynamic": "^15.0.0",
"@angular/platform-server": "^15.0.0",
"@angular/router": "^15.0.0",
"@ionic/angular": "^6.1.15",
"@ionic/angular-server": "^6.1.15",
"@nguniversal/express-engine": "^14.0.3",
"angular-in-memory-web-api": "^0.11.0",
"@nguniversal/express-engine": "^15.0.0",
"core-js": "^2.6.11",
"express": "^4.15.2",
"ionicons": "^6.0.4",
@ -42,15 +41,15 @@
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular-eslint/builder": "^14.0.2",
"@angular-eslint/eslint-plugin": "^14.0.2",
"@angular-eslint/eslint-plugin-template": "^14.0.2",
"@angular-eslint/schematics": "^14.0.2",
"@angular-eslint/template-parser": "^14.0.2",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular/language-service": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.0.0",
"@angular-eslint/builder": "^15.0.0-alpha.0",
"@angular-eslint/eslint-plugin": "^15.0.0-alpha.0",
"@angular-eslint/eslint-plugin-template": "^15.0.0-alpha.0",
"@angular-eslint/schematics": "^15.0.0-alpha.0",
"@angular-eslint/template-parser": "^15.0.0-alpha.0",
"@angular/cli": "^15.0.0",
"@angular/compiler-cli": "^15.0.0",
"@angular/language-service": "^15.0.0",
"@nguniversal/builders": "^15.0.0-next.0",
"@types/express": "^4.17.7",
"@types/node": "^12.12.54",

View File

@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.8](https://github.com/ionic-team/ionic/compare/v6.3.7...v6.3.8) (2022-11-22)
### Bug Fixes
* **modal:** status bar style defaults to app settings ([#26291](https://github.com/ionic-team/ionic/issues/26291)) ([a6c9e55](https://github.com/ionic-team/ionic/commit/a6c9e55adcaa963f4829d6963b9b1a7b246cef4e)), closes [#26173](https://github.com/ionic-team/ionic/issues/26173)
* **normalize:** normalize css resets button padding ([#26214](https://github.com/ionic-team/ionic/issues/26214)) ([e14c947](https://github.com/ionic-team/ionic/commit/e14c94722c2c8ec145d680f911b708a34f095cd3)), closes [#23928](https://github.com/ionic-team/ionic/issues/23928)
* **popover:** popover positions correctly on all frameworks ([#26306](https://github.com/ionic-team/ionic/issues/26306)) ([be9a399](https://github.com/ionic-team/ionic/commit/be9a399eeed37ae4a67add78ac1283ba0c5e4b14)), closes [#25337](https://github.com/ionic-team/ionic/issues/25337)
* **reorder-group:** support custom components ([#26289](https://github.com/ionic-team/ionic/issues/26289)) ([8425734](https://github.com/ionic-team/ionic/commit/842573477b1b498f2280badc8c7411832c1650a5)), closes [#19447](https://github.com/ionic-team/ionic/issues/19447)
* **segment:** scrollable segments center button on click ([#26285](https://github.com/ionic-team/ionic/issues/26285)) ([73ea64c](https://github.com/ionic-team/ionic/commit/73ea64c02fff1d63651f6c98f03b43265ba5227a)), closes [#25367](https://github.com/ionic-team/ionic/issues/25367)
* **toggle:** rtl layout renders correctly in safari ([#26315](https://github.com/ionic-team/ionic/issues/26315)) ([0932f89](https://github.com/ionic-team/ionic/commit/0932f89f5db63a1e6149f2f45de798d7485d72ee))
## [6.3.7](https://github.com/ionic-team/ionic/compare/v6.3.6...v6.3.7) (2022-11-16)

14029
core/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "6.3.7",
"version": "6.3.8",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@ -61,8 +61,8 @@
"eslint-plugin-custom-rules": "file:custom-rules",
"execa": "^5.0.0",
"fs-extra": "^9.0.1",
"jest": "^26.4.1",
"jest-cli": "^26.4.1",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"pixelmatch": "4.0.2",
"prettier": "^2.6.1",
"puppeteer": "^10.4.0",

View File

@ -9,6 +9,7 @@ import {
import type { GestureDetail } from '../../../utils/gesture';
import { createGesture } from '../../../utils/gesture';
import { clamp, getElementRoot } from '../../../utils/helpers';
import type { Style as StatusBarStyle } from '../../../utils/native/status-bar';
import { setCardStatusBarDark, setCardStatusBarDefault } from '../utils';
import { calculateSpringStep, handleCanDismiss } from './utils';
@ -18,7 +19,12 @@ export const SwipeToCloseDefaults = {
MIN_PRESENTING_SCALE: 0.93,
};
export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: Animation, onDismiss: () => void) => {
export const createSwipeToCloseGesture = (
el: HTMLIonModalElement,
animation: Animation,
statusBarStyle: StatusBarStyle,
onDismiss: () => void
) => {
/**
* The step value at which a card modal
* is eligible for dismissing via gesture.
@ -173,14 +179,14 @@ export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: An
* should block the gesture from
* proceeding,
*/
const isAttempingDismissWithCanDismiss = step >= 0 && canDismissBlocksGesture;
const isAttemptingDismissWithCanDismiss = step >= 0 && canDismissBlocksGesture;
/**
* If we are blocking the gesture from dismissing,
* set the max step value so that the sheet cannot be
* completely hidden.
*/
const maxStep = isAttempingDismissWithCanDismiss ? canDismissMaxStep : 0.9999;
const maxStep = isAttemptingDismissWithCanDismiss ? canDismissMaxStep : 0.9999;
/**
* If we are blocking the gesture from
@ -190,7 +196,7 @@ export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: An
* Note that the starting breakpoint is always 0,
* so we omit adding 0 to the result.
*/
const processedStep = isAttempingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
const clampedStep = clamp(0.0001, processedStep, maxStep);
@ -205,7 +211,7 @@ export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: An
* crossed a certain threshold.
*/
if (clampedStep >= DISMISS_THRESHOLD && lastStep < DISMISS_THRESHOLD) {
setCardStatusBarDefault();
setCardStatusBarDefault(statusBarStyle);
/**
* However, if we swipe back up, then the
@ -223,10 +229,10 @@ export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: An
const velocity = detail.velocityY;
const step = detail.deltaY / height;
const isAttempingDismissWithCanDismiss = step >= 0 && canDismissBlocksGesture;
const maxStep = isAttempingDismissWithCanDismiss ? canDismissMaxStep : 0.9999;
const isAttemptingDismissWithCanDismiss = step >= 0 && canDismissBlocksGesture;
const maxStep = isAttemptingDismissWithCanDismiss ? canDismissMaxStep : 0.9999;
const processedStep = isAttempingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
const clampedStep = clamp(0.0001, processedStep, maxStep);
@ -238,7 +244,7 @@ export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: An
* animation can never complete until
* canDismiss is checked.
*/
const shouldComplete = !isAttempingDismissWithCanDismiss && threshold >= DISMISS_THRESHOLD;
const shouldComplete = !isAttemptingDismissWithCanDismiss && threshold >= DISMISS_THRESHOLD;
let newStepValue = shouldComplete ? -0.001 : 0.001;
if (!shouldComplete) {
@ -280,7 +286,7 @@ export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: An
* check canDismiss. 25% was chosen
* to avoid accidental swipes.
*/
if (isAttempingDismissWithCanDismiss && clampedStep > maxStep / 4) {
if (isAttemptingDismissWithCanDismiss && clampedStep > maxStep / 4) {
handleCanDismiss(el, animation);
} else if (shouldComplete) {
onDismiss();

View File

@ -22,6 +22,7 @@ import { raf, inheritAttributes } from '../../utils/helpers';
import type { Attributes } from '../../utils/helpers';
import { KEYBOARD_DID_OPEN } from '../../utils/keyboard/keyboard';
import { printIonWarning } from '../../utils/logging';
import { Style as StatusBarStyle, StatusBar } from '../../utils/native/status-bar';
import { BACKDROP, activeAnimations, dismiss, eventMethod, prepareOverlay, present } from '../../utils/overlays';
import { getClassMap } from '../../utils/theme';
import { deepReady } from '../../utils/transition';
@ -68,6 +69,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
private keyboardOpenCallback?: () => void;
private moveSheetToBreakpoint?: (options: MoveSheetToBreakpointOptions) => Promise<void>;
private inheritedAttributes: Attributes = {};
private statusBarStyle?: StatusBarStyle;
private inline = false;
private workingDelegate?: FrameworkDelegate;
@ -519,7 +521,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
* If we did not do this check, then not using swipeToClose would mean you could
* not run canDismiss on swipe as there would be no swipe gesture created.
*/
const hasCardModal = this.swipeToClose || (this.canDismiss !== undefined && this.presentingElement !== undefined);
const hasCardModal = this.presentingElement !== undefined && (this.swipeToClose || this.canDismiss !== undefined);
/**
* We need to change the status bar at the
@ -527,6 +529,8 @@ export class Modal implements ComponentInterface, OverlayInterface {
* by the time the card animation is done.
*/
if (hasCardModal && getIonMode(this) === 'ios') {
// Cache the original status bar color before the modal is presented
this.statusBarStyle = await StatusBar.getStyle();
setCardStatusBarDark();
}
@ -584,7 +588,9 @@ export class Modal implements ComponentInterface, OverlayInterface {
return;
}
this.gesture = createSwipeToCloseGesture(el, ani, () => {
const statusBarStyle = this.statusBarStyle ?? StatusBarStyle.Default;
this.gesture = createSwipeToCloseGesture(el, ani, statusBarStyle, () => {
/**
* While the gesture animation is finishing
* it is possible for a user to tap the backdrop.
@ -691,9 +697,9 @@ export class Modal implements ComponentInterface, OverlayInterface {
* finishes when the dismiss animation does.
* TODO (FW-937)
*/
const hasCardModal = this.swipeToClose || (this.canDismiss !== undefined && this.presentingElement !== undefined);
const hasCardModal = this.presentingElement !== undefined && (this.swipeToClose || this.canDismiss !== undefined);
if (hasCardModal && getIonMode(this) === 'ios') {
setCardStatusBarDefault();
setCardStatusBarDefault(this.statusBarStyle);
}
/* tslint:disable-next-line */

View File

@ -81,10 +81,10 @@ export const setCardStatusBarDark = () => {
StatusBar.setStyle({ style: Style.Dark });
};
export const setCardStatusBarDefault = () => {
export const setCardStatusBarDefault = (defaultStyle = Style.Default) => {
if (!win || win.innerWidth >= 768 || !StatusBar.supportsDefaultStatusBarStyle()) {
return;
}
StatusBar.setStyle({ style: Style.Default });
StatusBar.setStyle({ style: defaultStyle });
};

View File

@ -1,22 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
const navChanged = () => new Promise((resolve) => window.addEventListener('ionNavDidChange', resolve));
test.skip('nav: basic', async () => {
const page = await newE2EPage({
url: '/src/components/nav/test/basic?ionic:_testing=true',
});
expect(await page.compareScreenshot()).toMatchScreenshot();
page.click('page-one ion-button.next');
await page.waitForTimeout(navChanged);
page.click('page-two ion-button.next');
await page.waitForTimeout(navChanged);
page.click('page-three ion-back-button');
await page.waitForTimeout(navChanged);
page.click('page-two ion-back-button');
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@ -12,54 +12,22 @@
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
:root {
--ion-background-color: #171717;
--ion-text-color: #ffffff;
--ion-color-step-50: #232323;
--ion-color-step-100: #2e2e2e;
--ion-color-step-150: #3a3a3a;
--ion-color-step-200: #454545;
--ion-color-step-250: #515151;
--ion-color-step-300: #5d5d5d;
--ion-color-step-350: #686868;
--ion-color-step-400: #747474;
--ion-color-step-450: #7f7f7f;
--ion-color-step-500: #8b8b8b;
--ion-color-step-550: #979797;
--ion-color-step-600: #a2a2a2;
--ion-color-step-650: #aeaeae;
--ion-color-step-700: #b9b9b9;
--ion-color-step-750: #c5c5c5;
--ion-color-step-800: #d1d1d1;
--ion-color-step-850: #dcdcdc;
--ion-color-step-900: #e8e8e8;
--ion-color-step-950: #f3f3f3;
--ion-background-color-rgb: 23, 23, 23;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color-rgb: 255, 255, 255;
}
[f] {
background: green;
width: 100px;
height: 100px;
}
</style>
<script>
class PageOne extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Page One</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>Page One</h1>
<ion-nav-link router-direction="forward" component="page-two">
<ion-button mode="md" class="next">Go to Page Two</ion-button>
<ion-button class="next">Go to Page Two</ion-button>
</ion-nav-link>
</ion-content>
`;
@ -75,19 +43,6 @@
</ion-buttons>
<ion-title>Page Two</ion-title>
</ion-toolbar>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button>
<ion-icon slot="icon-only" name="star"></ion-icon>
</ion-button>
</ion-buttons>
<ion-searchbar></ion-searchbar>
<ion-buttons slot="end">
<ion-button>
<ion-icon slot="icon-only" name="star"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>Page Two</h1>
@ -96,15 +51,6 @@
<ion-button class="next">Go to Page Three</ion-button>
</ion-nav-link>
</div>
<div f></div>
<div f></div>
<div f></div>
<div f></div>
<div f></div>
<div f></div>
<div f></div>
<div f></div>
</ion-content>
`;
}

View File

@ -0,0 +1,83 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('nav: basic', () => {
test.beforeEach(async ({ page, skip }) => {
skip.mode('md', 'Nav does not behave differently on md');
skip.rtl('Nav does not behave differently in rtl');
await page.goto('/src/components/nav/test/basic');
});
test('should render the root component', async ({ page }) => {
const pageOne = page.locator('page-one');
await expect(pageOne).toBeVisible();
});
test.describe('pushing a new page', () => {
test('should render the pushed component', async ({ page }) => {
const pageTwoButton = page.locator('ion-button:has-text("Go to Page Two")');
const pageOne = page.locator('page-one');
const pageTwo = page.locator('page-two');
await pageTwoButton.click();
await page.waitForChanges();
await expect(pageTwo).toBeVisible();
// Pushing a new page should hide the previous page
await expect(pageOne).not.toBeVisible();
});
test('should render the back button', async ({ page }) => {
const pageTwoButton = page.locator('ion-button:has-text("Go to Page Two")');
const pageTwoBackButton = page.locator('page-two ion-back-button');
await pageTwoButton.click();
await page.waitForChanges();
await expect(pageTwoBackButton).toBeVisible();
});
});
test('back button should pop to the previous page', async ({ page }) => {
const pageOne = page.locator('page-one');
const pageTwo = page.locator('page-two');
const pageTwoButton = page.locator('ion-button:has-text("Go to Page Two")');
const pageTwoBackButton = page.locator('page-two ion-back-button');
await pageTwoButton.click();
await page.waitForChanges();
await pageTwoBackButton.click();
await page.waitForChanges();
await expect(pageOne).toBeVisible();
// Popping a page should remove it from the DOM
await expect(pageTwo).toHaveCount(0);
});
test.describe('pushing multiple pages', () => {
test('should keep previous pages in the DOM', async ({ page }) => {
const pageOne = page.locator('page-one');
const pageTwo = page.locator('page-two');
const pageThree = page.locator('page-three');
const pageTwoButton = page.locator('ion-button:has-text("Go to Page Two")');
const pageThreeButton = page.locator('ion-button:has-text("Go to Page Three")');
await pageTwoButton.click();
await page.waitForChanges();
await expect(pageOne).toHaveCount(1);
await expect(pageTwo).toBeVisible();
await pageThreeButton.click();
await page.waitForChanges();
await expect(pageOne).toHaveCount(1);
await expect(pageTwo).toHaveCount(1);
await expect(pageThree).toBeVisible();
});
});
});

View File

@ -1,27 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
const navChanged = () => new Promise((resolve) => window.addEventListener('ionNavDidChange', resolve));
// TODO: get this to pass
test.skip('nav: nested', async () => {
const page = await newE2EPage({
url: '/src/components/nav/test/nested?ionic:_testing=true',
});
expect(await page.compareScreenshot()).toMatchScreenshot();
await page.click('page-one ion-button.next');
await page.waitForTimeout(navChanged);
await page.click('page-two-one ion-button.next');
await page.waitForTimeout(navChanged);
await page.click('page-two-two ion-button.next');
await page.waitForTimeout(navChanged);
await page.click('page-three ion-back-button');
await page.waitForTimeout(navChanged);
await page.click('page-two-two ion-back-button');
await page.waitForTimeout(navChanged);
await page.click('page-two-one ion-back-button');
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@ -0,0 +1,84 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
// Tests for ion-nav used in ion-router
test.describe('nav: nested', () => {
test.beforeEach(async ({ page, skip }) => {
skip.mode('md', 'Nav does not behave differently on md');
skip.rtl('Nav does not behave differently in rtl');
await page.goto('/src/components/nav/test/nested');
});
test('should push pages with nested ion-nav', async ({ page }) => {
const pageOne = page.locator('page-one');
const pageTwo = page.locator('page-two');
const pageTwoButton = page.locator('ion-button:has-text("Go to Page 2")');
const pageTwoTwoButton = page.locator('ion-button:has-text("Go to Page 2.2")');
await pageTwoButton.click();
await page.waitForChanges();
const pageTwoOne = page.locator('page-two-one');
const pageTwoTwo = page.locator('page-two-two');
await expect(pageOne).toHaveCount(1);
await expect(pageTwo).toBeVisible();
await expect(pageTwoOne).toBeVisible();
await pageTwoTwoButton.click();
await page.waitForChanges();
await expect(pageTwoOne).toHaveCount(1);
await expect(pageTwoTwo).toBeVisible();
const pageThreeButton = page.locator('ion-button:has-text("Go to Page 3")');
await pageThreeButton.click();
await page.waitForChanges();
const pageThree = page.locator('page-three');
await expect(pageThree).toBeVisible();
await expect(pageTwo).toHaveCount(1);
await expect(pageOne).toHaveCount(1);
});
test.describe('back button', () => {
test('should work with nested ion-nav', async ({ page }) => {
const pageTwoButton = page.locator('ion-button:has-text("Go to Page 2")');
const pageTwoTwoButton = page.locator('ion-button:has-text("Go to Page 2.2")');
await pageTwoButton.click();
await page.waitForChanges();
const pageTwoOne = page.locator('page-two-one');
const pageTwoTwo = page.locator('page-two-two');
await pageTwoTwoButton.click();
await page.waitForChanges();
const pageThreeButton = page.locator('ion-button:has-text("Go to Page 3")');
const pageThreeBackButton = page.locator('page-three ion-back-button');
await pageThreeButton.click();
await page.waitForChanges();
const pageThree = page.locator('page-three');
await pageThreeBackButton.click();
await page.waitForChanges();
await expect(pageThree).toHaveCount(0);
await expect(pageTwoTwo).toBeVisible();
const pageTwoTwoBackButton = page.locator('page-two-two ion-back-button');
await pageTwoTwoBackButton.click();
await page.waitForChanges();
await expect(pageTwoTwo).toHaveCount(0);
await expect(pageTwoOne).toBeVisible();
});
});
});

View File

@ -1,26 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
const navChanged = () => new Promise((resolve) => window.addEventListener('ionRouteDidChange', resolve));
test.skip('nav: routing', async () => {
const page = await newE2EPage({
url: '/src/components/nav/test/routing?ionic:_testing=true',
});
expect(await page.compareScreenshot()).toMatchScreenshot();
await page.click('page-root ion-button.next');
await page.waitForTimeout(navChanged);
await page.click('page-one ion-button.next');
await page.waitForTimeout(navChanged);
await page.click('page-two ion-button.next');
await page.waitForTimeout(navChanged);
await page.click('page-three ion-back-button');
await page.waitForTimeout(navChanged);
await page.click('page-two ion-back-button');
await page.waitForTimeout(navChanged);
await page.click('page-one ion-back-button');
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@ -0,0 +1,106 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
// Tests for ion-nav used in ion-router
test.describe('nav: routing', () => {
test.beforeEach(async ({ page, skip }) => {
skip.mode('md', 'Nav does not behave differently on md');
skip.rtl('Nav does not behave differently in rtl');
await page.goto('/src/components/nav/test/routing');
});
test('should render the root component', async ({ page }) => {
const pageRoot = page.locator('page-root');
await expect(pageRoot).toBeVisible();
});
test.describe('pushing a new page', () => {
test('should render the pushed component', async ({ page }) => {
const pageRoot = page.locator('page-root');
const pageOne = page.locator('page-one');
const pageTwo = page.locator('page-two');
const pageOneButton = page.locator('ion-button:has-text("Go to Page One")');
const pageTwoButton = page.locator('ion-button:has-text("Go to Page Two")');
await pageOneButton.click();
await page.waitForChanges();
await expect(pageOne).toBeVisible();
// Pushing a new page should hide the previous page
await expect(pageRoot).not.toBeVisible();
await expect(pageRoot).toHaveCount(1);
await pageTwoButton.click();
await page.waitForChanges();
await expect(pageTwo).toBeVisible();
// Pushing a new page should hide the previous page
await expect(pageOne).not.toBeVisible();
await expect(pageOne).toHaveCount(1);
});
test('should render the back button', async ({ page }) => {
const pageOneButton = page.locator('ion-button:has-text("Go to Page One")');
const pageOneBackButton = page.locator('page-one ion-back-button');
await pageOneButton.click();
await page.waitForChanges();
await expect(pageOneBackButton).toBeVisible();
});
});
test('back button should pop to the previous page', async ({ page }) => {
const pageRoot = page.locator('page-root');
const pageOne = page.locator('page-one');
const pageOneButton = page.locator('ion-button:has-text("Go to Page One")');
const pageOneBackButton = page.locator('page-one ion-back-button');
await pageOneButton.click();
await page.waitForChanges();
await pageOneBackButton.click();
await page.waitForChanges();
await expect(pageRoot).toBeVisible();
// Popping a page should remove it from the DOM
await expect(pageOne).toHaveCount(0);
});
test.describe('pushing multiple pages', () => {
test('should keep previous pages in the DOM', async ({ page }) => {
const pageRoot = page.locator('page-root');
const pageOne = page.locator('page-one');
const pageTwo = page.locator('page-two');
const pageThree = page.locator('page-three');
const pageOneButton = page.locator('ion-button:has-text("Go to Page One")');
const pageTwoButton = page.locator('ion-button:has-text("Go to Page Two")');
const pageThreeButton = page.locator('ion-button:has-text("Go to Page Three")');
await pageOneButton.click();
await page.waitForChanges();
await expect(pageRoot).toHaveCount(1);
await expect(pageOne).toBeVisible();
await pageTwoButton.click();
await page.waitForChanges();
await expect(pageRoot).toHaveCount(1);
await expect(pageOne).toHaveCount(1);
await expect(pageTwo).toBeVisible();
await pageThreeButton.click();
await page.waitForChanges();
await expect(pageRoot).toHaveCount(1);
await expect(pageOne).toHaveCount(1);
await expect(pageTwo).toHaveCount(1);
await expect(pageThree).toBeVisible();
});
});
});

View File

@ -27,12 +27,7 @@ import { iosEnterAnimation } from './animations/ios.enter';
import { iosLeaveAnimation } from './animations/ios.leave';
import { mdEnterAnimation } from './animations/md.enter';
import { mdLeaveAnimation } from './animations/md.leave';
import {
configureDismissInteraction,
configureKeyboardInteraction,
configureTriggerInteraction,
waitOneFrame,
} from './utils';
import { configureDismissInteraction, configureKeyboardInteraction, configureTriggerInteraction } from './utils';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
@ -464,18 +459,21 @@ export class Popover implements ComponentInterface, PopoverInterface {
}
this.configureDismissInteraction();
// TODO: FW-2773: Apply this to only the lazy build.
/**
* ionMount only needs to be emitted if the popover is inline.
*/
this.ionMount.emit();
/**
* Wait one raf before presenting the popover.
* This allows the lazy build enough time to
* calculate the popover dimensions for the animation.
*/
await waitOneFrame();
return new Promise((resolve) => {
/**
* Wait two request animation frame loops before presenting the popover.
* This allows the framework implementations enough time to mount
* the popover contents, so the bounding box is set when the popover
* transition starts.
*
* On Angular and React, a single raf is enough time, but for Vue
* we need to wait two rafs. As a result we are using two rafs for
* all frameworks to ensure the popover is presented correctly.
*/
raf(() => {
raf(async () => {
this.currentTransition = present(this, 'popoverEnter', iosEnterAnimation, mdEnterAnimation, {
event: event || this.event,
size: this.size,
@ -498,6 +496,11 @@ export class Popover implements ComponentInterface, PopoverInterface {
if (this.focusDescendantOnPresent) {
focusFirstDescendant(this.el, this.el);
}
resolve();
});
});
});
}
/**

View File

@ -928,7 +928,3 @@ export const shouldShowArrow = (side: PositionSide, didAdjustBounds = false, ev?
return true;
};
export const waitOneFrame = () => {
return new Promise<void>((resolve) => raf(() => resolve()));
};

View File

@ -4,6 +4,8 @@
// --------------------------------------------------
.reorder-list-active > * {
display: block;
transition: transform 300ms;
will-change: transform;
}

View File

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

View File

@ -0,0 +1,33 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('reorder group: basic', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('ios', 'Reorder group does not have per-mode styles');
});
test('should render the handle when reorder group is enabled', async ({ page }) => {
await page.setContent(`
<ion-reorder-group disabled="false">
<ion-item>
<ion-label>Item</ion-label>
<ion-reorder slot="end"></ion-reorder>
</ion-item>
</ion-reorder-group>
`);
const reorder = page.locator('ion-reorder');
await expect(reorder).toBeVisible();
});
test('should not render the handle when reorder group is disabled', async ({ page }) => {
await page.setContent(`
<ion-reorder-group disabled="true">
<ion-item>
<ion-label>Item</ion-label>
<ion-reorder slot="end"></ion-reorder>
</ion-item>
</ion-reorder-group>
`);
const reorder = page.locator('ion-reorder');
await expect(reorder).toBeHidden();
});
});

View File

@ -1,67 +0,0 @@
import type { E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';
import { getElementProperty, queryDeep } from '@utils/test';
import { moveReorderItem } from '../test.utils';
test('reorder: interactive', async () => {
const page = await newE2EPage({
url: '/src/components/reorder-group/test/interactive?ionic:_testing=true',
});
const compares = [];
compares.push(await page.compareScreenshot('reorder: interactive before move'));
const items = await page.$$('ion-reorder');
const getItemId = await getElementProperty(items[0], 'id');
expect(getItemId).toEqual('item-0');
await moveItem(getItemId, page, 'down', 1);
const itemsAfterFirstMove = await page.$$('ion-reorder');
expect(await getElementProperty(itemsAfterFirstMove[0], 'id')).toEqual('item-1');
await moveItem(getItemId, page, 'up', 1);
const itemsAfterSecondMove = await page.$$('ion-reorder');
expect(await getElementProperty(itemsAfterSecondMove[0], 'id')).toEqual('item-0');
compares.push(await page.compareScreenshot('reorder: interactive after move; before shadow move'));
const shadowDomList = await queryDeep(page, 'test-reorder-list-shadow-dom', 'ion-list');
const itemsInShadowRoot = await shadowDomList.$$('ion-reorder');
const getShadowItemId = await getElementProperty(itemsInShadowRoot[0], 'id');
expect(getShadowItemId).toEqual('item-0');
await moveItem(getShadowItemId, page, 'down', 1, 'test-reorder-list-shadow-dom', 'ion-list');
const itemsInShadowRootAfterFirstMove = await shadowDomList.$$('ion-reorder');
expect(await getElementProperty(itemsInShadowRootAfterFirstMove[0], 'id')).toEqual('item-1');
await moveItem(getShadowItemId, page, 'up', 1, 'test-reorder-list-shadow-dom', 'ion-list');
const itemsInShadowRootAfterSecondMove = await shadowDomList.$$('ion-reorder');
expect(await getElementProperty(itemsInShadowRootAfterSecondMove[0], 'id')).toEqual('item-0');
compares.push(await page.compareScreenshot('reorder: interactive after shadow move'));
for (const compare of compares) {
expect(compare).toMatchScreenshot();
}
});
const moveItem = async (
id: string,
page: E2EPage,
direction: 'up' | 'down' = 'up',
numberOfSpaces = 1,
...parentSelectors: string[]
) => {
try {
await moveReorderItem(`#${id}`, page, direction, numberOfSpaces, ...parentSelectors);
await page.waitForTimeout(50);
} catch (err) {
throw err;
}
};

View File

@ -15,44 +15,32 @@
</head>
<body>
<ion-list><ion-reorder-group disabled="false"></ion-reorder-group></ion-list>
<ion-reorder-group disabled="false">
<ion-item>
<ion-label>Item 1</ion-label>
<ion-reorder slot="end"></ion-reorder>
</ion-item>
<ion-reorder>
<ion-item>
<ion-label>Item 2</ion-label>
</ion-item>
</ion-reorder>
<ion-item>
<ion-label>Item 3</ion-label>
<ion-reorder slot="end"></ion-reorder>
</ion-item>
<ion-reorder>
<ion-item>
<ion-label>Item 4</ion-label>
</ion-item>
</ion-reorder>
</ion-reorder-group>
<script>
class TestReorderListShadowDom extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const wrapper = document.createElement('div');
wrapper.innerHTML = `<ion-list><ion-reorder-group disabled="false"></ion-reorder-group></ion-list>`;
shadow.appendChild(wrapper);
}
}
customElements.define('test-reorder-list-shadow-dom', TestReorderListShadowDom);
function addReorderItem(list) {
const reorderItem = document.createElement('ion-reorder');
reorderItem.innerHTML = `<ion-item>
<ion-label>
<h2>Item ${list.childElementCount}</h2>
</ion-label>
</ion-item>`;
reorderItem.id = `item-${list.childElementCount}`;
list.appendChild(reorderItem);
}
const testShadowDomEl = document.createElement('test-reorder-list-shadow-dom');
document.body.appendChild(testShadowDomEl);
let lists = Array.from(document.getElementsByTagName('ion-reorder-group'));
lists.push(testShadowDomEl.shadowRoot.querySelector('ion-reorder-group'));
for (var i = 0; i < lists.length; i++) {
lists[i].addEventListener('ionItemReorder', ({ detail }) => detail.complete(true));
for (var j = 0; j < 3; j++) addReorderItem(lists[i]);
}
const group = document.querySelector('ion-reorder-group');
group.addEventListener('ionItemReorder', (ev) => {
ev.detail.complete();
window.dispatchEvent(new CustomEvent('ionItemReorderComplete'));
});
</script>
</body>
</html>

View File

@ -0,0 +1,40 @@
import { expect } from '@playwright/test';
import { test, dragElementBy } from '@utils/test/playwright';
test.describe('reorder group: interactive', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('ios', 'Reorder group does not have per-mode styles');
skip.browser(
(browserName: string) => browserName !== 'chromium',
'dragElementBy is flaky outside of Chrome browsers.'
);
});
test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => {
await page.goto(`/src/components/reorder-group/test/interactive`);
const items = page.locator('ion-item');
const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete');
await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
await dragElementBy(items.nth(1), page, 0, 300);
await ionItemReorderComplete.next();
await expect(items).toContainText(['Item 1', 'Item 3', 'Item 4', 'Item 2']);
});
test('should drag and drop when ion-item wraps ion-reorder', async ({ page }) => {
await page.goto(`/src/components/reorder-group/test/interactive`);
const reorderHandle = page.locator('ion-reorder');
const items = page.locator('ion-item');
const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete');
await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
await dragElementBy(reorderHandle.nth(0), page, 0, 300);
await ionItemReorderComplete.next();
await expect(items).toContainText(['Item 2', 'Item 3', 'Item 4', 'Item 1']);
});
});

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Reorder - Nested</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Reorder - Nested</ion-title>
</ion-toolbar>
</ion-header>
<ion-content id="content">
<ion-list>
<ion-reorder-group disabled="false">
<app-reorder item="1"></app-reorder>
<app-reorder item="2" parent="true"></app-reorder>
<app-reorder item="3"></app-reorder>
<app-reorder item="4" parent="true"></app-reorder>
</ion-reorder-group>
</ion-list>
</ion-content>
<script>
class AppReorder extends HTMLElement {
constructor() {
super();
if (this.getAttribute('parent') === 'true') {
this.innerHTML = `
<ion-reorder>
<ion-item>
<ion-label>Item ${this.getItem()}</ion-label>
</ion-item>
</ion-reorder>
`;
} else {
this.innerHTML = `
<ion-item>
<ion-label>Item ${this.getItem()}</ion-label>
<ion-reorder slot="end"></ion-reorder>
</ion-item>
`;
}
}
getItem() {
return this.getAttribute('item');
}
}
customElements.define('app-reorder', AppReorder);
const group = document.querySelector('ion-reorder-group');
group.addEventListener('ionItemReorder', (ev) => {
ev.detail.complete();
window.dispatchEvent(new CustomEvent('ionItemReorderComplete'));
});
</script>
</ion-app>
</body>
</html>

View File

@ -0,0 +1,40 @@
import { expect } from '@playwright/test';
import { test, dragElementBy } from '@utils/test/playwright';
test.describe('reorder group: nested', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('ios', 'Reorder group does not have per-mode styles');
skip.browser(
(browserName: string) => browserName !== 'chromium',
'dragElementBy is flaky outside of Chrome browsers.'
);
});
test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => {
await page.goto(`/src/components/reorder-group/test/nested`);
const items = page.locator('app-reorder');
const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete');
await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
await dragElementBy(items.nth(1), page, 0, 300);
await ionItemReorderComplete.next();
await expect(items).toContainText(['Item 1', 'Item 3', 'Item 4', 'Item 2']);
});
test('should drag and drop when ion-item wraps ion-reorder', async ({ page }) => {
await page.goto(`/src/components/reorder-group/test/nested`);
const reorderHandle = page.locator('app-reorder ion-reorder');
const items = page.locator('app-reorder');
const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete');
await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
await dragElementBy(reorderHandle.nth(0), page, 0, 300);
await ionItemReorderComplete.next();
await expect(items).toContainText(['Item 2', 'Item 3', 'Item 4', 'Item 1']);
});
});

View File

@ -1,49 +0,0 @@
import type { E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';
import { getElementProperty } from '../../../../utils/test/utils';
import { moveReorderItem } from '../test.utils';
it('reorder: custom scroll target', async () => {
const page = await newE2EPage({
url: '/src/components/reorder-group/test/scroll-target?ionic:_testing=true',
});
const compares = [];
compares.push(await page.compareScreenshot('reorder: interactive before move'));
const items = await page.$$('ion-reorder');
const getItemId = await getElementProperty(items[0], 'id');
expect(getItemId).toEqual('item-0');
await moveItem(getItemId, page, 'down', 1);
const itemsAfterFirstMove = await page.$$('ion-reorder');
expect(await getElementProperty(itemsAfterFirstMove[0], 'id')).toEqual('item-1');
await moveItem(getItemId, page, 'up', 1);
const itemsAfterSecondMove = await page.$$('ion-reorder');
expect(await getElementProperty(itemsAfterSecondMove[0], 'id')).toEqual('item-0');
compares.push(await page.compareScreenshot('reorder: interactive after move'));
for (const compare of compares) {
expect(compare).toMatchScreenshot();
}
});
const moveItem = async (
id: string,
page: E2EPage,
direction: 'up' | 'down' = 'up',
numberOfSpaces = 1,
...parentSelectors: string[]
) => {
try {
await moveReorderItem(`#${id}`, page, direction, numberOfSpaces, ...parentSelectors);
await page.waitForTimeout(50);
} catch (err) {
throw err;
}
};

View File

@ -31,41 +31,36 @@
<ion-content scroll-y="false">
<div id="content" class="ion-content-scroll-host">
<ion-list>
<ion-reorder-group id="reorder" disabled="false">
<ion-reorder id="item-0">
<ion-reorder-group disabled="false">
<ion-item>
<ion-label>
<h2>Item 0</h2>
</ion-label>
<ion-label>Item 1</ion-label>
<ion-reorder slot="end"></ion-reorder>
</ion-item>
<ion-reorder>
<ion-item>
<ion-label>Item 2</ion-label>
</ion-item>
</ion-reorder>
<ion-reorder id="item-1">
<ion-item>
<ion-label>
<h2>Item 1</h2>
</ion-label>
<ion-label>Item 3</ion-label>
<ion-reorder slot="end"></ion-reorder>
</ion-item>
</ion-reorder>
<ion-reorder id="item-2">
<ion-reorder>
<ion-item>
<ion-label>
<h2>Item 2</h2>
</ion-label>
<ion-label>Item 4</ion-label>
</ion-item>
</ion-reorder>
</ion-reorder-group>
</ion-list>
</div>
</ion-content>
</ion-app>
<script>
const lists = Array.from(document.getElementsByTagName('ion-reorder-group'));
for (const list of lists) {
list.addEventListener('ionItemReorder', ({ detail }) => detail.complete(true));
}
const group = document.querySelector('ion-reorder-group');
group.addEventListener('ionItemReorder', (ev) => {
ev.detail.complete();
window.dispatchEvent(new CustomEvent('ionItemReorderComplete'));
});
</script>
</body>
</html>

View File

@ -0,0 +1,40 @@
import { expect } from '@playwright/test';
import { test, dragElementBy } from '@utils/test/playwright';
test.describe('reorder group: scroll-target', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('ios', 'Reorder group does not have per-mode styles');
skip.browser(
(browserName: string) => browserName !== 'chromium',
'dragElementBy is flaky outside of Chrome browsers.'
);
});
test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => {
await page.goto(`/src/components/reorder-group/test/scroll-target`);
const items = page.locator('ion-item');
const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete');
await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
await dragElementBy(items.nth(1), page, 0, 300);
await ionItemReorderComplete.next();
await expect(items).toContainText(['Item 1', 'Item 3', 'Item 4', 'Item 2']);
});
test('should drag and drop when ion-item wraps ion-reorder', async ({ page }) => {
await page.goto(`/src/components/reorder-group/test/scroll-target`);
const reorderHandle = page.locator('ion-reorder');
const items = page.locator('ion-item');
const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete');
await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']);
await dragElementBy(reorderHandle.nth(0), page, 0, 300);
await ionItemReorderComplete.next();
await expect(items).toContainText(['Item 2', 'Item 3', 'Item 4', 'Item 1']);
});
});

View File

@ -1,36 +0,0 @@
import type * as pd from '@stencil/core/testing';
import { dragElementBy, queryDeep } from '@utils/test';
/**
* Moves a reorder item by simulating a drag event
*/
export const moveReorderItem = async (
id: string,
page: pd.E2EPage,
direction: 'up' | 'down' = 'up',
numberOfSpaces = 1,
...parentSelectors: string[]
) => {
try {
const reorderItem =
parentSelectors.length > 0 ? await (await queryDeep(page, ...parentSelectors)).$(id) : await page.$(id);
if (!reorderItem) {
throw new Error('Reorder Item is undefined');
}
const boundingBox = await reorderItem.boundingBox();
if (!boundingBox) {
throw new Error('Reorder Item bounding box is undefined');
}
await dragElementBy(
reorderItem,
page,
0,
direction === 'up' ? -(boundingBox.height * numberOfSpaces) : boundingBox.height * numberOfSpaces
);
} catch (err) {
throw err;
}
};

View File

@ -1,41 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
test('getRouteId() should return the segment parameters', async () => {
const page = await newE2EPage({
url: '/src/components/router-outlet/test/basic?ionic:_testing=true',
});
await page.$eval('ion-item[href="#/two/segment"] ion-label', (el: any) => el.click());
await page.waitForChanges();
const routeId = await page.$eval('ion-router-outlet', async (el: any) => el.getRouteId());
expect(routeId.id).toEqual('PAGE-TWO');
expect(routeId.params).toEqual({ param: 'segment' });
});
test('getRouteId() should return the route parameters', async () => {
const page = await newE2EPage({
url: '/src/components/router-outlet/test/basic?ionic:_testing=true',
});
await page.$eval('ion-item[href="#/page-3"] ion-label', (el: any) => el.click());
await page.waitForChanges();
const routeId = await page.$eval('ion-router-outlet', async (el: any) => el.getRouteId());
expect(routeId.id).toEqual('PAGE-THREE');
expect(routeId.params).toEqual({ param: 'route' });
});
test('it should be possible to activate the same component provided parameters are different', async () => {
const page = await newE2EPage({
url: '/src/components/router-outlet/test/basic?ionic:_testing=true',
});
await page.$eval('ion-item[href="#/page-4.1/foo"] ion-label', (el: any) => el.click());
await page.waitForChanges();
expect(await page.$eval('ion-router-outlet', (el) => el.textContent)).toMatch(/text = foo/);
await page.$eval('ion-item[href="#/page-4.2/bar"] ion-label', (el: any) => el.click());
await page.waitForChanges();
expect(await page.$eval('ion-router-outlet', (el) => el.textContent)).toMatch(/text = bar/);
});

View File

@ -24,6 +24,8 @@
<ion-content class="ion-padding">
<h1>Page One</h1>
<ion-button href="#/two/segment">Go to Page Two</ion-button>
<ion-button href="#/page-3">Go to Page Three</ion-button>
<ion-button href="#/page-4.1/foo">Go to Page 4 (foo)</ion-button>
</ion-content>
`;
}
@ -71,6 +73,7 @@
</ion-header>
<ion-content class="ion-padding">
<h1>text = ${this.text}</h1>
<ion-button href="#/page-4.2/bar">Go to Page 4 (bar)</ion-button>
</ion-content>
`;
}

View File

@ -0,0 +1,52 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('router outlet: basic', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('ios');
});
test('getRouteId() should return the segment parameters', async ({ page }) => {
await page.goto('/src/components/router-outlet/test/basic');
await page.click('ion-button[href="#/two/segment"]');
await page.waitForChanges();
const routerOutlet = page.locator('ion-router-outlet');
const routeId = await routerOutlet.evaluate((el: HTMLIonRouterOutletElement) => el.getRouteId());
expect(routeId!.id).toEqual('PAGE-TWO');
expect(routeId!.params).toEqual({ param: 'segment' });
});
test('getRouteId() should return the route parameters', async ({ page }) => {
await page.goto('/src/components/router-outlet/test/basic');
await page.click('ion-button[href="#/page-3"]');
await page.waitForChanges();
const routerOutlet = page.locator('ion-router-outlet');
const routeId = await routerOutlet.evaluate((el: HTMLIonRouterOutletElement) => el.getRouteId());
expect(routeId!.id).toEqual('PAGE-THREE');
expect(routeId!.params).toEqual({ param: 'route' });
});
test('it should be possible to activate the same component provided parameters are different', async ({ page }) => {
await page.goto('/src/components/router-outlet/test/basic');
await page.click('ion-button[href="#/page-4.1/foo"]');
await page.waitForChanges();
await page.$eval('ion-item[href="#/page-4.1/foo"] ion-label', (el: any) => el.click());
await page.waitForChanges();
const routerOutlet = page.locator('ion-router-outlet');
await expect(routerOutlet).toHaveText(/text = foo/);
await page.click('ion-button[href="#/page-4.2/bar"]');
await page.waitForChanges();
await expect(routerOutlet).toHaveText(/text = bar/);
});
});

View File

@ -88,6 +88,22 @@ export class Segment implements ComponentInterface {
this.valueAfterGesture = value;
}
}
if (this.scrollable) {
const buttons = this.getButtons();
const activeButton = buttons.find((button) => button.value === value);
if (activeButton !== undefined) {
/**
* Scrollable segment buttons should be
* centered within the view including
* buttons that are partially offscreen.
*/
activeButton.scrollIntoView({
behavior: 'smooth',
inline: 'center',
});
}
}
}
/**

View File

@ -29,6 +29,17 @@
</ion-segment>
</ion-toolbar>
<ion-toolbar>
<ion-segment value="2" scrollable="true">
<ion-segment-button value="1">Button 1</ion-segment-button>
<ion-segment-button value="2">Button 3</ion-segment-button>
<ion-segment-button value="3">Button 3</ion-segment-button>
<ion-segment-button value="4">Button 4</ion-segment-button>
<ion-segment-button value="5">Button 5</ion-segment-button>
<ion-segment-button value="6">Button 6</ion-segment-button>
</ion-segment>
</ion-toolbar>
<ion-toolbar>
<ion-segment name="dynamicPropDisable" disabled color="danger">
<ion-segment-button value="Bookmarks"> Bookmarks </ion-segment-button>

View File

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

View File

@ -0,0 +1,29 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('text: basic', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('ios', 'Text does not have per-mode styles');
});
test('should render default text', async ({ page }) => {
await page.setContent(`
<ion-text>
<strong>The quick brown fox <ion-text><sup>jumps</sup></ion-text> over the <ion-text><sub>lazy dog</sub></ion-text></strong>
</ion-text>
`);
const text = page.locator('ion-text');
expect(await text.nth(0).screenshot()).toMatchSnapshot(`text-${page.getSnapshotSettings()}.png`);
});
test('should render text with color prop', async ({ page }) => {
await page.setContent(`
<ion-text color="primary">
<strong>The quick brown fox <ion-text color="success"><sup>jumps</sup></ion-text> over the <ion-text color="danger"><sub>lazy dog</sub></ion-text></strong>
</ion-text>
`);
const text = page.locator('ion-text');
expect(await text.nth(0).screenshot()).toMatchSnapshot(`text-color-${page.getSnapshotSettings()}.png`);
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

View File

@ -69,21 +69,21 @@
}
.toggle-switch-icon {
@include ltr() {
position: absolute;
color: var(--ion-color-dark);
}
:host(.toggle-ltr) .toggle-switch-icon {
/* stylelint-disable-next-line property-disallowed-list */
right: 6px;
}
}
@include rtl() {
:host(.toggle-rtl) .toggle-switch-icon {
/* stylelint-disable property-disallowed-list */
right: initial;
left: 6px;
/* stylelint-enable property-disallowed-list */
}
position: absolute;
color: var(--ion-color-dark);
}
:host(.toggle-checked) .toggle-switch-icon.toggle-switch-icon-checked {
@ -97,18 +97,6 @@
}
.toggle-switch-icon-checked {
@include ltr() {
/* stylelint-disable property-disallowed-list */
right: initial;
left: 4px;
/* stylelint-enable property-disallowed-list */
}
@include rtl() {
/* stylelint-disable-next-line property-disallowed-list */
right: 4px;
}
position: absolute;
width: 15px;
@ -117,6 +105,18 @@
transform: translateY(-50%) rotate(90deg);
}
:host(.toggle-ltr) .toggle-switch-icon-checked {
/* stylelint-disable property-disallowed-list */
right: initial;
left: 4px;
/* stylelint-enable property-disallowed-list */
}
:host(.toggle-rtl) .toggle-switch-icon-checked {
/* stylelint-disable-next-line property-disallowed-list */
right: 4px;
}
// iOS Toggle Background Oval: Activated or Checked
// ----------------------------------------------------------
@ -144,16 +144,15 @@
// when pressing down on the toggle and it IS checked
// the knob is wider so move it left by 6px in the transform
:host(.toggle-activated.toggle-checked) .toggle-icon-wrapper {
@include ltr() {
:host(.toggle-ltr.toggle-activated.toggle-checked) .toggle-icon-wrapper {
// transform by 100% - handle width - 6px (width addition on activated)
transform: translate3d(calc(100% - var(--handle-width) - 6px), 0, 0);
}
}
@include rtl() {
:host(.toggle-rtl.toggle-activated.toggle-checked) .toggle-icon-wrapper {
// transform by -100% + handle width + 6px (width addition on activated)
transform: translate3d(calc(-100% + var(--handle-width) + 6px), 0, 0);
}
}
// iOS Toggle: Disabled

View File

@ -107,7 +107,6 @@ input {
// --------------------------------------------------
.toggle-inner {
@include position(null, null, null, var(--handle-spacing));
@include border-radius(var(--handle-border-radius));
position: absolute;
@ -126,31 +125,44 @@ input {
contain: strict;
}
/**
* We do not use the @ltr and @rtl mixins
* here because ion-toggle uses the Shadow DOM
* and WebKit does not support :host-context.
*/
:host(.toggle-ltr) .toggle-inner {
// stylelint-disable-next-line property-disallowed-list
left: var(--handle-spacing);
}
:host(.toggle-rtl) .toggle-inner {
// stylelint-disable-next-line property-disallowed-list
right: var(--handle-spacing);
}
// Toggle Inner Knob: Checked
// ----------------------------------------------------------
:host(.toggle-checked) .toggle-icon-wrapper {
@include ltr() {
:host(.toggle-ltr.toggle-checked) .toggle-icon-wrapper {
// transform by 100% - handle width
transform: translate3d(calc(100% - var(--handle-width)), 0, 0);
}
transform: translate3d(calc(100% - var(--handle-width)), 0, 0)
}
@include rtl() {
:host(.toggle-rtl.toggle-checked) .toggle-icon-wrapper {
// transform by -100% + handle width
transform: translate3d(calc(-100% + var(--handle-width)), 0, 0);
}
}
:host(.toggle-checked) .toggle-inner {
@include ltr() {
// transform by handle spacing amount
transform: translate3d(calc(var(--handle-spacing) * -2), 0, 0);
}
@include rtl() {
// transform by handle spacing amount
transform: translate3d(calc(var(--handle-spacing) * 2), 0, 0);
}
background: var(--handle-background-checked);
}
:host(.toggle-ltr.toggle-checked) .toggle-inner {
// transform by handle spacing amount
transform: translate3d(calc(var(--handle-spacing) * -2), 0, 0);
}
:host(.toggle-rtl.toggle-checked) .toggle-inner {
// transform by handle spacing amount
transform: translate3d(calc(var(--handle-spacing) * 2), 0, 0);
}

View File

@ -210,6 +210,7 @@ export class Toggle implements ComponentInterface {
const mode = getIonMode(this);
const { label, labelId, labelText } = getAriaLabel(el, inputId);
const value = this.getValue();
const rtl = isRTL(el) ? 'rtl' : 'ltr';
renderHiddenInput(true, el, name, checked ? value : '', disabled);
@ -227,6 +228,7 @@ export class Toggle implements ComponentInterface {
'toggle-checked': checked,
'toggle-disabled': disabled,
interactive: true,
[`toggle-${rtl}`]: true,
})}
>
<div class="toggle-icon" part="track">

View File

@ -162,6 +162,7 @@ button ion-label {
}
button {
padding: 0;
border: 0;
border-radius: 0;
font-family: inherit;

View File

@ -31,4 +31,12 @@ export const StatusBar = {
engine.setStyle(options);
},
getStyle: async function (): Promise<Style> {
const engine = this.getEngine();
if (!engine) {
return Style.Default;
}
const { style } = await engine.getInfo();
return style;
},
};

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.8](https://github.com/ionic-team/ionic-docs/compare/v6.3.7...v6.3.8) (2022-11-22)
**Note:** Version bump only for package @ionic/docs
## [6.3.7](https://github.com/ionic-team/ionic-docs/compare/v6.3.6...v6.3.7) (2022-11-16)
**Note:** Version bump only for package @ionic/docs

View File

@ -1,12 +1,12 @@
{
"name": "@ionic/docs",
"version": "6.3.7",
"version": "6.3.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/docs",
"version": "6.3.7",
"version": "6.3.8",
"license": "MIT"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/docs",
"version": "6.3.7",
"version": "6.3.8",
"description": "Pre-packaged API documentation for the Ionic docs.",
"main": "core.json",
"types": "core.d.ts",

View File

@ -5,5 +5,5 @@
"angular",
"packages/*"
],
"version": "6.3.7"
"version": "6.3.8"
}

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.8](https://github.com/ionic-team/ionic/compare/v6.3.7...v6.3.8) (2022-11-22)
**Note:** Version bump only for package @ionic/angular-server
## [6.3.7](https://github.com/ionic-team/ionic/compare/v6.3.6...v6.3.7) (2022-11-16)
**Note:** Version bump only for package @ionic/angular-server

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