Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb7436f051 | ||
|
|
f1dc0b2b06 | ||
|
|
0ded8ba19f | ||
|
|
b09caeb053 | ||
|
|
42c9db52a7 |
67
.github/ionic-issue-bot.yml
vendored
@@ -56,6 +56,14 @@ closeAndLock:
|
||||
bug reports and feature requests. Please use our [forum](https://forum.ionicframework.com) for questions about the framework.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: appflow"
|
||||
message: >
|
||||
Thanks for the issue! This issue appears to be related to Ionic Appflow. We use this issue tracker exclusively for
|
||||
bug reports and feature requests. Please use the [Ionic Appflow Support Forum](https://ionic.zendesk.com/hc/en-us/requests/new)
|
||||
to report this issue.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: missing template"
|
||||
message: >
|
||||
@@ -137,6 +145,65 @@ noReproduction:
|
||||
lock: true
|
||||
dryRun: false
|
||||
|
||||
wrongRepo:
|
||||
repos:
|
||||
- label: "ionitron: capacitor"
|
||||
repo: capacitor
|
||||
message: >
|
||||
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
|
||||
associated with the Ionic Framework. It appears that this issue is associated with Capacitor.
|
||||
I am moving this issue to the Capacitor repository. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: v3"
|
||||
repo: ionic-v3
|
||||
message: >
|
||||
Thanks for the issue! We have moved the source code and issues for Ionic 3 into a separate repository.
|
||||
I am moving this issue to the repository for Ionic 3. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: cli"
|
||||
repo: ionic-cli
|
||||
message: >
|
||||
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
|
||||
associated with the Ionic Framework. It appears that this issue is associated with the Ionic CLI.
|
||||
I am moving this issue to the Ionic CLI repository. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: docs"
|
||||
repo: ionic-docs
|
||||
message: >
|
||||
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
|
||||
associated with the Ionic Framework. It appears that this issue is associated with the Ionic Documentation.
|
||||
I am moving this issue to the Ionic Docs repository. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: stencil"
|
||||
repo: stencil
|
||||
message: >
|
||||
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
|
||||
associated with the Ionic Framework. It appears that this issue is associated with Stencil.
|
||||
I am moving this issue to the Stencil repository. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: native"
|
||||
repo: ionic-native
|
||||
message: >
|
||||
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
|
||||
associated with the Ionic Framework. It appears that this issue is associated with Ionic Native.
|
||||
I am moving this issue to the Ionic Native repository. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
close: true
|
||||
lock: true
|
||||
dryRun: false
|
||||
|
||||
screenshot:
|
||||
appId: 18001
|
||||
checkName: "build"
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Build Ionic Angular Server'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Build Ionic Angular'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -8,8 +8,8 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- name: Install Dependencies
|
||||
@@ -31,6 +31,4 @@ runs:
|
||||
with:
|
||||
name: ionic-core
|
||||
output: core/CoreBuild.zip
|
||||
# Include generated proxy files from Stencil output targets so
|
||||
# framework builds can detect when they need to be updated
|
||||
paths: core/dist core/components core/css core/hydrate core/loader core/src/components.d.ts core/api.txt packages/angular/src/directives/proxies.ts packages/angular/src/directives/proxies-list.ts packages/angular/standalone/src/directives/proxies.ts packages/vue/src/proxies.ts packages/react/src/components/proxies.ts packages/react/src/components/inner-proxies.ts packages/react/src/components/routing-proxies.ts
|
||||
paths: core/dist core/components core/css core/hydrate core/loader core/src/components.d.ts core/api.txt
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Build Ionic React Router'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Build Ionic React'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Builds Ionic Vue Router'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Build Ionic Vue'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -10,7 +10,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/download-artifact@v5
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.name }}
|
||||
path: ${{ inputs.path }}
|
||||
|
||||
@@ -19,7 +19,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
# Provenance requires npm 9.5.0+
|
||||
|
||||
@@ -6,7 +6,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
@@ -32,10 +32,6 @@ runs:
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/angular/test/build/${{ inputs.app }}
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install
|
||||
shell: bash
|
||||
working-directory: ./packages/angular/test/build/${{ inputs.app }}
|
||||
- name: Sync Built Changes
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Test Core Clean Build'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ description: 'Test Core Lint'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- name: Install Dependencies
|
||||
|
||||
@@ -13,7 +13,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -6,7 +6,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- name: Install Dependencies
|
||||
|
||||
@@ -6,7 +6,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -6,7 +6,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -6,7 +6,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
|
||||
@@ -7,10 +7,10 @@ on:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: actions/download-artifact@v5
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./artifacts
|
||||
- name: Extract Archives
|
||||
|
||||
30
.github/workflows/build.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
build-core:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/test-core-clean-build
|
||||
|
||||
test-core-lint:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/test-core-lint
|
||||
|
||||
test-core-spec:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/build-vue
|
||||
|
||||
build-vue-router:
|
||||
needs: [build-vue]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/build-angular
|
||||
|
||||
build-angular-server:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/build-react
|
||||
|
||||
build-react-router:
|
||||
needs: [build-react]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/test-react-e2e
|
||||
with:
|
||||
app: ${{ matrix.apps }}
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: javascript
|
||||
|
||||
31
.github/workflows/conventional-commit.yml
vendored
@@ -9,35 +9,24 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate PR title
|
||||
if: |
|
||||
!contains(github.event.pull_request.title, 'release') &&
|
||||
!contains(github.event.pull_request.title, 'chore')
|
||||
uses: amannn/action-semantic-pull-request@v6
|
||||
uses: amannn/action-semantic-pull-request@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
# Configure that a scope must always be provided.
|
||||
requireScope: true
|
||||
# Configure allowed commit types
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
docs
|
||||
style
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
build
|
||||
ci
|
||||
revert
|
||||
release
|
||||
chore
|
||||
# Configure additional validation for the subject based on a regex.
|
||||
# This example ensures the subject doesn't start with an uppercase character.
|
||||
subjectPattern: ^(?![A-Z]).+$
|
||||
# If `subjectPattern` is configured, you can use this property to
|
||||
# override the default error message that is shown when the pattern
|
||||
# doesn't match. The variables `subject` and `title` can be used
|
||||
# If `subjectPattern` is configured, you can use this property to
|
||||
# override the default error message that is shown when the pattern
|
||||
# doesn't match. The variables `subject` and `title` can be used
|
||||
# within the message.
|
||||
subjectPatternError: |
|
||||
The subject "{subject}" found in the pull request title "{title}" didn't match the configured pattern. Please ensure that the subject doesn't start with an uppercase character.
|
||||
# If the PR contains one of these newline-delimited labels, the
|
||||
# validation is skipped. If you want to rerun the validation when
|
||||
# labels change, you might want to use the `labeled` and `unlabeled`
|
||||
# event triggers in your workflow.
|
||||
ignoreLabels: |
|
||||
release
|
||||
|
||||
2
.github/workflows/dev-build.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
outputs:
|
||||
dev-hash: ${{ steps.create-dev-hash.outputs.DEV_HASH }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
# 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
|
||||
|
||||
2
.github/workflows/label.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v6
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
|
||||
2
.github/workflows/nightly.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
outputs:
|
||||
nightly-hash: ${{ steps.create-nightly-hash.outputs.NIGHTLY_HASH }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
# 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
|
||||
|
||||
16
.github/workflows/release-ionic.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
release-core:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/publish-npm
|
||||
with:
|
||||
scope: '@ionic/core'
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
needs: [release-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore @ionic/docs built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
needs: [release-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore @ionic/core built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
needs: [release-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore @ionic/core built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
needs: [release-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore @ionic/core built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
needs: [release-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore @ionic/core built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
needs: [release-react]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore @ionic/core built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
@@ -194,7 +194,7 @@ jobs:
|
||||
needs: [release-vue]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore @ionic/core built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
|
||||
4
.github/workflows/release.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
needs: [release-ionic]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.IONITRON_TOKEN }}
|
||||
fetch-depth: 0
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
needs: [finalize-release]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
# Pull the latest version of the reference
|
||||
# branch instead of the revision that triggered
|
||||
# the workflow otherwise we won't get the commit
|
||||
|
||||
30
.github/workflows/stencil-nightly.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
build-core-with-stencil-nightly:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/test-core-lint
|
||||
|
||||
test-core-spec:
|
||||
needs: [build-core-with-stencil-nightly]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/build-vue
|
||||
|
||||
build-vue-router:
|
||||
needs: [build-vue]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/build-angular
|
||||
|
||||
build-angular-server:
|
||||
needs: [build-core-with-stencil-nightly]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/build-react
|
||||
|
||||
build-react-router:
|
||||
needs: [build-react]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/test-react-e2e
|
||||
with:
|
||||
app: ${{ matrix.apps }}
|
||||
|
||||
6
.github/workflows/update-screenshots.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
build-core:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v4
|
||||
# Normally, we could just push with the
|
||||
# default GITHUB_TOKEN, but that will
|
||||
# not cause the build workflow
|
||||
|
||||
137
CHANGELOG.md
@@ -3,143 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [8.7.5](https://github.com/ionic-team/ionic-framework/compare/v8.7.4...v8.7.5) (2025-09-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** allow sheet modals to skip focus trap ([#30689](https://github.com/ionic-team/ionic-framework/issues/30689)) ([a40d957](https://github.com/ionic-team/ionic-framework/commit/a40d957ad9c1897af365a91b45b00228a00d614c)), closes [#30684](https://github.com/ionic-team/ionic-framework/issues/30684)
|
||||
* **vue:** emit component-specific overlay events ([#30688](https://github.com/ionic-team/ionic-framework/issues/30688)) ([024d090](https://github.com/ionic-team/ionic-framework/commit/024d090122548e26ec2cdcfae4637dde8f288278)), closes [#30641](https://github.com/ionic-team/ionic-framework/issues/30641)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.4](https://github.com/ionic-team/ionic-framework/compare/v8.7.3...v8.7.4) (2025-09-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **input:** improve error text accessibility ([#30635](https://github.com/ionic-team/ionic-framework/issues/30635)) ([c339bc3](https://github.com/ionic-team/ionic-framework/commit/c339bc36827b62ef871325869a9a5db9b17ac785))
|
||||
* **overlays,picker:** remove invalid aria-hidden attribute ([#30563](https://github.com/ionic-team/ionic-framework/issues/30563)) ([49f96d7](https://github.com/ionic-team/ionic-framework/commit/49f96d7f1e9050a95e3e33a821c0467ecc0bed64)), closes [#30040](https://github.com/ionic-team/ionic-framework/issues/30040)
|
||||
* **segment-view:** scroll and select the right item when the component is in RTL context; ([#30675](https://github.com/ionic-team/ionic-framework/issues/30675)) ([66f517d](https://github.com/ionic-team/ionic-framework/commit/66f517d5b2154fff00b294a78f4107f057a580c6)), closes [#30079](https://github.com/ionic-team/ionic-framework/issues/30079)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.3](https://github.com/ionic-team/ionic-framework/compare/v8.7.2...v8.7.3) (2025-08-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **checkbox:** add aria attributes to ignore checkbox icon ([#30633](https://github.com/ionic-team/ionic-framework/issues/30633)) ([e9e6605](https://github.com/ionic-team/ionic-framework/commit/e9e6605862a05a46d26c26a144ed1cf22133a2b7)), closes [#30231](https://github.com/ionic-team/ionic-framework/issues/30231)
|
||||
* **refresher:** prevent focus-related scroll jumps on refresh ([#30636](https://github.com/ionic-team/ionic-framework/issues/30636)) ([1899b49](https://github.com/ionic-team/ionic-framework/commit/1899b49d252abc6003f763cea8db2a51efa941ec))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.2](https://github.com/ionic-team/ionic-framework/compare/v8.7.1...v8.7.2) (2025-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **reorder-group:** add children fallback for framework compatibility ([#30593](https://github.com/ionic-team/ionic-framework/issues/30593)) ([1cd81b9](https://github.com/ionic-team/ionic-framework/commit/1cd81b92301378d55bce63a01dfcf95a91c92652)), closes [#30592](https://github.com/ionic-team/ionic-framework/issues/30592)
|
||||
* **tabs:** add fallback to select tab if router integration fails ([#30599](https://github.com/ionic-team/ionic-framework/issues/30599)) ([a2e803a](https://github.com/ionic-team/ionic-framework/commit/a2e803a553dc58fc0e1599e515a56180a7ab263a)), closes [#30552](https://github.com/ionic-team/ionic-framework/issues/30552)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.1](https://github.com/ionic-team/ionic-framework/compare/v8.7.0...v8.7.1) (2025-07-31)
|
||||
|
||||
### Dependencies
|
||||
|
||||
* **stencil:** upgrade `@stencil/core` to version 4.36.2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [8.7.0](https://github.com/ionic-team/ionic-framework/compare/v8.6.7...v8.7.0) (2025-07-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **css:** add new css utility classes for display and flex utils ([#30567](https://github.com/ionic-team/ionic-framework/issues/30567)) ([75f6c05](https://github.com/ionic-team/ionic-framework/commit/75f6c05fb96313ef890cc80a229a3a3ed3d57460)), closes [#22469](https://github.com/ionic-team/ionic-framework/issues/22469)
|
||||
* **datetime:** add border property to highlightedDates ([#30534](https://github.com/ionic-team/ionic-framework/issues/30534)) ([d5627c7](https://github.com/ionic-team/ionic-framework/commit/d5627c73681faf658ea3b869f3fb04d708391eb9)), closes [#29833](https://github.com/ionic-team/ionic-framework/issues/29833)
|
||||
* **deps:** update ionicons to v8 ([#30390](https://github.com/ionic-team/ionic-framework/issues/30390)) ([74cd71a](https://github.com/ionic-team/ionic-framework/commit/74cd71af243183aa738d11b280e155bdfd652126)), closes [#30445](https://github.com/ionic-team/ionic-framework/issues/30445)
|
||||
* **modal:** add IonModalToken for injecting modal elements in Angular components ([#30474](https://github.com/ionic-team/ionic-framework/issues/30474)) ([30d1910](https://github.com/ionic-team/ionic-framework/commit/30d1910d6ea5428b414d0e127e7681f59426c538))
|
||||
* **reorder-group:** add ionReorderStart, ionReorderMove, ionReorderEnd events ([#30471](https://github.com/ionic-team/ionic-framework/issues/30471)) ([b154f4e](https://github.com/ionic-team/ionic-framework/commit/b154f4ed095890f57ccab539fd9217976a5466e5)), closes [#23148](https://github.com/ionic-team/ionic-framework/issues/23148) [#27614](https://github.com/ionic-team/ionic-framework/issues/27614)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.7](https://github.com/ionic-team/ionic-framework/compare/v8.6.6...v8.6.7) (2025-07-30)
|
||||
|
||||
### Dependencies
|
||||
|
||||
* **stencil:** downgrade `@stencil/core` to version 4.33.1
|
||||
|
||||
_Stencil has been downgraded due to an uncaught regression in Reorder._
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.6](https://github.com/ionic-team/ionic-framework/compare/v8.6.5...v8.6.6) (2025-07-30)
|
||||
|
||||
### Dependencies
|
||||
|
||||
* **stencil:** upgrade `@stencil/core` to version 4.36.2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.5](https://github.com/ionic-team/ionic-framework/compare/v8.6.4...v8.6.5) (2025-07-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **input-otp:** improve autofill detection and invalid character handling ([#30541](https://github.com/ionic-team/ionic-framework/issues/30541)) ([8b4023d](https://github.com/ionic-team/ionic-framework/commit/8b4023d520212c254395a5be6d3a76dcbee6f2da)), closes [#30459](https://github.com/ionic-team/ionic-framework/issues/30459)
|
||||
* **input:** prevent layout shift when hiding password toggle ([#30533](https://github.com/ionic-team/ionic-framework/issues/30533)) ([f1defba](https://github.com/ionic-team/ionic-framework/commit/f1defba2acb417c6f243b2902923d85efbb6f879)), closes [#29562](https://github.com/ionic-team/ionic-framework/issues/29562)
|
||||
* **item:** allow nested content to be conditionally interactive ([#30519](https://github.com/ionic-team/ionic-framework/issues/30519)) ([3f730ab](https://github.com/ionic-team/ionic-framework/commit/3f730ab1d77be54d1faf14168eee9e9dc41002d6)), closes [#29763](https://github.com/ionic-team/ionic-framework/issues/29763)
|
||||
* **modal:** dismiss child modals when parent is dismissed ([#30540](https://github.com/ionic-team/ionic-framework/issues/30540)) ([9b0099f](https://github.com/ionic-team/ionic-framework/commit/9b0099f462fda6d40b49dde1a1c97afbbbee2287)), closes [#30389](https://github.com/ionic-team/ionic-framework/issues/30389)
|
||||
* **modal:** dismiss modal when parent element is removed from DOM ([#30544](https://github.com/ionic-team/ionic-framework/issues/30544)) ([850338c](https://github.com/ionic-team/ionic-framework/commit/850338cbd5c76addbc2cc3068b93071dea14c0af)), closes [#30389](https://github.com/ionic-team/ionic-framework/issues/30389)
|
||||
* **modal:** improve card modal background transition from portrait to landscape ([#30551](https://github.com/ionic-team/ionic-framework/issues/30551)) ([d37b9b8](https://github.com/ionic-team/ionic-framework/commit/d37b9b8e468b7b2c9cda8b27fe7019bb905ad2bf))
|
||||
* **segment-view:** scroll to correct content when height is not set ([#30547](https://github.com/ionic-team/ionic-framework/issues/30547)) ([d14311f](https://github.com/ionic-team/ionic-framework/commit/d14311fb65ae3de7ba7578791ce1ea44f186c413)), closes [#30543](https://github.com/ionic-team/ionic-framework/issues/30543)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.4](https://github.com/ionic-team/ionic-framework/compare/v8.6.3...v8.6.4) (2025-07-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** support iOS card view transitions for viewport changes ([#30520](https://github.com/ionic-team/ionic-framework/issues/30520)) ([0fd9e82](https://github.com/ionic-team/ionic-framework/commit/0fd9e824508333a53175d7da5f681fc3126a2394)), closes [#30296](https://github.com/ionic-team/ionic-framework/issues/30296)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.3](https://github.com/ionic-team/ionic-framework/compare/v8.6.2...v8.6.3) (2025-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** update schematics to support Angular's latest build system ([#30525](https://github.com/ionic-team/ionic-framework/issues/30525)) ([08e3e7a](https://github.com/ionic-team/ionic-framework/commit/08e3e7ab5165baea668571af9845933b5befeb46)), closes [ionic-team/ionic-docs#2091](https://github.com/ionic-team/ionic-docs/issues/2091)
|
||||
* **modal:** add conditional tabIndex for handle cycling ([#30510](https://github.com/ionic-team/ionic-framework/issues/30510)) ([ee47660](https://github.com/ionic-team/ionic-framework/commit/ee47660745428e04c78cfef0555f3c5788959a8c))
|
||||
* **select:** focus the correct selected item in an action sheet interface with a header ([#30481](https://github.com/ionic-team/ionic-framework/issues/30481)) ([80a111c](https://github.com/ionic-team/ionic-framework/commit/80a111cffac70e831eb57e827301370163ef4e2a)), closes [#30480](https://github.com/ionic-team/ionic-framework/issues/30480)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.2](https://github.com/ionic-team/ionic-framework/compare/v8.6.1...v8.6.2) (2025-06-18)
|
||||
|
||||
|
||||
|
||||
@@ -3,140 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [8.7.5](https://github.com/ionic-team/ionic-framework/compare/v8.7.4...v8.7.5) (2025-09-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** allow sheet modals to skip focus trap ([#30689](https://github.com/ionic-team/ionic-framework/issues/30689)) ([a40d957](https://github.com/ionic-team/ionic-framework/commit/a40d957ad9c1897af365a91b45b00228a00d614c)), closes [#30684](https://github.com/ionic-team/ionic-framework/issues/30684)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.4](https://github.com/ionic-team/ionic-framework/compare/v8.7.3...v8.7.4) (2025-09-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **input:** improve error text accessibility ([#30635](https://github.com/ionic-team/ionic-framework/issues/30635)) ([c339bc3](https://github.com/ionic-team/ionic-framework/commit/c339bc36827b62ef871325869a9a5db9b17ac785))
|
||||
* **overlays,picker:** remove invalid aria-hidden attribute ([#30563](https://github.com/ionic-team/ionic-framework/issues/30563)) ([49f96d7](https://github.com/ionic-team/ionic-framework/commit/49f96d7f1e9050a95e3e33a821c0467ecc0bed64)), closes [#30040](https://github.com/ionic-team/ionic-framework/issues/30040)
|
||||
* **segment-view:** scroll and select the right item when the component is in RTL context; ([#30675](https://github.com/ionic-team/ionic-framework/issues/30675)) ([66f517d](https://github.com/ionic-team/ionic-framework/commit/66f517d5b2154fff00b294a78f4107f057a580c6)), closes [#30079](https://github.com/ionic-team/ionic-framework/issues/30079)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.3](https://github.com/ionic-team/ionic-framework/compare/v8.7.2...v8.7.3) (2025-08-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **checkbox:** add aria attributes to ignore checkbox icon ([#30633](https://github.com/ionic-team/ionic-framework/issues/30633)) ([e9e6605](https://github.com/ionic-team/ionic-framework/commit/e9e6605862a05a46d26c26a144ed1cf22133a2b7)), closes [#30231](https://github.com/ionic-team/ionic-framework/issues/30231)
|
||||
* **refresher:** prevent focus-related scroll jumps on refresh ([#30636](https://github.com/ionic-team/ionic-framework/issues/30636)) ([1899b49](https://github.com/ionic-team/ionic-framework/commit/1899b49d252abc6003f763cea8db2a51efa941ec))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.2](https://github.com/ionic-team/ionic-framework/compare/v8.7.1...v8.7.2) (2025-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **reorder-group:** add children fallback for framework compatibility ([#30593](https://github.com/ionic-team/ionic-framework/issues/30593)) ([1cd81b9](https://github.com/ionic-team/ionic-framework/commit/1cd81b92301378d55bce63a01dfcf95a91c92652)), closes [#30592](https://github.com/ionic-team/ionic-framework/issues/30592)
|
||||
* **tabs:** add fallback to select tab if router integration fails ([#30599](https://github.com/ionic-team/ionic-framework/issues/30599)) ([a2e803a](https://github.com/ionic-team/ionic-framework/commit/a2e803a553dc58fc0e1599e515a56180a7ab263a)), closes [#30552](https://github.com/ionic-team/ionic-framework/issues/30552)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.1](https://github.com/ionic-team/ionic-framework/compare/v8.7.0...v8.7.1) (2025-07-31)
|
||||
|
||||
### Dependencies
|
||||
|
||||
* **stencil:** upgrade `@stencil/core` to version 4.36.2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [8.7.0](https://github.com/ionic-team/ionic-framework/compare/v8.6.7...v8.7.0) (2025-07-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **css:** add new css utility classes for display and flex utils ([#30567](https://github.com/ionic-team/ionic-framework/issues/30567)) ([75f6c05](https://github.com/ionic-team/ionic-framework/commit/75f6c05fb96313ef890cc80a229a3a3ed3d57460)), closes [#22469](https://github.com/ionic-team/ionic-framework/issues/22469)
|
||||
* **datetime:** add border property to highlightedDates ([#30534](https://github.com/ionic-team/ionic-framework/issues/30534)) ([d5627c7](https://github.com/ionic-team/ionic-framework/commit/d5627c73681faf658ea3b869f3fb04d708391eb9)), closes [#29833](https://github.com/ionic-team/ionic-framework/issues/29833)
|
||||
* **deps:** update ionicons to v8 ([#30390](https://github.com/ionic-team/ionic-framework/issues/30390)) ([74cd71a](https://github.com/ionic-team/ionic-framework/commit/74cd71af243183aa738d11b280e155bdfd652126)), closes [#30445](https://github.com/ionic-team/ionic-framework/issues/30445)
|
||||
* **reorder-group:** add ionReorderStart, ionReorderMove, ionReorderEnd events ([#30471](https://github.com/ionic-team/ionic-framework/issues/30471)) ([b154f4e](https://github.com/ionic-team/ionic-framework/commit/b154f4ed095890f57ccab539fd9217976a5466e5)), closes [#23148](https://github.com/ionic-team/ionic-framework/issues/23148) [#27614](https://github.com/ionic-team/ionic-framework/issues/27614)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.7](https://github.com/ionic-team/ionic-framework/compare/v8.6.6...v8.6.7) (2025-07-30)
|
||||
|
||||
### Dependencies
|
||||
|
||||
* **stencil:** downgrade `@stencil/core` to version 4.33.1
|
||||
|
||||
_Stencil has been downgraded due to an uncaught regression in Reorder._
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.6](https://github.com/ionic-team/ionic-framework/compare/v8.6.5...v8.6.6) (2025-07-30)
|
||||
|
||||
### Dependencies
|
||||
|
||||
* **stencil:** upgrade `@stencil/core` to version 4.36.2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.5](https://github.com/ionic-team/ionic-framework/compare/v8.6.4...v8.6.5) (2025-07-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **input-otp:** improve autofill detection and invalid character handling ([#30541](https://github.com/ionic-team/ionic-framework/issues/30541)) ([8b4023d](https://github.com/ionic-team/ionic-framework/commit/8b4023d520212c254395a5be6d3a76dcbee6f2da)), closes [#30459](https://github.com/ionic-team/ionic-framework/issues/30459)
|
||||
* **input:** prevent layout shift when hiding password toggle ([#30533](https://github.com/ionic-team/ionic-framework/issues/30533)) ([f1defba](https://github.com/ionic-team/ionic-framework/commit/f1defba2acb417c6f243b2902923d85efbb6f879)), closes [#29562](https://github.com/ionic-team/ionic-framework/issues/29562)
|
||||
* **item:** allow nested content to be conditionally interactive ([#30519](https://github.com/ionic-team/ionic-framework/issues/30519)) ([3f730ab](https://github.com/ionic-team/ionic-framework/commit/3f730ab1d77be54d1faf14168eee9e9dc41002d6)), closes [#29763](https://github.com/ionic-team/ionic-framework/issues/29763)
|
||||
* **modal:** dismiss child modals when parent is dismissed ([#30540](https://github.com/ionic-team/ionic-framework/issues/30540)) ([9b0099f](https://github.com/ionic-team/ionic-framework/commit/9b0099f462fda6d40b49dde1a1c97afbbbee2287)), closes [#30389](https://github.com/ionic-team/ionic-framework/issues/30389)
|
||||
* **modal:** dismiss modal when parent element is removed from DOM ([#30544](https://github.com/ionic-team/ionic-framework/issues/30544)) ([850338c](https://github.com/ionic-team/ionic-framework/commit/850338cbd5c76addbc2cc3068b93071dea14c0af)), closes [#30389](https://github.com/ionic-team/ionic-framework/issues/30389)
|
||||
* **modal:** improve card modal background transition from portrait to landscape ([#30551](https://github.com/ionic-team/ionic-framework/issues/30551)) ([d37b9b8](https://github.com/ionic-team/ionic-framework/commit/d37b9b8e468b7b2c9cda8b27fe7019bb905ad2bf))
|
||||
* **segment-view:** scroll to correct content when height is not set ([#30547](https://github.com/ionic-team/ionic-framework/issues/30547)) ([d14311f](https://github.com/ionic-team/ionic-framework/commit/d14311fb65ae3de7ba7578791ce1ea44f186c413)), closes [#30543](https://github.com/ionic-team/ionic-framework/issues/30543)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.4](https://github.com/ionic-team/ionic-framework/compare/v8.6.3...v8.6.4) (2025-07-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** support iOS card view transitions for viewport changes ([#30520](https://github.com/ionic-team/ionic-framework/issues/30520)) ([0fd9e82](https://github.com/ionic-team/ionic-framework/commit/0fd9e824508333a53175d7da5f681fc3126a2394)), closes [#30296](https://github.com/ionic-team/ionic-framework/issues/30296)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.3](https://github.com/ionic-team/ionic-framework/compare/v8.6.2...v8.6.3) (2025-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** add conditional tabIndex for handle cycling ([#30510](https://github.com/ionic-team/ionic-framework/issues/30510)) ([ee47660](https://github.com/ionic-team/ionic-framework/commit/ee47660745428e04c78cfef0555f3c5788959a8c))
|
||||
* **select:** focus the correct selected item in an action sheet interface with a header ([#30481](https://github.com/ionic-team/ionic-framework/issues/30481)) ([80a111c](https://github.com/ionic-team/ionic-framework/commit/80a111cffac70e831eb57e827301370163ef4e2a)), closes [#30480](https://github.com/ionic-team/ionic-framework/issues/30480)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.6.2](https://github.com/ionic-team/ionic-framework/compare/v8.6.1...v8.6.2) (2025-06-18)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Get Playwright
|
||||
FROM mcr.microsoft.com/playwright:v1.55.1
|
||||
FROM mcr.microsoft.com/playwright:v1.53.1
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /ionic
|
||||
|
||||
@@ -1508,9 +1508,6 @@ ion-reorder-group,none
|
||||
ion-reorder-group,prop,disabled,boolean,true,false,false
|
||||
ion-reorder-group,method,complete,complete(listOrReorder?: boolean | any[]) => Promise<any>
|
||||
ion-reorder-group,event,ionItemReorder,ItemReorderEventDetail,true
|
||||
ion-reorder-group,event,ionReorderEnd,ReorderEndEventDetail,true
|
||||
ion-reorder-group,event,ionReorderMove,ReorderMoveEventDetail,true
|
||||
ion-reorder-group,event,ionReorderStart,void,true
|
||||
|
||||
ion-ripple-effect,shadow
|
||||
ion-ripple-effect,prop,type,"bounded" | "unbounded",'bounded',false,false
|
||||
|
||||
143
core/package-lock.json
generated
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "8.7.5",
|
||||
"version": "8.6.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/core",
|
||||
"version": "8.7.5",
|
||||
"version": "8.6.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@stencil/core": "4.36.2",
|
||||
"ionicons": "^8.0.13",
|
||||
"@stencil/core": "4.33.1",
|
||||
"ionicons": "^7.2.2",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -22,7 +22,7 @@
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@playwright/test": "^1.53.2",
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/angular-output-target": "^0.10.0",
|
||||
@@ -663,36 +663,36 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@capacitor/core": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.3.tgz",
|
||||
"integrity": "sha512-wCWr8fQ9Wxn0466vPg7nMn0tivbNVjNy1yL4GvDSIZuZx7UpU2HeVGNe9QjN/quEd+YLRFeKEBLBw619VqUiNg==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.0.tgz",
|
||||
"integrity": "sha512-P6NnjoHyobZgTjynlZSn27d0SUj6j38inlNxFnKZr9qwU7/r6+0Sg2nWkGkIH/pMmXHsvGD8zVe6KUq1UncIjA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@capacitor/haptics": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-7.0.2.tgz",
|
||||
"integrity": "sha512-vqfeEM6s2zMgLjpITCTUIy7P/hadq/Gr5E/RClFgMJPB41Y5FsqOKD+j85/uwh8N2cf/aWaPeXUmjnTzJbEB2g==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-7.0.1.tgz",
|
||||
"integrity": "sha512-ewZmspE5krgDUj5ZvUDcfNZvgerAIr+3bDSk6DLzyvBZ/dYmr/tMLu5H6WtYaaKYZJ32aZAudGpIal5epDyBYA==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@capacitor/keyboard": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.3.tgz",
|
||||
"integrity": "sha512-BIBKjmky5rOYNhvYhNeDi0MMvjwYZ6YF9JoCYcGKvKY+XLJKtezsEL78XfOlgWZBkbfR8uq3tzktY6PqgoYLKA==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.1.tgz",
|
||||
"integrity": "sha512-Gi064vOARMac+x9/DmEFeywN9oAETMf3OYsMuYm9gA8SvdsDJ3QJqMoFnSEIORYXe21Jzt2SIEdLlpT65P/b2g==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@capacitor/status-bar": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.3.tgz",
|
||||
"integrity": "sha512-JyRpVnKwHij9hgPWolF6PK+HT3e2HSPjN11/h2OmKxq8GAdPGARFLv+97eZl0pvuvm0Kka/LpiLb5whXISBg7Q==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.1.tgz",
|
||||
"integrity": "sha512-iDv3mXYo9CdxYRVwt3/pRyuk25p7Sn4GfaS/zMZyVIqTzsvKLCIIH3GdKK+ta+nsNcAVpCw/t5jFEBt1D18ctA==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": ">=7.0.0"
|
||||
@@ -1715,12 +1715,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz",
|
||||
"integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==",
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz",
|
||||
"integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.55.1"
|
||||
"playwright": "1.53.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -1914,9 +1914,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@stencil/core": {
|
||||
"version": "4.36.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.36.2.tgz",
|
||||
"integrity": "sha512-PRFSpxNzX9Oi0Wfh02asztN9Sgev/MacfZwmd+VVyE6ZxW+a/kEpAYZhzGAmE+/aKVOGYuug7R9SulanYGxiDQ==",
|
||||
"version": "4.33.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.33.1.tgz",
|
||||
"integrity": "sha512-12k9xhAJBkpg598it+NRmaYIdEe6TSnsL/v6/KRXDcUyTK11VYwZQej2eHnMWtqot+znJ+GNTqb5YbiXi+5Low==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"stencil": "bin/stencil"
|
||||
@@ -3474,9 +3474,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
@@ -5711,12 +5711,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ionicons": {
|
||||
"version": "8.0.13",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-8.0.13.tgz",
|
||||
"integrity": "sha512-2QQVyG2P4wszne79jemMjWYLp0DBbDhr4/yFroPCxvPP1wtMxgdIV3l5n+XZ5E9mgoXU79w7yTWpm2XzJsISxQ==",
|
||||
"license": "MIT",
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.2.2.tgz",
|
||||
"integrity": "sha512-I3iYIfc9Q9FRifWyFSwTAvbEABWlWY32i0sAVDDPGYnaIZVugkLCZFbEcrphW6ixVPg8tt1oLwalo/JJwbEqnA==",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^4.35.3"
|
||||
"@stencil/core": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphabetical": {
|
||||
@@ -8593,12 +8592,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
|
||||
"integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz",
|
||||
"integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.55.1"
|
||||
"playwright-core": "1.53.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -8611,9 +8610,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
|
||||
"integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz",
|
||||
"integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
@@ -11102,32 +11101,32 @@
|
||||
"dev": true
|
||||
},
|
||||
"@capacitor/core": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.3.tgz",
|
||||
"integrity": "sha512-wCWr8fQ9Wxn0466vPg7nMn0tivbNVjNy1yL4GvDSIZuZx7UpU2HeVGNe9QjN/quEd+YLRFeKEBLBw619VqUiNg==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.0.tgz",
|
||||
"integrity": "sha512-P6NnjoHyobZgTjynlZSn27d0SUj6j38inlNxFnKZr9qwU7/r6+0Sg2nWkGkIH/pMmXHsvGD8zVe6KUq1UncIjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"@capacitor/haptics": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-7.0.2.tgz",
|
||||
"integrity": "sha512-vqfeEM6s2zMgLjpITCTUIy7P/hadq/Gr5E/RClFgMJPB41Y5FsqOKD+j85/uwh8N2cf/aWaPeXUmjnTzJbEB2g==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-7.0.1.tgz",
|
||||
"integrity": "sha512-ewZmspE5krgDUj5ZvUDcfNZvgerAIr+3bDSk6DLzyvBZ/dYmr/tMLu5H6WtYaaKYZJ32aZAudGpIal5epDyBYA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@capacitor/keyboard": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.3.tgz",
|
||||
"integrity": "sha512-BIBKjmky5rOYNhvYhNeDi0MMvjwYZ6YF9JoCYcGKvKY+XLJKtezsEL78XfOlgWZBkbfR8uq3tzktY6PqgoYLKA==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.1.tgz",
|
||||
"integrity": "sha512-Gi064vOARMac+x9/DmEFeywN9oAETMf3OYsMuYm9gA8SvdsDJ3QJqMoFnSEIORYXe21Jzt2SIEdLlpT65P/b2g==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@capacitor/status-bar": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.3.tgz",
|
||||
"integrity": "sha512-JyRpVnKwHij9hgPWolF6PK+HT3e2HSPjN11/h2OmKxq8GAdPGARFLv+97eZl0pvuvm0Kka/LpiLb5whXISBg7Q==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.1.tgz",
|
||||
"integrity": "sha512-iDv3mXYo9CdxYRVwt3/pRyuk25p7Sn4GfaS/zMZyVIqTzsvKLCIIH3GdKK+ta+nsNcAVpCw/t5jFEBt1D18ctA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
@@ -11863,12 +11862,12 @@
|
||||
}
|
||||
},
|
||||
"@playwright/test": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz",
|
||||
"integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==",
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz",
|
||||
"integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"playwright": "1.55.1"
|
||||
"playwright": "1.53.2"
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-node-resolve": {
|
||||
@@ -11984,9 +11983,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"@stencil/core": {
|
||||
"version": "4.36.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.36.2.tgz",
|
||||
"integrity": "sha512-PRFSpxNzX9Oi0Wfh02asztN9Sgev/MacfZwmd+VVyE6ZxW+a/kEpAYZhzGAmE+/aKVOGYuug7R9SulanYGxiDQ==",
|
||||
"version": "4.33.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.33.1.tgz",
|
||||
"integrity": "sha512-12k9xhAJBkpg598it+NRmaYIdEe6TSnsL/v6/KRXDcUyTK11VYwZQej2eHnMWtqot+znJ+GNTqb5YbiXi+5Low==",
|
||||
"requires": {
|
||||
"@rollup/rollup-darwin-arm64": "4.34.9",
|
||||
"@rollup/rollup-darwin-x64": "4.34.9",
|
||||
@@ -13076,9 +13075,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk-template": {
|
||||
@@ -14745,11 +14744,11 @@
|
||||
}
|
||||
},
|
||||
"ionicons": {
|
||||
"version": "8.0.13",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-8.0.13.tgz",
|
||||
"integrity": "sha512-2QQVyG2P4wszne79jemMjWYLp0DBbDhr4/yFroPCxvPP1wtMxgdIV3l5n+XZ5E9mgoXU79w7yTWpm2XzJsISxQ==",
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.2.2.tgz",
|
||||
"integrity": "sha512-I3iYIfc9Q9FRifWyFSwTAvbEABWlWY32i0sAVDDPGYnaIZVugkLCZFbEcrphW6ixVPg8tt1oLwalo/JJwbEqnA==",
|
||||
"requires": {
|
||||
"@stencil/core": "^4.35.3"
|
||||
"@stencil/core": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"is-alphabetical": {
|
||||
@@ -16812,19 +16811,19 @@
|
||||
}
|
||||
},
|
||||
"playwright": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
|
||||
"integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz",
|
||||
"integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "2.3.2",
|
||||
"playwright-core": "1.55.1"
|
||||
"playwright-core": "1.53.2"
|
||||
}
|
||||
},
|
||||
"playwright-core": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
|
||||
"integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz",
|
||||
"integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "8.7.5",
|
||||
"version": "8.6.2",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -31,8 +31,8 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@stencil/core": "4.36.2",
|
||||
"ionicons": "^8.0.13",
|
||||
"@stencil/core": "4.33.1",
|
||||
"ionicons": "^7.2.2",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -44,7 +44,7 @@
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@playwright/test": "^1.53.2",
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/angular-output-target": "^0.10.0",
|
||||
|
||||
24
core/src/components.d.ts
vendored
@@ -30,7 +30,7 @@ import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAct
|
||||
import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
|
||||
import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
|
||||
import { RefresherEventDetail } from "./components/refresher/refresher-interface";
|
||||
import { ItemReorderEventDetail, ReorderEndEventDetail, ReorderMoveEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
import { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
import { NavigationHookCallback } from "./components/route/route-interface";
|
||||
import { SearchbarChangeEventDetail, SearchbarInputEventDetail } from "./components/searchbar/searchbar-interface";
|
||||
import { SegmentChangeEventDetail, SegmentValue } from "./components/segment/segment-interface";
|
||||
@@ -68,7 +68,7 @@ export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAct
|
||||
export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
|
||||
export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
|
||||
export { RefresherEventDetail } from "./components/refresher/refresher-interface";
|
||||
export { ItemReorderEventDetail, ReorderEndEventDetail, ReorderMoveEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
export { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
export { NavigationHookCallback } from "./components/route/route-interface";
|
||||
export { SearchbarChangeEventDetail, SearchbarInputEventDetail } from "./components/searchbar/searchbar-interface";
|
||||
export { SegmentChangeEventDetail, SegmentValue } from "./components/segment/segment-interface";
|
||||
@@ -2783,7 +2783,7 @@ export namespace Components {
|
||||
}
|
||||
interface IonReorderGroup {
|
||||
/**
|
||||
* Completes the reorder operation. Must be called by the `ionReorderEnd` event. If a list of items is passed, the list will be reordered and returned in the proper order. If no parameters are passed or if `true` is passed in, the reorder will complete and the item will remain in the position it was dragged to. If `false` is passed, the reorder will complete and the item will bounce back to its original position.
|
||||
* Completes the reorder operation. Must be called by the `ionItemReorder` event. If a list of items is passed, the list will be reordered and returned in the proper order. If no parameters are passed or if `true` is passed in, the reorder will complete and the item will remain in the position it was dragged to. If `false` is passed, the reorder will complete and the item will bounce back to its original position.
|
||||
* @param listOrReorder A list of items to be sorted and returned in the new order or a boolean of whether or not the reorder should reposition the item.
|
||||
*/
|
||||
"complete": (listOrReorder?: boolean | any[]) => Promise<any>;
|
||||
@@ -4769,9 +4769,6 @@ declare global {
|
||||
};
|
||||
interface HTMLIonReorderGroupElementEventMap {
|
||||
"ionItemReorder": ItemReorderEventDetail;
|
||||
"ionReorderStart": void;
|
||||
"ionReorderMove": ReorderMoveEventDetail;
|
||||
"ionReorderEnd": ReorderEndEventDetail;
|
||||
}
|
||||
interface HTMLIonReorderGroupElement extends Components.IonReorderGroup, HTMLStencilElement {
|
||||
addEventListener<K extends keyof HTMLIonReorderGroupElementEventMap>(type: K, listener: (this: HTMLIonReorderGroupElement, ev: IonReorderGroupCustomEvent<HTMLIonReorderGroupElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
@@ -8056,22 +8053,9 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"disabled"?: boolean;
|
||||
/**
|
||||
* Event that needs to be listened to in order to complete the reorder action.
|
||||
* @deprecated Use `ionReorderEnd` instead. If you are accessing `event.detail.from` or `event.detail.to` and relying on them being different you should now add checks as they are always emitted in `ionReorderEnd`, even when they are the same.
|
||||
* Event that needs to be listened to in order to complete the reorder action. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action.
|
||||
*/
|
||||
"onIonItemReorder"?: (event: IonReorderGroupCustomEvent<ItemReorderEventDetail>) => void;
|
||||
/**
|
||||
* Event that is emitted when the reorder gesture ends. The from and to properties are always available, regardless of if the reorder gesture moved the item. If the item did not change from its start position, the from and to properties will be the same. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action.
|
||||
*/
|
||||
"onIonReorderEnd"?: (event: IonReorderGroupCustomEvent<ReorderEndEventDetail>) => void;
|
||||
/**
|
||||
* Event that is emitted as the reorder gesture moves.
|
||||
*/
|
||||
"onIonReorderMove"?: (event: IonReorderGroupCustomEvent<ReorderMoveEventDetail>) => void;
|
||||
/**
|
||||
* Event that is emitted when the reorder gesture starts.
|
||||
*/
|
||||
"onIonReorderStart"?: (event: IonReorderGroupCustomEvent<void>) => void;
|
||||
}
|
||||
interface IonRippleEffect {
|
||||
/**
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -125,7 +125,7 @@
|
||||
|
||||
<ion-toolbar color="dark">
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button class="ion-display-none"></ion-back-button>
|
||||
<ion-back-button class="ion-hide"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Hidden</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1018 B |
|
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 1004 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -328,7 +328,7 @@ export class Checkbox implements ComponentInterface {
|
||||
{this.renderHintText()}
|
||||
</div>
|
||||
<div class="native-wrapper">
|
||||
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container" aria-hidden="true">
|
||||
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container">
|
||||
{path}
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
@@ -22,15 +22,15 @@ export type DatetimePresentation = 'date-time' | 'time-date' | 'date' | 'time' |
|
||||
|
||||
export type TitleSelectedDatesFormatter = (selectedDates: string[]) => string;
|
||||
|
||||
/**
|
||||
* DatetimeHighlightStyle must include textColor, backgroundColor, or border.
|
||||
* It cannot be an empty object.
|
||||
*/
|
||||
export type DatetimeHighlightStyle = {
|
||||
textColor?: string;
|
||||
backgroundColor?: string;
|
||||
border?: string;
|
||||
} & ({ textColor: string } | { backgroundColor: string } | { border: string });
|
||||
export type DatetimeHighlightStyle =
|
||||
| {
|
||||
textColor: string;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
| {
|
||||
textColor?: string;
|
||||
backgroundColor: string;
|
||||
};
|
||||
|
||||
export type DatetimeHighlight = { date: string } & DatetimeHighlightStyle;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTa
|
||||
import { startFocusVisible } from '@utils/focus-visible';
|
||||
import { getElementRoot, raf, renderHiddenInput } from '@utils/helpers';
|
||||
import { printIonError, printIonWarning } from '@utils/logging';
|
||||
import { FOCUS_TRAP_DISABLE_CLASS } from '@utils/overlays';
|
||||
import { isRTL } from '@utils/rtl';
|
||||
import { createColorClasses } from '@utils/theme';
|
||||
import { caretDownSharp, caretUpSharp, chevronBack, chevronDown, chevronForward } from 'ionicons/icons';
|
||||
@@ -1599,7 +1598,7 @@ export class Datetime implements ComponentInterface {
|
||||
forcePresentation === 'time-date'
|
||||
? [this.renderTimePickerColumns(forcePresentation), this.renderDatePickerColumns(forcePresentation)]
|
||||
: [this.renderDatePickerColumns(forcePresentation), this.renderTimePickerColumns(forcePresentation)];
|
||||
return <ion-picker class={FOCUS_TRAP_DISABLE_CLASS}>{renderArray}</ion-picker>;
|
||||
return <ion-picker>{renderArray}</ion-picker>;
|
||||
}
|
||||
|
||||
private renderDatePickerColumns(forcePresentation: string) {
|
||||
@@ -2336,7 +2335,6 @@ export class Datetime implements ComponentInterface {
|
||||
`${dateStyle ? dateStyle.backgroundColor : ''}`,
|
||||
'important'
|
||||
);
|
||||
el.style.setProperty('border', `${dateStyle ? dateStyle.border : ''}`, 'important');
|
||||
}
|
||||
}}
|
||||
tabindex="-1"
|
||||
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@@ -5,8 +5,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('datetime: custom'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/datetime/test/custom`, config);
|
||||
|
||||
await page.locator('.datetime-ready').last().waitFor();
|
||||
});
|
||||
|
||||
test('should allow styling wheel style datetimes', async ({ page }) => {
|
||||
@@ -32,13 +30,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test('should allow styling calendar days in grid style datetimes', async ({ page }) => {
|
||||
const datetime = page.locator('#custom-calendar-days');
|
||||
|
||||
// Wait for calendar days to be rendered
|
||||
await page.waitForFunction(() => {
|
||||
const datetime = document.querySelector('#custom-calendar-days');
|
||||
const calendarDays = datetime?.shadowRoot?.querySelectorAll('.calendar-day');
|
||||
return calendarDays && calendarDays.length > 0;
|
||||
});
|
||||
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-custom-calendar-days`));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
const customDatetime = document.querySelector('#custom-calendar-days');
|
||||
|
||||
// Mock the current day to always have the same screenshots
|
||||
const mockToday = '2023-06-10T16:22:00.000Z';
|
||||
const mockToday = '2023-06-10T16:22';
|
||||
Date = class extends Date {
|
||||
constructor(...args) {
|
||||
if (args.length === 0) {
|
||||
|
||||
@@ -22,23 +22,11 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
|
||||
await expect(monthYearToggle).toContainText('January 2022');
|
||||
|
||||
// Click to open the picker
|
||||
await monthYearToggle.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
// Wait for the picker to be open
|
||||
await page.locator('.month-year-picker-open').waitFor();
|
||||
|
||||
// Wait a bit for the picker to fully load
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
|
||||
// Click on February
|
||||
await monthColumnItems.filter({ hasText: 'February' }).click();
|
||||
|
||||
// Wait for changes
|
||||
await ionChange.next();
|
||||
// February
|
||||
await monthColumnItems.nth(1).click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(monthYearToggle).toContainText('February 2022');
|
||||
@@ -50,23 +38,13 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
const datetime = page.locator('ion-datetime');
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
|
||||
// Click to open the picker
|
||||
await monthYearToggle.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
// Wait for the picker to be open
|
||||
await page.locator('.month-year-picker-open').waitFor();
|
||||
// February
|
||||
await monthColumnItems.nth(1).click();
|
||||
|
||||
// Wait a bit for the picker to fully load
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
// Click on February
|
||||
await monthColumnItems.filter({ hasText: 'February' }).click();
|
||||
|
||||
// Wait for changes
|
||||
await ionChange.next();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(ionChange).toHaveReceivedEventTimes(1);
|
||||
await expect(datetime).toHaveJSProperty('value', '2022-02-28');
|
||||
});
|
||||
|
||||
@@ -21,19 +21,16 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
date: '2023-01-01', // ensure selected date style overrides highlight
|
||||
textColor: '#800080',
|
||||
backgroundColor: '#ffc0cb',
|
||||
border: '2px solid purple',
|
||||
},
|
||||
{
|
||||
date: '2023-01-02',
|
||||
textColor: '#b22222',
|
||||
backgroundColor: '#fa8072',
|
||||
border: '2px solid purple',
|
||||
},
|
||||
{
|
||||
date: '2023-01-03',
|
||||
textColor: '#0000ff',
|
||||
backgroundColor: '#add8e6',
|
||||
border: '2px solid purple',
|
||||
},
|
||||
];
|
||||
});
|
||||
@@ -55,7 +52,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
return {
|
||||
textColor: '#b22222',
|
||||
backgroundColor: '#fa8072',
|
||||
border: '2px solid purple',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,7 +59,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
return {
|
||||
textColor: '#800080',
|
||||
backgroundColor: '#ffc0cb',
|
||||
border: '2px solid purple',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,7 +66,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
return {
|
||||
textColor: '#0000ff',
|
||||
backgroundColor: '#add8e6',
|
||||
border: '2px solid purple',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,7 +77,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-highlightedDates-callback`));
|
||||
});
|
||||
|
||||
test('should render highlights correctly when only using only one color property', async ({ page }) => {
|
||||
test('should render highlights correctly when only using one color or the other', async ({ page }) => {
|
||||
const datetime = page.locator('ion-datetime');
|
||||
|
||||
await datetime.evaluate((el: HTMLIonDatetimeElement) => {
|
||||
@@ -96,10 +90,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
date: '2023-01-03',
|
||||
textColor: '#0000ff',
|
||||
},
|
||||
{
|
||||
date: '2023-01-04',
|
||||
border: '2px solid purple',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 18 KiB |
@@ -78,10 +78,6 @@
|
||||
textColor: 'blue',
|
||||
backgroundColor: 'lightblue',
|
||||
},
|
||||
{
|
||||
date: '2023-01-07',
|
||||
border: '2px dotted red',
|
||||
},
|
||||
];
|
||||
|
||||
document.querySelector('#withCallback').highlightedDates = (isoString) => {
|
||||
@@ -107,7 +103,6 @@
|
||||
date: new Date().toISOString().split('T')[0],
|
||||
textColor: 'purple',
|
||||
backgroundColor: 'pink',
|
||||
border: '2px solid purple',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
@@ -207,7 +207,6 @@ export const getHighlightStyles = (
|
||||
return {
|
||||
textColor: matchingHighlight.textColor,
|
||||
backgroundColor: matchingHighlight.backgroundColor,
|
||||
border: matchingHighlight.border,
|
||||
} as DatetimeHighlightStyle;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Infinite Scroll - Top</title>
|
||||
<title>Infinite Scroll - Basic</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
@@ -18,7 +18,7 @@
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Infinite Scroll - Top</ion-title>
|
||||
<ion-title>Infinite Scroll - Basic</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
</ion-infinite-scroll-content>
|
||||
</ion-infinite-scroll>
|
||||
|
||||
<div id="list"></div>
|
||||
|
||||
<button onclick="toggleInfiniteScroll()" class="expand">Toggle InfiniteScroll</button>
|
||||
|
||||
<ion-list id="list"></ion-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
@@ -46,26 +46,17 @@
|
||||
console.log('Loading data...');
|
||||
await wait(500);
|
||||
infiniteScroll.complete();
|
||||
appendItems(true);
|
||||
appendItems();
|
||||
// Custom event consumed in the e2e tests
|
||||
window.dispatchEvent(new CustomEvent('ionInfiniteComplete'));
|
||||
|
||||
console.log('Done');
|
||||
});
|
||||
|
||||
function appendItems(newItems = false) {
|
||||
const randomColor =
|
||||
'#' +
|
||||
Math.floor(Math.random() * 16777215)
|
||||
.toString(16)
|
||||
.padStart(6, '0');
|
||||
|
||||
function appendItems() {
|
||||
for (var i = 0; i < 30; i++) {
|
||||
const el = document.createElement('ion-item');
|
||||
el.textContent = `Item ${1 + i}`;
|
||||
if (newItems) {
|
||||
el.style.borderLeft = `4px solid ${randomColor}`;
|
||||
}
|
||||
el.textContent = `${1 + i}`;
|
||||
list.prepend(el);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ export class InputOTP implements ComponentInterface {
|
||||
|
||||
@State() private inputValues: string[] = [];
|
||||
@State() hasFocus = false;
|
||||
@State() private previousInputValues: string[] = [];
|
||||
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
||||
@@ -337,7 +336,6 @@ export class InputOTP implements ComponentInterface {
|
||||
});
|
||||
// Update the value without emitting events
|
||||
this.value = this.inputValues.join('');
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -527,12 +525,19 @@ export class InputOTP implements ComponentInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles keyboard navigation for the OTP component.
|
||||
* Handles keyboard navigation and input for the OTP component.
|
||||
*
|
||||
* Navigation:
|
||||
* - Backspace: Clears current input and moves to previous box if empty
|
||||
* - Arrow Left/Right: Moves focus between input boxes
|
||||
* - Tab: Allows normal tab navigation between components
|
||||
*
|
||||
* Input Behavior:
|
||||
* - Validates input against the allowed pattern
|
||||
* - When entering a key in a filled box:
|
||||
* - Shifts existing values right if there is room
|
||||
* - Updates the value of the input group
|
||||
* - Prevents default behavior to avoid automatic focus shift
|
||||
*/
|
||||
private onKeyDown = (index: number) => (event: KeyboardEvent) => {
|
||||
const { length } = this;
|
||||
@@ -590,32 +595,34 @@ export class InputOTP implements ComponentInterface {
|
||||
// Let all tab events proceed normally
|
||||
return;
|
||||
}
|
||||
|
||||
// If the input box contains a value and the key being
|
||||
// entered is a valid key for the input box update the value
|
||||
// and shift the values to the right if there is room.
|
||||
if (this.inputValues[index] && this.validKeyPattern.test(event.key)) {
|
||||
if (!this.inputValues[length - 1]) {
|
||||
for (let i = length - 1; i > index; i--) {
|
||||
this.inputValues[i] = this.inputValues[i - 1];
|
||||
this.inputRefs[i].value = this.inputValues[i] || '';
|
||||
}
|
||||
}
|
||||
this.inputValues[index] = event.key;
|
||||
this.inputRefs[index].value = event.key;
|
||||
this.updateValue(event);
|
||||
|
||||
// Prevent default to avoid the browser from
|
||||
// automatically moving the focus to the next input
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes all input scenarios for each input box.
|
||||
*
|
||||
* This function manages:
|
||||
* 1. Autofill handling
|
||||
* 2. Input validation
|
||||
* 3. Full selection replacement or typing in an empty box
|
||||
* 4. Inserting in the middle with available space (shifting)
|
||||
* 5. Single character replacement
|
||||
*/
|
||||
private onInput = (index: number) => (event: InputEvent) => {
|
||||
const { length, validKeyPattern } = this;
|
||||
const input = event.target as HTMLInputElement;
|
||||
const value = input.value;
|
||||
const previousValue = this.previousInputValues[index] || '';
|
||||
const value = (event.target as HTMLInputElement).value;
|
||||
|
||||
// 1. Autofill handling
|
||||
// If the length of the value increases by more than 1 from the previous
|
||||
// value, treat this as autofill. This is to prevent the case where the
|
||||
// user is typing a single character into an input box containing a value
|
||||
// as that will trigger this function with a value length of 2 characters.
|
||||
const isAutofill = value.length - previousValue.length > 1;
|
||||
if (isAutofill) {
|
||||
// Distribute valid characters across input boxes
|
||||
// If the value is longer than 1 character (autofill), split it into
|
||||
// characters and filter out invalid ones
|
||||
if (value.length > 1) {
|
||||
const validChars = value
|
||||
.split('')
|
||||
.filter((char) => validKeyPattern.test(char))
|
||||
@@ -632,10 +639,8 @@ export class InputOTP implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
this.inputValues[i] = validChars[i] || '';
|
||||
this.inputRefs[i].value = validChars[i] || '';
|
||||
}
|
||||
// Update the value of the input group and emit the input change event
|
||||
this.value = validChars.join('');
|
||||
this.updateValue(event);
|
||||
|
||||
// Focus the first empty input box or the last input box if all boxes
|
||||
@@ -646,85 +651,23 @@ export class InputOTP implements ComponentInterface {
|
||||
this.inputRefs[nextIndex]?.focus();
|
||||
}, 20);
|
||||
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Input validation
|
||||
// If the character entered is invalid (does not match the pattern),
|
||||
// restore the previous value and exit
|
||||
if (value.length > 0 && !validKeyPattern.test(value[value.length - 1])) {
|
||||
input.value = this.inputValues[index] || '';
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
// Only allow input if it matches the pattern
|
||||
if (value.length > 0 && !validKeyPattern.test(value)) {
|
||||
this.inputRefs[index].value = '';
|
||||
this.inputValues[index] = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Full selection replacement or typing in an empty box
|
||||
// If the user selects all text in the input box and types, or if the
|
||||
// input box is empty, replace only this input box. If the box is empty,
|
||||
// move to the next box, otherwise stay focused on this box.
|
||||
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === value.length;
|
||||
const isEmpty = !this.inputValues[index];
|
||||
if (isAllSelected || isEmpty) {
|
||||
this.inputValues[index] = value;
|
||||
input.value = value;
|
||||
this.updateValue(event);
|
||||
this.focusNext(index);
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Inserting in the middle with available space (shifting)
|
||||
// If typing in a filled input box and there are empty boxes at the end,
|
||||
// shift all values starting at the current box to the right, and insert
|
||||
// the new character at the current box.
|
||||
const hasAvailableBoxAtEnd = this.inputValues[this.inputValues.length - 1] === '';
|
||||
if (this.inputValues[index] && hasAvailableBoxAtEnd && value.length === 2) {
|
||||
// Get the inserted character (from event or by diffing value/previousValue)
|
||||
let newChar = (event as InputEvent).data;
|
||||
if (!newChar) {
|
||||
newChar = value.split('').find((c, i) => c !== previousValue[i]) || value[value.length - 1];
|
||||
}
|
||||
// Validate the new character before shifting
|
||||
if (!validKeyPattern.test(newChar)) {
|
||||
input.value = this.inputValues[index] || '';
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
return;
|
||||
}
|
||||
// Shift values right from the end to the insertion point
|
||||
for (let i = this.inputValues.length - 1; i > index; i--) {
|
||||
this.inputValues[i] = this.inputValues[i - 1];
|
||||
this.inputRefs[i].value = this.inputValues[i] || '';
|
||||
}
|
||||
this.inputValues[index] = newChar;
|
||||
this.inputRefs[index].value = newChar;
|
||||
this.updateValue(event);
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. Single character replacement
|
||||
// Handles replacing a single character in a box containing a value based
|
||||
// on the cursor position. We need the cursor position to determine which
|
||||
// character was the last character typed. For example, if the user types "2"
|
||||
// in an input box with the cursor at the beginning of the value of "6",
|
||||
// the value will be "26", but we want to grab the "2" as the last character
|
||||
// typed.
|
||||
const cursorPos = input.selectionStart ?? value.length;
|
||||
const newCharIndex = cursorPos - 1;
|
||||
const newChar = value[newCharIndex] ?? value[0];
|
||||
|
||||
// Check if the new character is valid before updating the value
|
||||
if (!validKeyPattern.test(newChar)) {
|
||||
input.value = this.inputValues[index] || '';
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
return;
|
||||
}
|
||||
|
||||
this.inputValues[index] = newChar;
|
||||
input.value = newChar;
|
||||
// For single character input, fill the current box
|
||||
this.inputValues[index] = value;
|
||||
this.updateValue(event);
|
||||
this.previousInputValues = [...this.inputValues];
|
||||
|
||||
if (value.length > 0) {
|
||||
this.focusNext(index);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -768,8 +711,12 @@ export class InputOTP implements ComponentInterface {
|
||||
|
||||
// Focus the next empty input after pasting
|
||||
// If all boxes are filled, focus the last input
|
||||
const nextEmptyIndex = validChars.length < length ? validChars.length : length - 1;
|
||||
inputRefs[nextEmptyIndex]?.focus();
|
||||
const nextEmptyIndex = validChars.length;
|
||||
if (nextEmptyIndex < length) {
|
||||
inputRefs[nextEmptyIndex]?.focus();
|
||||
} else {
|
||||
inputRefs[length - 1]?.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -442,67 +442,6 @@ configs({ modes: ['ios'] }).forEach(({ title, config }) => {
|
||||
|
||||
await verifyInputValues(inputOtp, ['1', '9', '3', '']);
|
||||
});
|
||||
|
||||
test('should replace the last value when typing one more than the length', async ({ page }) => {
|
||||
await page.setContent(`<ion-input-otp>Description</ion-input-otp>`, config);
|
||||
|
||||
const inputOtp = page.locator('ion-input-otp');
|
||||
const firstInput = inputOtp.locator('input').first();
|
||||
await firstInput.focus();
|
||||
|
||||
await page.keyboard.type('12345');
|
||||
|
||||
await verifyInputValues(inputOtp, ['1', '2', '3', '5']);
|
||||
});
|
||||
|
||||
test('should replace the last value when typing one more than the length and the type is text', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/30459',
|
||||
});
|
||||
|
||||
await page.setContent(`<ion-input-otp type="text">Description</ion-input-otp>`, config);
|
||||
|
||||
const inputOtp = page.locator('ion-input-otp');
|
||||
const firstInput = inputOtp.locator('input').first();
|
||||
await firstInput.focus();
|
||||
|
||||
await page.keyboard.type('abcde');
|
||||
|
||||
await verifyInputValues(inputOtp, ['a', 'b', 'c', 'e']);
|
||||
});
|
||||
|
||||
test('should not insert or shift when typing an invalid character before a number', async ({ page }) => {
|
||||
await page.setContent(`<ion-input-otp value="12">Description</ion-input-otp>`, config);
|
||||
|
||||
const inputOtp = page.locator('ion-input-otp');
|
||||
const firstInput = inputOtp.locator('input').first();
|
||||
await firstInput.focus();
|
||||
|
||||
// Move cursor to the start of the first input
|
||||
await firstInput.evaluate((el: HTMLInputElement) => el.setSelectionRange(0, 0));
|
||||
|
||||
await page.keyboard.type('w');
|
||||
|
||||
await verifyInputValues(inputOtp, ['1', '2', '', '']);
|
||||
});
|
||||
|
||||
test('should not insert or shift when typing an invalid character after a number', async ({ page }) => {
|
||||
await page.setContent(`<ion-input-otp value="12">Description</ion-input-otp>`, config);
|
||||
|
||||
const inputOtp = page.locator('ion-input-otp');
|
||||
const firstInput = inputOtp.locator('input').first();
|
||||
await firstInput.focus();
|
||||
|
||||
// Move cursor to the end of the first input
|
||||
await firstInput.evaluate((el: HTMLInputElement) => el.setSelectionRange(1, 1));
|
||||
|
||||
await page.keyboard.type('w');
|
||||
|
||||
await verifyInputValues(inputOtp, ['1', '2', '', '']);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe(title('input-otp: autofill functionality'), () => {
|
||||
@@ -521,53 +460,6 @@ configs({ modes: ['ios'] }).forEach(({ title, config }) => {
|
||||
await expect(lastInput).toBeFocused();
|
||||
});
|
||||
|
||||
test('should handle autofill correctly when all characters are the same', async ({ page }) => {
|
||||
await page.setContent(`<ion-input-otp>Description</ion-input-otp>`, config);
|
||||
|
||||
const firstInput = page.locator('ion-input-otp input').first();
|
||||
await firstInput.focus();
|
||||
|
||||
await simulateAutofill(firstInput, '1111');
|
||||
|
||||
const inputOtp = page.locator('ion-input-otp');
|
||||
await verifyInputValues(inputOtp, ['1', '1', '1', '1']);
|
||||
|
||||
const lastInput = page.locator('ion-input-otp input').last();
|
||||
await expect(lastInput).toBeFocused();
|
||||
});
|
||||
|
||||
test('should handle autofill correctly when length is 2', async ({ page }) => {
|
||||
await page.setContent(`<ion-input-otp length="2">Description</ion-input-otp>`, config);
|
||||
|
||||
const firstInput = page.locator('ion-input-otp input').first();
|
||||
await firstInput.focus();
|
||||
|
||||
await simulateAutofill(firstInput, '12');
|
||||
|
||||
const inputOtp = page.locator('ion-input-otp');
|
||||
await verifyInputValues(inputOtp, ['1', '2']);
|
||||
|
||||
const lastInput = page.locator('ion-input-otp input').last();
|
||||
await expect(lastInput).toBeFocused();
|
||||
});
|
||||
|
||||
test('should handle autofill correctly when length is 2 after typing 1 character', async ({ page }) => {
|
||||
await page.setContent(`<ion-input-otp length="2">Description</ion-input-otp>`, config);
|
||||
|
||||
await page.keyboard.type('1');
|
||||
|
||||
const secondInput = page.locator('ion-input-otp input').nth(1);
|
||||
await secondInput.focus();
|
||||
|
||||
await simulateAutofill(secondInput, '22');
|
||||
|
||||
const inputOtp = page.locator('ion-input-otp');
|
||||
await verifyInputValues(inputOtp, ['2', '2']);
|
||||
|
||||
const lastInput = page.locator('ion-input-otp input').last();
|
||||
await expect(lastInput).toBeFocused();
|
||||
});
|
||||
|
||||
test('should handle autofill correctly when it exceeds the length', async ({ page }) => {
|
||||
await page.setContent(`<ion-input-otp>Description</ion-input-otp>`, config);
|
||||
|
||||
|
||||
@@ -95,8 +95,6 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
||||
el.separators = [2, 3];
|
||||
});
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(await hasSeparatorAfter(page, 0)).toBe(false);
|
||||
await expect(await hasSeparatorAfter(page, 1)).toBe(true);
|
||||
await expect(await hasSeparatorAfter(page, 2)).toBe(true);
|
||||
|
||||
@@ -618,5 +618,5 @@
|
||||
*/
|
||||
:host([disabled]) ::slotted(ion-input-password-toggle),
|
||||
:host([readonly]) ::slotted(ion-input-password-toggle) {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -79,15 +79,8 @@ export class Input implements ComponentInterface {
|
||||
*/
|
||||
@State() hasFocus = false;
|
||||
|
||||
/**
|
||||
* Track validation state for proper aria-live announcements
|
||||
*/
|
||||
@State() isInvalid = false;
|
||||
|
||||
@Element() el!: HTMLIonInputElement;
|
||||
|
||||
private validationObserver?: MutationObserver;
|
||||
|
||||
/**
|
||||
* The color to use from your application's color palette.
|
||||
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
|
||||
@@ -403,16 +396,6 @@ export class Input implements ComponentInterface {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the input is in an invalid state based on Ionic validation classes
|
||||
*/
|
||||
private checkInvalidState(): boolean {
|
||||
const hasIonTouched = this.el.classList.contains('ion-touched');
|
||||
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
||||
|
||||
return hasIonTouched && hasIonInvalid;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const { el } = this;
|
||||
|
||||
@@ -423,26 +406,6 @@ export class Input implements ComponentInterface {
|
||||
() => this.labelSlot
|
||||
);
|
||||
|
||||
// Watch for class changes to update validation state
|
||||
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
||||
this.validationObserver = new MutationObserver(() => {
|
||||
const newIsInvalid = this.checkInvalidState();
|
||||
if (this.isInvalid !== newIsInvalid) {
|
||||
this.isInvalid = newIsInvalid;
|
||||
// Force a re-render to update aria-describedby immediately
|
||||
forceUpdate(this);
|
||||
}
|
||||
});
|
||||
|
||||
this.validationObserver.observe(el, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
}
|
||||
|
||||
// Always set initial state
|
||||
this.isInvalid = this.checkInvalidState();
|
||||
|
||||
this.debounceChanged();
|
||||
if (Build.isBrowser) {
|
||||
document.dispatchEvent(
|
||||
@@ -488,12 +451,6 @@ export class Input implements ComponentInterface {
|
||||
this.notchController.destroy();
|
||||
this.notchController = undefined;
|
||||
}
|
||||
|
||||
// Clean up validation observer to prevent memory leaks
|
||||
if (this.validationObserver) {
|
||||
this.validationObserver.disconnect();
|
||||
this.validationObserver = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -669,22 +626,22 @@ export class Input implements ComponentInterface {
|
||||
* Renders the helper text or error text values
|
||||
*/
|
||||
private renderHintText() {
|
||||
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
||||
const { helperText, errorText, helperTextId, errorTextId } = this;
|
||||
|
||||
return [
|
||||
<div id={helperTextId} class="helper-text" aria-live="polite">
|
||||
{!isInvalid ? helperText : null}
|
||||
<div id={helperTextId} class="helper-text">
|
||||
{helperText}
|
||||
</div>,
|
||||
<div id={errorTextId} class="error-text" role="alert">
|
||||
{isInvalid ? errorText : null}
|
||||
<div id={errorTextId} class="error-text">
|
||||
{errorText}
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
|
||||
private getHintTextID(): string | undefined {
|
||||
const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this;
|
||||
const { el, helperText, errorText, helperTextId, errorTextId } = this;
|
||||
|
||||
if (isInvalid && errorText) {
|
||||
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
|
||||
return errorTextId;
|
||||
}
|
||||
|
||||
@@ -907,7 +864,7 @@ export class Input implements ComponentInterface {
|
||||
onCompositionstart={this.onCompositionStart}
|
||||
onCompositionend={this.onCompositionEnd}
|
||||
aria-describedby={this.getHintTextID()}
|
||||
aria-invalid={this.isInvalid ? 'true' : undefined}
|
||||
aria-invalid={this.getHintTextID() === this.errorTextId}
|
||||
{...this.inheritedAttributes}
|
||||
/>
|
||||
{this.clearInput && !readonly && !disabled && (
|
||||
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -26,69 +26,5 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-disabled`));
|
||||
});
|
||||
|
||||
test('should maintain consistent height when password toggle is hidden on disabled input', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/29562',
|
||||
});
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label="Password" type="password" value="password123">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
|
||||
// Get the height when input is enabled
|
||||
const enabledHeight = await input.boundingBox().then((box) => box?.height);
|
||||
|
||||
// Disable the input
|
||||
await input.evaluate((el) => el.setAttribute('disabled', 'true'));
|
||||
await page.waitForChanges();
|
||||
|
||||
// Get the height when input is disabled
|
||||
const disabledHeight = await input.boundingBox().then((box) => box?.height);
|
||||
|
||||
// Verify heights are the same
|
||||
expect(enabledHeight).toBe(disabledHeight);
|
||||
});
|
||||
|
||||
test('should maintain consistent height when password toggle is hidden on readonly input', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/29562',
|
||||
});
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label="Password" type="password" value="password123">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
|
||||
// Get the height when input is enabled
|
||||
const enabledHeight = await input.boundingBox().then((box) => box?.height);
|
||||
|
||||
// Make the input readonly
|
||||
await input.evaluate((el) => el.setAttribute('readonly', 'true'));
|
||||
await page.waitForChanges();
|
||||
|
||||
// Get the height when input is readonly
|
||||
const readonlyHeight = await input.boundingBox().then((box) => box?.height);
|
||||
|
||||
// Verify heights are the same
|
||||
expect(enabledHeight).toBe(readonlyHeight);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Input - Validation</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: var(--ion-color-step-600);
|
||||
|
||||
margin-top: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.validation-info {
|
||||
margin: 20px;
|
||||
padding: 10px;
|
||||
background: var(--ion-color-light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Input - Validation Test</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<div class="validation-info">
|
||||
<h2>Screen Reader Testing Instructions:</h2>
|
||||
<ol>
|
||||
<li>Enable your screen reader (VoiceOver, NVDA, JAWS, etc.)</li>
|
||||
<li>Tab through the form fields</li>
|
||||
<li>When you tab away from an empty required field, the error should be announced immediately</li>
|
||||
<li>The error text should be announced BEFORE the next field is announced</li>
|
||||
<li>Test in Chrome, Safari, and Firefox to verify consistent behavior</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div>
|
||||
<h2>Required Email Field</h2>
|
||||
<ion-input
|
||||
id="email-input"
|
||||
type="email"
|
||||
label="Email"
|
||||
label-placement="floating"
|
||||
fill="outline"
|
||||
placeholder="Enter your email"
|
||||
helper-text="We'll never share your email"
|
||||
error-text="Please enter a valid email address"
|
||||
required
|
||||
></ion-input>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Required Name Field</h2>
|
||||
<ion-input
|
||||
id="name-input"
|
||||
type="text"
|
||||
label="Full Name"
|
||||
label-placement="floating"
|
||||
fill="outline"
|
||||
placeholder="Enter your full name"
|
||||
helper-text="First and last name"
|
||||
error-text="Name is required"
|
||||
required
|
||||
></ion-input>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Phone Number (Pattern Validation)</h2>
|
||||
<ion-input
|
||||
id="phone-input"
|
||||
type="tel"
|
||||
label="Phone"
|
||||
label-placement="floating"
|
||||
fill="outline"
|
||||
placeholder="(555) 555-5555"
|
||||
pattern="^\(\d{3}\) \d{3}-\d{4}$"
|
||||
helper-text="Format: (555) 555-5555"
|
||||
error-text="Please enter a valid phone number"
|
||||
required
|
||||
></ion-input>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Password (Min Length)</h2>
|
||||
<ion-input
|
||||
id="password-input"
|
||||
type="password"
|
||||
label="Password"
|
||||
label-placement="floating"
|
||||
fill="outline"
|
||||
placeholder="Enter password"
|
||||
minlength="8"
|
||||
helper-text="At least 8 characters"
|
||||
error-text="Password must be at least 8 characters"
|
||||
required
|
||||
></ion-input>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Age (Number Range)</h2>
|
||||
<ion-input
|
||||
id="age-input"
|
||||
type="number"
|
||||
label="Age"
|
||||
label-placement="floating"
|
||||
fill="outline"
|
||||
placeholder="Enter your age"
|
||||
min="18"
|
||||
max="120"
|
||||
helper-text="Must be 18 or older"
|
||||
error-text="Please enter a valid age (18-120)"
|
||||
required
|
||||
></ion-input>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Optional Field (No Validation)</h2>
|
||||
<ion-input
|
||||
id="optional-input"
|
||||
type="text"
|
||||
label="Optional Info"
|
||||
label-placement="floating"
|
||||
fill="outline"
|
||||
placeholder="This field is optional"
|
||||
helper-text="You can skip this field"
|
||||
></ion-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ion-padding">
|
||||
<ion-button id="submit-btn" expand="block" disabled>Submit Form</ion-button>
|
||||
<ion-button id="reset-btn" expand="block" fill="outline">Reset Form</ion-button>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
// Simple validation logic
|
||||
const inputs = document.querySelectorAll('ion-input');
|
||||
const submitBtn = document.getElementById('submit-btn');
|
||||
const resetBtn = document.getElementById('reset-btn');
|
||||
|
||||
// Track which fields have been touched
|
||||
const touchedFields = new Set();
|
||||
|
||||
// Validation functions
|
||||
const validators = {
|
||||
'email-input': (value) => {
|
||||
if (!value) return false;
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
||||
},
|
||||
'name-input': (value) => {
|
||||
return value && value.trim().length > 0;
|
||||
},
|
||||
'phone-input': (value) => {
|
||||
if (!value) return false;
|
||||
return /^\(\d{3}\) \d{3}-\d{4}$/.test(value);
|
||||
},
|
||||
'password-input': (value) => {
|
||||
return value && value.length >= 8;
|
||||
},
|
||||
'age-input': (value) => {
|
||||
if (!value) return false;
|
||||
const age = parseInt(value);
|
||||
return age >= 18 && age <= 120;
|
||||
},
|
||||
'optional-input': () => true, // Always valid
|
||||
};
|
||||
|
||||
function validateField(input) {
|
||||
const inputId = input.id;
|
||||
const value = input.value;
|
||||
const isValid = validators[inputId] ? validators[inputId](value) : true;
|
||||
|
||||
// Only show validation state if field has been touched
|
||||
if (touchedFields.has(inputId)) {
|
||||
if (isValid) {
|
||||
input.classList.remove('ion-invalid');
|
||||
input.classList.add('ion-valid');
|
||||
} else {
|
||||
input.classList.remove('ion-valid');
|
||||
input.classList.add('ion-invalid');
|
||||
}
|
||||
input.classList.add('ion-touched');
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
let allValid = true;
|
||||
inputs.forEach((input) => {
|
||||
if (input.id !== 'optional-input') {
|
||||
const isValid = validateField(input);
|
||||
if (!isValid) {
|
||||
allValid = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
submitBtn.disabled = !allValid;
|
||||
return allValid;
|
||||
}
|
||||
|
||||
// Add event listeners
|
||||
inputs.forEach((input) => {
|
||||
// Mark as touched on blur
|
||||
input.addEventListener('ionBlur', (e) => {
|
||||
touchedFields.add(input.id);
|
||||
validateField(input);
|
||||
validateForm();
|
||||
|
||||
const isInvalid = input.classList.contains('ion-invalid');
|
||||
if (isInvalid) {
|
||||
console.log('Field marked invalid:', input.label, input.errorText);
|
||||
}
|
||||
});
|
||||
|
||||
// Validate on input
|
||||
input.addEventListener('ionInput', (e) => {
|
||||
if (touchedFields.has(input.id)) {
|
||||
validateField(input);
|
||||
validateForm();
|
||||
}
|
||||
});
|
||||
|
||||
// Also validate on focus loss via native blur
|
||||
input.addEventListener('focusout', (e) => {
|
||||
// Small delay to ensure Ionic's classes are updated
|
||||
setTimeout(() => {
|
||||
touchedFields.add(input.id);
|
||||
validateField(input);
|
||||
validateForm();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
|
||||
// Reset button
|
||||
resetBtn.addEventListener('click', () => {
|
||||
inputs.forEach((input) => {
|
||||
input.value = '';
|
||||
input.classList.remove('ion-valid', 'ion-invalid', 'ion-touched');
|
||||
});
|
||||
touchedFields.clear();
|
||||
submitBtn.disabled = true;
|
||||
});
|
||||
|
||||
// Submit button
|
||||
submitBtn.addEventListener('click', () => {
|
||||
if (validateForm()) {
|
||||
alert('Form submitted successfully!');
|
||||
}
|
||||
});
|
||||
|
||||
// Initial setup
|
||||
validateForm();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.5 KiB |