Compare commits

..

25 Commits

Author SHA1 Message Date
ionitron
53caa22212 v6.3.3 2022-10-26 13:47:18 +00:00
Amanda Johnston
6bbb372980 test(menu-toggle): add basic Playwright test (#26164) 2022-10-24 15:22:35 -05:00
Brandy Carney
0fa68cfa61 test(footer): migrate to playwright (#26094) 2022-10-19 14:25:35 -04:00
Sean Perkins
8ec350ae65 fix(react): inline overlays can be conditionally rendered (#26111)
Resolves #25590

Co-authored-by: liamdebeasi <liamdebeasi@users.noreply.github.com>
2022-10-17 16:43:32 -04:00
Liam DeBeasi
74be79e9d8 fix(dependencies): latest patch is installed (#26148)
resolves #26137
2022-10-17 16:21:29 -04:00
Liam DeBeasi
51ab5f67b5 fix(datetime): empty string is treated as no value (#26131)
resolves #26116
2022-10-17 15:01:27 -04:00
Liam DeBeasi
0548fe8588 fix(datetime): values are adjusted to be in bounds (#26125)
resolves #25894, resolves #25708
2022-10-17 14:19:02 -04:00
Liam DeBeasi
479d56b3b2 fix(datetime): preferWheel can now show title (#26101)
resolves #26095
2022-10-17 13:12:45 -04:00
Liam DeBeasi
d4d569ac09 fix(many): haptics only fire on supported platforms (#26130)
resolves #26109
2022-10-17 12:07:38 -04:00
Liam DeBeasi
ffcf8ec4e0 merge release-6.3.2
Release 6.3.2
2022-10-17 10:39:44 -05:00
ionitron
5d77fde503 chore(): update package lock files 2022-10-17 14:15:29 +00:00
ionitron
1a6595b895 v6.3.2 2022-10-17 14:10:40 +00:00
Amanda Johnston
7e5c03ae40 test(item-sliding): skip flaky gesture test for now (#26136) 2022-10-14 13:07:12 -05:00
Liam DeBeasi
2192409123 test(alert): a11y test is not flaky (#26133) 2022-10-14 12:57:57 -04:00
Liam DeBeasi
0e7f7fcdca test(range): skip flaky test for now (#26134) 2022-10-14 12:39:26 -04:00
Amanda Johnston
fbbebee7be test(item-sliding): migrate tests to Playwright (#26092) 2022-10-14 11:15:18 -05:00
Liam DeBeasi
d76a24dd9e fix(datetime): selecting days updates value (#26121) 2022-10-13 16:34:28 -04:00
Liam DeBeasi
d60973b244 fix(vue): routing components define child components (#26107) 2022-10-13 13:35:15 -04:00
Liam DeBeasi
5e4892676c chore(ci): update actions versions (#26100) 2022-10-13 13:04:01 -04:00
Liam DeBeasi
04df45a443 fix(datetime): header renders correct date (#26120)
resolves #26116
2022-10-13 12:17:21 -04:00
Liam DeBeasi
256b03f12a fix(modal): sheet modal dismisses correctly (#26110)
resolves #26108
2022-10-13 09:58:06 -04:00
Liam DeBeasi
ff6df69a67 chore(core): lint.fix command now runs prettier (#26114) 2022-10-12 18:25:32 -04:00
Liam DeBeasi
bbd6c9ae6c merge release-6.3.1
v6.3.1
2022-10-12 10:06:55 -05:00
Liam DeBeasi
61cc278f05 chore(ci): release script bumps package-lock files 2022-10-12 14:12:40 +00:00
Liam DeBeasi
01840741ce chore(): bump package-lock files 2022-10-12 14:12:17 +00:00
187 changed files with 1651 additions and 1682 deletions

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic Angular Server'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x

View File

@@ -3,19 +3,19 @@ description: 'Build Ionic Angular'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json')}}-v2
- name: Cache Angular Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: angular-node-modules
with:

View File

@@ -3,16 +3,16 @@ description: 'Build Ionic Core'
runs:
using: 'composite'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
# Checkout the latest commit in this branch
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,12 +3,12 @@ description: 'Build Ionic React Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,12 +3,12 @@ description: 'Build Ionic React'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,12 +3,12 @@ description: 'Builds Ionic Vue Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,12 +3,12 @@ description: 'Build Ionic Vue'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

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

View File

@@ -6,12 +6,12 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,7 +3,7 @@ description: 'Test Core Clean Build'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x

View File

@@ -3,12 +3,12 @@ description: 'Test Core E2E'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,12 +3,12 @@ description: 'Test Core Lint'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -10,12 +10,12 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:
@@ -72,7 +72,7 @@ runs:
working-directory: ./core
- name: Archive Updated Screenshots
if: inputs.update == 'true' && steps.test-and-update.outputs.hasUpdatedScreenshots == 'true'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: updated-screenshots-${{ inputs.shard }}-${{ inputs.totalShards }}
path: UpdatedScreenshots-${{ inputs.shard }}-${{ inputs.totalShards }}.zip

View File

@@ -3,12 +3,12 @@ description: 'Test Core Spec'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,12 +3,12 @@ description: 'Test React E2E'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -3,12 +3,12 @@ description: 'Test React Router '
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -6,12 +6,12 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Cache Core Node Modules
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: core-node-modules
with:

View File

@@ -7,10 +7,10 @@ on:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v1
- uses: actions/setup-node@v3
with:
node-version: 16.x
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
path: ./artifacts
- name: Extract Archives

View File

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

View File

@@ -15,7 +15,7 @@ jobs:
build-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
# Checkout the latest commit in this branch
ref: ${{ github.event.pull_request.head.sha }}
@@ -25,28 +25,28 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-core-clean-build
test-core-lint:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-core-lint
test-core-spec:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-core-spec
test-core-e2e:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-core-e2e
test-core-screenshot:
@@ -67,7 +67,7 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-core-screenshot
with:
shard: ${{ matrix.shard }}
@@ -95,14 +95,14 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-vue
build-vue-router:
needs: [build-vue]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-vue-router
test-vue-e2e:
@@ -113,7 +113,7 @@ jobs:
needs: [build-vue, build-vue-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-vue-e2e
with:
app: ${{ matrix.apps }}
@@ -131,14 +131,14 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-angular
build-angular-server:
needs: [build-angular]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-angular-server
test-angular-e2e:
@@ -149,7 +149,7 @@ jobs:
needs: [build-angular, build-angular-server]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-angular-e2e
with:
app: ${{ matrix.apps }}
@@ -167,26 +167,26 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-react
build-react-router:
needs: [build-react]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-react-router
test-react-router-e2e:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-react-router-e2e
test-react-e2e:
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-react-e2e

View File

@@ -9,8 +9,8 @@ jobs:
outputs:
version: ${{ steps.dev-build.outputs.version }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Dependencies

View File

@@ -10,10 +10,10 @@ jobs:
nightly-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Dependencies

View File

@@ -25,8 +25,8 @@ jobs:
build-ionic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Configure Identity

View File

@@ -24,11 +24,11 @@ jobs:
build-ionic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
token: ${{ secrets.IONITRON_TOKEN }}
fetch-depth: 0
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Configure Identity
@@ -55,6 +55,15 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
# Lerna does not automatically bump versions
# of Ionic dependencies that have changed,
# so we do that here.
- name: Bump Package Lock
run: |
lerna exec "npm install --package-lock-only --legacy-peer-deps"
git add .
git commit -m "chore(): update package lock files"
git push
# Purge the JSDeliver CDN cache so
# component playgrounds always load
# the latest version of Ionic.

View File

@@ -7,7 +7,7 @@ jobs:
build-core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-core
test-core-screenshot:
@@ -28,7 +28,7 @@ jobs:
needs: [build-core]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-core-screenshot
with:
shard: ${{ matrix.shard }}
@@ -39,7 +39,7 @@ jobs:
runs-on: ubuntu-latest
needs: [test-core-screenshot]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
# Normally, we could just push with the
# default GITHUB_TOKEN, but that will
# not cause the build workflow

View File

@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.3](https://github.com/ionic-team/ionic-framework/compare/v6.3.2...v6.3.3) (2022-10-26)
### Bug Fixes
* **datetime:** empty string is treated as no value ([#26131](https://github.com/ionic-team/ionic-framework/issues/26131)) ([51ab5f6](https://github.com/ionic-team/ionic-framework/commit/51ab5f67b50013c0ed8ca3160d6dfc56bc269f2a)), closes [#26116](https://github.com/ionic-team/ionic-framework/issues/26116)
* **datetime:** preferWheel can now show title ([#26101](https://github.com/ionic-team/ionic-framework/issues/26101)) ([479d56b](https://github.com/ionic-team/ionic-framework/commit/479d56b3b26d45bfd03d4095458c37ed00485c54)), closes [#26095](https://github.com/ionic-team/ionic-framework/issues/26095)
* **datetime:** values are adjusted to be in bounds ([#26125](https://github.com/ionic-team/ionic-framework/issues/26125)) ([0548fe8](https://github.com/ionic-team/ionic-framework/commit/0548fe858854f0187e0dfe00efaec142cd5bb6cf)), closes [#25894](https://github.com/ionic-team/ionic-framework/issues/25894) [#25708](https://github.com/ionic-team/ionic-framework/issues/25708)
* **dependencies:** latest patch is installed ([#26148](https://github.com/ionic-team/ionic-framework/issues/26148)) ([74be79e](https://github.com/ionic-team/ionic-framework/commit/74be79e9d81fd5431ae2fc442fd6387cf37b2015)), closes [#26137](https://github.com/ionic-team/ionic-framework/issues/26137)
* **many:** haptics only fire on supported platforms ([#26130](https://github.com/ionic-team/ionic-framework/issues/26130)) ([d4d569a](https://github.com/ionic-team/ionic-framework/commit/d4d569ac09ab25ab5a490825cf1fc655fe97bb87)), closes [#26109](https://github.com/ionic-team/ionic-framework/issues/26109)
* **react:** inline overlays can be conditionally rendered ([#26111](https://github.com/ionic-team/ionic-framework/issues/26111)) ([8ec350a](https://github.com/ionic-team/ionic-framework/commit/8ec350ae652095ae29e2f02a7f105cb709a72583)), closes [#25590](https://github.com/ionic-team/ionic-framework/issues/25590)
## [6.3.2](https://github.com/ionic-team/ionic-framework/compare/v6.3.1...v6.3.2) (2022-10-17)
### Bug Fixes
* **datetime:** header renders correct date ([#26120](https://github.com/ionic-team/ionic-framework/issues/26120)) ([04df45a](https://github.com/ionic-team/ionic-framework/commit/04df45a443e4faeea644daa76dc509fea0d24ca2)), closes [#26116](https://github.com/ionic-team/ionic-framework/issues/26116)
* **datetime:** selecting days updates value ([#26121](https://github.com/ionic-team/ionic-framework/issues/26121)) ([d76a24d](https://github.com/ionic-team/ionic-framework/commit/d76a24dd9e485a2f3cc517231bbb1dab51fa1fd3))
* **modal:** sheet modal dismisses correctly ([#26110](https://github.com/ionic-team/ionic-framework/issues/26110)) ([256b03f](https://github.com/ionic-team/ionic-framework/commit/256b03f12a57c2b5904d9017e4fa93b11eea8fc7)), closes [#26108](https://github.com/ionic-team/ionic-framework/issues/26108)
* **vue:** routing components define child components ([#26107](https://github.com/ionic-team/ionic-framework/issues/26107)) ([d60973b](https://github.com/ionic-team/ionic-framework/commit/d60973b2449b29a982b752a98b10d2b043ecff2f))
## [6.3.1](https://github.com/ionic-team/ionic-framework/compare/v6.3.0...v6.3.1) (2022-10-12)

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.3](https://github.com/ionic-team/ionic/compare/v6.3.2...v6.3.3) (2022-10-26)
### Bug Fixes
* **datetime:** empty string is treated as no value ([#26131](https://github.com/ionic-team/ionic/issues/26131)) ([51ab5f6](https://github.com/ionic-team/ionic/commit/51ab5f67b50013c0ed8ca3160d6dfc56bc269f2a)), closes [#26116](https://github.com/ionic-team/ionic/issues/26116)
* **dependencies:** latest patch is installed ([#26148](https://github.com/ionic-team/ionic/issues/26148)) ([74be79e](https://github.com/ionic-team/ionic/commit/74be79e9d81fd5431ae2fc442fd6387cf37b2015)), closes [#26137](https://github.com/ionic-team/ionic/issues/26137)
## [6.3.2](https://github.com/ionic-team/ionic/compare/v6.3.1...v6.3.2) (2022-10-17)
**Note:** Version bump only for package @ionic/angular
## [6.3.1](https://github.com/ionic-team/ionic/compare/v6.3.0...v6.3.1) (2022-10-12)
**Note:** Version bump only for package @ionic/angular

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "6.3.1",
"version": "6.3.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "6.3.1",
"version": "6.3.3",
"license": "MIT",
"dependencies": {
"@ionic/core": "^6.3.1",
"@ionic/core": "^6.3.3",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
},
@@ -1023,9 +1023,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.0.tgz",
"integrity": "sha512-6MR0hOdIM6sQyZyXez3/jWAnHScrZKaQxzGT2XiXXIpBrKztNdeAhqDLXoxbDU1PLyDnXXPWBzT6xZ698gG67g==",
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.2.tgz",
"integrity": "sha512-L4xqJyixmGApwYc5fQgGoK80wXGCrbjL8vGfeNbjYqxxP0ZIKGAhURPoMAtSTqLLK9gdhh4Mv6gw4gNKvxodPA==",
"dependencies": {
"@stencil/core": "^2.18.0",
"ionicons": "^6.0.3",
@@ -7951,9 +7951,9 @@
"dev": true
},
"@ionic/core": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.0.tgz",
"integrity": "sha512-6MR0hOdIM6sQyZyXez3/jWAnHScrZKaQxzGT2XiXXIpBrKztNdeAhqDLXoxbDU1PLyDnXXPWBzT6xZ698gG67g==",
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.3.2.tgz",
"integrity": "sha512-L4xqJyixmGApwYc5fQgGoK80wXGCrbjL8vGfeNbjYqxxP0ZIKGAhURPoMAtSTqLLK9gdhh4Mv6gw4gNKvxodPA==",
"requires": {
"@stencil/core": "^2.18.0",
"ionicons": "^6.0.3",

View File

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

View File

@@ -19,7 +19,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
writeValue(value: any): void {
/**
* TODO for Ionic 6:
* TODO FW-2646
* Change `value == null ? '' : value;`
* to `value`. This was a fix for IE9, but IE9
* is no longer supported; however, this change

View File

@@ -1,3 +1,4 @@
import * as d from './proxies';
export const DIRECTIVES = [
@@ -76,5 +77,5 @@ export const DIRECTIVES = [
d.IonThumbnail,
d.IonTitle,
d.IonToggle,
d.IonToolbar,
d.IonToolbar
];

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.3.3](https://github.com/ionic-team/ionic/compare/v6.3.2...v6.3.3) (2022-10-26)
### Bug Fixes
* **datetime:** empty string is treated as no value ([#26131](https://github.com/ionic-team/ionic/issues/26131)) ([51ab5f6](https://github.com/ionic-team/ionic/commit/51ab5f67b50013c0ed8ca3160d6dfc56bc269f2a)), closes [#26116](https://github.com/ionic-team/ionic/issues/26116)
* **datetime:** preferWheel can now show title ([#26101](https://github.com/ionic-team/ionic/issues/26101)) ([479d56b](https://github.com/ionic-team/ionic/commit/479d56b3b26d45bfd03d4095458c37ed00485c54)), closes [#26095](https://github.com/ionic-team/ionic/issues/26095)
* **datetime:** values are adjusted to be in bounds ([#26125](https://github.com/ionic-team/ionic/issues/26125)) ([0548fe8](https://github.com/ionic-team/ionic/commit/0548fe858854f0187e0dfe00efaec142cd5bb6cf)), closes [#25894](https://github.com/ionic-team/ionic/issues/25894) [#25708](https://github.com/ionic-team/ionic/issues/25708)
* **many:** haptics only fire on supported platforms ([#26130](https://github.com/ionic-team/ionic/issues/26130)) ([d4d569a](https://github.com/ionic-team/ionic/commit/d4d569ac09ab25ab5a490825cf1fc655fe97bb87)), closes [#26109](https://github.com/ionic-team/ionic/issues/26109)
## [6.3.2](https://github.com/ionic-team/ionic/compare/v6.3.1...v6.3.2) (2022-10-17)
### Bug Fixes
* **datetime:** header renders correct date ([#26120](https://github.com/ionic-team/ionic/issues/26120)) ([04df45a](https://github.com/ionic-team/ionic/commit/04df45a443e4faeea644daa76dc509fea0d24ca2)), closes [#26116](https://github.com/ionic-team/ionic/issues/26116)
* **datetime:** selecting days updates value ([#26121](https://github.com/ionic-team/ionic/issues/26121)) ([d76a24d](https://github.com/ionic-team/ionic/commit/d76a24dd9e485a2f3cc517231bbb1dab51fa1fd3))
* **modal:** sheet modal dismisses correctly ([#26110](https://github.com/ionic-team/ionic/issues/26110)) ([256b03f](https://github.com/ionic-team/ionic/commit/256b03f12a57c2b5904d9017e4fa93b11eea8fc7)), closes [#26108](https://github.com/ionic-team/ionic/issues/26108)
## [6.3.1](https://github.com/ionic-team/ionic/compare/v6.3.0...v6.3.1) (2022-10-12)

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "6.3.1",
"version": "6.3.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "6.3.1",
"version": "6.3.3",
"license": "MIT",
"dependencies": {
"@stencil/core": "^2.18.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "6.3.1",
"version": "6.3.3",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -86,7 +86,7 @@
"css.sass": "sass --embed-sources src/css:./css",
"eslint": "eslint src",
"lint": "npm run lint.ts && npm run lint.sass && npm run prettier -- --write",
"lint.fix": "npm run lint.ts.fix && npm run lint.sass.fix",
"lint.fix": "npm run lint.ts.fix && npm run lint.sass.fix && npm run prettier -- --write",
"lint.sass": "stylelint \"src/**/*.scss\"",
"lint.sass.fix": "npm run lint.sass -- --fix",
"lint.ts": "npm run eslint",

View File

@@ -35,9 +35,12 @@ test.describe('alert: a11y', () => {
});
test('should not have accessibility violations when header and message are defined', async ({ page }) => {
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
const button = page.locator('#bothHeaders');
await button.click();
await didPresent.next();
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});

View File

@@ -160,7 +160,8 @@ export class DatetimeButton implements ComponentInterface {
* to keep checking if the datetime value is `string` or `string[]`.
*/
private getParsedDateValues = (value?: string[] | string | null): string[] => {
if (value === undefined || value === null) {
// TODO FW-2646 Remove value === ''
if (value === '' || value === undefined || value === null) {
return [];
}

View File

@@ -561,7 +561,7 @@ export class Datetime implements ComponentInterface {
* "activePartsClone" and then falling back to
* today's DatetimeParts if no active date is selected.
*/
private getDefaultPart = () => {
private getDefaultPart = (): DatetimeParts => {
const { activePartsClone, todayParts } = this;
const firstPart = Array.isArray(activePartsClone) ? activePartsClone[0] : activePartsClone;
@@ -585,7 +585,7 @@ export class Datetime implements ComponentInterface {
};
private setActiveParts = (parts: DatetimeParts, removeDate = false) => {
const { multiple, activePartsClone } = this;
const { multiple, minParts, maxParts, activePartsClone } = this;
/**
* When setting the active parts, it is possible
@@ -597,7 +597,7 @@ export class Datetime implements ComponentInterface {
* Additionally, we need to update the working parts
* too in the event that the validated parts are different.
*/
const validatedParts = validateParts(parts);
const validatedParts = validateParts(parts, minParts, maxParts);
this.setWorkingParts(validatedParts);
if (multiple) {
@@ -1154,8 +1154,11 @@ export class Datetime implements ComponentInterface {
}
private processValue = (value?: string | string[] | null) => {
const hasValue = value !== null && value !== undefined;
let valueToProcess = parseDate(value ?? getToday());
/**
* TODO FW-2646 remove value !== ''
*/
const hasValue = value !== '' && value !== null && value !== undefined;
let valueToProcess = parseDate(hasValue ? value : getToday());
const { minParts, maxParts, multiple } = this;
if (!multiple && Array.isArray(value)) {
@@ -1393,7 +1396,9 @@ export class Datetime implements ComponentInterface {
}
private renderCombinedDatePickerColumn() {
const { activeParts, workingParts, locale, minParts, maxParts, todayParts, isDateEnabled } = this;
const { workingParts, locale, minParts, maxParts, todayParts, isDateEnabled } = this;
const activePart = this.getDefaultPart();
/**
* By default, generate a range of 3 months:
@@ -1489,12 +1494,10 @@ export class Datetime implements ComponentInterface {
...findPart,
});
if (!Array.isArray(activeParts)) {
this.setActiveParts({
...activeParts,
...findPart,
});
}
this.setActiveParts({
...activePart,
...findPart,
});
// We can re-attach the scroll listener after
// the working parts have been updated.
@@ -1583,7 +1586,9 @@ export class Datetime implements ComponentInterface {
return [];
}
const { activeParts, workingParts } = this;
const { workingParts } = this;
const activePart = this.getDefaultPart();
return (
<ion-picker-column-internal
@@ -1605,12 +1610,10 @@ export class Datetime implements ComponentInterface {
day: ev.detail.value,
});
if (!Array.isArray(activeParts)) {
this.setActiveParts({
...activeParts,
day: ev.detail.value,
});
}
this.setActiveParts({
...activePart,
day: ev.detail.value,
});
// We can re-attach the scroll listener after
// the working parts have been updated.
@@ -1627,7 +1630,9 @@ export class Datetime implements ComponentInterface {
return [];
}
const { activeParts, workingParts } = this;
const { workingParts } = this;
const activePart = this.getDefaultPart();
return (
<ion-picker-column-internal
@@ -1649,12 +1654,10 @@ export class Datetime implements ComponentInterface {
month: ev.detail.value,
});
if (!Array.isArray(activeParts)) {
this.setActiveParts({
...activeParts,
month: ev.detail.value,
});
}
this.setActiveParts({
...activePart,
month: ev.detail.value,
});
// We can re-attach the scroll listener after
// the working parts have been updated.
@@ -1670,7 +1673,9 @@ export class Datetime implements ComponentInterface {
return [];
}
const { activeParts, workingParts } = this;
const { workingParts } = this;
const activePart = this.getDefaultPart();
return (
<ion-picker-column-internal
@@ -1692,12 +1697,10 @@ export class Datetime implements ComponentInterface {
year: ev.detail.value,
});
if (!Array.isArray(activeParts)) {
this.setActiveParts({
...activeParts,
year: ev.detail.value,
});
}
this.setActiveParts({
...activePart,
year: ev.detail.value,
});
// We can re-attach the scroll listener after
// the working parts have been updated.
@@ -1750,12 +1753,10 @@ export class Datetime implements ComponentInterface {
hour: ev.detail.value,
});
if (!Array.isArray(activePart)) {
this.setActiveParts({
...activePart,
hour: ev.detail.value,
});
}
this.setActiveParts({
...activePart,
hour: ev.detail.value,
});
ev.stopPropagation();
}}
@@ -1780,12 +1781,10 @@ export class Datetime implements ComponentInterface {
minute: ev.detail.value,
});
if (!Array.isArray(activePart)) {
this.setActiveParts({
...activePart,
minute: ev.detail.value,
});
}
this.setActiveParts({
...activePart,
minute: ev.detail.value,
});
ev.stopPropagation();
}}
@@ -1816,13 +1815,11 @@ export class Datetime implements ComponentInterface {
hour,
});
if (!Array.isArray(activePart)) {
this.setActiveParts({
...activePart,
ampm: ev.detail.value,
hour,
});
}
this.setActiveParts({
...activePart,
ampm: ev.detail.value,
hour,
});
ev.stopPropagation();
}}
@@ -1914,6 +1911,8 @@ export class Datetime implements ComponentInterface {
// can free-scroll the calendar.
const isWorkingMonth = this.workingParts.month === month && this.workingParts.year === year;
const activePart = this.getDefaultPart();
return (
<div
// Non-visible months should be hidden from screen readers
@@ -1998,7 +1997,7 @@ export class Datetime implements ComponentInterface {
);
} else {
this.setActiveParts({
...this.activeParts,
...activePart,
month,
day,
year,
@@ -2106,17 +2105,13 @@ export class Datetime implements ComponentInterface {
</ion-popover>,
];
}
private renderCalendarViewHeader() {
const hasSlottedTitle = this.el.querySelector('[slot="title"]') !== null;
if (!hasSlottedTitle && !this.showDefaultTitle) {
return;
}
const { activeParts, titleSelectedDatesFormatter } = this;
private getHeaderSelectedDateText() {
const { activeParts, multiple, titleSelectedDatesFormatter } = this;
const isArray = Array.isArray(activeParts);
let headerText: string;
if (isArray && activeParts.length !== 1) {
if (multiple && isArray && activeParts.length !== 1) {
headerText = `${activeParts.length} days`; // default/fallback for multiple selection
if (titleSelectedDatesFormatter !== undefined) {
try {
@@ -2130,12 +2125,21 @@ export class Datetime implements ComponentInterface {
headerText = getMonthAndDay(this.locale, this.getDefaultPart());
}
return headerText;
}
private renderCalendarViewHeader(showExpandedHeader = true) {
const hasSlottedTitle = this.el.querySelector('[slot="title"]') !== null;
if (!hasSlottedTitle && !this.showDefaultTitle) {
return;
}
return (
<div class="datetime-header">
<div class="datetime-title">
<slot name="title">Select Date</slot>
</div>
<div class="datetime-selected-date">{headerText}</div>
{showExpandedHeader && <div class="datetime-selected-date">{this.getHeaderSelectedDateText()}</div>}
</div>
);
}
@@ -2182,7 +2186,7 @@ export class Datetime implements ComponentInterface {
*/
const hasWheelVariant = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
if (preferWheel && hasWheelVariant) {
return [this.renderWheelView(), this.renderFooter()];
return [this.renderCalendarViewHeader(false), this.renderWheelView(), this.renderFooter()];
}
switch (presentation) {

View File

@@ -72,6 +72,39 @@ test.describe('datetime: selecting a day', () => {
await expect(activeDay).toHaveText('13');
});
test('should set both date and time when no value is initially set', async ({ page }) => {
await page.setContent(`
<ion-datetime locale="en-US" presentation="date-time"></ion-datetime>
<script>
const mockToday = '2022-10-10T16:22';
Date = class extends Date {
constructor(...args) {
if (args.length === 0) {
super(mockToday)
} else {
super(...args);
}
}
}
</script>
`);
await page.waitForSelector('.datetime-ready');
const datetime = page.locator('ion-datetime');
const ionChange = await page.spyOnEvent('ionChange');
// Oct 1, 2022
await page.click('.calendar-day[data-month="10"][data-year="2022"][data-day="1"]');
await ionChange.next();
const value = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
await expect(typeof value).toBe('string');
// Check to make sure value includes current time
await expect(value!.includes('2022-10-01T16:22')).toBe(true);
});
});
test.describe('datetime: confirm date', () => {

View File

@@ -14,6 +14,7 @@ import {
calculateHourFromAMPM,
subtractDays,
addDays,
validateParts,
} from '../utils/manipulation';
describe('addDays()', () => {
@@ -487,3 +488,72 @@ describe('getPreviousYear()', () => {
});
});
});
describe('validateParts()', () => {
it('should move day in bounds', () => {
expect(validateParts({ month: 2, day: 31, year: 2022, hour: 8, minute: 0 })).toEqual({
month: 2,
day: 28,
year: 2022,
hour: 8,
minute: 0,
});
});
it('should move the hour back in bounds according to the min', () => {
expect(
validateParts(
{ month: 1, day: 1, year: 2022, hour: 8, minute: 0 },
{ month: 1, day: 1, year: 2022, hour: 9, minute: 0 }
)
).toEqual({ month: 1, day: 1, year: 2022, hour: 9, minute: 0 });
});
it('should move the minute back in bounds according to the min', () => {
expect(
validateParts(
{ month: 1, day: 1, year: 2022, hour: 9, minute: 20 },
{ month: 1, day: 1, year: 2022, hour: 9, minute: 30 }
)
).toEqual({ month: 1, day: 1, year: 2022, hour: 9, minute: 30 });
});
it('should move the hour and minute back in bounds according to the min', () => {
expect(
validateParts(
{ month: 1, day: 1, year: 2022, hour: 8, minute: 30 },
{ month: 1, day: 1, year: 2022, hour: 9, minute: 0 }
)
).toEqual({ month: 1, day: 1, year: 2022, hour: 9, minute: 0 });
});
it('should move the hour back in bounds according to the max', () => {
expect(
validateParts({ month: 1, day: 1, year: 2022, hour: 10, minute: 0 }, undefined, {
month: 1,
day: 1,
year: 2022,
hour: 9,
minute: 0,
})
).toEqual({ month: 1, day: 1, year: 2022, hour: 9, minute: 0 });
});
it('should move the minute back in bounds according to the max', () => {
expect(
validateParts({ month: 1, day: 1, year: 2022, hour: 9, minute: 40 }, undefined, {
month: 1,
day: 1,
year: 2022,
hour: 9,
minute: 30,
})
).toEqual({ month: 1, day: 1, year: 2022, hour: 9, minute: 30 });
});
it('should move the hour and minute back in bounds according to the max', () => {
expect(
validateParts({ month: 1, day: 1, year: 2022, hour: 10, minute: 20 }, undefined, {
month: 1,
day: 1,
year: 2022,
hour: 9,
minute: 30,
})
).toEqual({ month: 1, day: 1, year: 2022, hour: 9, minute: 30 });
});
});

View File

@@ -234,4 +234,53 @@ test.describe('datetime: minmax', () => {
);
await expect(hourPickerItems).toHaveText(['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']);
});
test.describe('minmax value adjustment when out of bounds', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('ios', 'This implementation is the same across modes.');
});
test('should reset to min time if out of bounds', async ({ page }) => {
await page.setContent(`
<ion-datetime
min="2022-10-10T08:00"
value="2022-10-11T06:00"
></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
const datetime = page.locator('ion-datetime');
const ionChange = await page.spyOnEvent('ionChange');
const dayButton = page.locator('ion-datetime .calendar-day[data-day="10"][data-month="10"][data-year="2022"]');
await dayButton.click();
await ionChange.next();
const value = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
await expect(typeof value).toBe('string');
await expect(value!.includes('2022-10-10T08:00')).toBe(true);
});
test('should reset to max time if out of bounds', async ({ page }) => {
await page.setContent(`
<ion-datetime
max="2022-10-10T08:00"
value="2022-10-11T09:00"
></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
const datetime = page.locator('ion-datetime');
const ionChange = await page.spyOnEvent('ionChange');
const dayButton = page.locator('ion-datetime .calendar-day[data-day="10"][data-month="10"][data-year="2022"]');
await dayButton.click();
await ionChange.next();
const value = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
await expect(typeof value).toBe('string');
await expect(value!.includes('2022-10-10T08:00')).toBe(true);
});
});
});

View File

@@ -189,4 +189,28 @@ test.describe('datetime: multiple date selection (functionality)', () => {
await juneButtons.nth(0).click();
await expect(header).toHaveText('Selected: 0');
});
test('header text should render default date when multiple="false"', async ({ page }) => {
await page.setContent(`
<ion-datetime locale="en-US" show-default-title="true"></ion-datetime>
<script>
const mockToday = '2022-10-10T16:22';
Date = class extends Date {
constructor(...args) {
if (args.length === 0) {
super(mockToday)
} else {
super(...args);
}
}
}
</script>
`);
await page.waitForSelector(`.datetime-ready`);
const datetime = page.locator('ion-datetime');
const header = datetime.locator('.datetime-selected-date');
await expect(header).toHaveText('Mon, Oct 10');
});
});

View File

@@ -40,6 +40,18 @@ test.describe('datetime: prefer wheel', () => {
`datetime-wheel-time-date-diff-${page.getSnapshotSettings()}.png`
);
});
test('should render a condense header when specified', async ({ page }) => {
await page.setContent(`
<ion-datetime size="cover" presentation="time-date" prefer-wheel="true" value="2019-05-30T16:30:00"><div slot="title">My Custom Title</div></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
const datetime = page.locator('ion-datetime');
expect(await datetime.screenshot()).toMatchSnapshot(
`datetime-wheel-header-diff-${page.getSnapshotSettings()}.png`
);
});
});
test.describe('datetime: date wheel', () => {
test.beforeEach(({ skip }) => {
@@ -112,6 +124,64 @@ test.describe('datetime: prefer wheel', () => {
expect(await yearValues.count()).toBe(3);
expect(await dayValues.count()).toBe(5);
});
test('selecting month should update value when no value is set', async ({ page }) => {
await page.setContent(`
<ion-datetime
presentation="date"
prefer-wheel="true"
></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
const ionChange = await page.spyOnEvent('ionChange');
const monthValues = page.locator('.month-column .picker-item:not(.picker-item-empty)');
// Change month value
await monthValues.nth(0).click();
await ionChange.next();
});
test('selecting day should update value when no value is set', async ({ page }) => {
await page.setContent(`
<ion-datetime
presentation="date"
prefer-wheel="true"
></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
const ionChange = await page.spyOnEvent('ionChange');
const dayValues = page.locator('.day-column .picker-item:not(.picker-item-empty)');
// Change day value
await dayValues.nth(0).click();
await ionChange.next();
});
test('selecting year should update value when no value is set', async ({ page }) => {
await page.setContent(`
<ion-datetime
presentation="date"
prefer-wheel="true"
></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
const ionChange = await page.spyOnEvent('ionChange');
const yearValues = page.locator('.year-column .picker-item:not(.picker-item-empty)');
/**
* Change year value
* The 0th index is the current
* year, so select something other than that.
*/
await yearValues.nth(10).click();
await ionChange.next();
});
test.describe('datetime: date wheel localization', () => {
test('should correctly localize the date data', async ({ page }) => {
await page.setContent(`
@@ -314,6 +384,24 @@ test.describe('datetime: prefer wheel', () => {
expect(await dayValues.count()).toBe(15);
});
test('selecting date should update value when no value is set', async ({ page }) => {
await page.setContent(`
<ion-datetime
presentation="date-time"
prefer-wheel="true"
></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
const ionChange = await page.spyOnEvent('ionChange');
const dayValues = page.locator('.date-column .picker-item:not(.picker-item-empty)');
// Change day/month value
await dayValues.nth(0).click();
await ionChange.next();
});
});
test.describe('datetime: time-date wheel', () => {
test.beforeEach(({ skip }) => {

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -50,3 +50,17 @@ test.describe('datetime: values', () => {
await expect(items).toHaveText(['01', '02', '03']);
});
});
test('setting value to empty string should treat it as having no date', async ({ page, skip }) => {
skip.rtl();
skip.mode('ios');
await page.setContent(`
<ion-datetime value="" locale="en-US"></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');
// Should render current month with today outlined.
const calendarDayToday = page.locator('ion-datetime .calendar-day-today');
await expect(calendarDayToday).toBeVisible();
});

View File

@@ -1,5 +1,6 @@
import type { DatetimeParts } from '../datetime-interface';
import { isSameDay } from './comparison';
import { getNumDaysInMonth } from './helpers';
const twoDigit = (val: number | undefined): string => {
@@ -345,7 +346,11 @@ export const calculateHourFromAMPM = (currentParts: DatetimeParts, newAMPM: 'am'
* values are valid. For days that do not exist,
* the closest valid day is used.
*/
export const validateParts = (parts: DatetimeParts): DatetimeParts => {
export const validateParts = (
parts: DatetimeParts,
minParts?: DatetimeParts,
maxParts?: DatetimeParts
): DatetimeParts => {
const { month, day, year } = parts;
const partsCopy = { ...parts };
@@ -361,5 +366,66 @@ export const validateParts = (parts: DatetimeParts): DatetimeParts => {
partsCopy.day = numDays;
}
/**
* If value is same day as min day,
* make sure the time value is in bounds.
*/
if (minParts !== undefined && isSameDay(partsCopy, minParts)) {
/**
* If the hour is out of bounds,
* update both the hour and minute.
* This is done so that the new time
* is closest to what the user selected.
*/
if (partsCopy.hour !== undefined && minParts.hour !== undefined) {
if (partsCopy.hour < minParts.hour) {
partsCopy.hour = minParts.hour;
partsCopy.minute = minParts.minute;
/**
* If only the minute is out of bounds,
* set it to the min minute.
*/
} else if (
partsCopy.hour === minParts.hour &&
partsCopy.minute !== undefined &&
minParts.minute !== undefined &&
partsCopy.minute < minParts.minute
) {
partsCopy.minute = minParts.minute;
}
}
}
/**
* If value is same day as max day,
* make sure the time value is in bounds.
*/
if (maxParts !== undefined && isSameDay(parts, maxParts)) {
/**
* If the hour is out of bounds,
* update both the hour and minute.
* This is done so that the new time
* is closest to what the user selected.
*/
if (partsCopy.hour !== undefined && maxParts.hour !== undefined) {
if (partsCopy.hour > maxParts.hour) {
partsCopy.hour = maxParts.hour;
partsCopy.minute = maxParts.minute;
/**
* If only the minute is out of bounds,
* set it to the max minute.
*/
} else if (
partsCopy.hour === maxParts.hour &&
partsCopy.minute !== undefined &&
maxParts.minute !== undefined &&
partsCopy.minute > maxParts.minute
) {
partsCopy.minute = maxParts.minute;
}
}
}
return partsCopy;
};

View File

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

View File

@@ -0,0 +1,57 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('footer: basic', () => {
test.describe('footer: rendering', () => {
test('should not have visual regressions with basic footer', async ({ page }) => {
await page.setContent(`
<ion-footer>
<ion-toolbar>
<ion-title>Footer - Default</ion-title>
</ion-toolbar>
</ion-footer>
`);
const footer = page.locator('ion-footer');
expect(await footer.screenshot()).toMatchSnapshot(`footer-diff-${page.getSnapshotSettings()}.png`);
});
});
test.describe('footer: feature rendering', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
});
test('should not have visual regressions with no border', async ({ page }) => {
await page.setContent(`
<ion-footer class="ion-no-border">
<ion-toolbar>
<ion-title>Footer - No Border</ion-title>
</ion-toolbar>
</ion-footer>
`);
const footer = page.locator('ion-footer');
expect(await footer.screenshot()).toMatchSnapshot(`footer-no-border-diff-${page.getSnapshotSettings()}.png`);
});
test('should not have visual regressions with translucent footer', async ({ page, skip }) => {
skip.mode('md', 'Translucent effect is only available in iOS mode.');
skip.browser('firefox', 'Firefox has some issues rendering translucent effects on Linux.');
await page.setContent(`
<ion-footer translucent="true">
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0">
<img style="transform: rotate(145deg) scale(1.5)" src="/src/components/footer/test/img.jpg" />
</div>
<ion-toolbar>
<ion-title>Footer - Translucent</ion-title>
</ion-toolbar>
</ion-footer>
`);
const footer = page.locator('ion-footer');
expect(await footer.screenshot()).toMatchSnapshot(`footer-translucent-diff-${page.getSnapshotSettings()}.png`);
});
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -15,23 +15,14 @@
</head>
<body>
<ion-app>
<ion-content>
<br />
<ion-footer>
<ion-toolbar>
<ion-title>Footer - Default</ion-title>
</ion-toolbar>
</ion-footer>
<br />
<ion-footer class="ion-no-border">
<ion-toolbar>
<ion-title>Footer - No Border</ion-title>
</ion-toolbar>
</ion-footer>
<ion-content class="ion-padding">
<h1>Content</h1>
</ion-content>
<ion-footer>
<ion-toolbar>
<ion-title>Footer</ion-title>
</ion-toolbar>
</ion-footer>
</ion-app>
</body>
</html>

View File

@@ -1,27 +0,0 @@
import type { E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';
import { scrollToBottom } from '@utils/test';
describe('footer: fade: iOS', () => {
let page: E2EPage;
beforeEach(async () => {
page = await newE2EPage({
url: '/src/components/footer/test/fade?ionic:_testing=true&ionic:mode=ios',
});
});
it('should match existing visual screenshots', async () => {
const compares = [];
compares.push(await page.compareScreenshot('footer: blurred'));
await scrollToBottom(page, 'ion-content');
compares.push(await page.compareScreenshot('footer: not blurred'));
for (const compare of compares) {
expect(compare).toMatchScreenshot();
}
});
});

View File

@@ -0,0 +1,24 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('footer: fade', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
});
test('should not have visual regressions with fade footer', async ({ page, skip }) => {
skip.mode('md', 'Translucent effect is only available in iOS mode.');
skip.browser('firefox', 'Firefox has some issues rendering translucent effects on Linux.');
await page.goto('/src/components/footer/test/fade');
const footer = page.locator('ion-footer');
expect(await footer.screenshot()).toMatchSnapshot(`footer-fade-blurred-diff-${page.getSnapshotSettings()}.png`);
const content = page.locator('ion-content');
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
await page.waitForChanges();
expect(await footer.screenshot()).toMatchSnapshot(`footer-fade-not-blurred-diff-${page.getSnapshotSettings()}.png`);
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -12,78 +12,73 @@
<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>
.red {
background-color: #ea445a;
}
.green {
background-color: #76d672;
}
.blue {
background-color: #3478f6;
}
.yellow {
background-color: #ffff80;
}
.pink {
background-color: #ff6b86;
}
.purple {
background-color: #7e34f6;
}
.black {
background-color: #000;
}
.orange {
background-color: #f69234;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.grid-item {
height: 200px;
}
</style>
</head>
<body>
<ion-app>
<div class="ion-page">
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Mailboxes</ion-title>
<ion-title>Header</ion-title>
</ion-toolbar>
</ion-header>
<ion-content fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Mailboxes</ion-title>
<ion-title size="large">Header</ion-title>
</ion-toolbar>
</ion-header>
<div class="grid ion-padding">
<div class="grid-item red"></div>
<div class="grid-item green"></div>
<div class="grid-item blue"></div>
<div class="grid-item yellow"></div>
<div class="grid-item pink"></div>
<div class="grid-item purple"></div>
<div class="grid-item black"></div>
<div class="grid-item orange"></div>
<div class="ion-padding">
<h1>Content</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas blandit, justo vitae rhoncus porttitor,
massa elit molestie nisl, ut tincidunt orci enim ac ante. Vestibulum tincidunt dignissim elit. Morbi
cursus hendrerit turpis, ut egestas tortor pulvinar sit amet. Interdum et malesuada fames ac ante ipsum
primis in faucibus. Phasellus faucibus consequat purus vel mollis. Ut ultricies elit nunc. Quisque
ultrices turpis vel augue auctor accumsan. Donec bibendum at nisi vel finibus. Fusce id imperdiet odio.
Morbi orci ipsum, imperdiet ut iaculis sit amet, suscipit vulputate felis. Nullam arcu leo, volutpat eu
porttitor sed, fringilla et arcu. Pellentesque ac libero sapien. Quisque id dui velit. Mauris et pretium
tortor.
</p>
<p>
Ut ultricies id augue vel aliquam. Etiam ornare finibus nisl, nec egestas urna. Nam pellentesque libero
nec justo tristique lacinia. In sit amet gravida metus, ac tincidunt mauris. Fusce sit amet tempus turpis.
Nulla ligula nunc, vestibulum quis quam in, feugiat aliquet nibh. Quisque in ante non nulla luctus gravida
vel at lacus. Vivamus erat magna, elementum et dignissim nec, posuere eu ante. Praesent elementum, arcu
scelerisque venenatis sodales, turpis nulla aliquam urna, id hendrerit est orci et purus. Duis sem ipsum,
imperdiet eu elit id, tincidunt tempus sapien. Praesent tincidunt, sapien sed rhoncus euismod, lectus
velit ornare nunc, dapibus varius turpis leo ut magna.
</p>
<p>
Nam quis quam id ante mattis pulvinar non sed mauris. Donec tempor sed nulla at semper. Vivamus ac nunc
bibendum, ullamcorper lacus quis, ornare massa. Cras gravida nibh risus, id sollicitudin eros ultricies
non. Integer velit massa, suscipit tincidunt rhoncus ut, lacinia et nisl. Maecenas volutpat ipsum blandit
sollicitudin lobortis. Suspendisse potenti. Cras non mi non arcu varius dapibus. Suspendisse maximus eget
justo a lobortis. Donec nulla ipsum, efficitur eget velit eget, varius rutrum quam. Nulla metus risus,
accumsan a tellus ac, faucibus blandit quam. Donec luctus, nisl ac ultricies ornare, nunc elit finibus
magna, id elementum ante urna congue ex. Cras condimentum nisi sollicitudin tortor vestibulum luctus.
Curabitur non ipsum et ex vestibulum congue.
</p>
<p>
Maecenas rhoncus elit ut consectetur faucibus. Etiam sed sem sed mauris condimentum viverra sit amet at
nibh. Mauris bibendum at purus a cursus. Suspendisse potenti. Donec vel lacus ac odio euismod lacinia id
in urna. Donec commodo ipsum augue, at bibendum ex convallis suscipit. Nulla ac rhoncus odio. Aenean
elementum est nec arcu ultricies dignissim.
</p>
<p>
Sed tincidunt bibendum massa, egestas bibendum est imperdiet vitae. Fusce dignissim consectetur ante a
fermentum. Morbi suscipit turpis sapien. Suspendisse eleifend sapien eget nunc mattis mattis. Phasellus
rhoncus sodales libero a imperdiet. Nam in vulputate lectus. Proin accumsan enim non nibh sagittis
ultricies. Nullam vitae ultricies nunc. Nullam ultrices dolor nec vehicula posuere.
</p>
</div>
</ion-content>
<ion-footer collapse="fade" translucent="true">
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0">
<img src="/src/components/footer/test/img.jpg" />
</div>
<ion-toolbar>
<ion-title>Updated Just Now</ion-title>
<ion-title>Footer</ion-title>
</ion-toolbar>
</ion-footer>
</div>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -1,32 +0,0 @@
import type { E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';
import { scrollToBottom } from '@utils/test';
/**
* This test suite verifies that the fade effect for iOS is working correctly
* when the `ion-footer` is using a custom scroll target with the `.ion-content-scroll-host`
* selector.
*/
describe('footer: fade with custom scroll target: iOS', () => {
let page: E2EPage;
beforeEach(async () => {
page = await newE2EPage({
url: '/src/components/footer/test/scroll-target?ionic:_testing=true&ionic:mode=ios',
});
});
it('should match existing visual screenshots', async () => {
const compares = [];
compares.push(await page.compareScreenshot('footer: blurred'));
await scrollToBottom(page, '#scroll-target');
compares.push(await page.compareScreenshot('footer: not blurred'));
for (const compare of compares) {
expect(compare).toMatchScreenshot();
}
});
});

View File

@@ -0,0 +1,33 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('footer: scroll-target', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
});
/**
* This test suite verifies that the fade effect for iOS is working correctly
* when the `ion-footer` is using a custom scroll target with the `.ion-content-scroll-host`
* selector.
*/
test('should not have visual regressions with custom scroll target footer', async ({ page, skip }) => {
skip.mode('md', 'Translucent effect is only available in iOS mode.');
skip.browser('firefox', 'Firefox has some issues rendering translucent effects on Linux.');
await page.goto('/src/components/footer/test/scroll-target');
const footer = page.locator('ion-footer');
expect(await footer.screenshot()).toMatchSnapshot(
`footer-fade-scroll-target-blurred-diff-${page.getSnapshotSettings()}.png`
);
const scrollTarget = page.locator('#scroll-target');
await scrollTarget.evaluate((el: HTMLDivElement) => (el.scrollTop = el.scrollHeight));
await page.waitForChanges();
expect(await footer.screenshot()).toMatchSnapshot(
`footer-fade-scroll-target-not-blurred-diff-${page.getSnapshotSettings()}.png`
);
});
});

View File

@@ -2,7 +2,7 @@
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Footer - Fade (custom scroll host)</title>
<title>Footer - Scroll Target</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
@@ -12,49 +12,8 @@
<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>
.red {
background-color: #ea445a;
}
.green {
background-color: #76d672;
}
.blue {
background-color: #3478f6;
}
.yellow {
background-color: #ffff80;
}
.pink {
background-color: #ff6b86;
}
.purple {
background-color: #7e34f6;
}
.black {
background-color: #000;
}
.orange {
background-color: #f69234;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.grid-item {
height: 200px;
}
#scroll-target {
position: absolute;
top: 0;
@@ -68,32 +27,67 @@
}
</style>
</head>
<body>
<ion-app>
<div class="ion-page">
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Mailboxes</ion-title>
<ion-title>Header</ion-title>
</ion-toolbar>
</ion-header>
<ion-content fullscreen="true" scroll-y="false">
<div id="scroll-target" class="ion-content-scroll-host">
<div class="grid ion-padding">
<div class="grid-item red"></div>
<div class="grid-item green"></div>
<div class="grid-item blue"></div>
<div class="grid-item yellow"></div>
<div class="grid-item pink"></div>
<div class="grid-item purple"></div>
<div class="grid-item black"></div>
<div class="grid-item orange"></div>
</div>
<h1>Content</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas blandit, justo vitae rhoncus porttitor,
massa elit molestie nisl, ut tincidunt orci enim ac ante. Vestibulum tincidunt dignissim elit. Morbi
cursus hendrerit turpis, ut egestas tortor pulvinar sit amet. Interdum et malesuada fames ac ante ipsum
primis in faucibus. Phasellus faucibus consequat purus vel mollis. Ut ultricies elit nunc. Quisque
ultrices turpis vel augue auctor accumsan. Donec bibendum at nisi vel finibus. Fusce id imperdiet odio.
Morbi orci ipsum, imperdiet ut iaculis sit amet, suscipit vulputate felis. Nullam arcu leo, volutpat eu
porttitor sed, fringilla et arcu. Pellentesque ac libero sapien. Quisque id dui velit. Mauris et pretium
tortor.
</p>
<p>
Ut ultricies id augue vel aliquam. Etiam ornare finibus nisl, nec egestas urna. Nam pellentesque libero
nec justo tristique lacinia. In sit amet gravida metus, ac tincidunt mauris. Fusce sit amet tempus turpis.
Nulla ligula nunc, vestibulum quis quam in, feugiat aliquet nibh. Quisque in ante non nulla luctus gravida
vel at lacus. Vivamus erat magna, elementum et dignissim nec, posuere eu ante. Praesent elementum, arcu
scelerisque venenatis sodales, turpis nulla aliquam urna, id hendrerit est orci et purus. Duis sem ipsum,
imperdiet eu elit id, tincidunt tempus sapien. Praesent tincidunt, sapien sed rhoncus euismod, lectus
velit ornare nunc, dapibus varius turpis leo ut magna.
</p>
<p>
Nam quis quam id ante mattis pulvinar non sed mauris. Donec tempor sed nulla at semper. Vivamus ac nunc
bibendum, ullamcorper lacus quis, ornare massa. Cras gravida nibh risus, id sollicitudin eros ultricies
non. Integer velit massa, suscipit tincidunt rhoncus ut, lacinia et nisl. Maecenas volutpat ipsum blandit
sollicitudin lobortis. Suspendisse potenti. Cras non mi non arcu varius dapibus. Suspendisse maximus eget
justo a lobortis. Donec nulla ipsum, efficitur eget velit eget, varius rutrum quam. Nulla metus risus,
accumsan a tellus ac, faucibus blandit quam. Donec luctus, nisl ac ultricies ornare, nunc elit finibus
magna, id elementum ante urna congue ex. Cras condimentum nisi sollicitudin tortor vestibulum luctus.
Curabitur non ipsum et ex vestibulum congue.
</p>
<p>
Maecenas rhoncus elit ut consectetur faucibus. Etiam sed sem sed mauris condimentum viverra sit amet at
nibh. Mauris bibendum at purus a cursus. Suspendisse potenti. Donec vel lacus ac odio euismod lacinia id
in urna. Donec commodo ipsum augue, at bibendum ex convallis suscipit. Nulla ac rhoncus odio. Aenean
elementum est nec arcu ultricies dignissim.
</p>
<p>
Sed tincidunt bibendum massa, egestas bibendum est imperdiet vitae. Fusce dignissim consectetur ante a
fermentum. Morbi suscipit turpis sapien. Suspendisse eleifend sapien eget nunc mattis mattis. Phasellus
rhoncus sodales libero a imperdiet. Nam in vulputate lectus. Proin accumsan enim non nibh sagittis
ultricies. Nullam vitae ultricies nunc. Nullam ultrices dolor nec vehicula posuere.
</p>
</div>
</ion-content>
<ion-footer collapse="fade" translucent="true">
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0">
<img src="/src/components/footer/test/img.jpg" />
</div>
<ion-toolbar>
<ion-title>Updated Just Now</ion-title>
<ion-title>Footer</ion-title>
</ion-toolbar>
</ion-footer>
</div>

View File

@@ -1,14 +0,0 @@
import { newE2EPage } from '@stencil/core/testing';
import { checkComponentModeClasses } from '@utils/test';
test('footer: translucent', async () => {
const page = await newE2EPage({
url: '/src/components/footer/test/translucent?ionic:_testing=true',
});
const globalMode = await page.evaluate(() => document.documentElement.getAttribute('mode'));
await checkComponentModeClasses(await page.find('ion-footer'), globalMode!, 'footer-translucent');
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@@ -1,171 +0,0 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Header - Translucent</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
<ion-content fullscreen>
<ion-grid>
<ion-row>
<ion-col size="6">
<f class="red"></f>
</ion-col>
<ion-col size="6">
<f class="green"></f>
</ion-col>
<ion-col size="6">
<f class="blue"></f>
</ion-col>
<ion-col size="6">
<f class="yellow"></f>
</ion-col>
<ion-col size="6">
<f class="pink"></f>
</ion-col>
<ion-col size="6">
<f class="purple"></f>
</ion-col>
<ion-col size="6">
<f class="black"></f>
</ion-col>
<ion-col size="6">
<f class="orange"></f>
</ion-col>
</ion-row>
</ion-grid>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae lobortis felis, eu sodales enim. Nam
risus nibh, placerat at rutrum ac, vehicula vel velit. Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Vestibulum quis elementum ligula, ac aliquet nulla. Mauris non placerat mauris. Aenean dignissim lacinia
porttitor. Praesent fringilla at est et ullamcorper. In ac ante ac massa porta venenatis ut id nibh. Fusce
felis neque, aliquet in velit vitae, venenatis euismod libero. Donec vulputate, urna sed sagittis tempor, mi
arcu tristique lacus, eget fringilla urna sem eget felis. Fusce dignissim lacus a scelerisque vehicula. Nulla
nec enim nunc. Quisque nec dui eu nibh pulvinar bibendum quis ut nunc. Duis ex odio, sollicitudin ac mollis
nec, fringilla non lacus. Maecenas sed tincidunt urna. Nunc feugiat maximus venenatis. Donec porttitor, felis
eget porttitor tempor, quam nulla dapibus nisl, sit amet posuere sapien sapien malesuada tortor. Pellentesque
habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque luctus, sapien nec
tincidunt efficitur, nibh turpis faucibus felis, in sodales massa augue nec erat. Morbi sollicitudin nisi ex,
et gravida nisi euismod eu. Suspendisse hendrerit dapibus orci, non viverra neque vestibulum id. Quisque vitae
interdum ligula, quis consectetur nibh. Phasellus in mi at erat ultrices semper. Fusce sollicitudin at dolor
ac lobortis. Morbi sit amet sem quis nulla pellentesque imperdiet. Nullam eu sem a enim maximus eleifend non
vulputate leo. Proin quis congue lacus. Pellentesque placerat, quam at tempus pulvinar, nisl ligula tempor
risus, quis pretium arcu odio et nulla. Nullam mollis consequat pharetra. Phasellus dictum velit sed purus
mattis maximus. In molestie eget massa ut dignissim. In a interdum elit. In finibus nibh a mauris lobortis
aliquet. Proin rutrum varius consequat. In mollis dapibus nisl, eu finibus urna viverra ac. Quisque
scelerisque nisl eu suscipit consectetur.
</p>
<ion-grid>
<ion-row>
<ion-col size="6">
<f class="red"></f>
</ion-col>
<ion-col size="6">
<f class="green"></f>
</ion-col>
<ion-col size="6">
<f class="blue"></f>
</ion-col>
<ion-col size="6">
<f class="yellow"></f>
</ion-col>
<ion-col size="6">
<f class="pink"></f>
</ion-col>
<ion-col size="6">
<f class="purple"></f>
</ion-col>
<ion-col size="6">
<f class="black"></f>
</ion-col>
<ion-col size="6">
<f class="orange"></f>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
<ion-footer translucent>
<ion-toolbar>
<ion-title>Footer - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="primary">
<ion-title>Primary - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="secondary">
<ion-title>Secondary - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="tertiary">
<ion-title>Tertiary - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="success">
<ion-title>Success - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="warning">
<ion-title>Warning - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="danger">
<ion-title>Danger - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="light">
<ion-title>Light - Translucent</ion-title>
</ion-toolbar>
<ion-toolbar color="medium">
<ion-title>Medium - Translucent</ion-title>
</ion-toolbar>
</ion-footer>
</ion-app>
<style>
f {
display: block;
height: 200px;
}
.red {
background-color: #ea445a;
}
.green {
background-color: #76d672;
}
.blue {
background-color: #3478f6;
}
.yellow {
background-color: #ffff80;
}
.pink {
background-color: #ff6b86;
}
.purple {
background-color: #7e34f6;
}
.black {
background-color: #000;
}
.orange {
background-color: #f69234;
}
</style>
</body>
</html>

View File

@@ -12,6 +12,7 @@
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
:root {
--ion-safe-area-bottom: 40px;

View File

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

View File

@@ -19,33 +19,17 @@
<ion-header>
<ion-toolbar>
<ion-title>Item Sliding - Basic</ion-title>
<ion-buttons slot="secondary">
<ion-button onclick="changeDynamicText()">Dynamic</ion-button>
</ion-buttons>
<ion-buttons slot="primary">
<ion-button onclick="reload()">Reload</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher id="refresher">
<ion-refresher-content
pulling-text="Pull to refresh..."
refreshing-spinner="bubbles"
refreshing-text="Refreshing..."
>
</ion-refresher-content>
</ion-refresher>
<div class="ion-padding">
<ion-button expand="block" onclick="toggleSliding()">Toggle sliding</ion-button>
<ion-button expand="block" onclick="toggleDynamicOptions()">Toggle Dynamic Options</ion-button>
<ion-button expand="block" onclick="closeOpened()">Close Opened Items</ion-button>
<ion-button expand="block" onclick="openItem('start')">Open Item Start</ion-button>
<ion-button expand="block" onclick="openItem('end')">Open Item End</ion-button>
<ion-button expand="block" onclick="openItemOneSide()">Open Item with only one side</ion-button>
<ion-button expand="block" onclick="setDynaicItem()">Swap dynamic item</ion-button>
<ion-button expand="block" id="openItemStart" onclick="openItem('start')">Open Item Start</ion-button>
<ion-button expand="block" id="openItemEnd" onclick="openItem('end')">Open Item End</ion-button>
<ion-button expand="block" id="openItemOneSide" onclick="openItemOneSide()"
>Open Item with only one side</ion-button
>
</div>
<ion-list id="list">
@@ -59,66 +43,6 @@
</ion-item>
</ion-item-sliding>
<ion-item-sliding id="item6">
<ion-item>
<ion-label> One Line, dynamic option and text </ion-label>
</ion-item>
<ion-item-options class="show-options">
<ion-item-option color="primary">
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
<span class="more-text"></span>
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('item6')">
<ion-icon slot="start" name="archive"></ion-icon>
<span class="archive-text"></span>
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="two-options">
<ion-item>
<ion-label> Two options, one dynamic option and text </ion-label>
</ion-item>
<ion-item-options side="start">
<ion-item-option color="primary">
<ion-icon slot="icon-only" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
</ion-item-option>
</ion-item-options>
<ion-item-options side="end" class="show-options">
<ion-item-option color="primary">
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
<span class="more-text"></span>
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('two-options')">
<ion-icon slot="start" name="archive"></ion-icon>
<span class="archive-text"></span>
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item100">
<ion-item href="#">
<ion-label>
<h2>HubStruck Notifications</h2>
<p>A new message from a repo in your network</p>
<p>Oceanic Next has joined your network</p>
</ion-label>
<ion-note slot="end"> 10:45 AM </ion-note>
</ion-item>
<ion-item-options side="start">
<ion-item-option onclick="noclose('item100')"> No close </ion-item-option>
</ion-item-options>
<ion-item-options side="end">
<ion-item-option color="danger" onclick="unread('item100')">
<ion-icon slot="icon-only" name="trash"></ion-icon>
</ion-item-option>
<ion-item-option onclick="unread('item100')">
<ion-icon slot="icon-only" name="star"></ion-icon>
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item100">
<ion-item href="#">
<ion-label>
@@ -141,15 +65,15 @@
</ion-item-sliding>
<ion-item-sliding id="item0">
<ion-item onclick="clickedItem('item0')">
<ion-item>
<ion-label class="ion-text-wrap">
<h2>RIGHT side - no icons</h2>
<p>Hey do you want to go to the game tonight?</p>
</ion-label>
</ion-item>
<ion-item-options class="sliding-enabled">
<ion-item-option color="primary" onclick="archive('item0')">Archive</ion-item-option>
<ion-item-option color="danger" onclick="del('item0')">Delete</ion-item-option>
<ion-item-option color="primary">Archive</ion-item-option>
<ion-item-option color="danger">Delete</ion-item-option>
</ion-item-options>
</ion-item-sliding>
@@ -161,8 +85,8 @@
</ion-label>
</ion-item>
<ion-item-options side="start" class="sliding-enabled">
<ion-item-option color="primary" onclick="archive('item1')">Archive</ion-item-option>
<ion-item-option color="danger" onclick="del('item1')">Delete</ion-item-option>
<ion-item-option color="primary">Archive</ion-item-option>
<ion-item-option color="danger">Delete</ion-item-option>
</ion-item-options>
</ion-item-sliding>
@@ -174,138 +98,24 @@
</ion-label>
</ion-item>
<ion-item-options side="start" class="sliding-enabled">
<ion-item-option color="secondary" expandable onclick="unread('item2')">
<ion-item-option color="secondary" expandable>
<ion-icon slot="start" name="checkmark"></ion-icon>
Unread
</ion-item-option>
</ion-item-options>
<ion-item-options side="end" class="sliding-enabled">
<ion-item-option color="primary" onclick="archive('item2')">
<ion-icon slot="start" name="mail"></ion-icon>
Archive
</ion-item-option>
<ion-item-option color="danger" onclick="del('item2')" expandable>
<ion-icon slot="start" name="trash"></ion-icon>
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item3">
<ion-item detail>
<ion-label class="ion-text-wrap">
<h2>RIGHT/LEFT side - icons (slot="start")</h2>
<p>I think I figured out how to get more Mountain Dew</p>
</ion-label>
</ion-item>
<ion-item-options side="start" icon-start class="sliding-enabled">
<ion-item-option color="secondary" expandable onclick="unread('item3')">
<ion-icon slot="start" name="checkmark"></ion-icon>
Unread
</ion-item-option>
</ion-item-options>
<ion-item-options icon-start>
<ion-item-option color="primary" onclick="archive('item3')">
<ion-icon slot="start" name="mail"></ion-icon>
Archive
</ion-item-option>
<ion-item-option color="danger" onclick="del('item3')" expandable class="sliding-enabled">
<ion-icon slot="start" name="trash"></ion-icon>
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item4">
<ion-item>
<ion-icon name="mail" slot="start"></ion-icon>
<ion-label> One Line w/ Icon, div only text </ion-label>
</ion-item>
<ion-item-options icon-start>
<ion-item-option color="primary" onclick="archive('item4')" expandable class="sliding-enabled">
<ion-icon slot="start" name="archive"></ion-icon>
Archive
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item5" class="sliding-enabled">
<ion-item>
<ion-avatar slot="start">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
</ion-avatar>
<ion-label> One Line w/ Avatar, div only text </ion-label>
</ion-item>
<ion-item-options>
<ion-item-option color="primary" expandable>
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
More
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('item5')">
<ion-icon slot="start" name="archive"></ion-icon>
Archive
</ion-item-option>
<ion-item-option color="light" onclick="del('item5')">
<ion-icon slot="start" name="trash"></ion-icon>
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item7">
<ion-item>
<ion-label> One Line, dynamic icon-start option </ion-label>
</ion-item>
<ion-item-options icon-start>
<ion-item-option color="primary">
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
<span class="more-text"></span>
<ion-icon slot="start" name="mail"></ion-icon>
Archive
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('item7')">
<ion-icon slot="start" name="archive"></ion-icon>
<span class="archive-text"></span>
<ion-item-option color="danger" expandable>
<ion-icon slot="start" name="trash"></ion-icon>
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item8">
<ion-item>
<ion-thumbnail slot="start">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
</ion-thumbnail>
<ion-label>
<h2>DOWNLOAD</h2>
<p>Paragraph text.</p>
</ion-label>
</ion-item>
<ion-item-options>
<ion-item-option color="primary" onclick="archive('item8')">
<ion-icon name="archive"></ion-icon>Archive
</ion-item-option>
<ion-item-option color="secondary" expandable onclick="download('item8')">
<ion-icon slot="start" name="download" class="download-hide"></ion-icon>
<div class="download-hide">Download</div>
<ion-icon slot="start" class="download-spinner" name="refresh"></ion-icon>
<div class="download-spinner">Loading...</div>
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item9">
<ion-item>
<ion-thumbnail slot="start">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
</ion-thumbnail>
<ion-label>
<h2>ion-item-sliding without options (no sliding)</h2>
<p>Paragraph text.</p>
</ion-label>
</ion-item>
</ion-item-sliding>
<ion-item-sliding id="item10">
<ion-item detail>
<ion-label class="ion-text-wrap">
@@ -351,81 +161,10 @@
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="dynamic-item">
<ion-item>
<ion-label>Dynamic First Item</ion-label>
</ion-item>
<ion-item-options side="end">
<ion-item-option color="tertiary" expandable> First Item Options </ion-item-option>
</ion-item-options>
</ion-item-sliding>
<ion-item>
<ion-label class="ion-text-wrap">
<h2>Normal ion-item (no sliding)</h2>
<p>Paragraph text.</p>
</ion-label>
</ion-item>
<ion-item onclick="clickedItem('item9')">
<ion-label class="ion-text-wrap">
<h2>Normal button (no sliding)</h2>
<p>Hey do you want to go to the game tonight?</p>
</ion-label>
</ion-item>
</div>
</ion-list>
<script>
const setDynaicItem = () => {
const sliding = document.querySelector('#dynamic-item');
sliding.innerHTML = `
<ion-item>
<ion-label>Dynamic Second Item</ion-label>
</ion-item>
<ion-item-options side="end">
<ion-item-option color="tertiary" expandable>
Second Item Options
</ion-item-option>
</ion-item-options>
`;
};
var dynamicSlidingEnabled = document.getElementsByClassName('sliding-enabled');
// Toggle the dynamic options
var dynamicShowOptions = document.getElementsByClassName('show-options');
toggleDynamicOptions();
function toggleDynamicOptions() {
// TODO the element needs to be removed / added to the DOM
}
// Change the text for the more and archive buttons
var dynamicText = true;
var moreTextSpans = document.getElementsByClassName('more-text');
var archiveTextSpans = document.getElementsByClassName('archive-text');
changeDynamicText();
function changeDynamicText() {
dynamicText = !dynamicText;
for (var i = 0; i < moreTextSpans.length; i++) {
var moreText = dynamicText ? 'Changed More' : 'Dynamic More';
moreTextSpans[i].innerHTML = moreText;
}
for (var i = 0; i < archiveTextSpans.length; i++) {
var archiveText = dynamicText ? 'Changed Archive' : 'Dynamic Archive';
archiveTextSpans[i].innerHTML = archiveText;
}
}
function toggleSliding() {
// this.slidingEnabled = !this.slidingEnabled;
}
function closeOpened() {
var list = document.getElementById('list');
list.closeSlidingItems();
@@ -441,89 +180,14 @@
item.open();
}
function noclose(item) {
var itemEle = document.getElementById(item);
console.log('no close', itemEle);
}
function unread(item) {
closeSlidingItem('UNREAD', item);
}
function archive(item) {
closeSlidingItem('ARCHIVE', item);
}
function del(item) {
closeSlidingItem('DELETE', item);
}
function download(item) {
var itemEle = document.getElementById(item);
console.log(itemEle);
itemEle.classList.add('downloading');
setTimeout(() => {
alert('Item was downloaded!');
itemEle.classList.remove('downloading');
itemEle.close();
}, 1500);
}
function closeSlidingItem(option, item) {
var itemEle = document.getElementById(item);
// TODO open alert instead
if (itemEle) {
itemEle.close();
}
console.log(option, itemEle);
}
function clickedItem(item) {
var itemEle = document.getElementById(item);
console.log('Clicked, ion-item', itemEle);
}
function reload() {
window.location.reload();
}
document.addEventListener('ionSwipe', (ev) => console.log('SWIPE!!', ev.detail));
document.addEventListener('ionDrag', (ev) => {
// console.log('DRAG!!', ev.detail);
let slidingRatio = ev.target.getSlidingRatio();
console.log('sliding', slidingRatio);
if (slidingRatio > 0) {
// positive
console.log('right side');
} else {
// negative
console.log('left side');
}
if (Math.abs(slidingRatio) > 1) {
console.log('overscroll');
}
});
</script>
<style>
.download-spinner {
display: none;
}
svg circle {
stroke: white;
}
.downloading .download-spinner {
display: block;
}
.downloading .download-hide {
display: none;
}
</style>
</ion-content>
</ion-app>
</body>

View File

@@ -1,14 +1,52 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
import { dragElementBy, test } from '@utils/test/playwright';
import { testSlidingItem } from '../test.utils';
test.describe('item-sliding: basic', () => {
test.beforeEach(async ({ skip }) => {
skip.mode('ios', "item-sliding doesn't have mode-specific styling");
});
test('should not have visual regressions', async ({ page, browserName }, testInfo) => {
// TODO(FW-2608)
test.fixme(
testInfo.project.metadata.rtl === true && (browserName === 'firefox' || browserName === 'webkit'),
'https://github.com/ionic-team/ionic-framework/issues/26103'
);
await page.goto(`/src/components/item-sliding/test/basic`);
const item = page.locator('#item2');
await testSlidingItem(page, item, 'start', true);
await testSlidingItem(page, item, 'end');
});
// mouse gesture is flaky on CI, skip for now
test.fixme('should open when swiped', async ({ page, skip }) => {
skip.rtl();
skip.browser(
(browserName: string) => browserName !== 'chromium',
'dragElementBy is flaky outside of Chrome browsers.'
);
await page.goto(`/src/components/item-sliding/test/basic`);
const item = page.locator('#item2');
await dragElementBy(item, page, -150);
await page.waitForChanges();
// item-sliding doesn't have an easy way to tell whether it's fully open so just screenshot it
expect(await item.screenshot()).toMatchSnapshot(`item-sliding-gesture-${page.getSnapshotSettings()}.png`);
});
test('should not scroll when the item-sliding is swiped', async ({ page, skip }) => {
skip.browser('webkit', 'mouse.wheel is not available in WebKit');
skip.rtl();
await page.goto(`/src/components/item-sliding/test/basic`);
const itemSlidingEl = page.locator('#two-options');
const itemSlidingEl = page.locator('#item2');
const scrollEl = page.locator('ion-content .inner-scroll');
expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0);

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