Compare commits

..

104 Commits

Author SHA1 Message Date
Sean Perkins
cefcd02fb9 fix(tab-tab): iOS translucent tab bar 2022-03-24 18:49:25 -04:00
Sean Perkins
a296ca875c fix(react): treeshake ionic/core bundle (#24989)
Resolves #24497

Co-authored-by: mlegenhausen <mlegenhausen@mailbox.org>
2022-03-24 12:17:15 -04:00
Sean Perkins
c52f3ad2c2 docs(select,item-sliding): relative link paths (#24985) 2022-03-23 15:03:26 -04:00
Liam DeBeasi
8424210049 merge release-6.0.13
v6.0.13
2022-03-23 09:40:28 -04:00
github-actions
c35077884d v6.0.13 2022-03-23 13:14:17 +00:00
Sean Perkins
df71a0e625 docs(swiper): update swiper api link (#24972) 2022-03-22 14:11:51 -04:00
Sean Perkins
2909b080b7 fix(datetime): presentation time emits ionChange once (#24968)
Resolves #24967
2022-03-22 11:33:19 -04:00
Sean Perkins
a8fd2d9199 fix(angular): ngOnDestroy runs inside angular zone (#24949)
Resolves #22571
2022-03-21 22:16:24 -04:00
Liam DeBeasi
9e84ef7f91 fix(popover): dismissing nested popover via backdrop now works (#24957)
resolves #24954
2022-03-21 09:28:44 -04:00
Sean Perkins
63842a25c3 refactor(angular): unused subscription in routerlink (#24950) 2022-03-19 13:10:21 -04:00
Liam DeBeasi
0d0740161f test(angular): update to latest ionic in test app (#24961) 2022-03-17 16:52:36 -04:00
Liam DeBeasi
43ac6ac471 test(react): update react e2e test app (#24960) 2022-03-17 15:51:17 -04:00
Liam DeBeasi
02b7baf6a6 chore() bump stencil to 2.14.2 (#24959) 2022-03-17 14:46:32 -04:00
Liam DeBeasi
66e125bc5e test(vue): update vue test app dependencies (#24955) 2022-03-17 13:06:48 -04:00
Liam DeBeasi
fc0d7282f5 merge release-6.0.12
v6.0.12
2022-03-16 09:37:18 -04:00
github-actions
babe7d63ef v6.0.12 2022-03-16 13:20:38 +00:00
Liam DeBeasi
8a97f6b5c9 fix(toast): screen readers now announce toasts when presented (#24937)
resolves #22333
2022-03-15 09:14:48 -04:00
Liam DeBeasi
4534c8bc0b fix(vue): tapping the active tab button now correctly resets the tab stack (#24935)
resolves #24934
2022-03-14 14:44:47 -04:00
Amanda Smith
d46e1e8506 fix(datetime): reinit behavior on presentation change (#24828)
Co-authored-by: Sean Perkins <sean@ionic.io>
2022-03-11 15:45:46 -06:00
Sean Perkins
981eeba0e1 docs(modal): accessing modal element for controller modals (#24896)
Resolves #24673
2022-03-11 14:47:59 -05:00
Sean Perkins
c666deca4d test(refresher): pull to refresh test (#24903) 2022-03-10 14:17:48 -05:00
Liam DeBeasi
8e8783190f merge release-6.0.11
v6.0.11
2022-03-10 08:54:28 -05:00
Amanda Smith
d5efa11331 fix(tabs): angular, fire willChange event before selected tab changes (#24910)
Co-authored-by: Sean Perkins <sean@ionic.io>
2022-03-09 14:34:51 -06:00
Liam DeBeasi
3e7dfd5f73 chore(): add core.d.ts and core.json to gitignore but ensure they are published (#24911) 2022-03-09 15:29:33 -05:00
github-actions
c45b38f28a v6.0.11 2022-03-09 20:24:14 +00:00
Sean Perkins
44a6f032e7 test(footer): fade e2e test (#24901) 2022-03-09 10:53:29 -05:00
Sean Perkins
b193b83c7c test(infinite-scroll): scroll threshold (#24902) 2022-03-09 10:31:10 -05:00
Liam DeBeasi
11a5edfd9d docs(): update docs json file (#24899) 2022-03-08 10:22:44 -05:00
Sean Perkins
77a697ccd7 test(vue): formatting error in test (#24897) 2022-03-07 18:12:32 -05:00
Liam DeBeasi
805907af4e perf(): improve treeshaking functionality (#24895)
resolves #24280
2022-03-07 15:23:29 -05:00
Liam DeBeasi
8ed948e647 docs(infinite-scroll): update typings for Vue example (#24892)
Co-authored-by: ThomasHambach <ThomasHambach@users.noreply.github.com>
2022-03-07 11:05:30 -05:00
SirMathelot
f6cde30d3e docs(button): fix fill attribute typo (#24890) 2022-03-07 08:24:05 -06:00
Liam DeBeasi
2ac9105796 fix(ios): swipe to go back now works in rtl mode (#24866)
resolves #19488
2022-03-07 08:59:21 -05:00
Liam DeBeasi
331ce6d676 fix(datetime): time picker now scrolls to correct value (#24879)
resolves #24878
2022-03-07 08:50:17 -05:00
Liam DeBeasi
65b43aae2b chore(): run build to update readme files (#24880) 2022-03-04 14:14:57 -05:00
Liam DeBeasi
9154ce3bdc test(): add correct screenshot action file name (#24874) 2022-03-03 14:51:09 -05:00
Liam DeBeasi
c7f7db915a test: add stubbed update screenshot action (#24873) 2022-03-03 14:24:09 -05:00
Liam DeBeasi
479c1bac48 merge release-6.0.10
v6.0.10
2022-03-02 17:42:23 -05:00
Lars Mikkelsen
c87366b186 docs(virtual-scroll): fix links to guides (#24869) 2022-03-02 15:47:16 -06:00
github-actions
56b550e220 v6.0.10 2022-03-02 19:15:37 +00:00
flroent
7131e449e5 docs(loading): remove extra script tag from vue example 2022-03-02 12:13:57 -05:00
Liam DeBeasi
4b5843ed5d test(): update other tests to use the new path update (#24841) 2022-03-02 09:23:18 -05:00
Sean Perkins
aacb58a322 fix(datetime): persist minutes column on hour change (#24829)
Resolves #24821
2022-02-28 13:51:52 -05:00
Liam DeBeasi
e4ec572043 fix(modal): sheet modal now allows input focusing when backdrop disabled (#24840)
resolves #24581
2022-02-28 13:27:12 -05:00
Liam DeBeasi
9e0917597c docs(datetime): remove duplicate timezone information (#24851) 2022-02-25 15:35:17 -05:00
rafi365
5faf51caef docs(item): remove extra > in react example (#24844) 2022-02-25 14:38:25 -05:00
Liam DeBeasi
5baeeb1172 docs(): core.json now added to repo (#24850) 2022-02-25 14:37:43 -05:00
Sean Perkins
ea4a9bb694 fix(item-sliding): close() will maintain disabled state (#24847)
Resolves #24747
2022-02-25 11:49:03 -05:00
Sean Perkins
836c01c73e fix(modal): re-enable swipe gestures when modal is dismissed (#24846)
Resolves #24817
2022-02-24 15:57:18 -05:00
Liam DeBeasi
3d0f99904f fix(modal): .ion-page element is now correctly added (#24811)
resolves #24809
2022-02-24 09:28:52 -05:00
Sean Perkins
c35b898f1d fix(datetime): confirm method now uses selected date (#24827)
Resolves #24823
2022-02-23 15:14:53 -05:00
Sean Perkins
33292fbd92 test(datetime): listen for modal event in zoom out test (#24839) 2022-02-23 14:37:03 -05:00
Sean Perkins
1ac8ffb1b7 merge release-6.0.9
v6.0.9
2022-02-23 13:14:33 -05:00
github-actions
1f86b4ee5a v6.0.9 2022-02-23 17:32:34 +00:00
Sean Perkins
62878238fc test(datetime): event for calendar month text change (#24836) 2022-02-23 12:28:13 -05:00
Liam DeBeasi
b6d7e1c757 fix(datetime): month picker no longer gives duplicate months on ios 14 and older (#24792)
resolves #24663
2022-02-23 08:42:51 -05:00
Sean Perkins
b0ac7de168 fix(datetime): improve datetime sizing in modals (#24762)
Resolves #23992
2022-02-22 16:05:39 -05:00
Sean Perkins
7f1086740b test(datetime): getToday unit test (#24830) 2022-02-22 10:30:06 -05:00
Nikhil Arroju
cd05961ab1 docs(datetime): ISO Format examples fix (#24818) 2022-02-22 09:22:15 -06:00
Mat Zaremba
1c3b3791d0 docs(infinite-scroll): use proper equality check (#24767) 2022-02-22 09:02:54 -06:00
Liam DeBeasi
8246112ca1 fix(toast): toast is now correctly excluded from focus trapping (#24816)
resolves #24733
2022-02-22 08:56:30 -05:00
Sean Perkins
19ac2389eb fix(img): draggable attribute is now inherited to inner img element (#24781)
Resolves #21325

Co-authored-by: Celilsemi Sam Erkiner <celilsemi@erkiner.com>
Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
2022-02-21 11:50:57 -05:00
Amanda Smith
243f67362f fix(popover): default alignment to 'center' for ios mode (#24815) 2022-02-18 13:39:03 -06:00
Liam DeBeasi
0a8f44359a test(): run e2e tests in production mode (#24812) 2022-02-18 14:21:43 -05:00
Liam DeBeasi
2fc2de5177 fix(select): interface components now show correctly (#24810)
resolves #24807
2022-02-18 13:53:56 -05:00
Liam DeBeasi
99c91eff87 fix(react, vue): scroll is no longer interrupted on ios (#24791)
resolves #24435
2022-02-17 16:41:36 -05:00
Liam DeBeasi
16647b2c72 fix(datetime): month and year column order is now locale aware (#24802)
resolves #24548
2022-02-17 11:28:09 -05:00
Liam DeBeasi
fca3f56ca4 fix(modal): backdropBreakpoint allows interactivity behind sheet (#24798)
resolves #24797
2022-02-16 11:25:08 -05:00
Liam DeBeasi
866f261f07 merge release-6.0.8
v6.0.8
2022-02-15 16:49:20 -05:00
github-actions
cd486e71ff v6.0.8 2022-02-15 21:26:38 +00:00
Sean Perkins
fd031aa1c3 fix(input): only set native input value if different (#24758)
Resolves #24753
2022-02-14 14:33:17 -05:00
Wael M.B
a093544fdf fix(back-button, breadcrumb, item): flip chevron icons on RTL (#24705) 2022-02-14 09:34:10 -06:00
Victor Berchet
82c41510de doc(router-outlet): the outlet can be used without any framework (#24664) 2022-02-14 10:12:37 -05:00
Amanda Smith
38627ff277 chore(template): clarify that issue should be a link in PR template (#24775) 2022-02-11 09:31:35 -06:00
Liam DeBeasi
b401de1ab3 fix(vue): preserve custom classes on IonPage (#24776)
resolves #24772

Co-authored-by: bnachtweh <bnachtweh@users.noreply.github.com>
2022-02-11 10:11:04 -05:00
Victor Berchet
abc36ae80b fix(router-outlet): navigating to same route with different params now activates component (#24760)
resolves #24653
2022-02-11 10:08:53 -05:00
Sean Perkins
7b3838cc67 fix(datetime): navigate to month within min range (#24759)
Resolves #24757
2022-02-09 13:57:55 -05:00
Victor Berchet
be2205e5a2 fix(router-outlet): getRouteId() returns the params set in setRouteId(). (#24656)
resolves #24652
2022-02-09 13:38:46 -05:00
Victor Berchet
c40ff12052 chore(nav): code cleanup (#24730) 2022-02-09 13:22:53 -05:00
Liam DeBeasi
bae49ec5d9 merge release-6.0.7
v6.0.7
2022-02-09 11:45:36 -05:00
github-actions
87eab37ca8 v6.0.7 2022-02-09 16:00:04 +00:00
Liam DeBeasi
ef46eafc94 fix(angular): inline modals now add .ion-page class correctly (#24751)
resolves #24750
2022-02-09 10:57:21 -05:00
Liam DeBeasi
0580d65821 merge release-6.0.6
v6.0.6
2022-02-09 10:43:26 -05:00
github-actions
da4f750f8d v6.0.6 2022-02-09 14:26:46 +00:00
Liam DeBeasi
34a6ce6d7e tests(vue): enable autounmount and make tests less brittle (#24744) 2022-02-09 09:11:58 -05:00
Adam Duren
6ee7d159ec fix(select): value is selected when given array (#24687)
Resolves #24742
2022-02-08 22:56:48 -05:00
Liam DeBeasi
59bbd52e35 chore(): bump to latest stencil (#24741) 2022-02-08 16:30:33 -05:00
Sean Perkins
596aad435b fix(modal): inline modals inherit ion-page styling (#24723)
Resolves #24706

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
2022-02-08 13:47:51 -05:00
Sean Perkins
c6381ce4f9 fix(input): IME composition mode (#24735)
Resolves #24669
2022-02-08 13:14:05 -05:00
Sean Perkins
4ff9524e10 fix(input): cursor position does not jump to end (#24736)
Resolves #24727
2022-02-08 11:39:02 -05:00
Liam DeBeasi
721a461073 fix(vue): replacing routes now updates location state correctly (#24721)
resolves #24432

Co-authored-by: tigohenryschultz <tigohenryschultz@users.noreply.github.com>
Co-authored-by: yoyo930021 <yoyo930021@users.noreply.github.com>
2022-02-07 15:37:43 -05:00
Sean Perkins
8c22646d66 fix(action-sheet): background includes safe area margin (#24700)
Resolves #24699
2022-02-07 15:13:35 -05:00
Victor Berchet
d40c0c3a09 fix(router): router push with relative path (#24719)
Resolves #24718
2022-02-07 14:26:57 -05:00
Sean Perkins
231d6df622 fix(datetime): minutes only filtered when max hour matches current hour (#24710)
Resolves #24702
2022-02-04 11:40:02 -05:00
Sean Perkins
aab4d306f8 fix(datetime): disable intersection observer during month update (#24713)
Resolves #24712
2022-02-04 11:39:21 -05:00
Liam DeBeasi
df84d155ea ci(e2e): do not run sync automatically (#24717) 2022-02-04 11:27:16 -05:00
Sean Perkins
f5c5c3cffa fix(popover): use alignment with popover options (#24711)
Resolves #24709
2022-02-03 15:56:04 -05:00
Sean Perkins
34cae57acc docs(contributing): update requirements for contributing (#24708) 2022-02-03 14:41:13 -05:00
Liam DeBeasi
2a27befe46 fix(angular-server): publish only the dist directory to avoid import errors (#24701)
resolves #24605
2022-02-03 12:21:31 -05:00
Liam DeBeasi
897ae4a454 fix(angular, react, vue): overlays no longer throw errors when used inside tests (#24681)
resolves #24549, resolves #24590
2022-02-02 15:25:51 -05:00
Sean Perkins
b0c9f097d2 docs(datetime): example formatting date values in ISO-8601 (#24686) 2022-02-02 15:12:51 -05:00
Liam DeBeasi
928c5fbfcb fix(modal, popover): quickly opening modal/popover no longer presents duplicates (#24697) 2022-02-02 13:38:17 -05:00
Liam DeBeasi
0b18260da6 fix(vue): routing history is correctly replaced when overwriting browser history (#24670)
resolves #23873
2022-02-02 13:35:52 -05:00
Liam DeBeasi
bf9b4dfb4e merge release-6.0.5
v6.0.5
2022-02-02 10:03:07 -05:00
260 changed files with 33922 additions and 50721 deletions

View File

@@ -6,6 +6,7 @@ Thanks for your interest in contributing to the Ionic Framework! :tada:
- [Creating an Issue](#creating-an-issue)
* [Creating a Good Code Reproduction](#creating-a-good-code-reproduction)
- [Creating a Pull Request](#creating-a-pull-request)
* [Requirements](#requirements)
* [Setup](#setup)
* [Core](#core)
+ [Modifying Components](#modifying-components)
@@ -41,7 +42,7 @@ Please see our [Contributor Code of Conduct](https://github.com/ionic-team/ionic
## Creating an Issue
* If you have a question about using the framework, please ask on the [Ionic Forum](http://forum.ionicframework.com/) or in the [Ionic Worldwide Slack](http://ionicworldwide.herokuapp.com/) group.
* If you have a question about using the framework, please ask on the [Ionic Forum](http://forum.ionicframework.com/) or in the [Ionic Discord](https://ionic.link/discord).
* It is required that you clearly describe the steps necessary to reproduce the issue you are running into. Although we would love to help our users as much as possible, diagnosing issues without clear reproduction steps is extremely time-consuming and simply not sustainable.
@@ -83,10 +84,16 @@ Without a reliable code reproduction, it is unlikely we will be able to resolve
## Creating a Pull Request
* We appreciate you taking the time to contribute! Before submitting a pull request, we ask that you please [create an issue](#creating-an-issue) that explains the bug or feature request and let us know that you plan on creating a pull request for it. If an issue already exists, please comment on that issue letting us know you would like to submit a pull request for it. This helps us to keep track of the pull request and make sure there isn't duplicated effort.
Before creating a pull request, please read our requirements that explains the minimal details to have your PR considered and merged into the codebase.
* Looking for an issue to fix? Make sure to look through our issues with the [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label!
### Requirements
1. PRs must reference an existing issue that describes the issue or feature being submitted.
2. PRs must have a reproduction app or the issue must include a reproduction app to verify changes against.
3. PRs must include tests covering the changed behavior or a description of why tests cannot be written.
> Note: We appreciate you taking the time to contribute! Before submitting a pull request, please take the time to comment on the issue you are wanting to resolve. This helps us prevent duplicate effort or advise if the team is already addressing the issue.
* Looking for an issue to fix? Look through our issues with the [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label!
### Setup

View File

@@ -49,7 +49,7 @@ body:
label: Ionic Info
description: Please run `ionic info` from within your Ionic Framework project directory and paste the output below.
validations:
requred: true
required: true
- type: textarea
attributes:
label: Additional Information

View File

@@ -26,9 +26,11 @@ Please check the type of change your PR introduces:
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
<!-- Please describe the current behavior that you are modifying. -->
Issue Number: N/A
<!-- Issues are required for both bug fixes and features. -->
Issue URL:
## What is the new behavior?

View File

@@ -33,6 +33,10 @@ runs:
run: npm install
shell: bash
working-directory: ./angular/test/test-app
- name: Sync Built Changes
run: npm run sync
shell: bash
working-directory: ./angular/test/test-app
- name: Run Tests
run: npm run test
shell: bash

View File

@@ -29,9 +29,19 @@ runs:
name: ionic-react-router
path: ./packages/react-router
filename: ReactRouterBuild.zip
- uses: cypress-io/github-action@v2
with:
browser: chrome
headless: true
start: npm run start.ci
working-directory: ./packages/react/test-app
- name: Install Dependencies
run: npm install
shell: bash
working-directory: ./packages/react/test-app
- name: Sync
run: npm run sync
shell: bash
working-directory: ./packages/react/test-app
- name: Build
run: npm run build
shell: bash
working-directory: ./packages/react/test-app
- name: Run E2E Tests
run: npm run e2e
shell: bash
working-directory: ./packages/react/test-app

View File

@@ -29,9 +29,19 @@ runs:
name: ionic-react-router
path: ./packages/react-router
filename: ReactRouterBuild.zip
- uses: cypress-io/github-action@v2
with:
browser: chrome
headless: true
start: npm run start.ci
working-directory: ./packages/react-router/test-app
- name: Install Dependencies
run: npm install
shell: bash
working-directory: ./packages/react-router/test-app
- name: Sync
run: npm run sync
shell: bash
working-directory: ./packages/react-router/test-app
- name: Build
run: npm run build
shell: bash
working-directory: ./packages/react-router/test-app
- name: Run E2E Tests
run: npm run e2e
shell: bash
working-directory: ./packages/react-router/test-app

View File

@@ -21,10 +21,13 @@ jobs:
shell: bash
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# A 1 is required before the timestamp
# as lerna will fail when there is a leading 0
# See https://github.com/lerna/lerna/issues/2840
- name: Create Dev Hash
run: |
echo "HASH=$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_ENV
echo "TIMESTAMP=$(date +%s)" >> $GITHUB_ENV
echo "HASH=1$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_ENV
echo "TIMESTAMP=1$(date +%s)" >> $GITHUB_ENV
echo "CURRENT_VERSION=$(node ./.scripts/bump-version.js)" >> $GITHUB_ENV
shell: bash
- name: Create Dev Build

View File

@@ -0,0 +1,11 @@
name: 'Update Screenshot References'
on:
workflow_dispatch:
jobs:
stub:
steps:
- name: Stub
run: echo 'This is a stub'
shell: bash

View File

@@ -3,6 +3,136 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.13](https://github.com/ionic-team/ionic-framework/compare/v6.0.12...v6.0.13) (2022-03-23)
### Bug Fixes
* **angular:** ngOnDestroy runs inside angular zone ([#24949](https://github.com/ionic-team/ionic-framework/issues/24949)) ([a8fd2d9](https://github.com/ionic-team/ionic-framework/commit/a8fd2d9199ca92d62bce6abf8caccc7709fa5ca1)), closes [#22571](https://github.com/ionic-team/ionic-framework/issues/22571)
* **datetime:** presentation time emits ionChange once ([#24968](https://github.com/ionic-team/ionic-framework/issues/24968)) ([2909b08](https://github.com/ionic-team/ionic-framework/commit/2909b080b7020299a4554c1459b4b190ff739085)), closes [#24967](https://github.com/ionic-team/ionic-framework/issues/24967)
* **popover:** dismissing nested popover via backdrop now works ([#24957](https://github.com/ionic-team/ionic-framework/issues/24957)) ([9e84ef7](https://github.com/ionic-team/ionic-framework/commit/9e84ef7f91d76ca5a1ecaffc7592287267d5368b)), closes [#24954](https://github.com/ionic-team/ionic-framework/issues/24954)
## [6.0.12](https://github.com/ionic-team/ionic-framework/compare/v6.0.11...v6.0.12) (2022-03-16)
### Bug Fixes
* **datetime:** reinit behavior on presentation change ([#24828](https://github.com/ionic-team/ionic-framework/issues/24828)) ([d46e1e8](https://github.com/ionic-team/ionic-framework/commit/d46e1e8506ca5095817b421e9edb37d41451e885))
* **tabs:** angular, fire willChange event before selected tab changes ([#24910](https://github.com/ionic-team/ionic-framework/issues/24910)) ([d5efa11](https://github.com/ionic-team/ionic-framework/commit/d5efa113317eaf874712134dc9b8e4502aa4760f))
* **toast:** screen readers now announce toasts when presented ([#24937](https://github.com/ionic-team/ionic-framework/issues/24937)) ([8a97f6b](https://github.com/ionic-team/ionic-framework/commit/8a97f6b5c9ca1e77c1790abd1e924955b6b6ea27)), closes [#22333](https://github.com/ionic-team/ionic-framework/issues/22333)
* **vue:** tapping the active tab button now correctly resets the tab stack ([#24935](https://github.com/ionic-team/ionic-framework/issues/24935)) ([4534c8b](https://github.com/ionic-team/ionic-framework/commit/4534c8bc0b2bca7ab6eecd9886243116e9a039b7)), closes [#24934](https://github.com/ionic-team/ionic-framework/issues/24934)
## [6.0.11](https://github.com/ionic-team/ionic-framework/compare/v6.0.10...v6.0.11) (2022-03-09)
### Bug Fixes
* **datetime:** time picker now scrolls to correct value ([#24879](https://github.com/ionic-team/ionic-framework/issues/24879)) ([331ce6d](https://github.com/ionic-team/ionic-framework/commit/331ce6d6769900e2aec9e30d35b52cfd40cbb889)), closes [#24878](https://github.com/ionic-team/ionic-framework/issues/24878)
* **ios:** swipe to go back now works in rtl mode ([#24866](https://github.com/ionic-team/ionic-framework/issues/24866)) ([2ac9105](https://github.com/ionic-team/ionic-framework/commit/2ac9105796a0765fabc48592b5b44ac58c568579)), closes [#19488](https://github.com/ionic-team/ionic-framework/issues/19488)
### Performance Improvements
* improve treeshaking functionality ([#24895](https://github.com/ionic-team/ionic-framework/issues/24895)) ([805907a](https://github.com/ionic-team/ionic-framework/commit/805907af4e78179f1acc9cb02263b1ea10d4e3df)), closes [#24280](https://github.com/ionic-team/ionic-framework/issues/24280)
## [6.0.10](https://github.com/ionic-team/ionic-framework/compare/v6.0.9...v6.0.10) (2022-03-02)
### Bug Fixes
* **datetime:** confirm method now uses selected date ([#24827](https://github.com/ionic-team/ionic-framework/issues/24827)) ([c35b898](https://github.com/ionic-team/ionic-framework/commit/c35b898f1dc0fb706446b6971982df48fd72fe6d)), closes [#24823](https://github.com/ionic-team/ionic-framework/issues/24823)
* **datetime:** persist minutes column on hour change ([#24829](https://github.com/ionic-team/ionic-framework/issues/24829)) ([aacb58a](https://github.com/ionic-team/ionic-framework/commit/aacb58a3224e3cc51c731d0c9aa52f52c9276692)), closes [#24821](https://github.com/ionic-team/ionic-framework/issues/24821)
* **item-sliding:** close() will maintain disabled state ([#24847](https://github.com/ionic-team/ionic-framework/issues/24847)) ([ea4a9bb](https://github.com/ionic-team/ionic-framework/commit/ea4a9bb69465f8e97746b36638f0b3a26e45da18)), closes [#24747](https://github.com/ionic-team/ionic-framework/issues/24747)
* **modal:** .ion-page element is now correctly added ([#24811](https://github.com/ionic-team/ionic-framework/issues/24811)) ([3d0f999](https://github.com/ionic-team/ionic-framework/commit/3d0f99904fe192fcb5f529780858a0f25f076af7)), closes [#24809](https://github.com/ionic-team/ionic-framework/issues/24809)
* **modal:** re-enable swipe gestures when modal is dismissed ([#24846](https://github.com/ionic-team/ionic-framework/issues/24846)) ([836c01c](https://github.com/ionic-team/ionic-framework/commit/836c01c73e42caab0101ac4988f0a9b27cf96a5b)), closes [#24817](https://github.com/ionic-team/ionic-framework/issues/24817)
* **modal:** sheet modal now allows input focusing when backdrop disabled ([#24840](https://github.com/ionic-team/ionic-framework/issues/24840)) ([e4ec572](https://github.com/ionic-team/ionic-framework/commit/e4ec572043e22bd2626dbcfd204fc22a7335282c)), closes [#24581](https://github.com/ionic-team/ionic-framework/issues/24581)
## [6.0.9](https://github.com/ionic-team/ionic-framework/compare/v6.0.8...v6.0.9) (2022-02-23)
### Bug Fixes
* **datetime:** improve datetime sizing in modals ([#24762](https://github.com/ionic-team/ionic-framework/issues/24762)) ([b0ac7de](https://github.com/ionic-team/ionic-framework/commit/b0ac7de168c353ba4899cb74a2b38e25fd0cc0f1)), closes [#23992](https://github.com/ionic-team/ionic-framework/issues/23992)
* **datetime:** month and year column order is now locale aware ([#24802](https://github.com/ionic-team/ionic-framework/issues/24802)) ([16647b2](https://github.com/ionic-team/ionic-framework/commit/16647b2c7290389755a4093145788f281c84b7e2)), closes [#24548](https://github.com/ionic-team/ionic-framework/issues/24548)
* **datetime:** month picker no longer gives duplicate months on ios 14 and older ([#24792](https://github.com/ionic-team/ionic-framework/issues/24792)) ([b6d7e1c](https://github.com/ionic-team/ionic-framework/commit/b6d7e1c75740a61dcd02c21692e4d4632fb8df5c)), closes [#24663](https://github.com/ionic-team/ionic-framework/issues/24663)
* **img:** draggable attribute is now inherited to inner img element ([#24781](https://github.com/ionic-team/ionic-framework/issues/24781)) ([19ac238](https://github.com/ionic-team/ionic-framework/commit/19ac2389eb0843173f51a12de41ac808cd8f0569)), closes [#21325](https://github.com/ionic-team/ionic-framework/issues/21325)
* **modal:** backdropBreakpoint allows interactivity behind sheet ([#24798](https://github.com/ionic-team/ionic-framework/issues/24798)) ([fca3f56](https://github.com/ionic-team/ionic-framework/commit/fca3f56ca4568e63fd493debda088263caa86c64)), closes [#24797](https://github.com/ionic-team/ionic-framework/issues/24797)
* **popover:** default alignment to 'center' for ios mode ([#24815](https://github.com/ionic-team/ionic-framework/issues/24815)) ([243f673](https://github.com/ionic-team/ionic-framework/commit/243f67362f25699bdb373be6b72cb9c14dc95a32))
* **react, vue:** scroll is no longer interrupted on ios ([#24791](https://github.com/ionic-team/ionic-framework/issues/24791)) ([99c91ef](https://github.com/ionic-team/ionic-framework/commit/99c91eff8764c9a1630adedab6a9765dd9754f7d)), closes [#24435](https://github.com/ionic-team/ionic-framework/issues/24435)
* **select:** interface components now show correctly ([#24810](https://github.com/ionic-team/ionic-framework/issues/24810)) ([2fc2de5](https://github.com/ionic-team/ionic-framework/commit/2fc2de51771f4a5c3f20c6071284096e5bf31ec8)), closes [#24807](https://github.com/ionic-team/ionic-framework/issues/24807)
* **toast:** toast is now correctly excluded from focus trapping ([#24816](https://github.com/ionic-team/ionic-framework/issues/24816)) ([8246112](https://github.com/ionic-team/ionic-framework/commit/8246112ca12f90edb98629ab82e27a792a1fafad)), closes [#24733](https://github.com/ionic-team/ionic-framework/issues/24733)
## [6.0.8](https://github.com/ionic-team/ionic-framework/compare/v6.0.7...v6.0.8) (2022-02-15)
### Bug Fixes
* **back-button, breadcrumb, item:** flip chevron icons on RTL ([#24705](https://github.com/ionic-team/ionic-framework/issues/24705)) ([a093544](https://github.com/ionic-team/ionic-framework/commit/a093544fdfc438ed03024285b2a35c5f645ea011))
* **datetime:** navigate to month within min range ([#24759](https://github.com/ionic-team/ionic-framework/issues/24759)) ([7b3838c](https://github.com/ionic-team/ionic-framework/commit/7b3838cc670de7845bb5937d204e86cdba93b6e6)), closes [#24757](https://github.com/ionic-team/ionic-framework/issues/24757)
* **input:** only set native input value if different ([#24758](https://github.com/ionic-team/ionic-framework/issues/24758)) ([fd031aa](https://github.com/ionic-team/ionic-framework/commit/fd031aa1c3f05b7bfa3e0a0ee2a4793e29e22df5)), closes [#24753](https://github.com/ionic-team/ionic-framework/issues/24753)
* **router-outlet:** getRouteId() returns the params set in setRouteId(). ([#24656](https://github.com/ionic-team/ionic-framework/issues/24656)) ([be2205e](https://github.com/ionic-team/ionic-framework/commit/be2205e5a2b2f8778bd1f7b4ea5cae0bf96f9ef3)), closes [#24652](https://github.com/ionic-team/ionic-framework/issues/24652)
* **router-outlet:** navigating to same route with different params now activates component ([#24760](https://github.com/ionic-team/ionic-framework/issues/24760)) ([abc36ae](https://github.com/ionic-team/ionic-framework/commit/abc36ae80b060a659f7557ad90efe98b78f5ead9)), closes [#24653](https://github.com/ionic-team/ionic-framework/issues/24653)
* **vue:** preserve custom classes on IonPage ([#24776](https://github.com/ionic-team/ionic-framework/issues/24776)) ([b401de1](https://github.com/ionic-team/ionic-framework/commit/b401de1ab3385c67cc476ff90971ce131cefcc3f)), closes [#24772](https://github.com/ionic-team/ionic-framework/issues/24772)
## [6.0.7](https://github.com/ionic-team/ionic-framework/compare/v6.0.6...v6.0.7) (2022-02-09)
### Bug Fixes
* **angular:** inline modals now add .ion-page class correctly ([#24751](https://github.com/ionic-team/ionic-framework/issues/24751)) ([ef46eaf](https://github.com/ionic-team/ionic-framework/commit/ef46eafc9476a85ea3369e542f528d01d3cca0a8)), closes [#24750](https://github.com/ionic-team/ionic-framework/issues/24750)
## [6.0.6](https://github.com/ionic-team/ionic-framework/compare/v6.0.5...v6.0.6) (2022-02-09)
### Bug Fixes
* **action-sheet:** background includes safe area margin ([#24700](https://github.com/ionic-team/ionic-framework/issues/24700)) ([8c22646](https://github.com/ionic-team/ionic-framework/commit/8c22646d66e2077fc88aaacf350330097733bb9b)), closes [#24699](https://github.com/ionic-team/ionic-framework/issues/24699)
* **angular-server:** publish only the dist directory to avoid import errors ([#24701](https://github.com/ionic-team/ionic-framework/issues/24701)) ([2a27bef](https://github.com/ionic-team/ionic-framework/commit/2a27befe463832b9ca7709ba22421abbdaa4cfa4)), closes [#24605](https://github.com/ionic-team/ionic-framework/issues/24605)
* **angular, react, vue:** overlays no longer throw errors when used inside tests ([#24681](https://github.com/ionic-team/ionic-framework/issues/24681)) ([897ae4a](https://github.com/ionic-team/ionic-framework/commit/897ae4a4546ac0dd811125d5513ef23d133a1589)), closes [#24549](https://github.com/ionic-team/ionic-framework/issues/24549) [#24590](https://github.com/ionic-team/ionic-framework/issues/24590)
* **datetime:** disable intersection observer during month update ([#24713](https://github.com/ionic-team/ionic-framework/issues/24713)) ([aab4d30](https://github.com/ionic-team/ionic-framework/commit/aab4d306f80851bfd8a02a6361e26d60faeaadb4)), closes [#24712](https://github.com/ionic-team/ionic-framework/issues/24712)
* **datetime:** minutes only filtered when max hour matches current hour ([#24710](https://github.com/ionic-team/ionic-framework/issues/24710)) ([231d6df](https://github.com/ionic-team/ionic-framework/commit/231d6df622c1f5dd9ecdff6fed8f61a4bff332df)), closes [#24702](https://github.com/ionic-team/ionic-framework/issues/24702)
* **input:** cursor position does not jump to end ([#24736](https://github.com/ionic-team/ionic-framework/issues/24736)) ([4ff9524](https://github.com/ionic-team/ionic-framework/commit/4ff9524e1057aa487069b5982c5f1ecdf51d982b)), closes [#24727](https://github.com/ionic-team/ionic-framework/issues/24727)
* **input:** IME composition mode ([#24735](https://github.com/ionic-team/ionic-framework/issues/24735)) ([c6381ce](https://github.com/ionic-team/ionic-framework/commit/c6381ce4f90707774d1c8662bba874f7b306bd1c)), closes [#24669](https://github.com/ionic-team/ionic-framework/issues/24669)
* **modal, popover:** quickly opening modal/popover no longer presents duplicates ([#24697](https://github.com/ionic-team/ionic-framework/issues/24697)) ([928c5fb](https://github.com/ionic-team/ionic-framework/commit/928c5fbfcbf3ef1b2c3074464fc20dcf1fe143ae))
* **modal:** inline modals inherit ion-page styling ([#24723](https://github.com/ionic-team/ionic-framework/issues/24723)) ([596aad4](https://github.com/ionic-team/ionic-framework/commit/596aad435b5102307da89dd626ca4682b78db452)), closes [#24706](https://github.com/ionic-team/ionic-framework/issues/24706)
* **popover:** use alignment with popover options ([#24711](https://github.com/ionic-team/ionic-framework/issues/24711)) ([f5c5c3c](https://github.com/ionic-team/ionic-framework/commit/f5c5c3cffa4f34046b0e9471a9f193db3772180e)), closes [#24709](https://github.com/ionic-team/ionic-framework/issues/24709)
* **router:** router push with relative path ([#24719](https://github.com/ionic-team/ionic-framework/issues/24719)) ([d40c0c3](https://github.com/ionic-team/ionic-framework/commit/d40c0c3a0993eaefbe5107e98958c6b0699a62c2)), closes [#24718](https://github.com/ionic-team/ionic-framework/issues/24718)
* **select:** value is selected when given array ([#24687](https://github.com/ionic-team/ionic-framework/issues/24687)) ([6ee7d15](https://github.com/ionic-team/ionic-framework/commit/6ee7d159ecfff3382fadb524c5c430172d40c267)), closes [#24742](https://github.com/ionic-team/ionic-framework/issues/24742)
* **vue:** replacing routes now updates location state correctly ([#24721](https://github.com/ionic-team/ionic-framework/issues/24721)) ([721a461](https://github.com/ionic-team/ionic-framework/commit/721a461073bbd8e7218cd5ce02965d673f5a03e8)), closes [#24432](https://github.com/ionic-team/ionic-framework/issues/24432)
* **vue:** routing history is correctly replaced when overwriting browser history ([#24670](https://github.com/ionic-team/ionic-framework/issues/24670)) ([0b18260](https://github.com/ionic-team/ionic-framework/commit/0b18260da64334d8211c5a0cd806f7416274fc5e)), closes [#23873](https://github.com/ionic-team/ionic-framework/issues/23873)
## [6.0.5](https://github.com/ionic-team/ionic-framework/compare/v6.0.4...v6.0.5) (2022-02-02)

View File

@@ -3,6 +3,82 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.13](https://github.com/ionic-team/ionic/compare/v6.0.12...v6.0.13) (2022-03-23)
### Bug Fixes
* **angular:** ngOnDestroy runs inside angular zone ([#24949](https://github.com/ionic-team/ionic/issues/24949)) ([a8fd2d9](https://github.com/ionic-team/ionic/commit/a8fd2d9199ca92d62bce6abf8caccc7709fa5ca1)), closes [#22571](https://github.com/ionic-team/ionic/issues/22571)
## [6.0.12](https://github.com/ionic-team/ionic/compare/v6.0.11...v6.0.12) (2022-03-16)
### Bug Fixes
* **tabs:** angular, fire willChange event before selected tab changes ([#24910](https://github.com/ionic-team/ionic/issues/24910)) ([d5efa11](https://github.com/ionic-team/ionic/commit/d5efa113317eaf874712134dc9b8e4502aa4760f))
## [6.0.11](https://github.com/ionic-team/ionic/compare/v6.0.10...v6.0.11) (2022-03-09)
**Note:** Version bump only for package @ionic/angular
## [6.0.10](https://github.com/ionic-team/ionic/compare/v6.0.9...v6.0.10) (2022-03-02)
### Bug Fixes
* **modal:** .ion-page element is now correctly added ([#24811](https://github.com/ionic-team/ionic/issues/24811)) ([3d0f999](https://github.com/ionic-team/ionic/commit/3d0f99904fe192fcb5f529780858a0f25f076af7)), closes [#24809](https://github.com/ionic-team/ionic/issues/24809)
## [6.0.9](https://github.com/ionic-team/ionic/compare/v6.0.8...v6.0.9) (2022-02-23)
**Note:** Version bump only for package @ionic/angular
## [6.0.8](https://github.com/ionic-team/ionic/compare/v6.0.7...v6.0.8) (2022-02-15)
**Note:** Version bump only for package @ionic/angular
## [6.0.7](https://github.com/ionic-team/ionic/compare/v6.0.6...v6.0.7) (2022-02-09)
### Bug Fixes
* **angular:** inline modals now add .ion-page class correctly ([#24751](https://github.com/ionic-team/ionic/issues/24751)) ([ef46eaf](https://github.com/ionic-team/ionic/commit/ef46eafc9476a85ea3369e542f528d01d3cca0a8)), closes [#24750](https://github.com/ionic-team/ionic/issues/24750)
## [6.0.6](https://github.com/ionic-team/ionic/compare/v6.0.5...v6.0.6) (2022-02-09)
**Note:** Version bump only for package @ionic/angular
## [6.0.5](https://github.com/ionic-team/ionic/compare/v6.0.4...v6.0.5) (2022-02-02)
**Note:** Version bump only for package @ionic/angular

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "6.0.5",
"version": "6.0.13",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

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

View File

@@ -57,10 +57,10 @@ export class IonTabs {
onPageSelected(detail: StackEvent): void {
const stackId = detail.enteringView.stackId;
if (detail.tabSwitch && stackId !== undefined) {
this.ionTabsWillChange.emit({ tab: stackId });
if (this.tabBar) {
this.tabBar.selectedTab = stackId;
}
this.ionTabsWillChange.emit({ tab: stackId });
this.ionTabsDidChange.emit({ tab: stackId });
}
}

View File

@@ -1,17 +1,14 @@
import { LocationStrategy } from '@angular/common';
import { ElementRef, OnChanges, OnDestroy, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
import { ElementRef, OnChanges, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
import { Router, RouterLink } from '@angular/router';
import { AnimationBuilder, RouterDirection } from '@ionic/core';
import { Subscription } from 'rxjs';
import { NavController } from '../../providers/nav-controller';
@Directive({
selector: '[routerLink]',
})
export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy {
private subscription?: Subscription;
export class RouterLinkDelegateDirective implements OnInit, OnChanges {
@Input()
routerDirection: RouterDirection = 'forward';
@@ -34,12 +31,6 @@ export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy
this.updateTargetUrlAndHref();
}
ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
private updateTargetUrlAndHref() {
if (this.routerLink?.urlTree) {
const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree));

View File

@@ -139,7 +139,7 @@ export class StackController {
enteringView.ref.changeDetectorRef.reattach();
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false, animationBuilder)
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location, this.zone))
.then(() => ({
enteringView,
direction,
@@ -201,7 +201,7 @@ export class StackController {
this.skipTransition = true;
this.pop(1);
} else if (this.activeView) {
cleanup(this.activeView, this.views, this.views, this.location);
cleanup(this.activeView, this.views, this.views, this.location, this.zone);
}
}
@@ -294,11 +294,17 @@ export class StackController {
}
}
const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
const cleanupAsync = (
activeRoute: RouteView,
views: RouteView[],
viewsSnapshot: RouteView[],
location: Location,
zone: NgZone
) => {
if (typeof (requestAnimationFrame as any) === 'function') {
return new Promise<void>((resolve) => {
requestAnimationFrame(() => {
cleanup(activeRoute, views, viewsSnapshot, location);
cleanup(activeRoute, views, viewsSnapshot, location, zone);
resolve();
});
});
@@ -306,8 +312,18 @@ const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot:
return Promise.resolve();
};
const cleanup = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView);
const cleanup = (
activeRoute: RouteView,
views: RouteView[],
viewsSnapshot: RouteView[],
location: Location,
zone: NgZone
) => {
/**
* Re-enter the Angular zone when destroying page components. This will allow
* lifecycle events (`ngOnDestroy`) to be run inside the Angular zone.
*/
zone.run(() => viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView));
views.forEach((view) => {
/**

View File

@@ -73,7 +73,7 @@ export declare interface IonModal extends Components.IonModal {
@Component({
selector: 'ion-modal',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
template: `<div class="ion-page" *ngIf="isCmpOpen"><ng-container [ngTemplateOutlet]="template"></ng-container></div>`,
inputs: [
'animated',
'backdropBreakpoint',

View File

@@ -10,6 +10,7 @@ describe('Modals', () => {
cy.get('app-modal-example h2').should('have.text', '123');
cy.get('app-modal-example h3').should('have.text', '321');
cy.get('#modalInstance').should('have.text', 'true');
cy.get('#onWillDismiss').should('have.text', 'false');
cy.get('#onDidDismiss').should('have.text', 'false');
@@ -52,12 +53,27 @@ describe('Modals: Inline', () => {
cy.get('ion-list ion-item').should('not.exist');
});
it('should have items after 1500ms', () => {
cy.wait(1500);
it('should have items after opening', () => {
cy.get('#open-modal').click();
cy.get('ion-list ion-item:nth-child(1)').should('have.text', 'A');
cy.get('ion-list ion-item:nth-child(2)').should('have.text', 'B');
cy.get('ion-list ion-item:nth-child(3)').should('have.text', 'C');
cy.get('ion-list ion-item:nth-child(4)').should('have.text', 'D');
});
it('should have a div with .ion-page after opening', () => {
cy.get('#open-modal').click();
cy.get('ion-modal').children('.ion-page').should('exist');
});
it('should remove .ion-page when closing the modal', () => {
cy.get('#open-modal').click();
cy.get('ion-modal').children('.ion-page').should('exist');
cy.get('ion-modal').trigger('click', 20, 20);
cy.get('ion-modal').children('.ion-page').should('not.exist');
})
});

View File

@@ -20,6 +20,12 @@ describe('Nested Outlet', () => {
cy.ionPageVisible('app-nested-outlet-page2');
cy.get('ion-router-outlet ion-router-outlet app-nested-outlet-page2 h1').should('have.text', 'Nested page 2');
cy.get('#goto-nested-page1').click();
cy.ionPageVisible('app-nested-outlet-page');
cy.get('#goto-nested-page2').click();
});
});

View File

@@ -19,6 +19,30 @@ describe('Tabs', () => {
tab.find('.segment-changed').should('have.text', 'false');
});
describe('when navigating between tabs', () => {
it('should emit ionTabsWillChange before setting the selected tab', () => {
cy.get('#ionTabsWillChangeCounter').should('have.text', '1');
cy.get('#ionTabsWillChangeEvent').should('have.text', 'account');
cy.get('#ionTabsWillChangeSelectedTab').should('have.text', '');
cy.get('#ionTabsDidChangeCounter').should('have.text', '1');
cy.get('#ionTabsDidChangeEvent').should('have.text', 'account');
cy.get('#ionTabsDidChangeSelectedTab').should('have.text', 'account');
cy.get('#tab-button-contact').click();
cy.get('#ionTabsWillChangeCounter').should('have.text', '2');
cy.get('#ionTabsWillChangeEvent').should('have.text', 'contact');
cy.get('#ionTabsWillChangeSelectedTab').should('have.text', 'account');
cy.get('#ionTabsDidChangeCounter').should('have.text', '2');
cy.get('#ionTabsDidChangeEvent').should('have.text', 'contact');
cy.get('#ionTabsDidChangeSelectedTab').should('have.text', 'contact');
})
});
it('should simulate stack + double tab click', () => {
let tab = getSelectedTab();
tab.find('#goto-tab1-page2').click();

View File

@@ -18,8 +18,8 @@
"@angular/platform-browser-dynamic": "^13.1.3",
"@angular/platform-server": "^13.1.3",
"@angular/router": "^13.1.3",
"@ionic/angular": "^5.3.1",
"@ionic/angular-server": "^5.3.1",
"@ionic/angular": "^6.0.12",
"@ionic/angular-server": "^6.0.12",
"@nguniversal/express-engine": "^12.1.1",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
@@ -3242,53 +3242,44 @@
"dev": true
},
"node_modules/@ionic/angular": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.8.2.tgz",
"integrity": "sha512-m2dE8QcWyCkv4yB+6+9j9Qu6hI28+chWT7A6nmbD7A360bm26LjdvKiJaCEQUxIXH7CpFuYcURZvRrn9gMscNg==",
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-6.0.12.tgz",
"integrity": "sha512-gWY92k6tPefj2OG1ogOALfqEQah78l1OGUwjs3rOg3pliWH4aV0dq3CzgmJJwIBSnvAdW+iSSOfEoMbEVV0b7A==",
"dependencies": {
"@ionic/core": "5.8.2",
"tslib": "^1.9.3"
"@ionic/core": "^6.0.12",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": ">=8.2.7",
"@angular/forms": ">=8.2.7",
"@angular/router": ">=8.2.7",
"rxjs": ">=6.2.0",
"zone.js": ">=0.8.26"
"@angular/core": ">=12.0.0",
"@angular/forms": ">=12.0.0",
"@angular/router": ">=12.0.0",
"rxjs": ">=6.6.0",
"zone.js": ">=0.11.0"
}
},
"node_modules/@ionic/angular-server": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@ionic/angular-server/-/angular-server-5.8.2.tgz",
"integrity": "sha512-N141OZOmJZ0N6ASQelBSZnEzxHG8zoaVjwIgT41Q7mlEDC19jD0TtzyTzk8Ac4X/dFulKF2u6H32JG75oSEdhg==",
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@ionic/angular-server/-/angular-server-6.0.12.tgz",
"integrity": "sha512-A5vqoePVfo0bxyTKHEtagJpu8OEFMOzv/zWG4FastDsWu1K3Vx/EBnwdoq9WPQ0upbMOAWf1R4SUuNny5ZSdJA==",
"dependencies": {
"tslib": "^1.9.0"
"tslib": "^2.2.0"
},
"peerDependencies": {
"@angular/core": ">=8.2.7",
"@angular/platform-server": ">=8.2.7",
"@ionic/angular": "*",
"rxjs": ">=6.2.0",
"zone.js": ">=0.8.26"
"@angular/core": ">=12.0.0",
"@angular/platform-server": ">=12.0.0",
"@ionic/angular": "^6.0.2",
"rxjs": ">=6.6.0",
"zone.js": ">=0.11.0"
}
},
"node_modules/@ionic/angular-server/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@ionic/angular/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@ionic/core": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.8.2.tgz",
"integrity": "sha512-BWIh6hyhq+tzVvebPMfRa+IhTV5/yXh4wO3a0U3Qo4PL4KDzWyBJfoiPi6bOEc+BAlFnCtOooTTXTumgXXOojg==",
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.12.tgz",
"integrity": "sha512-WpcVzSXBuvdHUZuNg1NTdnF4Ur7AGNCpgc4t4lyjPcEr0j//E855gd6GtUD7imNFERdtrvVC86JTpj5t0L+OMg==",
"dependencies": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.3",
"@stencil/core": "^2.14.1",
"ionicons": "^6.0.0",
"tslib": "^2.1.0"
}
},
@@ -4320,9 +4311,9 @@
"dev": true
},
"node_modules/@stencil/core": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.8.1.tgz",
"integrity": "sha512-iv9J6oLO/lv7/aO45M05yw3pp1J7olY400vlOZgdMVs3s5zHfalY1ZPYM0KyqU4+7DZuadKYbd0aQZ/g2PInZw==",
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.14.2.tgz",
"integrity": "sha512-NMC5Xi8sPFJxaO4rz6CbMHuD6PteE/RJWtjrbkusmpjKRtMXkfZJPIgOrleZ4xO+vXcNyL535Ru7vUADqEsTiQ==",
"bin": {
"stencil": "bin/stencil"
},
@@ -11231,11 +11222,23 @@
}
},
"node_modules/ionicons": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.3.tgz",
"integrity": "sha512-L71djrMi8pAad66tpwdnO1vwcyluCFvehzxU1PpH1k/HpYBZhZ5IaYhqXipmqUvu5aEbd4cbRguYyI5Fd4bxTw==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-6.0.1.tgz",
"integrity": "sha512-xQekOJsxH82O7oB+3F60zeRggCdND9pJ/k0E6IJDVUGGlCj5mlyFqNgxUimytKgstPGv3S+3EmCxjefvtGgWUg==",
"dependencies": {
"@stencil/core": "^2.5.0"
"@stencil/core": "~2.12.0"
}
},
"node_modules/ionicons/node_modules/@stencil/core": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.12.1.tgz",
"integrity": "sha512-u24TZ+FEvjnZt5ZgIkLjLpUNsO6Ml3mUZqwmqk81w6RWWz75hgB5p4RFI5rvuErFeh2xvMIGo+pNdG24XUBz1A==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=12.10.0",
"npm": ">=6.0.0"
}
},
"node_modules/ip": {
@@ -11882,8 +11885,7 @@
"node_modules/jsonc-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
"integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
"dev": true
"integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA=="
},
"node_modules/jsonfile": {
"version": "3.0.1",
@@ -20944,43 +20946,30 @@
"dev": true
},
"@ionic/angular": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.8.2.tgz",
"integrity": "sha512-m2dE8QcWyCkv4yB+6+9j9Qu6hI28+chWT7A6nmbD7A360bm26LjdvKiJaCEQUxIXH7CpFuYcURZvRrn9gMscNg==",
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-6.0.12.tgz",
"integrity": "sha512-gWY92k6tPefj2OG1ogOALfqEQah78l1OGUwjs3rOg3pliWH4aV0dq3CzgmJJwIBSnvAdW+iSSOfEoMbEVV0b7A==",
"requires": {
"@ionic/core": "5.8.2",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
"@ionic/core": "^6.0.12",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
}
},
"@ionic/angular-server": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@ionic/angular-server/-/angular-server-5.8.2.tgz",
"integrity": "sha512-N141OZOmJZ0N6ASQelBSZnEzxHG8zoaVjwIgT41Q7mlEDC19jD0TtzyTzk8Ac4X/dFulKF2u6H32JG75oSEdhg==",
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@ionic/angular-server/-/angular-server-6.0.12.tgz",
"integrity": "sha512-A5vqoePVfo0bxyTKHEtagJpu8OEFMOzv/zWG4FastDsWu1K3Vx/EBnwdoq9WPQ0upbMOAWf1R4SUuNny5ZSdJA==",
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
"tslib": "^2.2.0"
}
},
"@ionic/core": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.8.2.tgz",
"integrity": "sha512-BWIh6hyhq+tzVvebPMfRa+IhTV5/yXh4wO3a0U3Qo4PL4KDzWyBJfoiPi6bOEc+BAlFnCtOooTTXTumgXXOojg==",
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.12.tgz",
"integrity": "sha512-WpcVzSXBuvdHUZuNg1NTdnF4Ur7AGNCpgc4t4lyjPcEr0j//E855gd6GtUD7imNFERdtrvVC86JTpj5t0L+OMg==",
"requires": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.3",
"@stencil/core": "^2.14.1",
"ionicons": "^6.0.0",
"tslib": "^2.1.0"
}
},
@@ -21745,9 +21734,9 @@
"dev": true
},
"@stencil/core": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.8.1.tgz",
"integrity": "sha512-iv9J6oLO/lv7/aO45M05yw3pp1J7olY400vlOZgdMVs3s5zHfalY1ZPYM0KyqU4+7DZuadKYbd0aQZ/g2PInZw=="
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.14.2.tgz",
"integrity": "sha512-NMC5Xi8sPFJxaO4rz6CbMHuD6PteE/RJWtjrbkusmpjKRtMXkfZJPIgOrleZ4xO+vXcNyL535Ru7vUADqEsTiQ=="
},
"@tootallnate/once": {
"version": "1.1.2",
@@ -27057,11 +27046,18 @@
"dev": true
},
"ionicons": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.3.tgz",
"integrity": "sha512-L71djrMi8pAad66tpwdnO1vwcyluCFvehzxU1PpH1k/HpYBZhZ5IaYhqXipmqUvu5aEbd4cbRguYyI5Fd4bxTw==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-6.0.1.tgz",
"integrity": "sha512-xQekOJsxH82O7oB+3F60zeRggCdND9pJ/k0E6IJDVUGGlCj5mlyFqNgxUimytKgstPGv3S+3EmCxjefvtGgWUg==",
"requires": {
"@stencil/core": "^2.5.0"
"@stencil/core": "~2.12.0"
},
"dependencies": {
"@stencil/core": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.12.1.tgz",
"integrity": "sha512-u24TZ+FEvjnZt5ZgIkLjLpUNsO6Ml3mUZqwmqk81w6RWWz75hgB5p4RFI5rvuErFeh2xvMIGo+pNdG24XUBz1A=="
}
}
},
"ip": {
@@ -27555,8 +27551,7 @@
"jsonc-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
"integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
"dev": true
"integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA=="
},
"jsonfile": {
"version": "3.0.1",

View File

@@ -4,19 +4,19 @@
"private": true,
"scripts": {
"ng": "ng",
"start": "npm run sync && ng serve",
"start": "ng serve",
"sync:build": "sh scripts/build-ionic.sh",
"sync": "sh scripts/sync.sh",
"build": "npm run sync && ng build --configuration production --no-progress",
"build": "ng build --configuration production --no-progress",
"lint": "ng lint",
"postinstall": "npm run sync && ngcc",
"postinstall": "ngcc",
"serve:ssr": "node dist/test-app/server/main.js",
"build:ssr": "ng build --prod && ng run test-app:server:production",
"dev:ssr": "ng run test-app:serve-ssr",
"prerender": "ng run test-app:prerender",
"cy.open": "cypress open",
"cy.run": "cypress run",
"test": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.run\" --kill-others --success first",
"test": "concurrently \"npm run start -- --configuration production\" \"wait-on http-get://localhost:4200 && npm run cy.run\" --kill-others --success first",
"test.watch": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.open\" --kill-others --success first"
},
"dependencies": {
@@ -29,8 +29,8 @@
"@angular/platform-browser-dynamic": "^13.1.3",
"@angular/platform-server": "^13.1.3",
"@angular/router": "^13.1.3",
"@ionic/angular": "^5.3.1",
"@ionic/angular-server": "^5.3.1",
"@ionic/angular": "^6.0.12",
"@ionic/angular-server": "^6.0.12",
"@nguniversal/express-engine": "^12.1.1",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",

View File

@@ -12,6 +12,7 @@
<h1>Value</h1>
<h2>{{value}}</h2>
<h3>{{valueFromParams}}</h3>
<p>modal is defined: <span id="modalInstance">{{ !!modal }}</span></p>
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>

View File

@@ -16,6 +16,8 @@ export class ModalExampleComponent implements OnInit, ViewWillLeave, ViewDidEnte
willLeave = 0;
didLeave = 0;
modal: HTMLElement;
constructor(
private modalCtrl: ModalController,
@Optional() public nav: IonNav,

View File

@@ -1,4 +1,6 @@
<ion-modal [isOpen]="true" [breakpoints]="[0.1, 0.5, 1]" [initialBreakpoint]="0.5">
<ion-button id="open-modal">Open Modal</ion-button>
<ion-modal [animated]="false" trigger="open-modal" [breakpoints]="[0.1, 0.5, 1]" [initialBreakpoint]="0.5">
<ng-template>
<ion-content>
<ion-list>

View File

@@ -1,8 +1,16 @@
import { Component } from '@angular/core';
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
@Component({
selector: 'app-nested-outlet-page',
templateUrl: './nested-outlet-page.component.html',
})
export class NestedOutletPageComponent {
export class NestedOutletPageComponent implements OnDestroy, OnInit {
ngOnInit() {
NgZone.assertInAngularZone();
}
ngOnDestroy() {
NgZone.assertInAngularZone();
}
}

View File

@@ -1,6 +1,6 @@
<ion-content>
<h1>Nested page 2</h1>
<p>
<ion-button routerLink="/nested-outlet/page">Go To FIRST</ion-button>
</p>
</ion-content>
<h1>Nested page 2</h1>
<p>
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go To FIRST</ion-button>
</p>
</ion-content>

View File

@@ -1,8 +1,16 @@
import { Component } from '@angular/core';
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
@Component({
selector: 'app-nested-outlet-page2',
templateUrl: './nested-outlet-page2.component.html',
})
export class NestedOutletPage2Component {
export class NestedOutletPage2Component implements OnDestroy, OnInit {
ngOnInit() {
NgZone.assertInAngularZone();
}
ngOnDestroy() {
NgZone.assertInAngularZone();
}
}

View File

@@ -0,0 +1,5 @@
#test {
position: absolute;
bottom: 100px;
left: 0;
}

View File

@@ -1,4 +1,4 @@
<ion-tabs (ionTabsDidChange)="tabChanged($event)">
<ion-tabs (ionTabsDidChange)="tabChanged($event)" (ionTabsWillChange)="tabsWillChange($event)">
<ion-tab-bar>
<ion-tab-button tab="account">
<ion-label>Tab One</ion-label>
@@ -18,5 +18,29 @@
</ion-tab-bar>
</ion-tabs>
<ion-fab horizontal="end" vertical="top">
<ion-fab-button id="tabs-state" style="width: 100px;">{{tabCounter}}.{{tabEvent}}</ion-fab-button>
<ion-fab-button id="tabs-state" style="width: 100px;">{{tabsDidChangeCounter}}.{{tabsDidChangeEvent}}</ion-fab-button>
</ion-fab>
<div id="test">
<ul>
<li>
ionTabsWillChange counter: <span id="ionTabsWillChangeCounter">{{ tabsWillChangeCounter }}</span>
</li>
<li>
ionTabsWillChange event: <span id="ionTabsWillChangeEvent">{{ tabsWillChangeEvent }}</span>
</li>
<li>
ionTabsWillChange selectedTab: <span id="ionTabsWillChangeSelectedTab">{{ tabsWillChangeSelectedTab }}</span>
</li>
</ul>
<ul>
<li>
ionTabsDidChange counter: <span id="ionTabsDidChangeCounter">{{ tabsDidChangeCounter }}</span>
</li>
<li>
ionTabsDidChange event: <span id="ionTabsDidChangeEvent">{{ tabsDidChangeEvent }}</span>
</li>
<li>
ionTabsDidChange selectedTab: <span id="ionTabsDidChangeSelectedTab">{{ tabsDidChangeSelectedTab }}</span>
</li>
</ul>
</div>

View File

@@ -1,15 +1,33 @@
import { Component } from '@angular/core';
import { Component, OnInit, ViewChild } from '@angular/core';
import { IonTabBar } from '@ionic/angular';
@Component({
selector: 'app-tabs',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.css']
})
export class TabsComponent {
tabCounter = 0;
tabEvent = '';
tabsDidChangeCounter = 0;
tabsDidChangeEvent = '';
tabsDidChangeSelectedTab = '';
tabChanged(ev: {tab: string}) {
this.tabCounter++;
this.tabEvent = ev.tab;
tabsWillChangeCounter = 0;
tabsWillChangeEvent = '';
tabsWillChangeSelectedTab = '';
@ViewChild(IonTabBar) tabBar: IonTabBar;
tabChanged(ev: { tab: string }) {
console.log('ionTabsDidChange', this.tabBar.selectedTab);
this.tabsDidChangeCounter++;
this.tabsDidChangeEvent = ev.tab;
this.tabsDidChangeSelectedTab = this.tabBar.selectedTab;
}
tabsWillChange(ev: { tab: string }) {
console.log('ionTabsWillChange', this.tabBar.selectedTab);
this.tabsWillChangeCounter++;
this.tabsWillChangeEvent = ev.tab;
this.tabsWillChangeSelectedTab = this.tabBar.selectedTab;
}
}

View File

@@ -3,6 +3,128 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.13](https://github.com/ionic-team/ionic/compare/v6.0.12...v6.0.13) (2022-03-23)
### Bug Fixes
* **datetime:** presentation time emits ionChange once ([#24968](https://github.com/ionic-team/ionic/issues/24968)) ([2909b08](https://github.com/ionic-team/ionic/commit/2909b080b7020299a4554c1459b4b190ff739085)), closes [#24967](https://github.com/ionic-team/ionic/issues/24967)
* **popover:** dismissing nested popover via backdrop now works ([#24957](https://github.com/ionic-team/ionic/issues/24957)) ([9e84ef7](https://github.com/ionic-team/ionic/commit/9e84ef7f91d76ca5a1ecaffc7592287267d5368b)), closes [#24954](https://github.com/ionic-team/ionic/issues/24954)
## [6.0.12](https://github.com/ionic-team/ionic/compare/v6.0.11...v6.0.12) (2022-03-16)
### Bug Fixes
* **datetime:** reinit behavior on presentation change ([#24828](https://github.com/ionic-team/ionic/issues/24828)) ([d46e1e8](https://github.com/ionic-team/ionic/commit/d46e1e8506ca5095817b421e9edb37d41451e885))
* **toast:** screen readers now announce toasts when presented ([#24937](https://github.com/ionic-team/ionic/issues/24937)) ([8a97f6b](https://github.com/ionic-team/ionic/commit/8a97f6b5c9ca1e77c1790abd1e924955b6b6ea27)), closes [#22333](https://github.com/ionic-team/ionic/issues/22333)
## [6.0.11](https://github.com/ionic-team/ionic/compare/v6.0.10...v6.0.11) (2022-03-09)
### Bug Fixes
* **datetime:** time picker now scrolls to correct value ([#24879](https://github.com/ionic-team/ionic/issues/24879)) ([331ce6d](https://github.com/ionic-team/ionic/commit/331ce6d6769900e2aec9e30d35b52cfd40cbb889)), closes [#24878](https://github.com/ionic-team/ionic/issues/24878)
* **ios:** swipe to go back now works in rtl mode ([#24866](https://github.com/ionic-team/ionic/issues/24866)) ([2ac9105](https://github.com/ionic-team/ionic/commit/2ac9105796a0765fabc48592b5b44ac58c568579)), closes [#19488](https://github.com/ionic-team/ionic/issues/19488)
### Performance Improvements
* improve treeshaking functionality ([#24895](https://github.com/ionic-team/ionic/issues/24895)) ([805907a](https://github.com/ionic-team/ionic/commit/805907af4e78179f1acc9cb02263b1ea10d4e3df)), closes [#24280](https://github.com/ionic-team/ionic/issues/24280)
## [6.0.10](https://github.com/ionic-team/ionic/compare/v6.0.9...v6.0.10) (2022-03-02)
### Bug Fixes
* **datetime:** confirm method now uses selected date ([#24827](https://github.com/ionic-team/ionic/issues/24827)) ([c35b898](https://github.com/ionic-team/ionic/commit/c35b898f1dc0fb706446b6971982df48fd72fe6d)), closes [#24823](https://github.com/ionic-team/ionic/issues/24823)
* **datetime:** persist minutes column on hour change ([#24829](https://github.com/ionic-team/ionic/issues/24829)) ([aacb58a](https://github.com/ionic-team/ionic/commit/aacb58a3224e3cc51c731d0c9aa52f52c9276692)), closes [#24821](https://github.com/ionic-team/ionic/issues/24821)
* **item-sliding:** close() will maintain disabled state ([#24847](https://github.com/ionic-team/ionic/issues/24847)) ([ea4a9bb](https://github.com/ionic-team/ionic/commit/ea4a9bb69465f8e97746b36638f0b3a26e45da18)), closes [#24747](https://github.com/ionic-team/ionic/issues/24747)
* **modal:** re-enable swipe gestures when modal is dismissed ([#24846](https://github.com/ionic-team/ionic/issues/24846)) ([836c01c](https://github.com/ionic-team/ionic/commit/836c01c73e42caab0101ac4988f0a9b27cf96a5b)), closes [#24817](https://github.com/ionic-team/ionic/issues/24817)
* **modal:** sheet modal now allows input focusing when backdrop disabled ([#24840](https://github.com/ionic-team/ionic/issues/24840)) ([e4ec572](https://github.com/ionic-team/ionic/commit/e4ec572043e22bd2626dbcfd204fc22a7335282c)), closes [#24581](https://github.com/ionic-team/ionic/issues/24581)
## [6.0.9](https://github.com/ionic-team/ionic/compare/v6.0.8...v6.0.9) (2022-02-23)
### Bug Fixes
* **datetime:** improve datetime sizing in modals ([#24762](https://github.com/ionic-team/ionic/issues/24762)) ([b0ac7de](https://github.com/ionic-team/ionic/commit/b0ac7de168c353ba4899cb74a2b38e25fd0cc0f1)), closes [#23992](https://github.com/ionic-team/ionic/issues/23992)
* **datetime:** month and year column order is now locale aware ([#24802](https://github.com/ionic-team/ionic/issues/24802)) ([16647b2](https://github.com/ionic-team/ionic/commit/16647b2c7290389755a4093145788f281c84b7e2)), closes [#24548](https://github.com/ionic-team/ionic/issues/24548)
* **datetime:** month picker no longer gives duplicate months on ios 14 and older ([#24792](https://github.com/ionic-team/ionic/issues/24792)) ([b6d7e1c](https://github.com/ionic-team/ionic/commit/b6d7e1c75740a61dcd02c21692e4d4632fb8df5c)), closes [#24663](https://github.com/ionic-team/ionic/issues/24663)
* **img:** draggable attribute is now inherited to inner img element ([#24781](https://github.com/ionic-team/ionic/issues/24781)) ([19ac238](https://github.com/ionic-team/ionic/commit/19ac2389eb0843173f51a12de41ac808cd8f0569)), closes [#21325](https://github.com/ionic-team/ionic/issues/21325)
* **modal:** backdropBreakpoint allows interactivity behind sheet ([#24798](https://github.com/ionic-team/ionic/issues/24798)) ([fca3f56](https://github.com/ionic-team/ionic/commit/fca3f56ca4568e63fd493debda088263caa86c64)), closes [#24797](https://github.com/ionic-team/ionic/issues/24797)
* **popover:** default alignment to 'center' for ios mode ([#24815](https://github.com/ionic-team/ionic/issues/24815)) ([243f673](https://github.com/ionic-team/ionic/commit/243f67362f25699bdb373be6b72cb9c14dc95a32))
* **react, vue:** scroll is no longer interrupted on ios ([#24791](https://github.com/ionic-team/ionic/issues/24791)) ([99c91ef](https://github.com/ionic-team/ionic/commit/99c91eff8764c9a1630adedab6a9765dd9754f7d)), closes [#24435](https://github.com/ionic-team/ionic/issues/24435)
* **select:** interface components now show correctly ([#24810](https://github.com/ionic-team/ionic/issues/24810)) ([2fc2de5](https://github.com/ionic-team/ionic/commit/2fc2de51771f4a5c3f20c6071284096e5bf31ec8)), closes [#24807](https://github.com/ionic-team/ionic/issues/24807)
* **toast:** toast is now correctly excluded from focus trapping ([#24816](https://github.com/ionic-team/ionic/issues/24816)) ([8246112](https://github.com/ionic-team/ionic/commit/8246112ca12f90edb98629ab82e27a792a1fafad)), closes [#24733](https://github.com/ionic-team/ionic/issues/24733)
## [6.0.8](https://github.com/ionic-team/ionic/compare/v6.0.7...v6.0.8) (2022-02-15)
### Bug Fixes
* **back-button, breadcrumb, item:** flip chevron icons on RTL ([#24705](https://github.com/ionic-team/ionic/issues/24705)) ([a093544](https://github.com/ionic-team/ionic/commit/a093544fdfc438ed03024285b2a35c5f645ea011))
* **datetime:** navigate to month within min range ([#24759](https://github.com/ionic-team/ionic/issues/24759)) ([7b3838c](https://github.com/ionic-team/ionic/commit/7b3838cc670de7845bb5937d204e86cdba93b6e6)), closes [#24757](https://github.com/ionic-team/ionic/issues/24757)
* **input:** only set native input value if different ([#24758](https://github.com/ionic-team/ionic/issues/24758)) ([fd031aa](https://github.com/ionic-team/ionic/commit/fd031aa1c3f05b7bfa3e0a0ee2a4793e29e22df5)), closes [#24753](https://github.com/ionic-team/ionic/issues/24753)
* **router-outlet:** getRouteId() returns the params set in setRouteId(). ([#24656](https://github.com/ionic-team/ionic/issues/24656)) ([be2205e](https://github.com/ionic-team/ionic/commit/be2205e5a2b2f8778bd1f7b4ea5cae0bf96f9ef3)), closes [#24652](https://github.com/ionic-team/ionic/issues/24652)
* **router-outlet:** navigating to same route with different params now activates component ([#24760](https://github.com/ionic-team/ionic/issues/24760)) ([abc36ae](https://github.com/ionic-team/ionic/commit/abc36ae80b060a659f7557ad90efe98b78f5ead9)), closes [#24653](https://github.com/ionic-team/ionic/issues/24653)
## [6.0.7](https://github.com/ionic-team/ionic/compare/v6.0.6...v6.0.7) (2022-02-09)
### Bug Fixes
* **angular:** inline modals now add .ion-page class correctly ([#24751](https://github.com/ionic-team/ionic/issues/24751)) ([ef46eaf](https://github.com/ionic-team/ionic/commit/ef46eafc9476a85ea3369e542f528d01d3cca0a8)), closes [#24750](https://github.com/ionic-team/ionic/issues/24750)
## [6.0.6](https://github.com/ionic-team/ionic/compare/v6.0.5...v6.0.6) (2022-02-09)
### Bug Fixes
* **action-sheet:** background includes safe area margin ([#24700](https://github.com/ionic-team/ionic/issues/24700)) ([8c22646](https://github.com/ionic-team/ionic/commit/8c22646d66e2077fc88aaacf350330097733bb9b)), closes [#24699](https://github.com/ionic-team/ionic/issues/24699)
* **angular, react, vue:** overlays no longer throw errors when used inside tests ([#24681](https://github.com/ionic-team/ionic/issues/24681)) ([897ae4a](https://github.com/ionic-team/ionic/commit/897ae4a4546ac0dd811125d5513ef23d133a1589)), closes [#24549](https://github.com/ionic-team/ionic/issues/24549) [#24590](https://github.com/ionic-team/ionic/issues/24590)
* **datetime:** disable intersection observer during month update ([#24713](https://github.com/ionic-team/ionic/issues/24713)) ([aab4d30](https://github.com/ionic-team/ionic/commit/aab4d306f80851bfd8a02a6361e26d60faeaadb4)), closes [#24712](https://github.com/ionic-team/ionic/issues/24712)
* **datetime:** minutes only filtered when max hour matches current hour ([#24710](https://github.com/ionic-team/ionic/issues/24710)) ([231d6df](https://github.com/ionic-team/ionic/commit/231d6df622c1f5dd9ecdff6fed8f61a4bff332df)), closes [#24702](https://github.com/ionic-team/ionic/issues/24702)
* **input:** cursor position does not jump to end ([#24736](https://github.com/ionic-team/ionic/issues/24736)) ([4ff9524](https://github.com/ionic-team/ionic/commit/4ff9524e1057aa487069b5982c5f1ecdf51d982b)), closes [#24727](https://github.com/ionic-team/ionic/issues/24727)
* **input:** IME composition mode ([#24735](https://github.com/ionic-team/ionic/issues/24735)) ([c6381ce](https://github.com/ionic-team/ionic/commit/c6381ce4f90707774d1c8662bba874f7b306bd1c)), closes [#24669](https://github.com/ionic-team/ionic/issues/24669)
* **modal, popover:** quickly opening modal/popover no longer presents duplicates ([#24697](https://github.com/ionic-team/ionic/issues/24697)) ([928c5fb](https://github.com/ionic-team/ionic/commit/928c5fbfcbf3ef1b2c3074464fc20dcf1fe143ae))
* **modal:** inline modals inherit ion-page styling ([#24723](https://github.com/ionic-team/ionic/issues/24723)) ([596aad4](https://github.com/ionic-team/ionic/commit/596aad435b5102307da89dd626ca4682b78db452)), closes [#24706](https://github.com/ionic-team/ionic/issues/24706)
* **popover:** use alignment with popover options ([#24711](https://github.com/ionic-team/ionic/issues/24711)) ([f5c5c3c](https://github.com/ionic-team/ionic/commit/f5c5c3cffa4f34046b0e9471a9f193db3772180e)), closes [#24709](https://github.com/ionic-team/ionic/issues/24709)
* **router:** router push with relative path ([#24719](https://github.com/ionic-team/ionic/issues/24719)) ([d40c0c3](https://github.com/ionic-team/ionic/commit/d40c0c3a0993eaefbe5107e98958c6b0699a62c2)), closes [#24718](https://github.com/ionic-team/ionic/issues/24718)
* **select:** value is selected when given array ([#24687](https://github.com/ionic-team/ionic/issues/24687)) ([6ee7d15](https://github.com/ionic-team/ionic/commit/6ee7d159ecfff3382fadb524c5c430172d40c267)), closes [#24742](https://github.com/ionic-team/ionic/issues/24742)
## [6.0.5](https://github.com/ionic-team/ionic/compare/v6.0.4...v6.0.5) (2022-02-02)

View File

@@ -44,19 +44,55 @@ The `@ionic/core` package can be used in simple HTML, or by vanilla JavaScript w
In addition to the default, self lazy-loading components built by Stencil, this package also comes with each component exported as a stand-alone custom element within `@ionic/core/components`. Each component extends `HTMLElement`, and does not lazy-load itself. Instead, this package is useful for projects already using a bundler such as Webpack or Rollup. While all components are available to be imported, the custom elements build also ensures bundlers only import what's used, and tree-shakes any unused components.
Below is an example of importing `ion-toggle`, and initializing Ionic so it's able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
Below is an example of importing `ion-badge`, and initializing Ionic so it is able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
```typescript
import { IonBadge } from "@ionic/core/components/ion-badge";
import { defineCustomElement } from "@ionic/core/components/ion-badge.js";
import { initialize } from "@ionic/core/components";
// Initializes the Ionic config and `mode` behavior
initialize();
customElements.define("ion-badge", IonBadge);
// Defines the `ion-badge` web component
defineCustomElement();
```
Notice how `IonBadge` is imported from `@ionic/core/components/ion-badge` rather than just `@ionic/core/components`. Additionally, the `initialize` function is imported from `@ionic/core/components` rather than `@ionic/core`. All of this helps to ensure bundlers do not pull in more code than is needed.
Notice how we import from `@ionic/core/components` as opposed to `@ionic/core`. This helps bundlers pull in only the code that is needed.
The `defineCustomElement` function will automatically define the component as well as any child components that may be required.
For example, if you wanted to use `ion-modal`, you would do the following:
```typescript
import { defineCustomElement } from "@ionic/core/components/ion-modal.js";
import { initialize } from "@ionic/core/components";
// Initializes the Ionic config and `mode` behavior
initialize();
// Defines the `ion-modal` and child `ion-backdrop` web components.
defineCustomElement();
```
The `defineCustomElement` function will define `ion-modal`, but it will also define `ion-backdrop`, which is a component that `ion-modal` uses internally.
### Using Overlay Controllers
When using an overlay controller, developers will need to define the overlay component before it can be used. Below is an example of using `modalController`:
```typescript
import { defineCustomElement } from '@ionic/core/components/ion-modal.js';
import { initialize, modalController } from '@ionic/core/components';
initialize();
defineCustomElement();
const showModal = async () => {
const modal = await modalController.create({ ... });
...
}
```
## How to contribute

View File

@@ -877,7 +877,7 @@ ion-picker,css-prop,--min-width
ion-picker,css-prop,--width
ion-popover,shadow
ion-popover,prop,alignment,"center" | "end" | "start",'start',false,false
ion-popover,prop,alignment,"center" | "end" | "start" | undefined,undefined,false,false
ion-popover,prop,animated,boolean,true,false,false
ion-popover,prop,arrow,boolean,true,false,false
ion-popover,prop,backdropDismiss,boolean,true,false,false
@@ -1045,7 +1045,7 @@ ion-router,none
ion-router,prop,root,string,'/',false,false
ion-router,prop,useHash,boolean,true,false,false
ion-router,method,back,back() => Promise<void>
ion-router,method,push,push(url: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>
ion-router,method,push,push(path: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>
ion-router,event,ionRouteDidChange,RouterEventDetail,true
ion-router,event,ionRouteWillChange,RouterEventDetail,true

18
core/package-lock.json generated
View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/core",
"version": "6.0.5",
"version": "6.0.13",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "6.0.1",
"version": "6.0.12",
"license": "MIT",
"dependencies": {
"@stencil/core": "~2.12.0",
"@stencil/core": "^2.14.2",
"ionicons": "^6.0.0",
"tslib": "^2.1.0"
},
@@ -1366,9 +1366,9 @@
}
},
"node_modules/@stencil/core": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.12.0.tgz",
"integrity": "sha512-hQlQKh5CUJe8g3L5avLLsfgVu95HMS2LToTtS7gpvvP0eKes1VvAC56uhI+vH4u44GZl9ck/g1rJBVRmMWu0LA==",
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.14.2.tgz",
"integrity": "sha512-NMC5Xi8sPFJxaO4rz6CbMHuD6PteE/RJWtjrbkusmpjKRtMXkfZJPIgOrleZ4xO+vXcNyL535Ru7vUADqEsTiQ==",
"bin": {
"stencil": "bin/stencil"
},
@@ -15055,9 +15055,9 @@
"dev": true
},
"@stencil/core": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.12.0.tgz",
"integrity": "sha512-hQlQKh5CUJe8g3L5avLLsfgVu95HMS2LToTtS7gpvvP0eKes1VvAC56uhI+vH4u44GZl9ck/g1rJBVRmMWu0LA=="
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.14.2.tgz",
"integrity": "sha512-NMC5Xi8sPFJxaO4rz6CbMHuD6PteE/RJWtjrbkusmpjKRtMXkfZJPIgOrleZ4xO+vXcNyL535Ru7vUADqEsTiQ=="
},
"@stencil/react-output-target": {
"version": "0.2.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "6.0.5",
"version": "6.0.13",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -31,7 +31,7 @@
"loader/"
],
"dependencies": {
"@stencil/core": "~2.12.0",
"@stencil/core": "^2.14.2",
"ionicons": "^6.0.0",
"tslib": "^2.1.0"
},

View File

@@ -1254,11 +1254,11 @@ export namespace Components {
}
interface IonItemSliding {
/**
* Close the sliding item. Items can also be closed from the [List](../list).
* Close the sliding item. Items can also be closed from the [List](./list).
*/
"close": () => Promise<void>;
/**
* Close all of the sliding items in the list. Items can also be closed from the [List](../list).
* Close all of the sliding items in the list. Items can also be closed from the [List](./list).
*/
"closeOpened": () => Promise<boolean>;
/**
@@ -1616,6 +1616,9 @@ export namespace Components {
* @param view The view to get.
*/
"getPrevious": (view?: ViewController | undefined) => Promise<ViewController | undefined>;
/**
* Called by <ion-router> to retrieve the current component.
*/
"getRouteId": () => Promise<RouteID | undefined>;
/**
* Inserts a component into the navigation stack at the specified index. This is useful to add a component at any point in the navigation stack.
@@ -1692,6 +1695,14 @@ export namespace Components {
* @param done The transition complete function.
*/
"setRoot": <T extends NavComponent>(component: T, componentProps?: ComponentProps<T> | null | undefined, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>;
/**
* Called by the router to update the view.
* @param id The component tag.
* @param params The component params.
* @param direction A direction hint.
* @param animation an AnimationBuilder.
* @return the status.
*/
"setRouteId": (id: string, params: ComponentProps | undefined, direction: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<RouteWrite>;
/**
* If the nav component should allow for swipe-to-go-back.
@@ -1837,9 +1848,9 @@ export namespace Components {
}
interface IonPopover {
/**
* Describes how to align the popover content with the `reference` point.
* Describes how to align the popover content with the `reference` point. Defaults to `'center'` for `ios` mode, and `'start'` for `md` mode.
*/
"alignment": PositionAlign;
"alignment"?: PositionAlign;
/**
* If `true`, the popover will animate.
*/
@@ -2197,11 +2208,11 @@ export namespace Components {
"navChanged": (direction: RouterDirection) => Promise<boolean>;
"printDebug": () => Promise<void>;
/**
* Navigate to the specified URL.
* @param url The url to navigate to.
* Navigate to the specified path.
* @param path The path to navigate to.
* @param direction The direction of the animation. Defaults to `"forward"`.
*/
"push": (url: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>;
"push": (path: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>;
/**
* The root path to use when matching URLs. By default, this is set to "/", but you can specify an alternate prefix for all URL paths.
*/
@@ -2243,7 +2254,7 @@ export namespace Components {
*/
"animated": boolean;
/**
* By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions.
* This property allows to create custom transition using AnimateBuilder functions.
*/
"animation"?: AnimationBuilder;
"commit": (enteringEl: HTMLElement, leavingEl: HTMLElement | undefined, opts?: RouterOutletOptions | undefined) => Promise<boolean>;
@@ -2414,7 +2425,7 @@ export namespace Components {
*/
"interface": SelectInterface;
/**
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](../alert), the [ion-action-sheet docs](../action-sheet) and the [ion-popover docs](../popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](./alert), the [ion-action-sheet docs](./action-sheet) and the [ion-popover docs](./popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
*/
"interfaceOptions": any;
/**
@@ -2501,7 +2512,7 @@ export namespace Components {
*/
"getPreviousIndex": () => Promise<number>;
/**
* Get the Swiper instance. Use this to access the full Swiper API. See https://idangero.us/swiper/api/ for all API options.
* Get the Swiper instance. Use this to access the full Swiper API. See https://swiperjs.com/swiper-api for all API options.
*/
"getSwiper": () => Promise<any>;
/**
@@ -2536,7 +2547,7 @@ export namespace Components {
*/
"mode"?: "ios" | "md";
/**
* Options to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options
* Options to pass to the swiper instance. See https://swiperjs.com/swiper-api for valid options
*/
"options": any;
/**
@@ -5474,7 +5485,7 @@ declare namespace LocalJSX {
}
interface IonPopover {
/**
* Describes how to align the popover content with the `reference` point.
* Describes how to align the popover content with the `reference` point. Defaults to `'center'` for `ios` mode, and `'start'` for `md` mode.
*/
"alignment"?: PositionAlign;
/**
@@ -5914,7 +5925,7 @@ declare namespace LocalJSX {
*/
"animated"?: boolean;
/**
* By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions.
* This property allows to create custom transition using AnimateBuilder functions.
*/
"animation"?: AnimationBuilder;
"delegate"?: FrameworkDelegate;
@@ -6117,7 +6128,7 @@ declare namespace LocalJSX {
*/
"interface"?: SelectInterface;
/**
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](../alert), the [ion-action-sheet docs](../action-sheet) and the [ion-popover docs](../popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](./alert), the [ion-action-sheet docs](./action-sheet) and the [ion-popover docs](./popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
*/
"interfaceOptions"?: any;
/**
@@ -6279,7 +6290,7 @@ declare namespace LocalJSX {
*/
"onIonSlidesDidLoad"?: (event: CustomEvent<void>) => void;
/**
* Options to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options
* Options to pass to the swiper instance. See https://swiperjs.com/swiper-api for valid options
*/
"options"?: any;
/**
@@ -6348,6 +6359,7 @@ declare namespace LocalJSX {
* The mode determines which platform styles to use.
*/
"mode"?: "ios" | "md";
"onIonStyle"?: (event: CustomEvent<StyleEventDetail>) => void;
"onIonTabBarChanged"?: (event: CustomEvent<TabBarChangedEventDetail>) => void;
/**
* The selected tab component

View File

@@ -22,6 +22,13 @@
text-align: $action-sheet-ios-text-align;
}
// iOS Action Sheet Wrapper
// ---------------------------------------------------
.action-sheet-wrapper {
@include margin(var(--ion-safe-area-top, 0), auto, var(--ion-safe-area-bottom, 0), auto);
}
// iOS Action Sheet Container
// ---------------------------------------------------

View File

@@ -20,6 +20,14 @@
--color: #{$action-sheet-md-title-color};
}
// Material Design Action Sheet Wrapper
// -----------------------------------------
.action-sheet-wrapper {
@include margin(var(--ion-safe-area-top, 0), auto, 0, auto);
}
.action-sheet-title {
@include padding($action-sheet-md-title-padding-top, $action-sheet-md-title-padding-end, $action-sheet-md-title-padding-bottom, $action-sheet-md-title-padding-start);

View File

@@ -13,7 +13,7 @@ $action-sheet-md-background-color: $overlay-md-background-c
$action-sheet-md-padding-top: 0 !default;
/// @prop - Padding bottom of the action sheet
$action-sheet-md-padding-bottom: 0 !default;
$action-sheet-md-padding-bottom: var(--ion-safe-area-bottom) !default;
// Action Sheet Title

View File

@@ -67,7 +67,6 @@
.action-sheet-wrapper {
@include position(null, 0, 0, 0);
@include margin(var(--ion-safe-area-top, 0), auto, var(--ion-safe-area-bottom, 0), auto);
@include transform(translate3d(0, 100%, 0));
display: block;

View File

@@ -661,6 +661,10 @@ Type: `Promise<void>`
## Dependencies
### Used by
- [ion-select](../select)
### Depends on
- [ion-backdrop](../backdrop)
@@ -673,6 +677,7 @@ graph TD;
ion-action-sheet --> ion-backdrop
ion-action-sheet --> ion-icon
ion-action-sheet --> ion-ripple-effect
ion-select --> ion-action-sheet
style ion-action-sheet fill:#f9f,stroke:#333,stroke-width:4px
```

View File

@@ -1,6 +1,6 @@
import { newE2EPage } from '@stencil/core/testing';
import { generateE2EUrl } from '../../../utils/test/utils';
import { generateE2EUrl } from '@utils/test';
export const testActionSheet = async (
type: string,

View File

@@ -1863,6 +1863,10 @@ Type: `Promise<void>`
## Dependencies
### Used by
- [ion-select](../select)
### Depends on
- [ion-ripple-effect](../ripple-effect)
@@ -1873,6 +1877,7 @@ Type: `Promise<void>`
graph TD;
ion-alert --> ion-ripple-effect
ion-alert --> ion-backdrop
ion-select --> ion-alert
style ion-alert fill:#f9f,stroke:#333,stroke-width:4px
```

View File

@@ -1,6 +1,6 @@
import { newE2EPage } from '@stencil/core/testing';
import { generateE2EUrl } from '../../../utils/test/utils';
import { generateE2EUrl } from '@utils/test';
export const testAlert = async (
type: string,

View File

@@ -5,7 +5,7 @@ import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import { AnimationBuilder, Color } from '../../interface';
import { ButtonInterface } from '../../utils/element-interface';
import { inheritAttributes } from '../../utils/helpers';
import { Attributes, inheritAttributes } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
/**
@@ -24,7 +24,7 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme';
shadow: true
})
export class BackButton implements ComponentInterface, ButtonInterface {
private inheritedAttributes: { [k: string]: any } = {};
private inheritedAttributes: Attributes = {};
@Element() el!: HTMLElement;
@@ -121,7 +121,7 @@ export class BackButton implements ComponentInterface, ButtonInterface {
}
render() {
const { color, defaultHref, disabled, type, hasIconOnly, backButtonIcon, backButtonText, inheritedAttributes } = this;
const { color, defaultHref, disabled, type, hasIconOnly, backButtonIcon, backButtonText, icon, inheritedAttributes } = this;
const showBackButton = defaultHref !== undefined;
const mode = getIonMode(this);
const ariaLabel = inheritedAttributes['aria-label'] || backButtonText || 'back';
@@ -149,7 +149,7 @@ export class BackButton implements ComponentInterface, ButtonInterface {
aria-label={ariaLabel}
>
<span class="button-inner">
{backButtonIcon && <ion-icon part="icon" icon={backButtonIcon} aria-hidden="true" lazy={false}></ion-icon>}
{backButtonIcon && <ion-icon part="icon" icon={backButtonIcon} aria-hidden="true" lazy={false} flip-rtl={icon === undefined}></ion-icon>}
{backButtonText && <span part="text" aria-hidden="true" class="button-text">{backButtonText}</span>}
</span>
{mode === 'md' && <ion-ripple-effect type={this.rippleType}></ion-ripple-effect>}

View File

@@ -8,3 +8,12 @@ test('back-button: basic', async () => {
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});
test('back-button: basic-rtl', async () => {
const page = await newE2EPage({
url: '/src/components/back-button/test/basic?ionic:_testing=true&rtl=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@@ -3,7 +3,7 @@ import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons';
import { getIonMode } from '../../global/ionic-global';
import { AnimationBuilder, BreadcrumbCollapsedClickEventDetail, Color, RouterDirection } from '../../interface';
import { inheritAttributes } from '../../utils/helpers';
import { Attributes, inheritAttributes } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
/**
@@ -22,7 +22,7 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme';
shadow: true
})
export class Breadcrumb implements ComponentInterface {
private inheritedAttributes: { [k: string]: any } = {};
private inheritedAttributes: Attributes = {};
private collapsedRef?: HTMLElement;
/** @internal */
@@ -212,7 +212,7 @@ export class Breadcrumb implements ComponentInterface {
<span class="breadcrumb-separator" part="separator">
<slot name="separator">
{ mode === 'ios'
? <ion-icon icon={chevronForwardOutline} lazy={false}></ion-icon>
? <ion-icon icon={chevronForwardOutline} lazy={false} flip-rtl></ion-icon>
: <span>/</span>
}
</slot>

View File

@@ -8,3 +8,12 @@ test('breadcrumbs: basic', async () => {
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});
test('breadcrumbs: basic-rtl', async () => {
const page = await newE2EPage({
url: '/src/components/breadcrumbs/test/basic?ionic:_testing=true&rtl=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@@ -3,7 +3,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop
import { getIonMode } from '../../global/ionic-global';
import { AnimationBuilder, Color, RouterDirection } from '../../interface';
import { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
import { hasShadowDom, inheritAttributes } from '../../utils/helpers';
import { Attributes, hasShadowDom, inheritAttributes } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
/**
@@ -28,7 +28,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
private inItem = false;
private inListHeader = false;
private inToolbar = false;
private inheritedAttributes: { [k: string]: any } = {};
private inheritedAttributes: Attributes = {};
@Element() el!: HTMLElement;

View File

@@ -13,7 +13,7 @@ This attribute lets you specify how wide the button should be. By default, butto
## Fill
This attributes determines the background and border color of the button. By default, buttons have a solid background unless the button is inside of a toolbar, in which case it has a transparent background.
This attribute determines the background and border color of the button. By default, buttons have a solid background unless the button is inside of a toolbar, in which case it has a transparent background.
| Value | Details |
|----------------|------------------------------------------------------------------------------|

View File

@@ -164,7 +164,7 @@
*/
top: 0;
bottom: 0;
margin-top: calc(var(--offset-top) * -1);
margin-bottom: calc(var(--offset-bottom) * -1);
}
@@ -173,10 +173,6 @@
display: none;
position: absolute;
/* stylelint-disable property-disallowed-list */
left: -100%;
/* stylelint-enable property-disallowed-list */
width: 100%;
height: 100vh;
@@ -185,6 +181,18 @@
pointer-events: none;
}
:host(.content-ltr) .transition-effect {
/* stylelint-disable property-disallowed-list */
left: -100%;
/* stylelint-enable property-disallowed-list */
}
:host(.content-rtl) .transition-effect {
/* stylelint-disable property-disallowed-list */
right: -100%;
/* stylelint-enable property-disallowed-list */
}
.transition-cover {
position: absolute;
@@ -204,10 +212,6 @@
display: block;
position: absolute;
/* stylelint-disable property-disallowed-list */
right: 0;
/* stylelint-enable property-disallowed-list */
width: 10px;
height: 100%;
@@ -216,6 +220,20 @@
background-size: 10px 16px;
}
:host(.content-ltr) .transition-shadow {
/* stylelint-disable property-disallowed-list */
right: 0;
/* stylelint-enable property-disallowed-list */
}
:host(.content-rtl) .transition-shadow {
/* stylelint-disable property-disallowed-list */
left: 0;
/* stylelint-enable property-disallowed-list */
transform: scaleX(-1);
}
// Content: Fixed
// --------------------------------------------------

View File

@@ -4,6 +4,7 @@ import { getIonMode } from '../../global/ionic-global';
import { Color, ScrollBaseDetail, ScrollDetail } from '../../interface';
import { componentOnReady } from '../../utils/helpers';
import { isPlatform } from '../../utils/platform';
import { isRTL } from '../../utils/rtl';
import { createColorClasses, hostContext } from '../../utils/theme';
/**
@@ -311,7 +312,8 @@ export class Content implements ComponentInterface {
}
render() {
const { isMainContent, scrollX, scrollY } = this;
const { isMainContent, scrollX, scrollY, el } = this;
const rtl = isRTL(el) ? 'rtl' : 'ltr';
const mode = getIonMode(this);
const forceOverscroll = this.shouldForceOverscroll();
const transitionShadow = mode === 'ios';
@@ -325,6 +327,7 @@ export class Content implements ComponentInterface {
[mode]: true,
'content-sizing': hostContext('ion-popover', this.el),
'overscroll': forceOverscroll,
[`content-${rtl}`]: true
})}
style={{
'--offset-top': `${this.cTop}px`,
@@ -339,7 +342,7 @@ export class Content implements ComponentInterface {
'scroll-y': scrollY,
'overscroll': (scrollX || scrollY) && forceOverscroll
}}
ref={(el: HTMLElement) => this.scrollEl = el!}
ref={(scrollEl: HTMLElement) => this.scrollEl = scrollEl!}
onScroll={(this.scrollEvents) ? (ev: UIEvent) => this.onScroll(ev) : undefined}
part="scroll"
>

View File

@@ -48,6 +48,35 @@
opacity: 1;
}
/**
* Changing the physical order of the
* picker columns in the DOM is added
* work, so we just use `order` instead.
*
* The picker automatically configures
* the text alignment, so when switching
* the order we need to manually switch
* the text alignment too.
*/
:host .datetime-year .order-month-first .month-column {
order: 1;
}
:host .datetime-year .order-month-first .year-column {
order: 2;
}
:host .datetime-year .order-year-first .month-column {
order: 2;
text-align: end;
}
:host .datetime-year .order-year-first .year-column {
order: 1;
text-align: start;
}
// Calendar
// -----------------------------------

View File

@@ -32,7 +32,8 @@ import {
getMonthAndYear
} from './utils/format';
import {
is24Hour
is24Hour,
isMonthFirstLocale
} from './utils/helpers';
import {
calculateHourFromAMPM,
@@ -93,9 +94,13 @@ export class Datetime implements ComponentInterface {
private destroyCalendarIO?: () => void;
private destroyKeyboardMO?: () => void;
private destroyOverlayListener?: () => void;
private minParts?: any;
private maxParts?: any;
private todayParts = parseDate(getToday());
private prevPresentation: string | null = null;
/**
* Duplicate reference to `activeParts` that does not trigger a re-render of the component.
@@ -123,8 +128,6 @@ export class Datetime implements ComponentInterface {
ampm: 'pm'
}
private todayParts = parseDate(getToday())
@Element() el!: HTMLIonDatetimeElement;
@State() isPresented = false;
@@ -423,10 +426,10 @@ export class Datetime implements ComponentInterface {
* the date that is currently selected, otherwise
* there can be 1 hr difference when dealing w/ DST
*/
const date = new Date(convertDataToISO(this.workingParts));
this.workingParts.tzOffset = date.getTimezoneOffset() * -1;
const date = new Date(convertDataToISO(this.activeParts));
this.activeParts.tzOffset = date.getTimezoneOffset() * -1;
this.value = convertDataToISO(this.workingParts);
this.value = convertDataToISO(this.activeParts);
if (closeOverlay) {
this.closeParentOverlay();
@@ -482,8 +485,18 @@ export class Datetime implements ComponentInterface {
this.confirm();
}
/**
* Stencil sometimes sets calendarBodyRef to null on rerender, even though
* the element is present. Query for it manually as a fallback.
*
* TODO(FW-901) Remove when issue is resolved: https://github.com/ionic-team/stencil/issues/3253
*/
private getCalendarBodyEl = () => {
return this.calendarBodyRef || this.el.shadowRoot?.querySelector('.calendar-body');
};
private initializeKeyboardListeners = () => {
const { calendarBodyRef } = this;
const calendarBodyRef = this.getCalendarBodyEl();
if (!calendarBodyRef) { return; }
const root = this.el!.shadowRoot!;
@@ -529,7 +542,7 @@ export class Datetime implements ComponentInterface {
* We must use keydown not keyup as we want
* to prevent scrolling when using the arrow keys.
*/
this.calendarBodyRef!.addEventListener('keydown', (ev: KeyboardEvent) => {
calendarBodyRef.addEventListener('keydown', (ev: KeyboardEvent) => {
const activeElement = root.activeElement;
if (!activeElement || !activeElement.classList.contains('calendar-day')) { return; }
@@ -656,7 +669,7 @@ export class Datetime implements ComponentInterface {
}
private initializeCalendarIOListeners = () => {
const { calendarBodyRef } = this;
const calendarBodyRef = this.getCalendarBodyEl();
if (!calendarBodyRef) { return; }
const mode = getIonMode(this);
@@ -720,8 +733,8 @@ export class Datetime implements ComponentInterface {
const { month, year, day } = refMonthFn(this.workingParts);
if (isMonthDisabled({ month, year, day: null }, {
minParts: this.minParts,
maxParts: this.maxParts
minParts: { ...this.minParts, day: null },
maxParts: { ...this.maxParts, day: null }
})) {
return;
}
@@ -771,17 +784,26 @@ export class Datetime implements ComponentInterface {
*/
writeTask(() => {
this.setWorkingParts({
...this.workingParts,
month,
day: day!,
year
});
// Disconnect all active intersection observers
// to avoid a re-render causing a duplicate event.
if (this.destroyCalendarIO) {
this.destroyCalendarIO();
}
raf(() => {
this.setWorkingParts({
...this.workingParts,
month,
day: day!,
year
});
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
calendarBodyRef.style.removeProperty('overflow');
calendarBodyRef.style.removeProperty('pointer-events');
endIO?.observe(endMonth);
startIO?.observe(startMonth);
});
/**
@@ -801,6 +823,12 @@ export class Datetime implements ComponentInterface {
navigator.maxTouchPoints > 1 ?
[0.7, 1] : 1;
// Intersection observers cannot accurately detect the
// intersection with a threshold of 1, when the observed
// element width is a sub-pixel value (i.e. 334.05px).
// Setting a root margin to 1px solves the issue.
const rootMargin = '1px';
/**
* Listen on the first month to
* prepend a new month and on the last
@@ -819,15 +847,18 @@ export class Datetime implements ComponentInterface {
* it applies to active gestures which is not
* something WebKit does.
*/
endIO = new IntersectionObserver(ev => ioCallback('end', ev), {
threshold,
root: calendarBodyRef
root: calendarBodyRef,
rootMargin
});
endIO.observe(endMonth);
startIO = new IntersectionObserver(ev => ioCallback('start', ev), {
threshold,
root: calendarBodyRef
root: calendarBodyRef,
rootMargin
});
startIO.observe(startMonth);
@@ -854,7 +885,7 @@ export class Datetime implements ComponentInterface {
* listener. This is so that we can re-create the listeners
* if the datetime has been hidden/presented by a modal or popover.
*/
private destroyListeners = () => {
private destroyInteractionListeners = () => {
const { destroyCalendarIO, destroyKeyboardMO } = this;
if (destroyCalendarIO !== undefined) {
@@ -866,6 +897,12 @@ export class Datetime implements ComponentInterface {
}
}
private initializeListeners() {
this.initializeCalendarIOListeners();
this.initializeKeyboardListeners();
this.initializeOverlayListener();
}
componentDidLoad() {
/**
* If a scrollable element is hidden using `display: none`,
@@ -879,9 +916,7 @@ export class Datetime implements ComponentInterface {
const ev = entries[0];
if (!ev.isIntersecting) { return; }
this.initializeCalendarIOListeners();
this.initializeKeyboardListeners();
this.initializeOverlayListener();
this.initializeListeners();
/**
* TODO: Datetime needs a frame to ensure that it
@@ -917,7 +952,7 @@ export class Datetime implements ComponentInterface {
const ev = entries[0];
if (ev.isIntersecting) { return; }
this.destroyListeners();
this.destroyInteractionListeners();
writeTask(() => {
this.el.classList.remove('datetime-ready');
@@ -940,6 +975,29 @@ export class Datetime implements ComponentInterface {
root.addEventListener('ionBlur', (ev: Event) => ev.stopPropagation());
}
/**
* When the presentation is changed, all calendar content is recreated,
* so we need to re-init behavior with the new elements.
*/
componentDidRender() {
const { presentation, prevPresentation } = this;
if (prevPresentation === null) {
this.prevPresentation = presentation;
return;
}
if (presentation === prevPresentation) { return; }
this.prevPresentation = presentation;
this.destroyInteractionListeners();
if (this.destroyOverlayListener !== undefined) {
this.destroyOverlayListener();
}
this.initializeListeners();
}
/**
* When doing subsequent presentations of an inline
* overlay, the IO callback will fire again causing
@@ -951,9 +1009,15 @@ export class Datetime implements ComponentInterface {
const overlay = this.el.closest('ion-popover, ion-modal');
if (overlay === null) { return; }
overlay.addEventListener('willPresent', () => {
const overlayListener = () => {
this.overlayIsPresenting = true;
});
};
overlay.addEventListener('willPresent', overlayListener);
this.destroyOverlayListener = () => {
overlay.removeEventListener('willPresent', overlayListener);
};
}
private processValue = (value?: string | null) => {
@@ -1015,7 +1079,7 @@ export class Datetime implements ComponentInterface {
}
private nextMonth = () => {
const { calendarBodyRef } = this;
const calendarBodyRef = this.getCalendarBodyEl();
if (!calendarBodyRef) { return; }
const nextMonth = calendarBodyRef.querySelector('.calendar-month:last-of-type');
@@ -1031,7 +1095,7 @@ export class Datetime implements ComponentInterface {
}
private prevMonth = () => {
const { calendarBodyRef } = this;
const calendarBodyRef = this.getCalendarBodyEl();
if (!calendarBodyRef) { return; }
const prevMonth = calendarBodyRef.querySelector('.calendar-month:first-of-type');
@@ -1088,25 +1152,31 @@ export class Datetime implements ComponentInterface {
}
private renderYearView() {
const { presentation, workingParts } = this;
const { presentation, workingParts, locale } = this;
const calendarYears = getCalendarYears(this.todayParts, this.minParts, this.maxParts, this.parsedYearValues);
const showMonth = presentation !== 'year';
const showYear = presentation !== 'month';
const months = getPickerMonths(this.locale, workingParts, this.minParts, this.maxParts, this.parsedMonthValues);
const months = getPickerMonths(locale, workingParts, this.minParts, this.maxParts, this.parsedMonthValues);
const years = calendarYears.map(year => {
return {
text: `${year}`,
value: year
}
})
const showMonthFirst = isMonthFirstLocale(locale);
const columnOrder = showMonthFirst ? 'month-first' : 'year-first';
return (
<div class="datetime-year">
<div class="datetime-year-body">
<div class={{
'datetime-year-body': true,
[`order-${columnOrder}`]: true
}}>
<ion-picker-internal>
{
showMonth &&
<ion-picker-column-internal
class="month-column"
color={this.color}
items={months}
value={workingParts.month}
@@ -1141,6 +1211,7 @@ export class Datetime implements ComponentInterface {
{
showYear &&
<ion-picker-column-internal
class="year-column"
color={this.color}
items={years}
value={workingParts.year}
@@ -1229,8 +1300,11 @@ export class Datetime implements ComponentInterface {
year,
day: null
}, {
minParts: this.minParts,
maxParts: this.maxParts
// The day is not used when checking if a month is disabled.
// Users should be able to access the min or max month, even if the
// min/max date is out of bounds (e.g. min is set to Feb 15, Feb should not be disabled).
minParts: { ...this.minParts, day: null },
maxParts: { ...this.maxParts, day: null }
});
// The working month should never have swipe disabled.
// Otherwise the CSS scroll snap will not work and the user

View File

@@ -31,8 +31,8 @@ the same ISO format which datetime value was originally given as.
| Year and Month | YYYY-MM | 1994-12 |
| Complete Date | YYYY-MM-DD | 1994-12-15 |
| Date and Time | YYYY-MM-DDTHH:mm | 1994-12-15T13:47 |
| UTC Timezone | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20.789Z |
| Timezone Offset | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20.789+05:00 |
| UTC Timezone | YYYY-MM-DDTHH:mm:ssZ | 1994-12-15T13:47:20Z |
| Timezone Offset | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20+05:00 |
| Hour and Minute | HH:mm | 13:47 |
| Hour, Minute, Second | HH:mm:ss | 13:47:20 |
@@ -147,11 +147,11 @@ const zonedTime = dateFnsTz.utcToZonedTime(date, userTimeZone);
format(zonedTime, 'yyyy-MM-dd HH:mm:ssXXX', { timeZone: userTimeZone });
```
## Parsing Dates
### Parsing Date Values
When `ionChange` is emitted, we provide an ISO-8601 string in the event payload. From there, it is the developer's responsibility to format it as they see fit. We recommend using a library like [date-fns](https://date-fns.org) to format their dates properly.
The `ionChange` event will emit the date value as an ISO-8601 string in the event payload. It is the developer's responsibility to format it based on their application needs. We recommend using [date-fns](https://date-fns.org) to format the date value.
Below is an example of formatting an ISO-8601 string to display the month, date, and year:
Below is an example of formatting an ISO-8601 string to display the month, date and year:
```typescript
import { format, parseISO } from 'date-fns';
@@ -159,6 +159,8 @@ import { format, parseISO } from 'date-fns';
/**
* This is provided in the event
* payload from the `ionChange` event.
*
* The value is an ISO-8601 date string.
*/
const dateFromIonDatetime = '2021-06-04T14:23:00-04:00';
const formattedString = format(parseISO(dateFromIonDatetime), 'MMM d, yyyy');

View File

@@ -110,3 +110,41 @@ test('datetime:rtl: basic', async () => {
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});
describe('datetime: confirm date', () => {
test('should set the date value based on the selected date', async () => {
const page = await newE2EPage({
html: `
<button>Bind datetimeMonthDidChange event</button>
<ion-datetime value="2021-12-25T12:40:00.000Z"></ion-datetime>
<script type="module">
import { InitMonthDidChangeEvent } from '/src/components/datetime/test/utils/month-did-change-event.js';
document.querySelector('button').addEventListener('click', function() {
InitMonthDidChangeEvent();
});
</script>
`
});
const eventButton = await page.find('button');
await eventButton.click();
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button');
await buttons[1].click();
await page.waitForEvent('datetimeMonthDidChange');
await page.$eval('ion-datetime', async (el: any) => {
await el.confirm();
});
const value = await (await page.find('ion-datetime')).getProperty('value');
expect(value).toMatch('2021-12-25T12:40:00');
});
});

View File

@@ -1,7 +1,8 @@
import {
generateMonths,
getDaysOfWeek,
generateTime
generateTime,
getToday
} from '../utils/data';
describe('generateMonths()', () => {
@@ -95,7 +96,7 @@ describe('generateTime()', () => {
day: 19,
month: 5,
year: 2021,
hour: 5,
hour: 7,
minute: 43
}
const max = {
@@ -287,5 +288,69 @@ describe('generateTime()', () => {
expect(hours).toStrictEqual([19, 20]);
});
it('should return the filtered minutes when the max bound is set', () => {
const refValue = {
day: undefined,
month: undefined,
year: undefined,
hour: 13,
minute: 0
};
const maxParts = {
day: undefined,
month: undefined,
year: undefined,
hour: 13,
minute: 2
};
const { minutes } = generateTime(refValue, 'h23', undefined, maxParts);
expect(minutes).toStrictEqual([0, 1, 2]);
});
it('should not filter minutes when the current hour is less than the max hour bound', () => {
const refValue = {
day: undefined,
month: undefined,
year: undefined,
hour: 12,
minute: 0
};
const maxParts = {
day: undefined,
month: undefined,
year: undefined,
hour: 13,
minute: 2
};
const { minutes } = generateTime(refValue, 'h23', undefined, maxParts);
expect(minutes.length).toEqual(60);
});
})
})
describe('getToday', () => {
beforeAll(() => {
jest.useFakeTimers('modern');
// System time is zero based, 1 = February
jest.setSystemTime(new Date(2022, 1, 21));
});
it('should return today', () => {
const res = getToday();
const expected = new Date();
expected.setHours(expected.getHours() - (expected.getTimezoneOffset() / 60));
expect(res).toEqual('2022-02-21T00:00:00.000Z');
});
});

View File

@@ -52,3 +52,41 @@ test('display', async () => {
expect(screenshotCompare).toMatchScreenshot();
}
});
test('month selection should work after changing presentation', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/display?ionic:_testing=true'
});
const ionWorkingPartsDidChange = await page.spyOnEvent('ionWorkingPartsDidChange', 'document');
let calendarMonthYear;
await page.select('#presentation', 'date-time');
await page.waitForChanges();
await page.select('#presentation', 'time-date');
await page.waitForChanges();
const nextMonthButton = await page.find('ion-datetime >>> .calendar-next-prev ion-button + ion-button');
await nextMonthButton.click();
await page.waitForChanges();
await ionWorkingPartsDidChange.next();
calendarMonthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(calendarMonthYear.textContent).toContain('March 2022');
// ensure it still works if presentation is changed more than once
await page.select('#presentation', 'date-time');
await page.waitForChanges();
const prevMonthButton = await page.find('ion-datetime >>> .calendar-next-prev ion-button:first-child');
await prevMonthButton.click();
await page.waitForChanges();
await ionWorkingPartsDidChange.next();
calendarMonthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(calendarMonthYear.textContent).toContain('February 2022');
});

View File

@@ -1,52 +1,80 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Datetime - Standalone</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
body {
padding: 20px;
}
ion-datetime {
border: 1px solid black;
}
</style>
</head>
<body>
<label for="presentation">Presentation</label>
<select id="presentation" onchange="changePresentation(event)">
<option value="date-time" selected>date-time</option>
<option value="time-date">time-date</option>
<option value="date">date</option>
<option value="time">time</option>
</select>
<head>
<meta charset="UTF-8">
<title>Datetime - Standalone</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
body {
padding: 20px;
}
ion-datetime {
border: 1px solid black;
}
</style>
</head>
<body>
<label for="presentation">Presentation</label>
<select id="presentation" onchange="changePresentation(event)">
<option value="date-time" selected>date-time</option>
<option value="time-date">time-date</option>
<option value="date">date</option>
<option value="time">time</option>
</select>
<label for="size">Size</label>
<select id="size" onchange="changeSize(event)">
<option value="fixed" selected>fixed</option>
<option value="cover">cover</option>
</select>
<label for="size">Size</label>
<select id="size" onchange="changeSize(event)">
<option value="fixed" selected>fixed</option>
<option value="cover">cover</option>
</select>
<br /><br />
<br /><br />
<ion-datetime></ion-datetime>
<ion-datetime value="2022-02-22"></ion-datetime>
<script>
const datetime = document.querySelector('ion-datetime');
<script>
const datetime = document.querySelector('ion-datetime');
const mutationObserver = new MutationObserver(() => {
document.dispatchEvent(new CustomEvent('ionWorkingPartsDidChange'));
});
const initCalendarMonthChangeObserver = async () => {
if (!datetime.componentOnReady) return;
await datetime.componentOnReady();
// We have to requestAnimationFrame to allow the datetime to render completely.
requestAnimationFrame(() => {
const calendarBody = datetime.shadowRoot.querySelector('.calendar-body');
if (calendarBody) {
mutationObserver.observe(calendarBody, {
childList: true,
subtree: true
});
}
});
}
const changePresentation = (ev) => {
mutationObserver.disconnect();
datetime.presentation = ev.target.value;
initCalendarMonthChangeObserver();
};
const changeSize = (ev) => {
datetime.size = ev.target.value;
};
initCalendarMonthChangeObserver();
</script>
</body>
const changePresentation = (ev) => {
datetime.presentation = ev.target.value;
}
const changeSize = (ev) => {
datetime.size = ev.target.value;
}
</script>
</body>
</html>

View File

@@ -27,6 +27,11 @@ describe('generateDayAriaLabel()', () => {
expect(generateDayAriaLabel('en-US', false, reference)).toEqual('Monday, May 31');
});
it('should return Saturday, April 1', () => {
const reference = { month: 4, day: 1, year: 2006 };
expect(generateDayAriaLabel('en-US', false, reference)).toEqual('Saturday, April 1');
});
});
describe('getMonthAndDay()', () => {
@@ -37,6 +42,14 @@ describe('getMonthAndDay()', () => {
it('should return mar, 11 may', () => {
expect(getMonthAndDay('es-ES', { month: 5, day: 11, year: 2021 })).toEqual('mar, 11 may');
});
it('should return Sat, Apr 1', () => {
expect(getMonthAndDay('en-US', { month: 4, day: 1, year: 2006 })).toEqual('Sat, Apr 1');
});
it('should return sáb, 1 abr', () => {
expect(getMonthAndDay('es-ES', { month: 4, day: 1, year: 2006 })).toEqual('sáb, 1 abr');
});
})
describe('getFormattedHour()', () => {
@@ -63,7 +76,15 @@ describe('getMonthAndYear()', () => {
expect(getMonthAndYear('en-US', { month: 5, day: 11, year: 2021 })).toEqual('May 2021');
});
it('should return mar, 11 may', () => {
it('should return mayo de 2021', () => {
expect(getMonthAndYear('es-ES', { month: 5, day: 11, year: 2021 })).toEqual('mayo de 2021');
});
it('should return April 2006', () => {
expect(getMonthAndYear('en-US', { month: 4, day: 1, year: 2006 })).toEqual('April 2006');
});
it('should return abril de 2006', () => {
expect(getMonthAndYear('es-ES', { month: 4, day: 1, year: 2006 })).toEqual('abril de 2006');
});
})

View File

@@ -1,7 +1,8 @@
import {
isLeapYear,
getNumDaysInMonth,
is24Hour
is24Hour,
isMonthFirstLocale
} from '../utils/helpers';
describe('daysInMonth()', () => {
@@ -51,3 +52,18 @@ describe('is24Hour()', () => {
expect(is24Hour('en-GB-u-hc-h12')).toBe(false);
})
})
describe('isMonthFirstLocale()', () => {
it('should return true if the locale shows months first', () => {
expect(isMonthFirstLocale('en-US')).toBe(true);
expect(isMonthFirstLocale('en-GB')).toBe(true);
expect(isMonthFirstLocale('es-ES')).toBe(true);
expect(isMonthFirstLocale('ro-RO')).toBe(true);
});
it('should return false if the locale shows years first', () => {
expect(isMonthFirstLocale('zh-CN')).toBe(false);
expect(isMonthFirstLocale('ja-JP')).toBe(false);
expect(isMonthFirstLocale('ko-KR')).toBe(false);
});
})

View File

@@ -19,3 +19,53 @@ test('locale', async () => {
expect(screenshotCompare).toMatchScreenshot();
}
});
test('it should render month and year with an en-US locale', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/locale?ionic:_testing=true'
});
const screenshotCompares = [];
const datetime = await page.find('ion-datetime');
datetime.setProperty('locale', 'en-US');
await page.waitForChanges();
const button = await page.find('ion-datetime >>> .calendar-month-year ion-item');
await button.click();
await page.waitForChanges();
const yearBody = await page.find('ion-datetime >>> .datetime-year-body');
expect(yearBody).toHaveClass('order-month-first');
screenshotCompares.push(await page.compareScreenshot());
for (const screenshotCompare of screenshotCompares) {
expect(screenshotCompare).toMatchScreenshot();
}
});
test('it should render year and month with a ja-JP locale', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/locale?ionic:_testing=true'
});
const screenshotCompares = [];
const datetime = await page.find('ion-datetime');
datetime.setProperty('locale', 'ja-JP');
await page.waitForChanges();
const button = await page.find('ion-datetime >>> .calendar-month-year ion-item');
await button.click();
await page.waitForChanges();
const yearBody = await page.find('ion-datetime >>> .datetime-year-body');
expect(yearBody).toHaveClass('order-year-first');
screenshotCompares.push(await page.compareScreenshot());
for (const screenshotCompare of screenshotCompares) {
expect(screenshotCompare).toMatchScreenshot();
}
});

View File

@@ -47,3 +47,17 @@ test('datetime: minmax navigation disabled', async () => {
expect(navButtons[1]).toHaveAttribute('disabled');
});
test('datetime: min including day should not disable month', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/minmax?ionic:_testing=true'
});
const calendarMonths = await page.findAll('ion-datetime#min-with-day >>> .calendar-month');
await page.waitForChanges();
expect(calendarMonths[0]).toHaveClass('calendar-month-disabled');
expect(calendarMonths[1]).not.toHaveClass('calendar-month-disabled');
expect(calendarMonths[2]).not.toHaveClass('calendar-month-disabled');
})

View File

@@ -62,6 +62,10 @@
<h2>locale: en-GB</h2>
<ion-datetime locale="en-GB" presentation="time" min="19:30" value="20:30" max="20:40"></ion-datetime>
</div>
<div class="grid-item">
<h2>Min with year/month/day</h2>
<ion-datetime id="min-with-day" min="2022-02-09" value="2022-02-09"></ion-datetime>
</div>
</div>
</ion-content>

View File

@@ -1,4 +1,4 @@
import { newE2EPage } from '@stencil/core/testing';
import { newE2EPage, E2EPage } from '@stencil/core/testing';
test('presentation', async () => {
const page = await newE2EPage({
@@ -13,3 +13,37 @@ test('presentation', async () => {
expect(screenshotCompare).toMatchScreenshot();
}
});
describe('presentation: time', () => {
let page: E2EPage;
beforeEach(async () => {
page = await newE2EPage({
url: '/src/components/datetime/test/presentation?ionic:_testing=true'
});
});
describe('when the time picker is visible in the view', () => {
it('manually setting the value should emit ionChange once', async () => {
const datetime = await page.find('ion-datetime[presentation="time"]');
const didChange = await datetime.spyOnEvent('ionChange');
await page.$eval('ion-datetime[presentation="time"]', (el: any) => {
el.scrollIntoView();
});
await page.$eval('ion-datetime[presentation="time"]', (el: any) => {
el.value = '06:02:40';
});
await page.waitForChanges();
expect(didChange).toHaveReceivedEventTimes(1);
expect(didChange).toHaveReceivedEventDetail({ value: '06:02:40' });
});
});
});

View File

@@ -0,0 +1,55 @@
import { newE2EPage } from '@stencil/core/testing';
describe('datetime: sub-pixel width', () => {
test('should update the month when next button is clicked', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/sub-pixel-width?ionic:_testing=true'
});
const openModalBtn = await page.find('#open-modal');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const modal = await page.find('ion-modal');
await openModalBtn.click();
await modal.waitForVisible();
await ionModalDidPresent.next();
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button')
await buttons[1].click();
await page.waitForEvent('datetimeMonthDidChange');
const monthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(monthYear.textContent.trim()).toBe('March 2022');
});
test('should update the month when prev button is clicked', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/sub-pixel-width?ionic:_testing=true'
});
const openModalBtn = await page.find('#open-modal');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const modal = await page.find('ion-modal');
await openModalBtn.click();
await modal.waitForVisible();
await ionModalDidPresent.next();
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button')
await buttons[0].click();
await page.waitForEvent('datetimeMonthDidChange');
const monthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(monthYear.textContent.trim()).toBe('January 2022');
});
});

View File

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Datetime - Sub Pixel Width</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
ion-datetime {
width: 334.05px;
height: 500px;
}
#background {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<ion-app>
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Datetime - Sub Pixel Width</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h2>Modal</h2>
<ion-button id="open-modal">Present Modal</ion-button>
<ion-modal trigger="open-modal" id="modal">
<div id="background">
<ion-datetime id="picker" value="2022-02-01"></ion-datetime>
</div>
</ion-modal>
</ion-content>
</ion-app>
<script type="module">
import { InitMonthDidChangeEvent } from '../test/utils/month-did-change-event.js';
document.getElementById('open-modal').addEventListener('click', () => {
InitMonthDidChangeEvent();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,19 @@
/**
* Initializes a mutation observer to detect when the calendar month
* text is updated as a result of a month change in `ion-datetime`.
*
* @param {*} datetimeSelector The element selector for the `ion-datetime` component.
*/
export function InitMonthDidChangeEvent(datetimeSelector = 'ion-datetime') {
const observer = new MutationObserver(mutationRecords => {
if (mutationRecords[0].type === 'characterData') {
document.dispatchEvent(new CustomEvent('datetimeMonthDidChange'));
}
});
observer.observe(document.querySelector(datetimeSelector).shadowRoot.querySelector('.calendar-month-year'), {
characterData: true,
subtree: true
});
}

View File

@@ -0,0 +1,140 @@
import { newE2EPage } from '@stencil/core/testing';
/**
* This test emulates zoom behavior in the browser to make sure
* that key functions of the ion-datetime continue to function even
* if the page is zoomed in or out.
*/
describe('datetime: zoom interactivity', () => {
let deviceScaleFactor;
describe('zoom out', () => {
beforeEach(() => {
deviceScaleFactor = 0.75;
});
test('should update the month when next button is clicked', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/zoom?ionic:_testing=true'
});
page.setViewport({
width: 640,
height: 480,
deviceScaleFactor
});
const openModalBtn = await page.find('#open-modal');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const modal = await page.find('ion-modal');
await openModalBtn.click();
await modal.waitForVisible();
await ionModalDidPresent.next();
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button')
await buttons[1].click();
await page.waitForEvent('datetimeMonthDidChange');
const monthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(monthYear.textContent.trim()).toBe('March 2022');
});
test('should update the month when prev button is clicked', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/zoom?ionic:_testing=true'
});
const openModalBtn = await page.find('#open-modal');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const modal = await page.find('ion-modal');
await openModalBtn.click();
await modal.waitForVisible();
await ionModalDidPresent.next();
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button')
await buttons[0].click();
await page.waitForEvent('datetimeMonthDidChange');
const monthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(monthYear.textContent.trim()).toBe('January 2022');
});
});
describe('zoom in', () => {
beforeEach(() => {
deviceScaleFactor = 2;
});
test('should update the month when next button is clicked', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/zoom?ionic:_testing=true'
});
page.setViewport({
width: 640,
height: 480,
deviceScaleFactor
});
const openModalBtn = await page.find('#open-modal');
const modal = await page.find('ion-modal');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
await openModalBtn.click();
await modal.waitForVisible();
await ionModalDidPresent.next();
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button')
await buttons[1].click();
await page.waitForEvent('datetimeMonthDidChange');
const monthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(monthYear.textContent.trim()).toBe('March 2022');
});
test('should update the month when prev button is clicked', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/zoom?ionic:_testing=true'
});
const openModalBtn = await page.find('#open-modal');
const modal = await page.find('ion-modal');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
await openModalBtn.click();
await modal.waitForVisible();
await ionModalDidPresent.next();
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button')
await buttons[0].click();
await page.waitForEvent('datetimeMonthDidChange');
const monthYear = await page.find('ion-datetime >>> .calendar-month-year');
expect(monthYear.textContent.trim()).toBe('January 2022');
});
});
});

View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Datetime - Zoom</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
#background {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<ion-app>
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Datetime - Zoom</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h2>Modal</h2>
<ion-button id="open-modal">Present Modal</ion-button>
<ion-modal trigger="open-modal" id="modal">
<div id="background">
<ion-datetime id="picker" value="2022-02-01"></ion-datetime>
</div>
</ion-modal>
</ion-content>
</ion-app>
<script type="module">
import { InitMonthDidChangeEvent } from '../test/utils/month-did-change-event.js';
document.getElementById('open-modal').addEventListener('click', () => {
InitMonthDidChangeEvent();
});
</script>
</body>
</html>

View File

@@ -223,7 +223,11 @@ export const generateTime = (
});
isPMAllowed = maxParts.hour >= 13;
}
if (maxParts.minute !== undefined) {
if (maxParts.minute !== undefined && refParts.hour === maxParts.hour) {
// The available minutes should only be filtered when the hour is the same as the max hour.
// For example if the max hour is 10:30 and the current hour is 10:00,
// users should be able to select 00-30 minutes.
// If the current hour is 09:00, users should be able to select 00-60 minutes.
processedMinutes = processedMinutes.filter(minute => minute <= maxParts.minute!);
}
@@ -278,9 +282,9 @@ export const getPickerMonths = (
}
processedMonths.forEach(processedMonth => {
const date = new Date(`${processedMonth}/1/${year}`);
const date = new Date(`${processedMonth}/1/${year} GMT+0000`);
const monthString = new Intl.DateTimeFormat(locale, { month: 'long' }).format(date);
const monthString = new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' }).format(date);
months.push({ text: monthString, value: processedMonth });
});
} else {
@@ -288,9 +292,34 @@ export const getPickerMonths = (
const minMonth = minParts && minParts.year === year ? minParts.month : 1;
for (let i = minMonth; i <= maxMonth; i++) {
const date = new Date(`${i}/1/${year}`);
const monthString = new Intl.DateTimeFormat(locale, { month: 'long' }).format(date);
/**
*
* There is a bug on iOS 14 where
* Intl.DateTimeFormat takes into account
* the local timezone offset when formatting dates.
*
* Forcing the timezone to 'UTC' fixes the issue. However,
* we should keep this workaround as it is safer. In the event
* this breaks in another browser, we will not be impacted
* because all dates will be interpreted in UTC.
*
* Example:
* new Intl.DateTimeFormat('en-US', { month: 'long' }).format(new Date('Sat Apr 01 2006 00:00:00 GMT-0400 (EDT)')) // "March"
* new Intl.DateTimeFormat('en-US', { month: 'long', timeZone: 'UTC' }).format(new Date('Sat Apr 01 2006 00:00:00 GMT-0400 (EDT)')) // "April"
*
* In certain timezones, iOS 14 shows the wrong
* date for .toUTCString(). To combat this, we
* force all of the timezones to GMT+0000 (UTC).
*
* Example:
* Time Zone: Central European Standard Time
* new Date('1/1/1992').toUTCString() // "Tue, 31 Dec 1991 23:00:00 GMT"
* new Date('1/1/1992 GMT+0000').toUTCString() // "Wed, 01 Jan 1992 00:00:00 GMT"
*/
const date = new Date(`${i}/1/${year} GMT+0000`);
const monthString = new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' }).format(date);
months.push({ text: monthString, value: i });
}
}

View File

@@ -56,9 +56,9 @@ export const generateDayAriaLabel = (locale: string, today: boolean, refParts: D
/**
* MM/DD/YYYY will return midnight in the user's timezone.
*/
const date = new Date(`${refParts.month}/${refParts.day}/${refParts.year}`);
const date = new Date(`${refParts.month}/${refParts.day}/${refParts.year} GMT+0000`);
const labelString = new Intl.DateTimeFormat(locale, { weekday: 'long', month: 'long', day: 'numeric' }).format(date);
const labelString = new Intl.DateTimeFormat(locale, { weekday: 'long', month: 'long', day: 'numeric', timeZone: 'UTC' }).format(date);
/**
* If date is today, prepend "Today" so screen readers indicate
@@ -72,8 +72,8 @@ export const generateDayAriaLabel = (locale: string, today: boolean, refParts: D
* Used for the header in MD mode.
*/
export const getMonthAndDay = (locale: string, refParts: DatetimeParts) => {
const date = new Date(`${refParts.month}/${refParts.day}/${refParts.year}`);
return new Intl.DateTimeFormat(locale, { weekday: 'short', month: 'short', day: 'numeric' }).format(date);
const date = new Date(`${refParts.month}/${refParts.day}/${refParts.year} GMT+0000`);
return new Intl.DateTimeFormat(locale, { weekday: 'short', month: 'short', day: 'numeric', timeZone: 'UTC' }).format(date);
}
/**
@@ -83,6 +83,6 @@ export const getMonthAndDay = (locale: string, refParts: DatetimeParts) => {
* Example: May 2021
*/
export const getMonthAndYear = (locale: string, refParts: DatetimeParts) => {
const date = new Date(`${refParts.month}/${refParts.day}/${refParts.year}`);
return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(date);
const date = new Date(`${refParts.month}/${refParts.day}/${refParts.year} GMT+0000`);
return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric', timeZone: 'UTC' }).format(date);
}

View File

@@ -62,3 +62,28 @@ export const is24Hour = (locale: string, hourCycle?: 'h23' | 'h12') => {
export const getNumDaysInMonth = (month: number, year: number) => {
return (month === 4 || month === 6 || month === 9 || month === 11) ? 30 : (month === 2) ? isLeapYear(year) ? 29 : 28 : 31;
}
/**
* Certain locales display month then year while
* others display year then month.
* We can use Intl.DateTimeFormat to determine
* the ordering for each locale.
*/
export const isMonthFirstLocale = (locale: string) => {
/**
* By setting month and year we guarantee that only
* month, year, and literal (slashes '/', for example)
* values are included in the formatToParts results.
*
* The ordering of the parts will be determined by
* the locale. So if the month is the first value,
* then we know month should be shown first. If the
* year is the first value, then we know year should be shown first.
*
* This ordering can be controlled by customizing the locale property.
*/
const parts = new Intl.DateTimeFormat(locale, { month: 'numeric', year: 'numeric' }).formatToParts(new Date());
return parts[0].type === 'month';
}

View File

@@ -44,7 +44,7 @@
:host(.fab-vertical-bottom) {
bottom: $fab-content-margin;
bottom: var(--tab-translucent-safe-area, $fab-content-margin);
}
:host(.fab-vertical-bottom.fab-edge) {

View File

@@ -1,6 +1,6 @@
import { newE2EPage } from '@stencil/core/testing';
import { generateE2EUrl } from '../../../utils/test/utils';
import { generateE2EUrl } from '@utils/test';
export const testFab = async (
type: string,

View File

@@ -15,5 +15,5 @@ ion-footer {
}
ion-footer ion-toolbar:last-of-type {
padding-bottom: var(--ion-safe-area-bottom, 0);
}
padding-bottom: calc(var(--ion-safe-area-bottom, 0) + var(--tab-translucent-safe-area, 0));
}

View File

@@ -1,10 +1,30 @@
import type { E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';
test('footer: fade', async () => {
const page = await newE2EPage({
url: '/src/components/footer/test/fade?ionic:_testing=true'
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();
}
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@@ -1,6 +1,6 @@
import { newE2EPage } from '@stencil/core/testing';
import { checkComponentModeClasses } from '../../../../utils/test/utils';
import { checkComponentModeClasses } from '@utils/test';
test('footer: translucent', async () => {
const page = await newE2EPage({

View File

@@ -1,7 +1,7 @@
import { Component, ComponentInterface, Element, Host, Prop, h, writeTask } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { componentOnReady, inheritAttributes } from '../../utils/helpers';
import { Attributes, componentOnReady, inheritAttributes } from '../../utils/helpers';
import { hostContext } from '../../utils/theme';
import { cloneElement, createHeaderIndex, handleContentScroll, handleHeaderFade, handleToolbarIntersection, setHeaderActive, setToolbarBackgroundOpacity } from './header.utils';
@@ -21,7 +21,7 @@ export class Header implements ComponentInterface {
private contentScrollCallback?: any;
private intersectionObserver?: any;
private collapsibleMainHeader?: HTMLElement;
private inheritedAttributes: { [k: string]: any } = {};
private inheritedAttributes: Attributes = {};
@Element() el!: HTMLElement;

View File

@@ -1,6 +1,6 @@
import { newE2EPage } from '@stencil/core/testing';
import { checkComponentModeClasses } from '../../../../utils/test/utils';
import { checkComponentModeClasses } from '@utils/test';
test('header: translucent', async () => {
const page = await newE2EPage({

View File

@@ -1,6 +1,7 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop, State, Watch, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { Attributes, inheritAttributes } from '../../utils/helpers';
/**
* @part image - The inner `img` element.
@@ -13,6 +14,7 @@ import { getIonMode } from '../../global/ionic-global';
export class Img implements ComponentInterface {
private io?: IntersectionObserver;
private inheritedAttributes: Attributes = {};
@Element() el!: HTMLElement;
@@ -45,6 +47,10 @@ export class Img implements ComponentInterface {
/** Emitted when the img fails to load */
@Event() ionError!: EventEmitter<void>;
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['draggable']);
}
componentDidLoad() {
this.addIO();
}
@@ -100,17 +106,38 @@ export class Img implements ComponentInterface {
}
render() {
const { loadSrc, alt, onLoad, loadError, inheritedAttributes } = this;
const { draggable } = inheritedAttributes;
return (
<Host class={getIonMode(this)}>
<img
decoding="async"
src={this.loadSrc}
alt={this.alt}
onLoad={this.onLoad}
onError={this.loadError}
src={loadSrc}
alt={alt}
onLoad={onLoad}
onError={loadError}
part="image"
draggable={isDraggable(draggable)}
/>
</Host>
);
}
}
/**
* Enumerated strings must be set as booleans
* as Stencil will not render 'false' in the DOM.
* The need to explicitly render draggable="true"
* as only certain elements are draggable by default.
* https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable.
*/
const isDraggable = (draggable?: string): boolean | undefined => {
switch (draggable) {
case 'true':
return true;
case 'false':
return false;
default:
return undefined;
}
}

View File

@@ -0,0 +1,17 @@
import { newE2EPage } from '@stencil/core/testing';
test('img: draggable', async () => {
const page = await newE2EPage({
url: '/src/components/img/test/draggable?ionic:_testing=true'
});
const imgDraggableTrue = await page.find('#img-draggable-true >>> img');
expect(imgDraggableTrue.getAttribute('draggable')).toEqual('true');
const imgDraggableFalse = await page.find('#img-draggable-false >>> img');
expect(imgDraggableFalse.getAttribute('draggable')).toEqual('false');
const imgDraggableUnset = await page.find('#img-draggable-unset >>> img');
expect(imgDraggableUnset.getAttribute('draggable')).toEqual(null);
});

View File

@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Img - Draggable</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<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>
ion-img::part(image) {
border: 1px solid rgba(0, 0, 0, 0.5);
border-radius: 4px;
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Img - Draggable</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-list>
<ion-item>
<ion-label>Draggable</ion-label>
<ion-img id="img-draggable-true" draggable="true"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAARQAQMAAAA2ut43AAAABlBMVEXMzMz////TjRV2AAAEPUlEQVR42u3RMREAMBDDsPAn/eXhavOq87Ztu7u7+6eBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBf2hvgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgatgn4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGr7S0wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBwFewzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcLW9BQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq6CfQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgautrfAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAVbDPwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNX2FhgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGLgK9hkYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBi42t4CAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV8E+AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA1fbW2BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYOAq2GdgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGDgansLDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMXAX7DAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDFxtb4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrYJ+BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq+0tMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcBXsMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHC1vQUGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgaugn0GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGjvYD5WuYrpZqdmcAAAAASUVORK5CYII=">
</ion-img>
</ion-item>
<ion-item>
<ion-label>Not draggable (draggable="false")</ion-label>
<ion-img id="img-draggable-false" draggable="false"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAARQAQMAAAA2ut43AAAABlBMVEXMzMz////TjRV2AAAEPUlEQVR42u3RMREAMBDDsPAn/eXhavOq87Ztu7u7+6eBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBf2hvgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgatgn4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGr7S0wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBwFewzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcLW9BQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq6CfQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgautrfAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAVbDPwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNX2FhgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGLgK9hkYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBi42t4CAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV8E+AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA1fbW2BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYOAq2GdgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGDgansLDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMXAX7DAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDFxtb4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrYJ+BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq+0tMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcBXsMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHC1vQUGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgaugn0GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGjvYD5WuYrpZqdmcAAAAASUVORK5CYII=">
</ion-img>
</ion-item>
<ion-item>
<ion-label>Draggable (draggable not set)</ion-label>
<ion-img id="img-draggable-unset"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAARQAQMAAAA2ut43AAAABlBMVEXMzMz////TjRV2AAAEPUlEQVR42u3RMREAMBDDsPAn/eXhavOq87Ztu7u7+6eBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBf2hvgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgatgn4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGr7S0wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBwFewzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcLW9BQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq6CfQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgautrfAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAVbDPwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNX2FhgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGLgK9hkYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBi42t4CAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV8E+AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA1fbW2BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYOAq2GdgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGDgansLDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMXAX7DAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDFxtb4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrYJ+BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq+0tMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcBXsMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHC1vQUGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgaugn0GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGjvYD5WuYrpZqdmcAAAAASUVORK5CYII">
</ion-img>
</ion-item>
</ion-list>
</ion-content>
</ion-app>
</body>
</html>

View File

@@ -69,7 +69,7 @@ export class InfiniteScrollExample {
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (data.length == 1000) {
if (data.length === 1000) {
event.target.disabled = true;
}
}, 500);
@@ -111,7 +111,7 @@ infiniteScroll.addEventListener('ionInfinite', function(event) {
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (data.length == 1000) {
if (data.length === 1000) {
event.target.disabled = true;
}
}, 500);
@@ -164,7 +164,7 @@ const InfiniteScrollExample: React.FC = () => {
pushData();
console.log('Loaded data');
ev.target.complete();
if (data.length == 1000) {
if (data.length === 1000) {
setInfiniteDisabled(true);
}
}, 500);
@@ -263,7 +263,7 @@ export class InfiniteScrollExample {
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (this.data.length == 1000) {
if (this.data.length === 1000) {
ev.target.disabled = true;
}
}, 500);
@@ -335,7 +335,8 @@ export class InfiniteScrollExample {
</template>
<script lang="ts">
import {
import {
InfiniteScrollCustomEvent,
IonButton,
IonContent,
IonInfiniteScroll,
@@ -363,7 +364,7 @@ export default defineComponent({
const toggleInfiniteScroll = () => {
isDisabled.value = !isDisabled.value;
}
const items = ref([]);
const items = ref<number[]>([]);
const pushData = () => {
const max = items.value.length + 20;
const min = max - 20;
@@ -372,7 +373,7 @@ export default defineComponent({
}
}
const loadData = (ev: CustomEvent) => {
const loadData = (ev: InfiniteScrollCustomEvent) => {
setTimeout(() => {
pushData();
console.log('Loaded data');
@@ -380,7 +381,7 @@ export default defineComponent({
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (items.value.length == 1000) {
if (items.value.length === 1000) {
ev.target.disabled = true;
}
}, 500);

View File

@@ -1,10 +1,49 @@
import { newE2EPage } from '@stencil/core/testing';
import type { E2EPage } from '@stencil/core/testing';
/**
* Scrolls an `ion-content` element to the bottom, triggering the `ionInfinite` event.
* Waits for the custom event to complete.
*/
async function scrollIonContentPage(page: E2EPage) {
const content = await page.find('ion-content');
await content.callMethod('scrollToBottom');
await page.waitForChanges();
const ev = await page.spyOnEvent('ionInfiniteComplete', 'document');
await ev.next();
}
describe('infinite-scroll: basic', () => {
it('should match existing visual screenshots', async () => {
const page = await newE2EPage({
url: '/src/components/infinite-scroll/test/basic?ionic:_testing=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});
describe('when enabled', () => {
it('should load more items when scrolled to the bottom', async () => {
const page = await newE2EPage({
url: '/src/components/infinite-scroll/test/basic?ionic:_testing=true'
});
const initialItems = await page.findAll('ion-item');
expect(initialItems.length).toBe(30);
await scrollIonContentPage(page);
const updatedItems = await page.findAll('ion-item');
expect(updatedItems.length).toBe(60);
});
test('infinite-scroll: basic', async () => {
const page = await newE2EPage({
url: '/src/components/infinite-scroll/test/basic?ionic:_testing=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@@ -4,12 +4,14 @@
<head>
<meta charset="UTF-8">
<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">
<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>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
@@ -35,8 +37,6 @@
</ion-infinite-scroll>
</ion-content>
</ion-app>
<script>
@@ -48,12 +48,11 @@
}
infiniteScroll.addEventListener('ionInfinite', async function () {
console.log('Loading data...');
await wait(500);
infiniteScroll.complete();
appendItems();
console.log('Done');
// Custom event consumed in the e2e tests
document.dispatchEvent(new CustomEvent('ionInfiniteComplete'));
});
function appendItems() {

View File

@@ -36,7 +36,7 @@ export class InfiniteScrollExample {
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (data.length == 1000) {
if (data.length === 1000) {
event.target.disabled = true;
}
}, 500);

View File

@@ -25,7 +25,7 @@ infiniteScroll.addEventListener('ionInfinite', function(event) {
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (data.length == 1000) {
if (data.length === 1000) {
event.target.disabled = true;
}
}, 500);

View File

@@ -37,7 +37,7 @@ const InfiniteScrollExample: React.FC = () => {
pushData();
console.log('Loaded data');
ev.target.complete();
if (data.length == 1000) {
if (data.length === 1000) {
setInfiniteDisabled(true);
}
}, 500);

View File

@@ -38,7 +38,7 @@ export class InfiniteScrollExample {
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (this.data.length == 1000) {
if (this.data.length === 1000) {
ev.target.disabled = true;
}
}, 500);

View File

@@ -28,7 +28,8 @@
</template>
<script lang="ts">
import {
import {
InfiniteScrollCustomEvent,
IonButton,
IonContent,
IonInfiniteScroll,
@@ -56,7 +57,7 @@ export default defineComponent({
const toggleInfiniteScroll = () => {
isDisabled.value = !isDisabled.value;
}
const items = ref([]);
const items = ref<number[]>([]);
const pushData = () => {
const max = items.value.length + 20;
const min = max - 20;
@@ -65,7 +66,7 @@ export default defineComponent({
}
}
const loadData = (ev: CustomEvent) => {
const loadData = (ev: InfiniteScrollCustomEvent) => {
setTimeout(() => {
pushData();
console.log('Loaded data');
@@ -73,7 +74,7 @@ export default defineComponent({
// App logic to determine if all data is loaded
// and disable the infinite scroll
if (items.value.length == 1000) {
if (items.value.length === 1000) {
ev.target.disabled = true;
}
}, 500);

View File

@@ -2,7 +2,7 @@ import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Hos
import { getIonMode } from '../../global/ionic-global';
import { AutocompleteTypes, Color, InputChangeEventDetail, StyleEventDetail, TextFieldTypes } from '../../interface';
import { debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
import { Attributes, debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';
/**
@@ -21,7 +21,8 @@ export class Input implements ComponentInterface {
private nativeInput?: HTMLInputElement;
private inputId = `ion-input-${inputIds++}`;
private didBlurAfterEdit = false;
private inheritedAttributes: { [k: string]: any } = {};
private inheritedAttributes: Attributes = {};
private isComposing = false;
/**
* This is required for a WebKit bug which requires us to
@@ -231,7 +232,9 @@ export class Input implements ComponentInterface {
*/
@Watch('value')
protected valueChanged() {
if (this.nativeInput) {
const nativeInput = this.nativeInput;
const value = this.getValue();
if (nativeInput && nativeInput.value !== value && !this.isComposing) {
/**
* Assigning the native input's value on attribute
* value change, allows `ionInput` implementations
@@ -240,7 +243,7 @@ export class Input implements ComponentInterface {
* Used for patterns such as input trimming (removing whitespace),
* or input masking.
*/
this.nativeInput.value = this.getValue();
nativeInput.value = value;
}
this.emitStyle();
this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
@@ -260,12 +263,27 @@ export class Input implements ComponentInterface {
}
}
componentDidLoad() {
const nativeInput = this.nativeInput;
if (nativeInput) {
// TODO: FW-729 Update to JSX bindings when Stencil resolves bug with:
// https://github.com/ionic-team/stencil/issues/3235
nativeInput.addEventListener('compositionstart', this.onCompositionStart);
nativeInput.addEventListener('compositionend', this.onCompositionEnd);
}
}
disconnectedCallback() {
if (Build.isBrowser) {
document.dispatchEvent(new CustomEvent('ionInputDidUnload', {
detail: this.el
}));
}
const nativeInput = this.nativeInput;
if (nativeInput) {
nativeInput.removeEventListener('compositionstart', this.onCompositionStart);
nativeInput.removeEventListener('compositionEnd', this.onCompositionEnd);
}
}
/**
@@ -364,6 +382,14 @@ export class Input implements ComponentInterface {
}
}
private onCompositionStart = () => {
this.isComposing = true;
}
private onCompositionEnd = () => {
this.isComposing = false;
}
private clearTextOnEnter = (ev: KeyboardEvent) => {
if (ev.key === 'Enter') { this.clearTextInput(ev); }
}

View File

@@ -36,4 +36,29 @@ test('input: basic', async () => {
for (const compare of compares) {
expect(compare).toMatchScreenshot();
}
});
test('input: basic should not error on input', async () => {
const page = await newE2EPage({
url: '/src/components/input/test/basic?ionic:_testing=true'
});
const errors = [];
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(msg.text);
}
});
const inputs = await page.findAll('ion-input');
for (const input of inputs) {
await input.click();
await input.type('letters and 12345');
}
expect(errors.length).toEqual(0);
})

View File

@@ -168,7 +168,7 @@ export class ItemSliding implements ComponentInterface {
}
/**
* Close the sliding item. Items can also be closed from the [List](../list).
* Close the sliding item. Items can also be closed from the [List](./list).
*/
@Method()
async close() {
@@ -176,7 +176,7 @@ export class ItemSliding implements ComponentInterface {
}
/**
* Close all of the sliding items in the list. Items can also be closed from the [List](../list).
* Close all of the sliding items in the list. Items can also be closed from the [List](./list).
*/
@Method()
async closeOpened(): Promise<boolean> {
@@ -408,7 +408,7 @@ export class ItemSliding implements ComponentInterface {
this.state = SlidingState.Disabled;
this.tmr = undefined;
if (this.gesture) {
this.gesture.enable(true);
this.gesture.enable(!this.disabled);
}
}, 600) as any;

View File

@@ -369,7 +369,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
<slot></slot>
</div>
<slot name="end"></slot>
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon" aria-hidden="true"></ion-icon>}
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon" aria-hidden="true" flip-rtl={detailIcon === chevronForward}></ion-icon>}
<div class="item-inner-highlight"></div>
</div>
{canActivate && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}

View File

@@ -944,7 +944,7 @@ export const ItemExamples: React.FC = () => {
<IonItem>
<IonButton slot="start">
Start Icon
<IonIcon icon={home} slot="end" />>
<IonIcon icon={home} slot="end" />
</IonButton>
<IonLabel>Buttons with Icons</IonLabel>
<IonButton slot="end">

View File

@@ -120,6 +120,10 @@
<ion-item class="overflow-scroll">
<ion-label class="ion-text-wrap">Item with overflow scroll. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mattis molestie a iaculis at erat pellentesque adipiscing commodo. Vulputate enim nulla aliquet porttitor. Fermentum dui faucibus in ornare quam viverra orci sagittis eu. Faucibus scelerisque eleifend donec pretium vulputate sapien nec. Cras semper auctor neque vitae. Cursus eget nunc scelerisque viverra mauris in aliquam. Non sodales neque sodales ut etiam sit amet. Sit amet nulla facilisi morbi tempus. Accumsan in nisl nisi scelerisque eu. Sed elementum tempus egestas sed sed. Urna nunc id cursus metus aliquam. Gravida dictum fusce ut placerat orci nulla pellentesque. Id diam maecenas ultricies mi eget. Elementum nisi quis eleifend quam adipiscing vitae proin.</ion-label>
</ion-item>
<ion-item detail>
<ion-label>Item with details.</ion-label>
</ion-item>
</ion-content>
<ion-footer>

View File

@@ -203,7 +203,7 @@ export const ItemExamples: React.FC = () => {
<IonItem>
<IonButton slot="start">
Start Icon
<IonIcon icon={home} slot="end" />>
<IonIcon icon={home} slot="end" />
</IonButton>
<IonLabel>Buttons with Icons</IonLabel>
<IonButton slot="end">

View File

@@ -289,8 +289,6 @@ export class LoadingExample {
<ion-button @click="presentLoadingWithOptions">Show Loading</ion-button>
</template>
<script>
<script>
import { IonButton, loadingController } from '@ionic/vue';
import { defineComponent } from 'vue';

View File

@@ -1,6 +1,6 @@
import { newE2EPage } from '@stencil/core/testing';
import { generateE2EUrl } from '../../../utils/test/utils';
import { generateE2EUrl } from '@utils/test';
export const testLoading = async (
type: string,

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