Compare commits

..

4 Commits

449 changed files with 1364 additions and 9700 deletions

View File

@@ -22,7 +22,7 @@ runs:
using: 'composite'
steps:
- name: 🟢 Configure Node for Publish
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: ${{ inputs.node-version }}
registry-url: 'https://registry.npmjs.org'

View File

@@ -40,7 +40,7 @@ comment:
If the requested feature is something you would find useful for your applications, please react to the original post with 👍 (`+1`). If you would like to provide an additional use case for the feature, please post a comment.
The team will review this feedback and make a final decision. Any decision will be posted on this thread, but please note that we may ultimately decide not to pursue this feature.
@@ -83,7 +83,6 @@ stale:
exemptLabels:
- "good first issue"
- "triage"
- "bug: external"
- "type: bug"
- "type: feature request"
- "needs: investigation"

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic Angular Server'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -27,10 +27,6 @@ runs:
run: npm run build
shell: bash
working-directory: ./packages/angular
- name: Clean core package.json
run: git checkout ./package.json
shell: bash
working-directory: ./core
- name: 🔍 Check Diff
run: git diff --exit-code
shell: bash

View File

@@ -8,8 +8,8 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
@@ -29,4 +29,4 @@ runs:
with:
name: ionic-core
output: core/CoreBuild.zip
paths: core/dist core/components core/css core/hydrate core/loader core/src/components.d.ts core/package.json
paths: core/dist core/components core/css core/hydrate core/loader core/src/components.d.ts

View File

@@ -8,8 +8,8 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- name: 🕸️ Install Dependencies

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic React Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic React'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive
@@ -31,10 +31,6 @@ runs:
run: npm run test.spec
shell: bash
working-directory: ./packages/react
- name: Clean core package.json
run: git checkout ./package.json
shell: bash
working-directory: ./core
- name: 🔍 Check Diff
run: git diff --exit-code
shell: bash

View File

@@ -3,7 +3,7 @@ description: 'Builds Ionic Vue Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic Vue'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive
@@ -27,10 +27,6 @@ runs:
run: npm run build
shell: bash
working-directory: ./packages/vue
- name: Clean core package.json
run: git checkout ./package.json
shell: bash
working-directory: ./core
- name: 🔍 Check Diff
run: git diff --exit-code
shell: bash

View File

@@ -10,7 +10,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
name: ${{ inputs.name }}
path: ${{ inputs.path }}

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive
@@ -17,7 +17,7 @@ runs:
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-angular
path: ./packages/angular
path: ./angular
filename: AngularBuild.zip
- uses: ./.github/workflows/actions/download-archive
with:

View File

@@ -3,7 +3,7 @@ description: 'Test Core Clean Build'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
@@ -12,10 +12,6 @@ runs:
name: ionic-core
path: ./core
filename: CoreBuild.zip
- name: Clean core package.json
run: git checkout ./package.json
shell: bash
working-directory: ./core
- name: 🔍 Check Diff
run: |
git diff --exit-code || {

View File

@@ -3,17 +3,13 @@ description: 'Test Core Lint'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- name: 🕸️ Install Dependencies
run: npm ci
working-directory: ./core
shell: bash
- name: Clean core package.json
run: git checkout ./package.json
shell: bash
working-directory: ./core
- name: 🖌️ Lint
run: npm run lint
shell: bash

View File

@@ -13,7 +13,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive
@@ -30,10 +30,6 @@ runs:
run: npm run test.e2e.docker.ci ${{ inputs.component }} -- --shard=${{ inputs.shard }}/${{ inputs.totalShards }}
shell: bash
working-directory: ./core
- name: Clean core package.json
run: git checkout ./package.json
shell: bash
working-directory: ./core
- name: Test and Update
id: test-and-update
if: inputs.update == 'true'
@@ -66,7 +62,7 @@ runs:
working-directory: ./core
- name: 📦 Archive Updated Screenshots
if: inputs.update == 'true' && steps.test-and-update.outputs.hasUpdatedScreenshots == 'true'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: updated-screenshots-${{ inputs.shard }}-${{ inputs.totalShards }}
path: UpdatedScreenshots-${{ inputs.shard }}-${{ inputs.totalShards }}.zip

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- name: 🕸️ Install Dependencies

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -7,10 +7,10 @@ on:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: 24.x
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
path: ./artifacts
- name: 🔎 Extract Archives
@@ -21,10 +21,6 @@ runs:
find . -type f -name 'UpdatedScreenshots-*.zip' -exec unzip -q -o -d ../ {} \;
shell: bash
working-directory: ./artifacts
- name: Clean core package.json
run: git checkout ./package.json
shell: bash
working-directory: ./core
- name: 📸 Push Screenshots
# Configure user as Ionitron
# and push only the changed .png snapshots

View File

@@ -13,7 +13,7 @@ runs:
- name: 🗄️ Create Archive
run: zip -q -r ${{ inputs.output }} ${{ inputs.paths }}
shell: bash
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v5
with:
name: ${{ inputs.name }}
path: ${{ inputs.output }}

View File

@@ -22,7 +22,7 @@ jobs:
build-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-core
with:
ionicons-version: ${{ inputs.ionicons_npm_release_tag }}
@@ -31,21 +31,21 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-clean-build
test-core-lint:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-lint
test-core-spec:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-spec
test-core-screenshot:
@@ -62,7 +62,7 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-screenshot
with:
shard: ${{ matrix.shard }}
@@ -90,14 +90,14 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-vue
build-vue-router:
needs: [build-vue]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-vue-router
test-vue-e2e:
@@ -108,7 +108,7 @@ jobs:
needs: [build-vue, build-vue-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-vue-e2e
with:
app: ${{ matrix.apps }}
@@ -126,14 +126,14 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-angular
build-angular-server:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-angular-server
test-angular-e2e:
@@ -144,7 +144,7 @@ jobs:
needs: [build-angular, build-angular-server]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-angular-e2e
with:
app: ${{ matrix.apps }}
@@ -162,14 +162,14 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-react
build-react-router:
needs: [build-react]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-react-router
test-react-router-e2e:
@@ -180,7 +180,7 @@ jobs:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-react-router-e2e
with:
app: ${{ matrix.apps }}
@@ -202,7 +202,7 @@ jobs:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-react-e2e
with:
app: ${{ matrix.apps }}

View File

@@ -14,7 +14,7 @@ jobs:
permissions:
security-events: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: github/codeql-action/init@v4
with:
languages: javascript

View File

@@ -13,7 +13,7 @@ jobs:
outputs:
dev-hash: ${{ steps.create-dev-hash.outputs.DEV_HASH }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
# 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

View File

@@ -13,7 +13,7 @@ jobs:
outputs:
nightly-hash: ${{ steps.create-nightly-hash.outputs.NIGHTLY_HASH }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
# 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

View File

@@ -23,7 +23,7 @@ jobs:
release-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/publish-npm
with:
scope: '@ionic/core'
@@ -48,7 +48,7 @@ jobs:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Restore @ionic/docs built cache
uses: ./.github/workflows/actions/download-archive
with:
@@ -67,7 +67,7 @@ jobs:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
@@ -93,7 +93,7 @@ jobs:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
@@ -118,7 +118,7 @@ jobs:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
@@ -143,7 +143,7 @@ jobs:
needs: [release-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
@@ -163,7 +163,7 @@ jobs:
needs: [release-react]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:
@@ -188,7 +188,7 @@ jobs:
needs: [release-vue]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Restore @ionic/core built cache
uses: ./.github/workflows/actions/download-archive
with:

View File

@@ -58,7 +58,7 @@ jobs:
contents: write
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
token: ${{ secrets.IONITRON_TOKEN }}
fetch-depth: 0
@@ -89,7 +89,7 @@ jobs:
contents: write
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
# Pull the latest version of the reference
# branch instead of the revision that triggered
# the workflow otherwise we won't get the commit

View File

@@ -26,7 +26,7 @@ jobs:
build-core-with-stencil-nightly:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-core-stencil-prerelease
with:
stencil-version: ${{ inputs.npm_release_tag || 'nightly' }}
@@ -35,21 +35,21 @@ jobs:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-clean-build
test-core-lint:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-lint
test-core-spec:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-spec
with:
stencil-version: ${{ inputs.npm_release_tag || 'nightly' }}
@@ -72,7 +72,7 @@ jobs:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-screenshot
with:
shard: ${{ matrix.shard }}
@@ -100,14 +100,14 @@ jobs:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-vue
build-vue-router:
needs: [build-vue]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-vue-router
test-vue-e2e:
@@ -118,7 +118,7 @@ jobs:
needs: [build-vue, build-vue-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-vue-e2e
with:
app: ${{ matrix.apps }}
@@ -136,14 +136,14 @@ jobs:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-angular
build-angular-server:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-angular-server
test-angular-e2e:
@@ -154,7 +154,7 @@ jobs:
needs: [build-angular, build-angular-server]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-angular-e2e
with:
app: ${{ matrix.apps }}
@@ -172,14 +172,14 @@ jobs:
needs: [build-core-with-stencil-nightly]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-react
build-react-router:
needs: [build-react]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-react-router
test-react-router-e2e:
@@ -190,7 +190,7 @@ jobs:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-react-router-e2e
with:
app: ${{ matrix.apps }}
@@ -212,7 +212,7 @@ jobs:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-react-e2e
with:
app: ${{ matrix.apps }}

View File

@@ -26,7 +26,7 @@ jobs:
build-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/build-core
test-core-screenshot:
@@ -47,7 +47,7 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/workflows/actions/test-core-screenshot
with:
shard: ${{ matrix.shard }}
@@ -59,7 +59,7 @@ jobs:
runs-on: ubuntu-latest
needs: [test-core-screenshot]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
# Normally, we could just push with the
# default GITHUB_TOKEN, but that will
# not cause the build workflow

View File

@@ -3,122 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.8.1](https://github.com/ionic-team/ionic-framework/compare/v8.8.0...v8.8.1) (2026-03-06)
### Bug Fixes
* **accordion:** update tabindex based on disabled state ([#30986](https://github.com/ionic-team/ionic-framework/issues/30986)) ([0e76a69](https://github.com/ionic-team/ionic-framework/commit/0e76a69370083702568825c29d63cf257d6b88f1))
* **angular:** export RefresherPullEnd types ([#30991](https://github.com/ionic-team/ionic-framework/issues/30991)) ([72abcca](https://github.com/ionic-team/ionic-framework/commit/72abccaad8df3c1db004da28610fddd95ac93c02))
### Features
* **toast:** add wrapper and content parts (originally intended for 8.8.0 but omitted from that release) ([#30992](https://github.com/ionic-team/ionic-framework/issues/30992)) ([366f00e](https://github.com/ionic-team/ionic-framework/commit/366f00e25f06e28aa7166275445716c2d301e44a)), closes [#30735](https://github.com/ionic-team/ionic-framework/issues/30735)
# [8.8.0](https://github.com/ionic-team/ionic-framework/compare/v8.7.18...v8.8.0) (2026-03-04)
### Features
* **angular:** add custom injector support for modal and popover controllers ([#30899](https://github.com/ionic-team/ionic-framework/issues/30899)) ([822da42](https://github.com/ionic-team/ionic-framework/commit/822da428af86cd9b036b81515272321eb8fa586c)), closes [#30638](https://github.com/ionic-team/ionic-framework/issues/30638)
* **content:** add content-fullscreen class when fullscreen is true ([#30926](https://github.com/ionic-team/ionic-framework/issues/30926)) ([d74b11b](https://github.com/ionic-team/ionic-framework/commit/d74b11bc19d6268b256daf23ba6f107483c00320))
* **datetime:** add header parts ([#30945](https://github.com/ionic-team/ionic-framework/issues/30945)) ([6ea186d](https://github.com/ionic-team/ionic-framework/commit/6ea186d96d80a94b774d4d0a51d536e0e5599935))
* **datetime:** add wheel part to ion-picker-column ([#30934](https://github.com/ionic-team/ionic-framework/issues/30934)) ([0cf4c03](https://github.com/ionic-team/ionic-framework/commit/0cf4c03e298bb4f7eea71c966a1473765ebd6d7a))
* **item-divider:** add inner and container parts ([#30928](https://github.com/ionic-team/ionic-framework/issues/30928)) ([5cdeb7f](https://github.com/ionic-team/ionic-framework/commit/5cdeb7fd357298f15e7ae29b14412d97bdc7c656))
* **item-option:** add inner and container parts ([#30929](https://github.com/ionic-team/ionic-framework/issues/30929)) ([f8f7ffd](https://github.com/ionic-team/ionic-framework/commit/f8f7ffda318c0143d9bb5c79fe55b4ecb88e6ce3))
* **item:** add inner and container parts ([#30927](https://github.com/ionic-team/ionic-framework/issues/30927)) ([a2c6559](https://github.com/ionic-team/ionic-framework/commit/a2c655923bb1cff51864949575e19028623c695d))
* **list-header:** add inner part ([#30930](https://github.com/ionic-team/ionic-framework/issues/30930)) ([ef73476](https://github.com/ionic-team/ionic-framework/commit/ef73476e08670630907e775a38f9ed30a84e3f1f))
* **modal:** add drag events for sheet and card modals ([#30962](https://github.com/ionic-team/ionic-framework/issues/30962)) ([d29ac71](https://github.com/ionic-team/ionic-framework/commit/d29ac713fad604c256fb385eb0c26eb9717e1ff4)), closes [#23955](https://github.com/ionic-team/ionic-framework/issues/23955)
* **range:** add classes and expose parts to allow individual styling of dual knobs ([#30941](https://github.com/ionic-team/ionic-framework/issues/30941)) ([5bcf921](https://github.com/ionic-team/ionic-framework/commit/5bcf92184118055483bf306ab9e319b8e3e61870)), closes [#29862](https://github.com/ionic-team/ionic-framework/issues/29862)
* **range:** add classes to the range when the value is at the min or max ([#30932](https://github.com/ionic-team/ionic-framework/issues/30932)) ([fac1a66](https://github.com/ionic-team/ionic-framework/commit/fac1a6673c88a531f1d79656be4eb544f235f819))
* **refresher:** add ionPullStart and ionPullEnd events ([#30946](https://github.com/ionic-team/ionic-framework/issues/30946)) ([814c2e5](https://github.com/ionic-team/ionic-framework/commit/814c2e5ccd6d5bfda12bdf13a566cd66ff830d5b)), closes [#24524](https://github.com/ionic-team/ionic-framework/issues/24524)
* **segment-view:** add swipeGesture property to disable swiping ([#30948](https://github.com/ionic-team/ionic-framework/issues/30948)) ([46806bd](https://github.com/ionic-team/ionic-framework/commit/46806bd6e2af90a0b31fca68f508c06d3d281ec0)), closes [#30290](https://github.com/ionic-team/ionic-framework/issues/30290)
* **select:** add wrapper and bottom shadow parts ([#30951](https://github.com/ionic-team/ionic-framework/issues/30951)) ([5cea5ae](https://github.com/ionic-team/ionic-framework/commit/5cea5aeb44393edab7064e5980a1eb7e607d1b8d))
* **select:** pass cancelText property to modal interface ([#30282](https://github.com/ionic-team/ionic-framework/issues/30282)) ([6e4f60a](https://github.com/ionic-team/ionic-framework/commit/6e4f60af4c188ae04028b444aa21118ae27c2ca7))
* **textarea:** reflect disabled and readonly props ([#30910](https://github.com/ionic-team/ionic-framework/issues/30910)) ([55735df](https://github.com/ionic-team/ionic-framework/commit/55735df3fa62c8e259c56db3169f3d5459e71c0c))
## [8.7.18](https://github.com/ionic-team/ionic-framework/compare/v8.7.17...v8.7.18) (2026-02-25)
### Bug Fixes
* **datetime:** stretch ion-buttons to fill space for ios ([#30963](https://github.com/ionic-team/ionic-framework/issues/30963)) ([d46b0b1](https://github.com/ionic-team/ionic-framework/commit/d46b0b15f6a652da6f863cf303e7ce06cfc820a8))
* **many:** clear timeouts ([#30851](https://github.com/ionic-team/ionic-framework/issues/30851)) ([70b1237](https://github.com/ionic-team/ionic-framework/commit/70b1237823dd0cdab852486a6b2cbbfe0d0aaae9)), closes [#30860](https://github.com/ionic-team/ionic-framework/issues/30860)
* **modal, popover:** respect safe area insets on popovers and modals ([#30949](https://github.com/ionic-team/ionic-framework/issues/30949)) ([6490797](https://github.com/ionic-team/ionic-framework/commit/6490797851cede3bfda893a19b10f165259ec988)), closes [#28411](https://github.com/ionic-team/ionic-framework/issues/28411)
* **nav-controller:** reset direction state when navigation is canceled ([#30955](https://github.com/ionic-team/ionic-framework/issues/30955)) ([53172d1](https://github.com/ionic-team/ionic-framework/commit/53172d1a4035d5b510c230553aabd53dc1389e4b))
* **radio-group:** prevent DOMException and NotFoundError when filtering radios ([#30958](https://github.com/ionic-team/ionic-framework/issues/30958)) ([682a17e](https://github.com/ionic-team/ionic-framework/commit/682a17ebb754da7714989623cf84b75e715e20e1)), closes [#30279](https://github.com/ionic-team/ionic-framework/issues/30279) [#30359](https://github.com/ionic-team/ionic-framework/issues/30359)
* **toast:** keep icon on the same line as long message in stacked layout ([#30923](https://github.com/ionic-team/ionic-framework/issues/30923)) ([442e3e9](https://github.com/ionic-team/ionic-framework/commit/442e3e983107a69cea4fb5587fb33da718eee8a3)), closes [#30908](https://github.com/ionic-team/ionic-framework/issues/30908)
## [8.7.17](https://github.com/ionic-team/ionic-framework/compare/v8.7.15...v8.7.17) (2026-01-14)
### Bug Fixes
* **input:** prevent Android TalkBack from focusing label separately ([#30895](https://github.com/ionic-team/ionic-framework/issues/30895)) ([ab733b7](https://github.com/ionic-team/ionic-framework/commit/ab733b71dd355d9486757f219fe09acaefeeefcc))
* **input:** prevent placeholder from overlapping start slot during scroll assist ([#30896](https://github.com/ionic-team/ionic-framework/issues/30896)) ([3b3318d](https://github.com/ionic-team/ionic-framework/commit/3b3318da513b199128f3822bd8226797cd118b0f))
* **tab-bar:** prevent keyboard controller memory leak on rapid mount/unmount ([#30906](https://github.com/ionic-team/ionic-framework/issues/30906)) ([f99d000](https://github.com/ionic-team/ionic-framework/commit/f99d0007a8ffc9c7d3d2636e912c37c12112b21d))
## [8.7.16](https://github.com/ionic-team/ionic-framework/compare/v8.7.15...v8.7.16) (2025-12-31)
### Bug Fixes
* **modal:** prevent card modal animation on viewport resize when modal is closed ([#30894](https://github.com/ionic-team/ionic-framework/issues/30894)) ([e5634d4](https://github.com/ionic-team/ionic-framework/commit/e5634d45ee5fd32715f6e6b75e0448f74ee1f8f2)), closes [#30679](https://github.com/ionic-team/ionic-framework/issues/30679)
## [8.7.15](https://github.com/ionic-team/ionic-framework/compare/v8.7.14...v8.7.15) (2025-12-23)
### Bug Fixes
* **core:** use Capacitor safe-area CSS variables on older WebViews ([#30865](https://github.com/ionic-team/ionic-framework/issues/30865)) ([8573bf8](https://github.com/ionic-team/ionic-framework/commit/8573bf8083f75eda13c954a56731a6aac8ca5724))
* **header:** show iOS condense header when app is in MD mode ([#30690](https://github.com/ionic-team/ionic-framework/issues/30690)) ([f83b000](https://github.com/ionic-team/ionic-framework/commit/f83b0005309400d674e43c497bdffbcb9d2c4d94)), closes [#29929](https://github.com/ionic-team/ionic-framework/issues/29929)
* **input-password-toggle:** improve screen reader announcements ([#30885](https://github.com/ionic-team/ionic-framework/issues/30885)) ([12ede4b](https://github.com/ionic-team/ionic-framework/commit/12ede4b79c8d5cffc2b014c7c8a0d2ef1d3bf90d))
* **modal:** dismiss top-most overlay when multiple IDs match ([#30883](https://github.com/ionic-team/ionic-framework/issues/30883)) ([3b60a1d](https://github.com/ionic-team/ionic-framework/commit/3b60a1d68a1df1606ffee0bde7db7a206bac404a)), closes [#30030](https://github.com/ionic-team/ionic-framework/issues/30030)
## [8.7.14](https://github.com/ionic-team/ionic-framework/compare/v8.7.13...v8.7.14) (2025-12-17)
### Bug Fixes
* **tabs:** select correct tab when routes have similar prefixes ([#30863](https://github.com/ionic-team/ionic-framework/issues/30863)) ([03fb422](https://github.com/ionic-team/ionic-framework/commit/03fb422bfa775e3e9dd695ea1857fa88d4245ecd)), closes [#30448](https://github.com/ionic-team/ionic-framework/issues/30448)
## [8.7.13](https://github.com/ionic-team/ionic-framework/compare/v8.7.12...v8.7.13) (2025-12-13)
**Note:** Version bump only for package ionic-framework
## [8.7.12](https://github.com/ionic-team/ionic-framework/compare/v8.7.11...v8.7.12) (2025-12-10)

View File

@@ -3,117 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.8.1](https://github.com/ionic-team/ionic-framework/compare/v8.8.0...v8.8.1) (2026-03-06)
### Bug Fixes
* **accordion:** update tabindex based on disabled state ([#30986](https://github.com/ionic-team/ionic-framework/issues/30986)) ([0e76a69](https://github.com/ionic-team/ionic-framework/commit/0e76a69370083702568825c29d63cf257d6b88f1))
* **angular:** export RefresherPullEnd types ([#30991](https://github.com/ionic-team/ionic-framework/issues/30991)) ([72abcca](https://github.com/ionic-team/ionic-framework/commit/72abccaad8df3c1db004da28610fddd95ac93c02))
### Features
* **toast:** add wrapper and content parts (originally intended for 8.8.0 but omitted from that release) ([#30992](https://github.com/ionic-team/ionic-framework/issues/30992)) ([366f00e](https://github.com/ionic-team/ionic-framework/commit/366f00e25f06e28aa7166275445716c2d301e44a)), closes [#30735](https://github.com/ionic-team/ionic-framework/issues/30735)
# [8.8.0](https://github.com/ionic-team/ionic-framework/compare/v8.7.18...v8.8.0) (2026-03-04)
### Features
* **content:** add content-fullscreen class when fullscreen is true ([#30926](https://github.com/ionic-team/ionic-framework/issues/30926)) ([d74b11b](https://github.com/ionic-team/ionic-framework/commit/d74b11bc19d6268b256daf23ba6f107483c00320))
* **datetime:** add header parts ([#30945](https://github.com/ionic-team/ionic-framework/issues/30945)) ([6ea186d](https://github.com/ionic-team/ionic-framework/commit/6ea186d96d80a94b774d4d0a51d536e0e5599935))
* **datetime:** add wheel part to ion-picker-column ([#30934](https://github.com/ionic-team/ionic-framework/issues/30934)) ([0cf4c03](https://github.com/ionic-team/ionic-framework/commit/0cf4c03e298bb4f7eea71c966a1473765ebd6d7a))
* **item-divider:** add inner and container parts ([#30928](https://github.com/ionic-team/ionic-framework/issues/30928)) ([5cdeb7f](https://github.com/ionic-team/ionic-framework/commit/5cdeb7fd357298f15e7ae29b14412d97bdc7c656))
* **item-option:** add inner and container parts ([#30929](https://github.com/ionic-team/ionic-framework/issues/30929)) ([f8f7ffd](https://github.com/ionic-team/ionic-framework/commit/f8f7ffda318c0143d9bb5c79fe55b4ecb88e6ce3))
* **item:** add inner and container parts ([#30927](https://github.com/ionic-team/ionic-framework/issues/30927)) ([a2c6559](https://github.com/ionic-team/ionic-framework/commit/a2c655923bb1cff51864949575e19028623c695d))
* **list-header:** add inner part ([#30930](https://github.com/ionic-team/ionic-framework/issues/30930)) ([ef73476](https://github.com/ionic-team/ionic-framework/commit/ef73476e08670630907e775a38f9ed30a84e3f1f))
* **modal:** add drag events for sheet and card modals ([#30962](https://github.com/ionic-team/ionic-framework/issues/30962)) ([d29ac71](https://github.com/ionic-team/ionic-framework/commit/d29ac713fad604c256fb385eb0c26eb9717e1ff4)), closes [#23955](https://github.com/ionic-team/ionic-framework/issues/23955)
* **range:** add classes and expose parts to allow individual styling of dual knobs ([#30941](https://github.com/ionic-team/ionic-framework/issues/30941)) ([5bcf921](https://github.com/ionic-team/ionic-framework/commit/5bcf92184118055483bf306ab9e319b8e3e61870)), closes [#29862](https://github.com/ionic-team/ionic-framework/issues/29862)
* **range:** add classes to the range when the value is at the min or max ([#30932](https://github.com/ionic-team/ionic-framework/issues/30932)) ([fac1a66](https://github.com/ionic-team/ionic-framework/commit/fac1a6673c88a531f1d79656be4eb544f235f819))
* **refresher:** add ionPullStart and ionPullEnd events ([#30946](https://github.com/ionic-team/ionic-framework/issues/30946)) ([814c2e5](https://github.com/ionic-team/ionic-framework/commit/814c2e5ccd6d5bfda12bdf13a566cd66ff830d5b)), closes [#24524](https://github.com/ionic-team/ionic-framework/issues/24524)
* **segment-view:** add swipeGesture property to disable swiping ([#30948](https://github.com/ionic-team/ionic-framework/issues/30948)) ([46806bd](https://github.com/ionic-team/ionic-framework/commit/46806bd6e2af90a0b31fca68f508c06d3d281ec0)), closes [#30290](https://github.com/ionic-team/ionic-framework/issues/30290)
* **select:** add wrapper and bottom shadow parts ([#30951](https://github.com/ionic-team/ionic-framework/issues/30951)) ([5cea5ae](https://github.com/ionic-team/ionic-framework/commit/5cea5aeb44393edab7064e5980a1eb7e607d1b8d))
* **select:** pass cancelText property to modal interface ([#30282](https://github.com/ionic-team/ionic-framework/issues/30282)) ([6e4f60a](https://github.com/ionic-team/ionic-framework/commit/6e4f60af4c188ae04028b444aa21118ae27c2ca7))
* **textarea:** reflect disabled and readonly props ([#30910](https://github.com/ionic-team/ionic-framework/issues/30910)) ([55735df](https://github.com/ionic-team/ionic-framework/commit/55735df3fa62c8e259c56db3169f3d5459e71c0c))
## [8.7.18](https://github.com/ionic-team/ionic-framework/compare/v8.7.17...v8.7.18) (2026-02-25)
### Bug Fixes
* **datetime:** stretch ion-buttons to fill space for ios ([#30963](https://github.com/ionic-team/ionic-framework/issues/30963)) ([d46b0b1](https://github.com/ionic-team/ionic-framework/commit/d46b0b15f6a652da6f863cf303e7ce06cfc820a8))
* **many:** clear timeouts ([#30851](https://github.com/ionic-team/ionic-framework/issues/30851)) ([70b1237](https://github.com/ionic-team/ionic-framework/commit/70b1237823dd0cdab852486a6b2cbbfe0d0aaae9)), closes [#30860](https://github.com/ionic-team/ionic-framework/issues/30860)
* **modal, popover:** respect safe area insets on popovers and modals ([#30949](https://github.com/ionic-team/ionic-framework/issues/30949)) ([6490797](https://github.com/ionic-team/ionic-framework/commit/6490797851cede3bfda893a19b10f165259ec988)), closes [#28411](https://github.com/ionic-team/ionic-framework/issues/28411)
* **radio-group:** prevent DOMException and NotFoundError when filtering radios ([#30958](https://github.com/ionic-team/ionic-framework/issues/30958)) ([682a17e](https://github.com/ionic-team/ionic-framework/commit/682a17ebb754da7714989623cf84b75e715e20e1)), closes [#30279](https://github.com/ionic-team/ionic-framework/issues/30279) [#30359](https://github.com/ionic-team/ionic-framework/issues/30359)
* **toast:** keep icon on the same line as long message in stacked layout ([#30923](https://github.com/ionic-team/ionic-framework/issues/30923)) ([442e3e9](https://github.com/ionic-team/ionic-framework/commit/442e3e983107a69cea4fb5587fb33da718eee8a3)), closes [#30908](https://github.com/ionic-team/ionic-framework/issues/30908)
## [8.7.17](https://github.com/ionic-team/ionic-framework/compare/v8.7.15...v8.7.17) (2026-01-14)
### Bug Fixes
* **input:** prevent Android TalkBack from focusing label separately ([#30895](https://github.com/ionic-team/ionic-framework/issues/30895)) ([ab733b7](https://github.com/ionic-team/ionic-framework/commit/ab733b71dd355d9486757f219fe09acaefeeefcc))
* **input:** prevent placeholder from overlapping start slot during scroll assist ([#30896](https://github.com/ionic-team/ionic-framework/issues/30896)) ([3b3318d](https://github.com/ionic-team/ionic-framework/commit/3b3318da513b199128f3822bd8226797cd118b0f))
* **tab-bar:** prevent keyboard controller memory leak on rapid mount/unmount ([#30906](https://github.com/ionic-team/ionic-framework/issues/30906)) ([f99d000](https://github.com/ionic-team/ionic-framework/commit/f99d0007a8ffc9c7d3d2636e912c37c12112b21d))
## [8.7.16](https://github.com/ionic-team/ionic-framework/compare/v8.7.15...v8.7.16) (2025-12-31)
### Bug Fixes
* **modal:** prevent card modal animation on viewport resize when modal is closed ([#30894](https://github.com/ionic-team/ionic-framework/issues/30894)) ([e5634d4](https://github.com/ionic-team/ionic-framework/commit/e5634d45ee5fd32715f6e6b75e0448f74ee1f8f2)), closes [#30679](https://github.com/ionic-team/ionic-framework/issues/30679)
## [8.7.15](https://github.com/ionic-team/ionic-framework/compare/v8.7.14...v8.7.15) (2025-12-23)
### Bug Fixes
* **core:** use Capacitor safe-area CSS variables on older WebViews ([#30865](https://github.com/ionic-team/ionic-framework/issues/30865)) ([8573bf8](https://github.com/ionic-team/ionic-framework/commit/8573bf8083f75eda13c954a56731a6aac8ca5724))
* **header:** show iOS condense header when app is in MD mode ([#30690](https://github.com/ionic-team/ionic-framework/issues/30690)) ([f83b000](https://github.com/ionic-team/ionic-framework/commit/f83b0005309400d674e43c497bdffbcb9d2c4d94)), closes [#29929](https://github.com/ionic-team/ionic-framework/issues/29929)
* **input-password-toggle:** improve screen reader announcements ([#30885](https://github.com/ionic-team/ionic-framework/issues/30885)) ([12ede4b](https://github.com/ionic-team/ionic-framework/commit/12ede4b79c8d5cffc2b014c7c8a0d2ef1d3bf90d))
* **modal:** dismiss top-most overlay when multiple IDs match ([#30883](https://github.com/ionic-team/ionic-framework/issues/30883)) ([3b60a1d](https://github.com/ionic-team/ionic-framework/commit/3b60a1d68a1df1606ffee0bde7db7a206bac404a)), closes [#30030](https://github.com/ionic-team/ionic-framework/issues/30030)
## [8.7.14](https://github.com/ionic-team/ionic-framework/compare/v8.7.13...v8.7.14) (2025-12-17)
**Note:** Version bump only for package @ionic/core
## [8.7.13](https://github.com/ionic-team/ionic-framework/compare/v8.7.12...v8.7.13) (2025-12-13)
**Note:** Version bump only for package @ionic/core
## [8.7.12](https://github.com/ionic-team/ionic-framework/compare/v8.7.11...v8.7.12) (2025-12-10)

View File

@@ -566,18 +566,9 @@ ion-datetime,part,calendar-day
ion-datetime,part,calendar-day active
ion-datetime,part,calendar-day disabled
ion-datetime,part,calendar-day today
ion-datetime,part,calendar-days-of-week
ion-datetime,part,calendar-header
ion-datetime,part,datetime-header
ion-datetime,part,datetime-selected-date
ion-datetime,part,datetime-title
ion-datetime,part,month-year-button
ion-datetime,part,navigation-button
ion-datetime,part,next-button
ion-datetime,part,previous-button
ion-datetime,part,time-button
ion-datetime,part,time-button active
ion-datetime,part,wheel
ion-datetime,part,wheel-item
ion-datetime,part,wheel-item active
@@ -712,7 +703,7 @@ ion-infinite-scroll-content,prop,loadingText,IonicSafeString | string | undefine
ion-input,scoped
ion-input,prop,autocapitalize,string,'off',false,false
ion-input,prop,autocomplete,"additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday" | "bday-day" | "bday-month" | "bday-year" | "cc-additional-name" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "email" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "impp" | "language" | "name" | "new-password" | "nickname" | "off" | "on" | "one-time-code" | "organization" | "organization-title" | "photo" | "postal-code" | "sex" | "street-address" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "transaction-amount" | "transaction-currency" | "url" | "username",'off',false,false
ion-input,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
ion-input,prop,autocorrect,"off" | "on",'off',false,false
ion-input,prop,autofocus,boolean,false,false,false
ion-input,prop,clearInput,boolean,false,false,false
@@ -939,9 +930,7 @@ ion-item,css-prop,--ripple-color,ios
ion-item,css-prop,--ripple-color,md
ion-item,css-prop,--transition,ios
ion-item,css-prop,--transition,md
ion-item,part,container
ion-item,part,detail-icon
ion-item,part,inner
ion-item,part,native
ion-item-divider,shadow
@@ -968,8 +957,6 @@ ion-item-divider,css-prop,--padding-start,ios
ion-item-divider,css-prop,--padding-start,md
ion-item-divider,css-prop,--padding-top,ios
ion-item-divider,css-prop,--padding-top,md
ion-item-divider,part,container
ion-item-divider,part,inner
ion-item-group,none
@@ -987,8 +974,6 @@ ion-item-option,css-prop,--background,ios
ion-item-option,css-prop,--background,md
ion-item-option,css-prop,--color,ios
ion-item-option,css-prop,--color,md
ion-item-option,part,container
ion-item-option,part,inner
ion-item-option,part,native
ion-item-options,none
@@ -1033,7 +1018,6 @@ ion-list-header,css-prop,--color,ios
ion-list-header,css-prop,--color,md
ion-list-header,css-prop,--inner-border-width,ios
ion-list-header,css-prop,--inner-border-width,md
ion-list-header,part,inner
ion-loading,scoped
ion-loading,prop,animated,boolean,true,false,false
@@ -1187,9 +1171,6 @@ ion-modal,method,setCurrentBreakpoint,setCurrentBreakpoint(breakpoint: number) =
ion-modal,event,didDismiss,OverlayEventDetail<any>,true
ion-modal,event,didPresent,void,true
ion-modal,event,ionBreakpointDidChange,ModalBreakpointChangeEventDetail,true
ion-modal,event,ionDragEnd,ModalDragEventDetail,true
ion-modal,event,ionDragMove,ModalDragEventDetail,true
ion-modal,event,ionDragStart,void,true
ion-modal,event,ionModalDidDismiss,OverlayEventDetail<any>,true
ion-modal,event,ionModalDidPresent,void,true
ion-modal,event,ionModalWillDismiss,OverlayEventDetail<any>,true
@@ -1228,7 +1209,7 @@ ion-nav,shadow
ion-nav,prop,animated,boolean,true,false,false
ion-nav,prop,animation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-nav,prop,root,Function | HTMLElement | ViewController | null | string | undefined,undefined,false,false
ion-nav,prop,rootParams,T | undefined,undefined,false,false
ion-nav,prop,rootParams,undefined | { [key: string]: any; },undefined,false,false
ion-nav,prop,swipeGesture,boolean | undefined,undefined,false,false
ion-nav,method,canGoBack,canGoBack(view?: ViewController) => Promise<boolean>
ion-nav,method,getActive,getActive() => Promise<ViewController | undefined>
@@ -1249,7 +1230,7 @@ ion-nav,event,ionNavWillChange,void,false
ion-nav-link,none
ion-nav-link,prop,component,Function | HTMLElement | ViewController | null | string | undefined,undefined,false,false
ion-nav-link,prop,componentProps,T | undefined,undefined,false,false
ion-nav-link,prop,componentProps,undefined | { [key: string]: any; },undefined,false,false
ion-nav-link,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-nav-link,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
@@ -1342,7 +1323,7 @@ ion-popover,prop,animated,boolean,true,false,false
ion-popover,prop,arrow,boolean,true,false,false
ion-popover,prop,backdropDismiss,boolean,true,false,false
ion-popover,prop,component,Function | HTMLElement | null | string | undefined,undefined,false,false
ion-popover,prop,componentProps,T | undefined,undefined,false,false
ion-popover,prop,componentProps,undefined | { [key: string]: any; },undefined,false,false
ion-popover,prop,dismissOnSelect,boolean,false,false,false
ion-popover,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-popover,prop,event,any,undefined,false,false
@@ -1491,28 +1472,11 @@ ion-range,css-prop,--pin-background,ios
ion-range,css-prop,--pin-background,md
ion-range,css-prop,--pin-color,ios
ion-range,css-prop,--pin-color,md
ion-range,part,activated
ion-range,part,bar
ion-range,part,bar-active
ion-range,part,focused
ion-range,part,hover
ion-range,part,knob
ion-range,part,knob-a
ion-range,part,knob-b
ion-range,part,knob-handle
ion-range,part,knob-handle-a
ion-range,part,knob-handle-b
ion-range,part,knob-handle-lower
ion-range,part,knob-handle-upper
ion-range,part,knob-lower
ion-range,part,knob-upper
ion-range,part,label
ion-range,part,pin
ion-range,part,pin-a
ion-range,part,pin-b
ion-range,part,pin-lower
ion-range,part,pin-upper
ion-range,part,pressed
ion-range,part,tick
ion-range,part,tick-active
@@ -1528,8 +1492,6 @@ ion-refresher,method,cancel,cancel() => Promise<void>
ion-refresher,method,complete,complete() => Promise<void>
ion-refresher,method,getProgress,getProgress() => Promise<number>
ion-refresher,event,ionPull,void,true
ion-refresher,event,ionPullEnd,RefresherPullEndEventDetail,true
ion-refresher,event,ionPullStart,void,true
ion-refresher,event,ionRefresh,RefresherEventDetail,true
ion-refresher,event,ionStart,void,true
@@ -1595,7 +1557,7 @@ ion-row,shadow
ion-searchbar,scoped
ion-searchbar,prop,animated,boolean,false,false,false
ion-searchbar,prop,autocapitalize,string,'off',false,false
ion-searchbar,prop,autocomplete,"additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday" | "bday-day" | "bday-month" | "bday-year" | "cc-additional-name" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "email" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "impp" | "language" | "name" | "new-password" | "nickname" | "off" | "on" | "one-time-code" | "organization" | "organization-title" | "photo" | "postal-code" | "sex" | "street-address" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "transaction-amount" | "transaction-currency" | "url" | "username",'off',false,false
ion-searchbar,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
ion-searchbar,prop,cancelButtonIcon,string,config.get('backButtonIcon', arrowBackSharp) as string,false,false
ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false
@@ -1730,7 +1692,6 @@ ion-segment-content,shadow
ion-segment-view,shadow
ion-segment-view,prop,disabled,boolean,false,false,false
ion-segment-view,prop,swipeGesture,boolean,true,false,false
ion-segment-view,event,ionSegmentViewScroll,SegmentViewScrollEvent,true
ion-select,shadow
@@ -1795,20 +1756,16 @@ ion-select,css-prop,--placeholder-opacity,ios
ion-select,css-prop,--placeholder-opacity,md
ion-select,css-prop,--ripple-color,ios
ion-select,css-prop,--ripple-color,md
ion-select,part,bottom
ion-select,part,container
ion-select,part,error-text
ion-select,part,helper-text
ion-select,part,icon
ion-select,part,inner
ion-select,part,label
ion-select,part,placeholder
ion-select,part,supporting-text
ion-select,part,text
ion-select,part,wrapper
ion-select-modal,scoped
ion-select-modal,prop,cancelText,string,'Close',false,false
ion-select-modal,prop,header,string | undefined,undefined,false,false
ion-select-modal,prop,multiple,boolean | undefined,undefined,false,false
ion-select-modal,prop,options,SelectModalOption[],[],false,false
@@ -1916,7 +1873,7 @@ ion-textarea,prop,cols,number | undefined,undefined,false,true
ion-textarea,prop,counter,boolean,false,false,false
ion-textarea,prop,counterFormatter,((inputLength: number, maxLength: number) => string) | undefined,undefined,false,false
ion-textarea,prop,debounce,number | undefined,undefined,false,false
ion-textarea,prop,disabled,boolean,false,false,true
ion-textarea,prop,disabled,boolean,false,false,false
ion-textarea,prop,enterkeyhint,"done" | "enter" | "go" | "next" | "previous" | "search" | "send" | undefined,undefined,false,false
ion-textarea,prop,errorText,string | undefined,undefined,false,false
ion-textarea,prop,fill,"outline" | "solid" | undefined,undefined,false,false
@@ -1929,7 +1886,7 @@ ion-textarea,prop,minlength,number | undefined,undefined,false,false
ion-textarea,prop,mode,"ios" | "md",undefined,false,false
ion-textarea,prop,name,string,this.inputId,false,false
ion-textarea,prop,placeholder,string | undefined,undefined,false,false
ion-textarea,prop,readonly,boolean,false,false,true
ion-textarea,prop,readonly,boolean,false,false,false
ion-textarea,prop,required,boolean,false,false,false
ion-textarea,prop,rows,number | undefined,undefined,false,false
ion-textarea,prop,shape,"round" | undefined,undefined,false,false
@@ -2059,11 +2016,9 @@ ion-toast,css-prop,--width,md
ion-toast,part,button
ion-toast,part,button cancel
ion-toast,part,container
ion-toast,part,content
ion-toast,part,header
ion-toast,part,icon
ion-toast,part,message
ion-toast,part,wrapper
ion-toggle,shadow
ion-toggle,prop,alignment,"center" | "start" | undefined,undefined,false,false

93
core/package-lock.json generated
View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/core",
"version": "8.8.1",
"version": "8.7.12",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "8.8.1",
"version": "8.7.12",
"license": "MIT",
"dependencies": {
"@stencil/core": "4.43.0",
"@stencil/core": "4.38.0",
"ionicons": "^8.0.13",
"tslib": "^2.1.0"
},
@@ -19,6 +19,7 @@
"@capacitor/haptics": "^8.0.0",
"@capacitor/keyboard": "^8.0.0",
"@capacitor/status-bar": "^8.0.0",
"@clack/prompts": "^0.11.0",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@playwright/test": "^1.56.1",
@@ -51,7 +52,7 @@
"stylelint-order": "^4.1.0"
},
"engines": {
"node": ">= 16"
"node": "24.x"
}
},
"custom-rules": {
@@ -93,6 +94,7 @@
"version": "7.16.12",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.16.7",
"@babel/generator": "^7.16.8",
@@ -627,19 +629,20 @@
"license": "MIT"
},
"node_modules/@capacitor/core": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.1.0.tgz",
"integrity": "sha512-UfMBMWc1v7J+14AhH03QmeNwV3HZx3qnOWhpwnHfzALEwAwlV/itQOQqcasMQYhOHWL0tiymc5ByaLTn7KKQxw==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.0.0.tgz",
"integrity": "sha512-250HTVd/W/KdMygoqaedisvNbHbpbQTN2Hy/8ZYGm1nAqE0Fx7sGss4l0nDg33STxEdDhtVRoL2fIaaiukKseA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@capacitor/haptics": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-8.0.1.tgz",
"integrity": "sha512-8v8rowLBMeb3CryqoQvXndwyUsoi4pPXf0qFw7IGA4D32Uk7+K6juN2SjRowqunoovkvvbFmU9TD7JIAz2zmFw==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-8.0.0.tgz",
"integrity": "sha512-DY1IUOjke1T4ITl7mFHQIKCaJJyHYAYRYHG9bVApU7PDOZiMVGMp48Yjzdqjya+wv/AHS5mDabSTUmhJ5uDvBA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -647,9 +650,9 @@
}
},
"node_modules/@capacitor/keyboard": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-8.0.1.tgz",
"integrity": "sha512-HDf4qrvvhLRMsgBoqeqIld6hP8JMK/WPbCYMvz8ajhY6TaibYt6B+NQyky4oIPCOfHTz5OcVsuHkbb8fQvGDAg==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-8.0.0.tgz",
"integrity": "sha512-ycPW6iQyFwzDK95jihesj5EGiyyGSfbBqNek11iNp9tBOB7zDeYkUA2S/vPpOETt3dhP6pWr7a9gNVGuEfj11g==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -657,15 +660,34 @@
}
},
"node_modules/@capacitor/status-bar": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-8.0.1.tgz",
"integrity": "sha512-OR59dlbwvmrV5dKsC9lvwv48QaGbqcbSTBpk+9/WXWxXYSdXXdzJZU9p8oyNPAkuJhCdnSa3XmU43fZRPBJJ5w==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-8.0.0.tgz",
"integrity": "sha512-aIj3bc7z8lfPgOen8HlrBrkfnxpFnh21OCx6jCUx4Mvv+B6eEkUQ49b32DOddgVfr+igRHLX2SYi7duqIsNDXg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@capacitor/core": ">=8.0.0"
}
},
"node_modules/@clack/core": {
"version": "0.5.0",
"dev": true,
"license": "MIT",
"dependencies": {
"picocolors": "^1.0.0",
"sisteransi": "^1.0.5"
}
},
"node_modules/@clack/prompts": {
"version": "0.11.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@clack/core": "0.5.0",
"picocolors": "^1.0.0",
"sisteransi": "^1.0.5"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"dev": true,
@@ -850,6 +872,7 @@
"version": "4.33.0",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "4.33.0",
"@typescript-eslint/types": "4.33.0",
@@ -1785,10 +1808,9 @@
}
},
"node_modules/@stencil/core": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.43.0.tgz",
"integrity": "sha512-6Uj2Z3lzLuufYAE7asZ6NLKgSwsB9uxl84Eh34PASnUjfj32GkrP4DtKK7fNeh1WFGGyffsTDka3gwtl+4reUg==",
"version": "4.38.0",
"license": "MIT",
"peer": true,
"bin": {
"stencil": "bin/stencil"
},
@@ -2213,6 +2235,7 @@
"version": "6.7.2",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.7.2",
"@typescript-eslint/types": "6.7.2",
@@ -2438,7 +2461,6 @@
"integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/shared": "3.5.25",
@@ -2453,7 +2475,6 @@
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"engines": {
"node": ">=0.12"
},
@@ -2466,8 +2487,7 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.25",
@@ -2475,7 +2495,6 @@
"integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-core": "3.5.25",
"@vue/shared": "3.5.25"
@@ -2487,7 +2506,6 @@
"integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/compiler-core": "3.5.25",
@@ -2505,8 +2523,7 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@vue/compiler-sfc/node_modules/postcss": {
"version": "8.5.6",
@@ -2528,7 +2545,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -2544,7 +2560,6 @@
"integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.25",
"@vue/shared": "3.5.25"
@@ -2556,7 +2571,6 @@
"integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/shared": "3.5.25"
}
@@ -2567,7 +2581,6 @@
"integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/reactivity": "3.5.25",
"@vue/shared": "3.5.25"
@@ -2579,7 +2592,6 @@
"integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/reactivity": "3.5.25",
"@vue/runtime-core": "3.5.25",
@@ -2593,7 +2605,6 @@
"integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-ssr": "3.5.25",
"@vue/shared": "3.5.25"
@@ -2607,8 +2618,7 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz",
"integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@zeit/schemas": {
"version": "2.21.0",
@@ -2631,6 +2641,7 @@
"version": "7.4.0",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3791,8 +3802,7 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/debug": {
"version": "2.6.9",
@@ -4086,6 +4096,7 @@
"version": "7.32.0",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "7.12.11",
"@eslint/eslintrc": "^0.4.3",
@@ -7281,7 +7292,6 @@
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
@@ -7603,7 +7613,6 @@
}
],
"license": "MIT",
"peer": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -7958,6 +7967,7 @@
"integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"playwright-core": "cli.js"
},
@@ -7969,6 +7979,7 @@
"version": "7.0.35",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
@@ -8074,6 +8085,7 @@
"version": "0.36.2",
"dev": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"postcss": ">=5.0.0"
}
@@ -8122,6 +8134,7 @@
"version": "2.6.1",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin-prettier.js"
},
@@ -8479,6 +8492,7 @@
"version": "2.35.1",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@@ -8700,7 +8714,6 @@
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}

View File

@@ -1,9 +1,9 @@
{
"name": "@ionic/core",
"version": "8.8.1",
"version": "8.7.12",
"description": "Base components for Ionic",
"engines": {
"node": ">= 16"
"node": "24.x"
},
"keywords": [
"ionic",
@@ -34,7 +34,7 @@
"loader/"
],
"dependencies": {
"@stencil/core": "4.43.0",
"@stencil/core": "4.38.0",
"ionicons": "^8.0.13",
"tslib": "^2.1.0"
},
@@ -44,6 +44,7 @@
"@capacitor/haptics": "^8.0.0",
"@capacitor/keyboard": "^8.0.0",
"@capacitor/status-bar": "^8.0.0",
"@clack/prompts": "^0.11.0",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@playwright/test": "^1.56.1",
@@ -103,7 +104,8 @@
"docker.build": "docker build -t ionic-playwright .",
"test.e2e.docker": "npm run docker.build && node ./scripts/docker.mjs",
"test.e2e.docker.update-snapshots": "npm run test.e2e.docker -- --update-snapshots='changed'",
"test.e2e.docker.ci": "npm run docker.build && CI=true node ./scripts/docker.mjs"
"test.e2e.docker.ci": "npm run docker.build && CI=true node ./scripts/docker.mjs",
"test.e2e.script": "node scripts/testing/e2e-script.mjs"
},
"author": "Ionic Team",
"license": "MIT",

View File

@@ -0,0 +1,260 @@
// The purpose of this script is to provide a way run the E2E tests
// without having the developer to manually run multiple commands based
// on the desired end result.
// E.g. update the local ground truths for a specific component or
// open the Playwright report after running the E2E tests.
import {
intro,
outro,
confirm,
spinner,
isCancel,
cancel,
text,
log,
} from '@clack/prompts';
import { exec, spawn } from 'child_process';
import fs from 'node:fs';
import { setTimeout as sleep } from 'node:timers/promises';
import util from 'node:util';
import color from 'picocolors';
async function main() {
const execAsync = util.promisify(exec);
const cleanUpFiles = async () => {
// Clean up the local ground truths.
const cleanUp = spinner();
// Inform the user that the local ground truths are being cleaned up.
cleanUp.start('Restoring local ground truths');
// Reset the local ground truths.
await execAsync('git reset -- src/**/*-linux.png').catch((error) => {
cleanUp.stop('Failed to reset local ground truths');
console.error(error);
return process.exit(0);
});
// Restore the local ground truths.
await execAsync('git restore -- src/**/*-linux.png').catch((error) => {
cleanUp.stop('Failed to restore local ground truths');
console.error(error);
return process.exit(0);
});
// Inform the user that the local ground truths have been cleaned up.
cleanUp.stop('Local ground truths have been restored to their original state in order to avoid committing them.');
};
intro(color.inverse(' Update Local Ground Truths'));
// Ask user for the component name they want to test.
const componentValue = await text({
message: 'Enter the component or path you want to test (e.g. chip, src/components/chip)',
placeholder: 'Empty for all components',
});
// User cancelled the operation with `Ctrl+C` or `CMD+C`.
if (isCancel(componentValue)) {
cancel('Operation cancelled');
return process.exit(0);
}
// Ask user if they want to update their local ground truths.
const shouldUpdateTruths = await confirm({
message: 'Do you want to update your local ground truths?',
});
// User cancelled the operation with `Ctrl+C` or `CMD+C`.
if (isCancel(shouldUpdateTruths)) {
cancel('Operation cancelled');
return process.exit(0);
}
if (shouldUpdateTruths) {
const defaultBaseBranch = 'main';
// Ask user for the base branch.
let baseBranch = await text({
message: 'Enter the base branch name:',
placeholder: `default: ${defaultBaseBranch}`,
})
// User cancelled the operation with `Ctrl+C` or `CMD+C`.
if (isCancel(baseBranch)) {
cancel('Operation cancelled');
return process.exit(0);
}
// User didn't provide a base branch.
if (!baseBranch) {
baseBranch = defaultBaseBranch;
}
/**
* The provided base branch needs to be fetched.
* This ensures that the local base branch is up-to-date with the
* remote base branch. Otherwise, there might be errors stating that
* certain files don't exist in the local base branch.
*/
const fetchBaseBranch = spinner();
// Inform the user that the base branch is being fetched.
fetchBaseBranch.start(`Fetching "${baseBranch}" to have the latest changes`);
// Fetch the base branch.
await execAsync(`git fetch origin ${baseBranch}`).catch((error) => {
fetchBaseBranch.stop(`Failed to fetch "${baseBranch}"`);
console.error(error);
return process.exit(0);
});
// Inform the user that the base branch has been fetched.
fetchBaseBranch.stop(`Fetched "${baseBranch}"`);
const updateGroundTruth = spinner();
// Inform the user that the local ground truths are being updated.
updateGroundTruth.start('Updating local ground truths');
// Check if user provided an existing file or directory.
const isValidLocation = fs.existsSync(componentValue);
// User provided an existing file or directory.
if (isValidLocation) {
const stats = fs.statSync(componentValue);
// User provided a file as the component.
// ex: `componentValue` = `src/components/chip/test/basic/chip.e2e.ts`
if (stats.isFile()) {
// Update the local ground truths for the provided path.
await execAsync(`git checkout origin/${baseBranch} -- ${componentValue}-snapshots/*-linux.png`).catch((error) => {
updateGroundTruth.stop('Failed to update local ground truths');
console.error(error);
return process.exit(0);
});
}
// User provided a directory as the component.
// ex: `componentValue` = `src/components/chip`
if (stats.isDirectory()) {
// Update the local ground truths for the provided directory.
await execAsync(`git checkout origin/${baseBranch} -- ${componentValue}/test/*/*.e2e.ts-snapshots/*-linux.png`).catch((error) => {
updateGroundTruth.stop('Failed to update local ground truths');
console.error(error);
return process.exit(0);
});
}
}
// User provided a component name as the component.
// ex: `componentValue` = `chip`
else if (componentValue) {
// Update the local ground truths for the provided component.
await execAsync(`git checkout origin/${baseBranch} -- src/components/${componentValue}/test/*/${componentValue}.e2e.ts-snapshots/*-linux.png`).catch((error) => {
updateGroundTruth.stop('Failed to update local ground truths');
console.error(error);
return process.exit(0);
});
}
// User provided an empty string.
else {
// Update the local ground truths for all components.
await execAsync(`git checkout origin/${baseBranch} -- src/components/*/test/*/*.e2e.ts-snapshots/*-linux.png`).catch((error) => {
updateGroundTruth.stop('Failed to update local ground truths');
console.error(error);
return process.exit(0);
});
}
// Inform the user that the local ground truths have been updated.
updateGroundTruth.stop('Updated local ground truths');
}
const buildCore = spinner();
// Inform the user that the core is being built.
buildCore.start('Building core');
/**
* Build core
* Otherwise, the uncommitted changes will not be reflected in the tests because:
* - popping the stash doesn't trigger a re-render even if `npm start` is running
* - app is not running the `npm start` command
*/
await execAsync('npm run build').catch((error) => {
// Clean up the local ground truths.
cleanUpFiles();
buildCore.stop('Failed to build core');
console.error(error);
return process.exit(0);
});
buildCore.stop('Built core');
const runE2ETests = spinner();
// Inform the user that the E2E tests are being run.
runE2ETests.start('Running E2E tests');
// User provided a component value.
if (componentValue) {
await execAsync(`npm run test.e2e.docker.ci ${componentValue}`).catch((error) => {
// Clean up the local ground truths.
cleanUpFiles();
runE2ETests.stop('Failed to run E2E tests');
console.error(error);
return process.exit(0);
});
} else {
await execAsync('npm run test.e2e.docker.ci').catch((error) => {
// Clean up the local ground truths.
cleanUpFiles();
runE2ETests.stop('Failed to run E2E tests');
console.error(error);
return process.exit(0);
});
}
runE2ETests.stop('Ran E2E tests');
// Clean up the local ground truths.
await cleanUpFiles();
// Ask user if they want to open the Playwright report.
const shouldOpenReport = await confirm({
message: 'Do you want to open the Playwright report?',
});
// User cancelled the operation with `Ctrl+C` or `CMD+C`.
if (isCancel(shouldOpenReport)) {
cancel('Operation cancelled');
return process.exit(0);
}
// User chose to open the Playwright report.
if (shouldOpenReport) {
// Use spawn to display the server information and the key to quit the server.
spawn('npx', ['playwright', 'show-report'], {
stdio: 'inherit',
});
} else {
// Inform the user that the Playwright report can be opened by running the following command.
log.info('If you change your mind, you can open the Playwright report by running the following command:');
log.info(color.bold('npx playwright show-report'));
}
if (shouldOpenReport) {
outro("You're all set! Don't forget to quit serving the Playwright report when you're done.");
} else {
outro("You're all set!");
}
await sleep(1000);
}
main().catch(console.error);

View File

@@ -8,9 +8,7 @@ expect.extend({
throw new Error('expected toHaveShadowPart to be called on an element with a shadow root');
}
// Use attribute selector with ~= to match space-separated part values
// e.g., [part~="knob"] matches elements with part="knob" or part="knob knob-a"
const shadowPart = received.shadowRoot.querySelector(`[part~="${part}"]`);
const shadowPart = received.shadowRoot.querySelector(`[part="${part}"]`);
const pass = shadowPart !== null;
const message = `expected ${received.tagName.toLowerCase()} to have shadow part "${part}"`;

1177
core/src/components.d.ts vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -514,7 +514,6 @@ export class Accordion implements ComponentInterface {
'accordion-animated': this.shouldAnimate(),
}}
tabindex={disabled ? '-1' : undefined}
>
<div
onClick={() => this.toggleExpanded()}

View File

@@ -14,13 +14,12 @@ import { getIonMode } from '../../global/ionic-global';
})
export class App implements ComponentInterface {
private focusVisible?: FocusVisibleUtility;
private loadTimeout?: ReturnType<typeof setTimeout> | undefined;
@Element() el!: HTMLElement;
componentDidLoad() {
if (Build.isBrowser) {
this.rIC(async () => {
rIC(async () => {
const isHybrid = isPlatform(window, 'hybrid');
if (!config.getBoolean('_testing')) {
import('../../utils/tap-click').then((module) => module.startTapClick(config));
@@ -61,12 +60,6 @@ export class App implements ComponentInterface {
}
}
disconnectedCallback() {
if (this.loadTimeout) {
clearTimeout(this.loadTimeout);
}
}
/**
* Used to set focus on an element that uses `ion-focusable`.
* Do not use this if focusing the element as a result of a keyboard
@@ -85,14 +78,6 @@ export class App implements ComponentInterface {
}
}
private rIC(callback: () => void) {
if ('requestIdleCallback' in window) {
(window as any).requestIdleCallback(callback);
} else {
this.loadTimeout = setTimeout(callback, 32);
}
}
render() {
const mode = getIonMode(this);
return (
@@ -128,3 +113,11 @@ const needInputShims = () => {
return false;
};
const rIC = (callback: () => void) => {
if ('requestIdleCallback' in window) {
(window as any).requestIdleCallback(callback);
} else {
setTimeout(callback, 32);
}
};

View File

@@ -18,66 +18,20 @@ configs({ directions: ['ltr'] }).forEach(({ config, title, screenshot }) => {
await expect(page).toHaveScreenshot(screenshot(`app-${screenshotModifier}-diff`));
};
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/app/test/safe-area`, config);
});
test.describe(title('Ionic safe area variables'), () => {
test.beforeEach(async ({ page }) => {
const htmlTag = page.locator('html');
const hasSafeAreaClass = await htmlTag.evaluate((el) => el.classList.contains('safe-area'));
expect(hasSafeAreaClass).toBe(true);
});
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');
});
test('should not have visual regressions with action sheet', async ({ page }) => {
await testOverlay(page, '#show-action-sheet', 'ionActionSheetDidPresent', 'action-sheet');
});
test.describe(title('Capacitor safe area variables'), () => {
test('should use safe-area-inset vars when safe-area class is not defined', async ({ page }) => {
await page.evaluate(() => {
const html = document.documentElement;
// Remove the safe area class
html.classList.remove('safe-area');
// Set the safe area inset variables
html.style.setProperty('--safe-area-inset-top', '10px');
html.style.setProperty('--safe-area-inset-bottom', '20px');
html.style.setProperty('--safe-area-inset-left', '30px');
html.style.setProperty('--safe-area-inset-right', '40px');
});
const top = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-top').trim()
);
const bottom = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-bottom').trim()
);
const left = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-left').trim()
);
const right = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-right').trim()
);
expect(top).toBe('10px');
expect(bottom).toBe('20px');
expect(left).toBe('30px');
expect(right).toBe('40px');
});
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');
});
});
});

View File

@@ -170,7 +170,6 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
*/
@Watch('aria-checked')
@Watch('aria-label')
@Watch('aria-pressed')
onAriaChanged(newValue: string, _oldValue: string, propName: string) {
this.inheritedAttributes = {
...this.inheritedAttributes,

View File

@@ -188,11 +188,6 @@ export class Content implements ComponentInterface {
this.tabsElement = null;
this.tabsLoadCallback = undefined;
}
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
this.resizeTimeout = null;
}
}
/**
@@ -467,7 +462,6 @@ export class Content implements ComponentInterface {
role={isMainContent ? 'main' : undefined}
class={createColorClasses(this.color, {
[mode]: true,
'content-fullscreen': this.fullscreen,
'content-sizing': hostContext('ion-popover', this.el),
overscroll: forceOverscroll,
[`content-${rtl}`]: true,

View File

@@ -13,38 +13,5 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
await expect(page).toHaveScreenshot(screenshot(`content-fullscreen`));
});
/**
* The content-fullscreen class is added when fullscreen is true. The
* fullscreen attribute is not reflected in Angular, Vue, or React, so
* the class is needed for users to create custom themes.
*/
test('should have content-fullscreen class when fullscreen is true', async ({ page }) => {
await page.setContent(
`
<ion-content fullscreen>
<p>Hello</p>
</ion-content>
`,
config
);
const content = page.locator('ion-content');
await expect(content).toHaveClass(/content-fullscreen/);
});
test('should not have content-fullscreen class when fullscreen is false', async ({ page }) => {
await page.setContent(
`
<ion-content>
<p>Hello</p>
</ion-content>
`,
config
);
const content = page.locator('ion-content');
await expect(content).not.toHaveClass(/content-fullscreen/);
});
});
});

View File

@@ -21,6 +21,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, screenshot, c
);
const datetimeButton = page.locator('ion-datetime-button');
await page.locator('.datetime-ready').waitFor();
await expect(datetimeButton).toHaveScreenshot(screenshot(`datetime-button-scale`));
});
@@ -40,6 +41,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, screenshot, c
);
const datetimeButton = page.locator('ion-datetime-button');
await page.locator('.datetime-ready').waitFor();
await expect(datetimeButton).toHaveScreenshot(screenshot(`datetime-button-scale-wrap`));
});

View File

@@ -24,6 +24,9 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
await dateButton.click();
await ionModalDidPresent.next();
// Wait for datetime to be ready before taking screenshot
await page.locator('ion-datetime.datetime-ready').waitFor();
await expect(page).toHaveScreenshot(screenshot(`datetime-overlay-modal`));
});
@@ -44,6 +47,9 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
await dateButton.click();
await ionPopoverDidPresent.next();
// Wait for datetime to be ready before taking screenshot
await page.locator('ion-datetime.datetime-ready').waitFor();
await expect(page).toHaveScreenshot(screenshot(`datetime-overlay-popover`));
});
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -57,9 +57,7 @@
}
:host .calendar-action-buttons ion-buttons {
align-items: stretch;
height: 100%;
@include padding($datetime-ios-padding * 0.5, 0, 0, 0);
}
:host .calendar-action-buttons ion-buttons ion-button {

View File

@@ -79,7 +79,6 @@ import { checkForPresentationFormatMismatch, warnIfTimeZoneProvided } from './ut
* @slot buttons - The buttons in the datetime.
* @slot time-label - The label for the time selector in the datetime.
*
* @part wheel - The wheel container when using a wheel style layout, or in the month/year picker when using a grid style layout.
* @part wheel-item - The individual items when using a wheel style layout, or in the
* month/year picker when using a grid style layout.
* @part wheel-item active - The currently selected wheel-item.
@@ -88,23 +87,14 @@ import { checkForPresentationFormatMismatch, warnIfTimeZoneProvided } from './ut
* layout with `presentation="date-time"` or `"time-date"`.
* @part time-button active - The time picker button when the picker is open.
*
* @part calendar-header - The calendar header manages the date navigation controls (month/year picker and previous/next buttons) and the days of the week when using a grid style layout.
* @part month-year-button - The button that opens the month/year picker when
* using a grid style layout.
* @part navigation-button - The buttons used to navigate to the next or previous month when using a grid style layout.
* @part previous-button - The button used to navigate to the previous month when using a grid style layout.
* @part next-button - The button used to navigate to the next month when using a grid style layout.
* @part calendar-days-of-week - The container for the day-of-the-week header (both weekdays and weekends) when using a grid style layout.
*
* @part calendar-day - The individual buttons that display a day inside of the datetime
* calendar.
* @part calendar-day active - The currently selected calendar day.
* @part calendar-day today - The calendar day that contains the current day.
* @part calendar-day disabled - The calendar day that is disabled.
*
* @part datetime-header - The datetime header contains the content for the `title` slot and the selected date.
* @part datetime-title - The element that contains the `title` slot content.
* @part datetime-selected-date - The element that contains the selected date.
*/
@Component({
tag: 'ion-datetime',
@@ -118,8 +108,8 @@ export class Datetime implements ComponentInterface {
private inputId = `ion-dt-${datetimeIds++}`;
private calendarBodyRef?: HTMLElement;
private popoverRef?: HTMLIonPopoverElement;
private intersectionTrackerRef?: HTMLElement;
private clearFocusVisible?: () => void;
private resizeObserver?: ResizeObserver;
private parsedMinuteValues?: number[];
private parsedHourValues?: number[];
private parsedMonthValues?: number[];
@@ -128,13 +118,13 @@ export class Datetime implements ComponentInterface {
private destroyCalendarListener?: () => void;
private destroyKeyboardMO?: () => void;
private destroyOverlayListeners?: () => void;
// TODO(FW-2832): types (DatetimeParts causes some errors that need untangling)
private minParts?: any;
private maxParts?: any;
private todayParts!: DatetimeParts;
private defaultParts!: DatetimeParts;
private loadTimeout: ReturnType<typeof setTimeout> | undefined;
private prevPresentation: string | null = null;
@@ -1088,8 +1078,13 @@ export class Datetime implements ComponentInterface {
this.clearFocusVisible();
this.clearFocusVisible = undefined;
}
if (this.loadTimeout) {
clearTimeout(this.loadTimeout);
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = undefined;
}
if (this.destroyOverlayListeners) {
this.destroyOverlayListeners();
this.destroyOverlayListeners = undefined;
}
}
@@ -1116,113 +1111,100 @@ export class Datetime implements ComponentInterface {
}
/**
* TODO(FW-6931): Remove this fallback upon solving the root cause
* Fallback to ensure the datetime becomes ready even if
* IntersectionObserver never reports it as intersecting.
* Sets up visibility detection for the datetime component.
*
* This is primarily used in environments where the observer
* might not fire as expected, such as when running under
* synthetic tests that stub IntersectionObserver.
* Uses multiple strategies to reliably detect when the datetime becomes
* visible, which is necessary for proper initialization of scrollable areas:
* 1. ResizeObserver - detects dimension changes
* 2. Overlay event listeners - for datetime inside modals/popovers
* 3. Polling fallback - for browsers where observers are unreliable (WebKit)
*/
private ensureReadyIfVisible = () => {
if (this.el.classList.contains('datetime-ready')) {
return;
private initializeVisibilityObserver() {
const { el } = this;
const markReady = () => {
if (el.classList.contains('datetime-ready')) {
return;
}
this.initializeListeners();
writeTask(() => {
el.classList.add('datetime-ready');
});
};
const markHidden = () => {
this.destroyInteractionListeners();
this.showMonthAndYear = false;
writeTask(() => {
el.classList.remove('datetime-ready');
});
startVisibilityPolling();
};
/**
* FW-6931: Poll for visibility as a fallback for browsers where
* ResizeObserver doesn't fire reliably (e.g., WebKit).
*/
const startVisibilityPolling = () => {
let pollCount = 0;
const poll = () => {
if (el.classList.contains('datetime-ready') || pollCount++ >= 60) {
return;
}
const { width, height } = el.getBoundingClientRect();
if (width > 0 && height > 0) {
markReady();
} else {
raf(poll);
}
};
raf(poll);
};
/**
* FW-6931: Listen for overlay present/dismiss events when datetime
* is inside a modal or popover.
*/
const parentOverlay = el.closest('ion-modal, ion-popover') as HTMLIonModalElement | HTMLIonPopoverElement | null;
if (parentOverlay) {
const handlePresent = () => markReady();
const handleDismiss = () => markHidden();
parentOverlay.addEventListener('didPresent', handlePresent);
parentOverlay.addEventListener('didDismiss', handleDismiss);
this.destroyOverlayListeners = () => {
parentOverlay.removeEventListener('didPresent', handlePresent);
parentOverlay.removeEventListener('didDismiss', handleDismiss);
};
}
const rect = this.el.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) {
return;
if (typeof ResizeObserver !== 'undefined') {
this.resizeObserver = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
const isVisible = width > 0 && height > 0;
const isReady = el.classList.contains('datetime-ready');
if (isVisible && !isReady) {
markReady();
} else if (!isVisible && isReady) {
markHidden();
}
});
// Use raf to avoid race condition with modal/popover animations
raf(() => this.resizeObserver?.observe(el));
startVisibilityPolling();
} else {
// Test environment fallback - mark ready immediately
writeTask(() => {
el.classList.add('datetime-ready');
});
}
this.initializeListeners();
writeTask(() => {
this.el.classList.add('datetime-ready');
});
};
}
componentDidLoad() {
const { el, intersectionTrackerRef } = this;
/**
* If a scrollable element is hidden using `display: none`,
* it will not have a scroll height meaning we cannot scroll elements
* into view. As a result, we will need to wait for the datetime to become
* visible if used inside of a modal or a popover otherwise the scrollable
* areas will not have the correct values snapped into place.
*/
const visibleCallback = (entries: IntersectionObserverEntry[]) => {
const ev = entries[0];
if (!ev.isIntersecting) {
return;
}
this.initializeListeners();
/**
* TODO FW-2793: Datetime needs a frame to ensure that it
* can properly scroll contents into view. As a result
* we hide the scrollable content until after that frame
* so users do not see the content quickly shifting. The downside
* is that the content will pop into view a frame after. Maybe there
* is a better way to handle this?
*/
writeTask(() => {
this.el.classList.add('datetime-ready');
});
};
const visibleIO = new IntersectionObserver(visibleCallback, { threshold: 0.01, root: el });
/**
* Use raf to avoid a race condition between the component loading and
* its display animation starting (such as when shown in a modal). This
* could cause the datetime to start at a visibility of 0, erroneously
* triggering the `hiddenIO` observer below.
*/
raf(() => visibleIO?.observe(intersectionTrackerRef!));
/**
* TODO(FW-6931): Remove this fallback upon solving the root cause
* Fallback: If IntersectionObserver never reports that the
* datetime is visible but the host clearly has layout, ensure
* we still initialize listeners and mark the component as ready.
*
* We schedule this after everything has had a chance to run.
*/
this.loadTimeout = setTimeout(() => {
this.ensureReadyIfVisible();
}, 100);
/**
* We need to clean up listeners when the datetime is hidden
* in a popover/modal so that we can properly scroll containers
* back into view if they are re-presented. When the datetime is hidden
* the scroll areas have scroll widths/heights of 0px, so any snapping
* we did originally has been lost.
*/
const hiddenCallback = (entries: IntersectionObserverEntry[]) => {
const ev = entries[0];
if (ev.isIntersecting) {
return;
}
this.destroyInteractionListeners();
/**
* When datetime is hidden, we need to make sure that
* the month/year picker is closed. Otherwise,
* it will be open when the datetime re-appears
* and the scroll area of the calendar grid will be 0.
* As a result, the wrong month will be shown.
*/
this.showMonthAndYear = false;
writeTask(() => {
this.el.classList.remove('datetime-ready');
});
};
const hiddenIO = new IntersectionObserver(hiddenCallback, { threshold: 0, root: el });
raf(() => hiddenIO?.observe(intersectionTrackerRef!));
this.initializeVisibilityObserver();
/**
* Datetime uses Ionic components that emit
@@ -1738,7 +1720,6 @@ export class Datetime implements ComponentInterface {
return (
<ion-picker-column
part={WHEEL_PART}
aria-label="Select a date"
class="date-column"
color={this.color}
@@ -1859,7 +1840,6 @@ export class Datetime implements ComponentInterface {
return (
<ion-picker-column
part={WHEEL_PART}
aria-label="Select a day"
class="day-column"
color={this.color}
@@ -1904,7 +1884,6 @@ export class Datetime implements ComponentInterface {
return (
<ion-picker-column
part={WHEEL_PART}
aria-label="Select a month"
class="month-column"
color={this.color}
@@ -1948,7 +1927,6 @@ export class Datetime implements ComponentInterface {
return (
<ion-picker-column
part={WHEEL_PART}
aria-label="Select a year"
class="year-column"
color={this.color}
@@ -2023,7 +2001,6 @@ export class Datetime implements ComponentInterface {
return (
<ion-picker-column
part={WHEEL_PART}
aria-label="Select an hour"
color={this.color}
disabled={disabled}
@@ -2064,7 +2041,6 @@ export class Datetime implements ComponentInterface {
return (
<ion-picker-column
part={WHEEL_PART}
aria-label="Select a minute"
color={this.color}
disabled={disabled}
@@ -2108,7 +2084,6 @@ export class Datetime implements ComponentInterface {
return (
<ion-picker-column
part={WHEEL_PART}
aria-label="Select a day period"
style={isDayPeriodRTL ? { order: '-1' } : {}}
color={this.color}
@@ -2179,7 +2154,7 @@ export class Datetime implements ComponentInterface {
const hostDir = this.el.getAttribute('dir') || undefined;
return (
<div class="calendar-header" part="calendar-header">
<div class="calendar-header">
<div class="calendar-action-buttons">
<div class="calendar-month-year">
<button
@@ -2208,12 +2183,7 @@ export class Datetime implements ComponentInterface {
<div class="calendar-next-prev">
<ion-buttons>
<ion-button
aria-label="Previous month"
disabled={prevMonthDisabled}
onClick={() => this.prevMonth()}
part="navigation-button previous-button"
>
<ion-button aria-label="Previous month" disabled={prevMonthDisabled} onClick={() => this.prevMonth()}>
<ion-icon
dir={hostDir}
aria-hidden="true"
@@ -2223,12 +2193,7 @@ export class Datetime implements ComponentInterface {
flipRtl
></ion-icon>
</ion-button>
<ion-button
aria-label="Next month"
disabled={nextMonthDisabled}
onClick={() => this.nextMonth()}
part="navigation-button next-button"
>
<ion-button aria-label="Next month" disabled={nextMonthDisabled} onClick={() => this.nextMonth()}>
<ion-icon
dir={hostDir}
aria-hidden="true"
@@ -2241,7 +2206,7 @@ export class Datetime implements ComponentInterface {
</ion-buttons>
</div>
</div>
<div class="calendar-days-of-week" aria-hidden="true" part="calendar-days-of-week">
<div class="calendar-days-of-week" aria-hidden="true">
{getDaysOfWeek(this.locale, mode, this.firstDayOfWeek % 7).map((d) => {
return <div class="day-of-week">{d}</div>;
})}
@@ -2594,15 +2559,11 @@ export class Datetime implements ComponentInterface {
}
return (
<div class="datetime-header" part="datetime-header">
<div class="datetime-title" part="datetime-title">
<div class="datetime-header">
<div class="datetime-title">
<slot name="title">Select Date</slot>
</div>
{showExpandedHeader && (
<div class="datetime-selected-date" part="datetime-selected-date">
{this.getHeaderSelectedDateText()}
</div>
)}
{showExpandedHeader && <div class="datetime-selected-date">{this.getHeaderSelectedDateText()}</div>}
</div>
);
}
@@ -2728,20 +2689,6 @@ export class Datetime implements ComponentInterface {
}),
}}
>
{/*
WebKit has a quirk where IntersectionObserver callbacks are delayed until after
an accelerated animation finishes if the "root" specified in the config is the
browser viewport (the default behavior if "root" is not specified). This means
that when presenting a datetime in a modal on iOS the calendar body appears
blank until the modal animation finishes.
We can work around this by observing .intersection-tracker and using the host
(ion-datetime) as the "root". This allows the IO callback to fire the moment
the datetime is visible. The .intersection-tracker element should not have
dimensions or additional styles, and it should not be positioned absolutely
otherwise the IO callback may fire at unexpected times.
*/}
<div class="intersection-tracker" ref={(el) => (this.intersectionTrackerRef = el)}></div>
{this.renderDatetime(mode)}
</Host>
);
@@ -2751,6 +2698,5 @@ export class Datetime implements ComponentInterface {
let datetimeIds = 0;
const CANCEL_ROLE = 'datetime-cancel';
const CONFIRM_ROLE = 'datetime-confirm';
const WHEEL_PART = 'wheel';
const WHEEL_ITEM_PART = 'wheel-item';
const WHEEL_ITEM_ACTIVE_PART = `active`;

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -395,40 +395,18 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
});
/**
* Synthetic IntersectionObserver fallback behavior.
*
* This test stubs IntersectionObserver so that the callback
* never reports an intersecting entry. The datetime should
* still become ready via its internal fallback logic.
* Verify that datetime becomes ready via ResizeObserver.
* This tests that the datetime properly initializes when it has
* dimensions, using ResizeObserver to detect visibility.
*/
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('datetime: IO fallback'), () => {
test('should become ready even if IntersectionObserver never reports visible', async ({ page }, testInfo) => {
test.describe(title('datetime: visibility detection'), () => {
test('should become ready when rendered with dimensions', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30706',
});
await page.addInitScript(() => {
const OriginalIO = window.IntersectionObserver;
(window as any).IntersectionObserver = function (callback: any, options: any) {
const instance = new OriginalIO(() => {}, options);
const originalObserve = instance.observe.bind(instance);
instance.observe = (target: Element) => {
originalObserve(target);
callback([
{
isIntersecting: false,
target,
} as IntersectionObserverEntry,
]);
};
return instance;
} as any;
});
await page.setContent(
`
<ion-datetime value="2022-05-03"></ion-datetime>
@@ -438,8 +416,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
const datetime = page.locator('ion-datetime');
// Give the fallback a short amount of time to run
await page.waitForTimeout(100);
// Wait for the datetime to become ready via ResizeObserver
await page.locator('.datetime-ready').waitFor();
await expect(datetime).toHaveClass(/datetime-ready/);

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -42,324 +42,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
await expect(datetime).toHaveScreenshot(screenshot(`datetime-custom-calendar-days`));
});
});
test.describe(title('CSS shadow parts'), () => {
test('should be able to customize wheel part within the wheel style', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30420',
});
await page.setContent(
`
<style>
ion-datetime::part(wheel) {
background-color: red;
}
</style>
<ion-datetime
prefer-wheel="true"
value="2020-03-14T14:23:00.000Z"
></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const pickerColumn = datetime.locator('ion-picker-column').first();
const backgroundColor = await pickerColumn.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(255, 0, 0)');
});
test('should be able to customize wheel part within the month/year picker', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30420',
});
await page.setContent(
`
<style>
ion-datetime::part(wheel) {
background-color: orange;
}
</style>
<ion-datetime
value="2020-03-14T14:23:00.000Z"
></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const monthYearButton = datetime.locator('.calendar-month-year-toggle');
await monthYearButton.click();
const pickerColumn = datetime.locator('ion-picker-column').first();
const backgroundColor = await pickerColumn.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(255, 165, 0)');
});
test('should be able to customize wheel part within the time picker', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30420',
});
await page.setContent(
`
<style>
ion-picker-column {
background-color: green;
}
</style>
<ion-datetime
value="2020-03-14T14:23:00.000Z"
></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const timeButton = datetime.locator('.time-body');
await timeButton.click();
const pickerColumn = page.locator('ion-picker-column').first();
const backgroundColor = await pickerColumn.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(0, 128, 0)');
});
test('should be able to customize wheel part when focused', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30420',
});
await page.setContent(
`
<style>
ion-datetime::part(wheel):focus {
background-color: blue;
}
</style>
<ion-datetime
prefer-wheel="true"
value="2020-03-14T14:23:00.000Z"
></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const pickerColumn = datetime.locator('ion-picker-column').first();
await pickerColumn.click({ position: { x: 10, y: 10 } });
const backgroundColor = await pickerColumn.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(0, 0, 255)');
});
test('should be able to customize datetime header parts', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30083',
});
await page.setContent(
`
<style>
ion-datetime::part(datetime-header) {
background-color: orange;
}
ion-datetime::part(datetime-title) {
background-color: pink;
}
ion-datetime::part(datetime-selected-date) {
background-color: violet;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z">
<span slot="title">Select Date</span>
</ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const header = datetime.locator('.datetime-header');
const title = datetime.locator('.datetime-title');
const selectedDate = datetime.locator('.datetime-selected-date');
const headerBackgroundColor = await header.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const titleBackgroundColor = await title.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const selectedDateBackgroundColor = await selectedDate.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(headerBackgroundColor).toBe('rgb(255, 165, 0)');
expect(titleBackgroundColor).toBe('rgb(255, 192, 203)');
expect(selectedDateBackgroundColor).toBe('rgb(238, 130, 238)');
});
test('should be able to customize calendar header part', async ({ page }) => {
await page.setContent(
`
<style>
ion-datetime::part(calendar-header) {
background-color: orange;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const header = datetime.locator('.calendar-header');
const backgroundColor = await header.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(255, 165, 0)');
});
test('should be able to customize month/year picker part', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/26596',
});
await page.setContent(
`
<style>
ion-datetime::part(month-year-button) {
background-color: lightblue;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const monthYearButton = datetime.locator('.calendar-month-year-toggle');
const backgroundColor = await monthYearButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(173, 216, 230)');
});
test('should be able to customize navigation button parts', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30830',
});
await page.setContent(
`
<style>
ion-datetime::part(navigation-button) {
background-color: firebrick;
}
ion-datetime::part(previous-button) {
color: blue;
}
ion-datetime::part(next-button) {
color: green;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const prevButton = datetime.locator('.calendar-next-prev ion-button').first();
const nextButton = datetime.locator('.calendar-next-prev ion-button').last();
const prevBackgroundColor = await prevButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const prevColor = await prevButton.evaluate((el) => {
return window.getComputedStyle(el).color;
});
const nextBackgroundColor = await nextButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const nextColor = await nextButton.evaluate((el) => {
return window.getComputedStyle(el).color;
});
// Verify the navigation-button part applies the styles
expect(prevBackgroundColor).toBe('rgb(178, 34, 34)');
expect(nextBackgroundColor).toBe('rgb(178, 34, 34)');
// Verify the previous-button part applies the styles
expect(prevColor).toBe('rgb(0, 0, 255)');
// Verify the next-button part applies the styles
expect(nextColor).toBe('rgb(0, 128, 0)');
});
test('should be able to customize days of the week part', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30830',
});
await page.setContent(
`
<style>
ion-datetime::part(calendar-days-of-week) {
background-color: green;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const daysOfWeek = datetime.locator('.calendar-days-of-week');
const backgroundColor = await daysOfWeek.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(0, 128, 0)');
});
});
});
/**
@@ -370,6 +52,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('datetime: custom focus'), () => {
test('should focus the selected day and then the day after', async ({ page }) => {
await page.goto(`/src/components/datetime/test/custom`, config);
await page.locator('.datetime-ready').last().waitFor();
const datetime = page.locator('#custom-calendar-days');

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

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