mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdfe0b10d7 | ||
|
|
b839e6fd97 | ||
|
|
eb3cbe45a7 | ||
|
|
6fbb90896b | ||
|
|
73e2123ce8 | ||
|
|
70db7080b1 | ||
|
|
9e63947e3c | ||
|
|
0dd2f34dfa | ||
|
|
07e739a364 | ||
|
|
e5c8c10029 | ||
|
|
38ae3620a2 | ||
|
|
72be80cb58 | ||
|
|
cc60b60135 | ||
|
|
447497427e | ||
|
|
18b347b4e9 | ||
|
|
29bb4fcb05 | ||
|
|
11aa48c83c | ||
|
|
f13722cc20 | ||
|
|
983382c327 | ||
|
|
1b16e1f378 | ||
|
|
494991e9fb | ||
|
|
1199c53437 | ||
|
|
6e1a8f1df2 | ||
|
|
52e5a8d3e3 | ||
|
|
0d17e05edc | ||
|
|
fa1317359a | ||
|
|
d87170db3a | ||
|
|
5756789b42 | ||
|
|
b081ca4dd0 | ||
|
|
77c980b032 | ||
|
|
05eb5ddaf8 | ||
|
|
9de09bd4f5 | ||
|
|
2f34f52536 | ||
|
|
ec3e19e66c | ||
|
|
a512287e4b | ||
|
|
48814ab134 | ||
|
|
28249924a8 | ||
|
|
e620d813b3 | ||
|
|
0db905a871 | ||
|
|
c87e26131a | ||
|
|
06c4233a54 | ||
|
|
9585723dda | ||
|
|
046ca8f732 | ||
|
|
fdd2978774 | ||
|
|
72a47548ce | ||
|
|
db1f90d111 | ||
|
|
9443bfeca6 | ||
|
|
2ceacf9d50 | ||
|
|
06c3048828 | ||
|
|
b47c53df72 | ||
|
|
fdd424a2c0 | ||
|
|
a710fdcb66 | ||
|
|
d9a7c635ee | ||
|
|
f2c8db9a0b | ||
|
|
346ecb2a3c | ||
|
|
51614c1b32 | ||
|
|
f69f221e10 | ||
|
|
29d0d0ef28 | ||
|
|
17e8c73b9d | ||
|
|
dbb39cd00d | ||
|
|
04f931f694 | ||
|
|
352797e932 | ||
|
|
a8a48a4ca4 | ||
|
|
016fa16d44 | ||
|
|
583c43127b | ||
|
|
c47764c670 | ||
|
|
1ecfcd1902 | ||
|
|
dceec07390 | ||
|
|
eaec9ca791 | ||
|
|
fccaaf8a91 | ||
|
|
34f6f1d736 | ||
|
|
0cb7db0599 | ||
|
|
c148d3125b | ||
|
|
c29f5a65b4 | ||
|
|
424879df8e | ||
|
|
71e5994c94 | ||
|
|
ee7167512f | ||
|
|
439b10e10d | ||
|
|
27168d938a | ||
|
|
b6569254a3 | ||
|
|
e915aeda6b | ||
|
|
9273f97a0c | ||
|
|
aca78f5ac6 |
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
||||
<!-- Before submitting an issue, please consult our docs (https://beta.ionicframework.com/docs/) and API reference (https://beta.ionicframework.com/docs/api/) -->
|
||||
<!-- Before submitting an issue, please consult our docs (https://ionicframework.com/docs/) and API reference (https://ionicframework.com/docs/api/) -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
title: 'bug: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
title: 'feat: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/support_question.md
vendored
2
.github/ISSUE_TEMPLATE/support_question.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Support Question
|
||||
about: Question on how to use this project
|
||||
title: ''
|
||||
title: 'support: '
|
||||
labels: 'ionitron: support'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
18
.github/PROCESS.md
vendored
18
.github/PROCESS.md
vendored
@@ -140,6 +140,14 @@ Once the release is ready to ship, it will get merged into `stable` and `master`
|
||||
|
||||
See the [steps for releasing](#releasing) below for detailed information on how to publish a release.
|
||||
|
||||
### Version Branches
|
||||
|
||||
Once a release has shipped and the release branch has been merged into `stable` and `master` it should also be merged into its corrsponding version branch. These version branches allow us to ship updates for specific versions of the framework (i.e. Lets us ship a bug fix that only affects 4.2.x).
|
||||
|
||||
Patch releases should be merged into their corresponding version branches. For example, a `release-4.1.1` branch should be merged into the `4.1.x` version branch and a `release-5.0.1` branch should be merged into the `5.0.x` version branch.
|
||||
|
||||
When releasing a major version such as `5.0.0 ` or a minor version such as `4.1.0` , the version branch will not exist. The version branch should be created once the release branch has been merged into `stable` and `master`. For example, when releasing `4.1.0`, the `release-4.1.0` release branch should be merged into `stable` and `master` and then the `4.1.x` version branch should be created off the latest `stable`.
|
||||
|
||||
|
||||
### Hotfix Branches
|
||||
|
||||
@@ -214,19 +222,29 @@ Hotfixes bypass `master` and should only be used for urgent fixes that can't wai
|
||||
## Releasing
|
||||
|
||||
1. Create the release branch from `master`, for example: `release-4.1.0`.
|
||||
|
||||
1. Submit a pull request from the release branch into `stable`. Do not merge this pull request yet.
|
||||
|
||||
1. Verify all tests are passing, fix any bugs if needed and make sure no undesired commits are in.
|
||||
|
||||
1. Navigate to the root of the repository while on the release branch.
|
||||
|
||||
1. Run `npm i` if it hasn't already been done.
|
||||
|
||||
1. Run `npm run release.prepare`
|
||||
- Select the version based on the type of commits and the [Ionic Versioning](https://ionicframework.com/docs/intro/versioning)
|
||||
- After the process completes, verify the version number in all packages (`core`, `docs`, `angular`)
|
||||
- Verify the changelog commits are accurate and follow the [proper format]((https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format))
|
||||
- Commit these changes with the version number as the message, e.g. `git commit -m "4.1.0"`
|
||||
|
||||
1. Run `npm run release`
|
||||
|
||||
1. Click **Merge pull request**. Use the dropdown to select this option if necessary.
|
||||
|
||||
<img width="191" alt="Merge pull request button" src="https://user-images.githubusercontent.com/236501/47032669-8be1b980-d138-11e8-9a90-d1518c223184.png">
|
||||
|
||||
1. Rewrite the commit message to `merge release-4.1.0` with the proper release branch.
|
||||
|
||||
1. Create a pull request and merge the release branch back into `master` using the same commit format in the last step, to ensure any changes made on the release branch get added to future releases.
|
||||
|
||||
1. Merge the release branch into its corresponding version branch. If this is a major or minor release, create the version branch off the latest `stable`.
|
||||
|
||||
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,12 +1,51 @@
|
||||
#### Short description of what this resolves:
|
||||
<!-- Please refer to our contributing documentation for any questions on submitting a pull request, or let us know here if you need any help: https://ionicframework.com/docs/building/contributing -->
|
||||
|
||||
## Pull request checklist
|
||||
|
||||
Please check if your PR fulfills the following requirements:
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features)
|
||||
- [ ] Build (`npm run build`) was run locally and any changes were pushed
|
||||
- [ ] Lint (`npm run lint`) has passed locally and any fixes were made for failures
|
||||
|
||||
|
||||
#### Changes proposed in this pull request:
|
||||
## Pull request type
|
||||
|
||||
<!-- Please do not submit updates to dependencies unless it fixes an issue. -->
|
||||
|
||||
<!-- Please try to limit your pull request to one type, submit multiple pull requests if needed. -->
|
||||
|
||||
Please check the type of change your PR introduces:
|
||||
- [ ] Bugfix
|
||||
- [ ] Feature
|
||||
- [ ] Code style update (formatting, renaming)
|
||||
- [ ] Refactoring (no functional changes, no api changes)
|
||||
- [ ] Build related changes
|
||||
- [ ] Documentation content changes
|
||||
- [ ] Other (please describe):
|
||||
|
||||
|
||||
## What is the current behavior?
|
||||
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
|
||||
|
||||
Issue Number: N/A
|
||||
|
||||
|
||||
## What is the new behavior?
|
||||
<!-- Please describe the behavior or changes that are being added by this PR. -->
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
**Ionic Version**:
|
||||
## Does this introduce a breaking change?
|
||||
|
||||
**Fixes**: #
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
<!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||
|
||||
|
||||
## Other information
|
||||
|
||||
<!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
|
||||
|
||||
29
.github/ionic-issue-bot.yml
vendored
29
.github/ionic-issue-bot.yml
vendored
@@ -3,6 +3,26 @@ triage:
|
||||
removeLabelWhenProjectAssigned: true
|
||||
dryRun: false
|
||||
|
||||
comment:
|
||||
labels:
|
||||
- label: "help wanted"
|
||||
message: >
|
||||
This issue has been labeled as `help wanted`. This label is added to issues
|
||||
that we believe would be good for contributors.
|
||||
|
||||
|
||||
If you'd like to work on this issue, please comment here letting us know that
|
||||
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.
|
||||
|
||||
|
||||
For a guide on how to create a pull request and test this project locally to see
|
||||
your changes, see our [contributing documentation](https://ionicframework.com/docs/building/contributing).
|
||||
|
||||
|
||||
Thank you!
|
||||
dryRun: false
|
||||
|
||||
closeAndLock:
|
||||
labels:
|
||||
- label: "ionitron: support"
|
||||
@@ -98,6 +118,15 @@ labelPullRequest:
|
||||
|
||||
wrongRepo:
|
||||
repos:
|
||||
- label: "ionitron: capacitor"
|
||||
repo: capacitor
|
||||
message: >
|
||||
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
|
||||
associated with the Ionic Framework. It appears that this issue is associated with Capacitor.
|
||||
I am moving this issue to the Capacitor repository. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: v3"
|
||||
repo: ionic-v3
|
||||
message: >
|
||||
|
||||
73
CHANGELOG.md
73
CHANGELOG.md
@@ -1,9 +1,67 @@
|
||||
## [4.1.3](https://github.com/ionic-team/ionic/compare/v4.1.2...v4.1.3) (2019-04-26)
|
||||
## [4.3.1](https://github.com/ionic-team/ionic/compare/v4.3.0...v4.3.1) (2019-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sanitize components using innerHTML ([#18144](https://github.com/ionic-team/ionic/issues/18144)) ([f95facf](https://github.com/ionic-team/ionic/commit/f95facf))
|
||||
* **angular:** support replaceUrl with angular <7.2 ([#18106](https://github.com/ionic-team/ionic/issues/18106)) ([eb3cbe4](https://github.com/ionic-team/ionic/commit/eb3cbe4))
|
||||
* sanitize components using innerHTML ([#18146](https://github.com/ionic-team/ionic/issues/18146)) ([b839e6f](https://github.com/ionic-team/ionic/commit/b839e6f))
|
||||
|
||||
|
||||
|
||||
# [4.3.0 Lithium](https://github.com/ionic-team/ionic/compare/v4.2.0...v4.3.0) (2019-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **action-sheet:** default buttons to empty array ([9e63947](https://github.com/ionic-team/ionic/commit/9e63947))
|
||||
* **angular:** back button correctly goes back to proper tab ([#18005](https://github.com/ionic-team/ionic/issues/18005)) ([52e5a8d](https://github.com/ionic-team/ionic/commit/52e5a8d)), closes [#17278](https://github.com/ionic-team/ionic/issues/17278) [#15216](https://github.com/ionic-team/ionic/issues/15216)
|
||||
* **components:** add mode classes to components for use in shadow elements ([#17838](https://github.com/ionic-team/ionic/issues/17838)) ([e5c8c10](https://github.com/ionic-team/ionic/commit/e5c8c10)), closes [#17608](https://github.com/ionic-team/ionic/issues/17608)
|
||||
* **datetime:** date strings no longer revert to previous day ([#18018](https://github.com/ionic-team/ionic/issues/18018)) ([cc60b60](https://github.com/ionic-team/ionic/commit/cc60b60)), closes [#17977](https://github.com/ionic-team/ionic/issues/17977)
|
||||
* **input:** prevent input from losing focus when tapping clear button ([#18004](https://github.com/ionic-team/ionic/issues/18004)) ([29bb4fc](https://github.com/ionic-team/ionic/commit/29bb4fc)), closes [#18002](https://github.com/ionic-team/ionic/issues/18002)
|
||||
* **item:** use the correct input highlight for an inset line item ([#18052](https://github.com/ionic-team/ionic/issues/18052)) ([72be80c](https://github.com/ionic-team/ionic/commit/72be80c)), closes [#18051](https://github.com/ionic-team/ionic/issues/18051)
|
||||
* **item-sliding:** hide closed side options while dragging side options open ([#17986](https://github.com/ionic-team/ionic/issues/17986)) ([f13722c](https://github.com/ionic-team/ionic/commit/f13722c)), closes [#17822](https://github.com/ionic-team/ionic/issues/17822)
|
||||
* **slides:** allow zoom to work ([18b347b](https://github.com/ionic-team/ionic/commit/18b347b)), closes [#17981](https://github.com/ionic-team/ionic/issues/17981)
|
||||
* **slides:** expose interface to provide custom animations ([#17959](https://github.com/ionic-team/ionic/issues/17959)) ([4474974](https://github.com/ionic-team/ionic/commit/4474974)), closes [#16616](https://github.com/ionic-team/ionic/issues/16616)
|
||||
* **textarea:** float label when a value is changed async ([#18024](https://github.com/ionic-team/ionic/issues/18024)) ([494991e](https://github.com/ionic-team/ionic/commit/494991e)), closes [#17555](https://github.com/ionic-team/ionic/issues/17555) [#17559](https://github.com/ionic-team/ionic/issues/17559)
|
||||
* **textarea:** update label alignment for inputs and textareas ([#18042](https://github.com/ionic-team/ionic/issues/18042)) ([38ae362](https://github.com/ionic-team/ionic/commit/38ae362)), closes [#16187](https://github.com/ionic-team/ionic/issues/16187)
|
||||
* **vue:** use direction type from core ([#17901](https://github.com/ionic-team/ionic/issues/17901)) ([fa13173](https://github.com/ionic-team/ionic/commit/fa13173))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **toast:** add header and additional custom toast buttons ([#17147](https://github.com/ionic-team/ionic/issues/17147)) ([6e1a8f1](https://github.com/ionic-team/ionic/commit/6e1a8f1)), closes [#16791](https://github.com/ionic-team/ionic/issues/16791) [#16237](https://github.com/ionic-team/ionic/issues/16237) [#17611](https://github.com/ionic-team/ionic/issues/17611)
|
||||
* **toast:** add variables to change position start/end of toast ([#17961](https://github.com/ionic-team/ionic/issues/17961)) ([07e739a](https://github.com/ionic-team/ionic/commit/07e739a)), closes [#17854](https://github.com/ionic-team/ionic/issues/17854)
|
||||
|
||||
|
||||
|
||||
# [4.2.0 Helium](https://github.com/ionic-team/ionic/compare/v4.1.2...v4.2.0) (2019-04-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** properly check for replaceUrl in routing ([#17879](https://github.com/ionic-team/ionic/issues/17879)) ([f2c8db9](https://github.com/ionic-team/ionic/commit/f2c8db9)), closes [#15181](https://github.com/ionic-team/ionic/issues/15181)
|
||||
* **angular:** return proper types in the overlay.getTop method ([#17802](https://github.com/ionic-team/ionic/issues/17802)) ([439b10e](https://github.com/ionic-team/ionic/commit/439b10e))
|
||||
* **angular:** support relative router links ([d9a7c63](https://github.com/ionic-team/ionic/commit/d9a7c63)), closes [#17888](https://github.com/ionic-team/ionic/issues/17888) [#16534](https://github.com/ionic-team/ionic/issues/16534) [#16736](https://github.com/ionic-team/ionic/issues/16736) [#16954](https://github.com/ionic-team/ionic/issues/16954)
|
||||
* **angular:** update ng-add schematic ([9443bfe](https://github.com/ionic-team/ionic/commit/9443bfe))
|
||||
* **css:** resolve spinner and checkbox issues on older chrome versions ([#17896](https://github.com/ionic-team/ionic/issues/17896)) ([dbb39cd](https://github.com/ionic-team/ionic/commit/dbb39cd)), closes [#17524](https://github.com/ionic-team/ionic/issues/17524) [#17501](https://github.com/ionic-team/ionic/issues/17501)
|
||||
* **datetime:** default to local date when no date given ([#17706](https://github.com/ionic-team/ionic/issues/17706)) ([bab56e8](https://github.com/ionic-team/ionic/commit/bab56e8))
|
||||
* **datetime:** recalculate day column when month or year is changed ([#17815](https://github.com/ionic-team/ionic/issues/17815)) ([9273f97](https://github.com/ionic-team/ionic/commit/9273f97)), closes [#14233](https://github.com/ionic-team/ionic/issues/14233) [#14732](https://github.com/ionic-team/ionic/issues/14732) [#15452](https://github.com/ionic-team/ionic/issues/15452) [#15794](https://github.com/ionic-team/ionic/issues/15794) [#17633](https://github.com/ionic-team/ionic/issues/16733) [#17060](https://github.com/ionic-team/ionic/issues/17060) [#17510](https://github.com/ionic-team/ionic/issues/17510) [#17521](https://github.com/ionic-team/ionic/issues/17521)
|
||||
* **item-option:** add styling and behaviour for disabled item-option ([#17909](https://github.com/ionic-team/ionic/issues/17909)) ([346ecb2](https://github.com/ionic-team/ionic/commit/346ecb2)), closes [#17905](https://github.com/ionic-team/ionic/issues/17905)
|
||||
* **progress-bar:** flip rtl using the existing reversed property ([#17464](https://github.com/ionic-team/ionic/issues/17464)) ([fccaaf8](https://github.com/ionic-team/ionic/commit/fccaaf8)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **react:** ensure tabs are resilient to optional tabs ([#17862](https://github.com/ionic-team/ionic/issues/17862)) ([c29f5a6](https://github.com/ionic-team/ionic/commit/c29f5a6))
|
||||
* **reorder-group:** add ability to reorder items inside shadow ([#17747](https://github.com/ionic-team/ionic/issues/17747)) ([352797e](https://github.com/ionic-team/ionic/commit/352797e)), closes [#17746](https://github.com/ionic-team/ionic/issues/17746)
|
||||
* **select:** update overlay options when added asynchronously ([#17860](https://github.com/ionic-team/ionic/issues/17860)) ([1ecfcd1](https://github.com/ionic-team/ionic/commit/1ecfcd1)), closes [#15716](https://github.com/ionic-team/ionic/issues/15716) [#17851](https://github.com/ionic-team/ionic/issues/17851)
|
||||
* **virtual-scroll:** use correct item top calculation with header or footer function ([#15948](https://github.com/ionic-team/ionic/issues/15948)) ([#17345](https://github.com/ionic-team/ionic/issues/17345)) ([a8a48a4](https://github.com/ionic-team/ionic/commit/a8a48a4)), closes [#17298](https://github.com/ionic-team/ionic/issues/17298)
|
||||
* **vue:** back button event handling ([#17877](https://github.com/ionic-team/ionic/issues/17877)) ([dceec07](https://github.com/ionic-team/ionic/commit/dceec07))
|
||||
* **vue:** update popover to use correct controller ([#17865](https://github.com/ionic-team/ionic/issues/17865)) ([0cb7db0](https://github.com/ionic-team/ionic/commit/0cb7db0))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **img:** add ionError event ([#17134](https://github.com/ionic-team/ionic/issues/17134)) ([04f931f](https://github.com/ionic-team/ionic/commit/04f931f)), closes [#16947](https://github.com/ionic-team/ionic/issues/16947)
|
||||
* **range:** add ticks attribute ([#17718](https://github.com/ionic-team/ionic/issues/17718)) ([016fa16](https://github.com/ionic-team/ionic/commit/016fa16)), closes [#17717](https://github.com/ionic-team/ionic/issues/17717)
|
||||
* **vue:** support for ion-tabs ([#17678](https://github.com/ionic-team/ionic/issues/17678)) ([ee71675](https://github.com/ionic-team/ionic/commit/ee71675))
|
||||
* **vue:** support ion-vue-router ([#17821](https://github.com/ionic-team/ionic/issues/17821)) ([71e5994](https://github.com/ionic-team/ionic/commit/71e5994))
|
||||
|
||||
|
||||
|
||||
@@ -487,7 +545,7 @@ Tabs got the last bit of changes needed in order to support lazy loading of comp
|
||||
pathMatch: 'full'
|
||||
}
|
||||
];
|
||||
```
|
||||
```
|
||||
|
||||
</detail>
|
||||
|
||||
@@ -618,14 +676,14 @@ Segment Button now requires the text to be wrapped in an `ion-label` element for
|
||||
<ion-segment-button>
|
||||
Item One
|
||||
</ion-segment-button>
|
||||
```
|
||||
```
|
||||
*New usage:*
|
||||
|
||||
```html
|
||||
<ion-segment-button>
|
||||
<ion-label>Item One</ion-label>
|
||||
</ion-segment-button>
|
||||
```
|
||||
```
|
||||
|
||||
#### Simplifying Chip
|
||||
|
||||
@@ -784,8 +842,8 @@ Here, we have an `ion-tab` element that accepts an icon, a label, and a link to
|
||||
<ion-tab-bar>
|
||||
|
||||
<!-- No ion-tab, just a link that looks like a tab -->
|
||||
<ion-tab-button href="https://beta.ionicframework.com">
|
||||
<!-- <a href="https://beta.ionicframework.com"> -->
|
||||
<ion-tab-button href="https://ionicframework.com">
|
||||
<!-- <a href="https://ionicframework.com"> -->
|
||||
<ion-icon name="globe"></ion-icon>
|
||||
<ion-label>Schedule</ion-label>
|
||||
</ion-tab-button>
|
||||
@@ -2141,4 +2199,5 @@ The following dependencies need to be updated to resolve build errors
|
||||
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
|
||||
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
### Migration Guide
|
||||
|
||||
If you aren't sure where to start in upgrading to v4, we recommend reading through our [migration guide](https://beta.ionicframework.com/docs/building/migration) first.
|
||||
If you aren't sure where to start in upgrading to v4, we recommend reading through our [migration guide](https://ionicframework.com/docs/building/migration) first.
|
||||
|
||||
### Migration Linter
|
||||
|
||||
@@ -182,13 +182,14 @@ These have been renamed to the following, and moved from the button element to t
|
||||
|
||||
In addition, several sets of mutually exclusive boolean attributes have been combined into a single string attribute.
|
||||
|
||||
The `small` and `large` attributes are now combined under the `size` attribute. The `clear`, `outline`, and `solid` attributes have been combined under `fill`. And, lastly, the `full` and `block` attributes have been combined under `expand`.
|
||||
The `small` and `large` attributes are now combined under the `size` attribute. The `clear`, `outline`, and `solid` attributes have been combined under `fill`. The `full` and `block` attributes have been combined under `expand`. And, lastly, the `round` attribute is now used under `shape`.
|
||||
|
||||
| Old Property | New Property | Property Behavior |
|
||||
| --------------------------- | ------------ | --------------------------- |
|
||||
| `small`, `large` | `size` | Sets the button size. |
|
||||
| `clear`, `outline`, `solid` | `fill` | Sets the button fill style. |
|
||||
| `full`, `block` | `expand` | Sets the button width. |
|
||||
| `round` | `shape` | Sets the button shape. |
|
||||
|
||||
|
||||
**Old Usage Example:**
|
||||
@@ -225,6 +226,10 @@ The `small` and `large` attributes are now combined under the `size` attribute.
|
||||
<ion-button full>
|
||||
Full-width Button
|
||||
</ion-button>
|
||||
|
||||
<ion-button round>
|
||||
Round Button
|
||||
</ion-button>
|
||||
```
|
||||
|
||||
**New Usage Example:**
|
||||
@@ -251,6 +256,10 @@ The `small` and `large` attributes are now combined under the `size` attribute.
|
||||
<ion-button expand="full">
|
||||
Full-width Button
|
||||
</ion-button>
|
||||
|
||||
<ion-button shape="round">
|
||||
Round Button
|
||||
</ion-button>
|
||||
```
|
||||
|
||||
|
||||
@@ -1065,11 +1074,11 @@ async openLoading() {
|
||||
let loading = this.loadingCtrl.create({
|
||||
content: 'Loading...'
|
||||
});
|
||||
|
||||
|
||||
await loading.present();
|
||||
|
||||
|
||||
const { role, data } = await loading.onDidDismiss();
|
||||
|
||||
|
||||
console.log('Loading dismissed!');
|
||||
}
|
||||
```
|
||||
@@ -1740,19 +1749,19 @@ Changes the `font-family` of the whole page based on the mode selected (iOS or M
|
||||
The following set of CSS files are optional and can safely be commented out if the application is not using any of the features.
|
||||
|
||||
- **padding.css**
|
||||
Adds utility attributes that allow adding `padding` and `margin` attributes to any element. See [content space](https://beta.ionicframework.com/docs/layout/css-utilities#content-space) for what this includes.
|
||||
Adds utility attributes that allow adding `padding` and `margin` attributes to any element. See [content space](https://ionicframework.com/docs/layout/css-utilities#content-space) for what this includes.
|
||||
|
||||
- **float-elements.css**
|
||||
Adds utility attributes that allow adding `float` attributes to any element. See [element placement](https://beta.ionicframework.com/docs/layout/css-utilities/#element-placement) for what this includes.
|
||||
Adds utility attributes that allow adding `float` attributes to any element. See [element placement](https://ionicframework.com/docs/layout/css-utilities/#element-placement) for what this includes.
|
||||
|
||||
- **text-alignment.css**
|
||||
Adds utility attributes that allow adding text alignment attributes to any element. See [text alignment](https://beta.ionicframework.com/docs/layout/css-utilities/#text-alignment) for what this includes.
|
||||
Adds utility attributes that allow adding text alignment attributes to any element. See [text alignment](https://ionicframework.com/docs/layout/css-utilities/#text-alignment) for what this includes.
|
||||
|
||||
- **text-transformation.css**
|
||||
Adds utility attributes that allow adding text transformation attributes to any element. See [text transformation](https://beta.ionicframework.com/docs/layout/css-utilities/#text-transformation) for what this includes.
|
||||
Adds utility attributes that allow adding text transformation attributes to any element. See [text transformation](https://ionicframework.com/docs/layout/css-utilities/#text-transformation) for what this includes.
|
||||
|
||||
- **flex-utils.css**
|
||||
Adds utility attributes that allow adding flex container and item attributes to any element. See [flex properties](https://beta.ionicframework.com/docs/layout/css-utilities/#flex-properties) for what this includes.
|
||||
Adds utility attributes that allow adding flex container and item attributes to any element. See [flex properties](https://ionicframework.com/docs/layout/css-utilities/#flex-properties) for what this includes.
|
||||
|
||||
|
||||
#### Including the CSS Files
|
||||
@@ -1831,7 +1840,7 @@ p {
|
||||
|
||||
Sass variables should no longer be used to change Ionic components. We have built Ionic to be customizable using CSS variables, instead.
|
||||
|
||||
For more information on theming, check out the [theming documentation](https://beta.ionicframework.com/docs/theming/basics).
|
||||
For more information on theming, check out the [theming documentation](https://ionicframework.com/docs/theming/basics).
|
||||
|
||||
|
||||
## Toast
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "4.1.3",
|
||||
"version": "4.3.1",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -45,7 +45,7 @@
|
||||
"css/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ionic/core": "4.1.3",
|
||||
"@ionic/core": "4.3.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
|
||||
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { Config } from '../../providers/config';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
@@ -13,7 +15,6 @@ import { RouteView, getUrl } from './stack-utils';
|
||||
inputs: ['animated', 'swipeGesture']
|
||||
})
|
||||
export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
|
||||
private activated: ComponentRef<any> | null = null;
|
||||
private activatedView: RouteView | null = null;
|
||||
|
||||
@@ -23,6 +24,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
private stackCtrl: StackController;
|
||||
private nativeEl: HTMLIonRouterOutletElement;
|
||||
|
||||
// Maintain map of activated route proxies for each component instance
|
||||
private proxyMap = new WeakMap<any, ActivatedRoute>();
|
||||
|
||||
// Keep the latest activated route in a subject for the proxy routes to switch map to
|
||||
private currentActivatedRoute$ = new BehaviorSubject<{ component: any; activatedRoute: ActivatedRoute } | null>(null);
|
||||
|
||||
tabsPrefix: string | undefined;
|
||||
|
||||
@Output() stackEvents = new EventEmitter<any>();
|
||||
@@ -159,6 +166,8 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
const context = this.getContext()!;
|
||||
context.children['contexts'] = saved;
|
||||
}
|
||||
// Updated activated route proxy for this component
|
||||
this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute);
|
||||
} else {
|
||||
const snapshot = (activatedRoute as any)._futureSnapshot;
|
||||
const component = snapshot.routeConfig!.component as any;
|
||||
@@ -167,12 +176,25 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
const factory = resolver.resolveComponentFactory(component);
|
||||
const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
|
||||
|
||||
const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector);
|
||||
// We create an activated route proxy object that will maintain future updates for this component
|
||||
// over its lifecycle in the stack.
|
||||
const component$ = new BehaviorSubject<any>(null);
|
||||
const activatedRouteProxy = this.createActivatedRouteProxy(component$, activatedRoute);
|
||||
|
||||
const injector = new OutletInjector(activatedRouteProxy, childContexts, this.location.injector);
|
||||
cmpRef = this.activated = this.location.createComponent(factory, this.location.length, injector);
|
||||
|
||||
// Once the component is created we can push it to our local subject supplied to the proxy
|
||||
component$.next(cmpRef.instance);
|
||||
|
||||
// Calling `markForCheck` to make sure we will run the change detection when the
|
||||
// `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
|
||||
enteringView = this.stackCtrl.createView(this.activated, activatedRoute);
|
||||
|
||||
// Store references to the proxy by component
|
||||
this.proxyMap.set(cmpRef.instance, activatedRouteProxy);
|
||||
this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute });
|
||||
|
||||
this.changeDetector.markForCheck();
|
||||
}
|
||||
|
||||
@@ -212,6 +234,66 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
getActiveStackId(): string | undefined {
|
||||
return this.stackCtrl.getActiveStackId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the activated route can change over the life time of a component in an ion router outlet, we create
|
||||
* a proxy so that we can update the values over time as a user navigates back to components already in the stack.
|
||||
*/
|
||||
private createActivatedRouteProxy(component$: Observable<any>, activatedRoute: ActivatedRoute): ActivatedRoute {
|
||||
const proxy: any = new ActivatedRoute();
|
||||
|
||||
proxy._futureSnapshot = (activatedRoute as any)._futureSnapshot;
|
||||
proxy._routerState = (activatedRoute as any)._routerState;
|
||||
proxy.snapshot = activatedRoute.snapshot;
|
||||
proxy.outlet = activatedRoute.outlet;
|
||||
proxy.component = activatedRoute.component;
|
||||
|
||||
// Setup wrappers for the observables so consumers don't have to worry about switching to new observables as the state updates
|
||||
(proxy as any)._paramMap = this.proxyObservable(component$, 'paramMap');
|
||||
(proxy as any)._queryParamMap = this.proxyObservable(component$, 'queryParamMap');
|
||||
proxy.url = this.proxyObservable(component$, 'url');
|
||||
proxy.params = this.proxyObservable(component$, 'params');
|
||||
proxy.queryParams = this.proxyObservable(component$, 'queryParams');
|
||||
proxy.fragment = this.proxyObservable(component$, 'fragment');
|
||||
proxy.data = this.proxyObservable(component$, 'data');
|
||||
|
||||
return proxy as ActivatedRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped observable that will switch to the latest activated route matched by the given component
|
||||
*/
|
||||
private proxyObservable(component$: Observable<any>, path: string): Observable<any> {
|
||||
return component$.pipe(
|
||||
// First wait until the component instance is pushed
|
||||
filter(component => !!component),
|
||||
switchMap(component =>
|
||||
this.currentActivatedRoute$.pipe(
|
||||
filter(current => current !== null && current.component === component),
|
||||
switchMap(current => current && (current.activatedRoute as any)[path]),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the activated route proxy for the given component to the new incoming router state
|
||||
*/
|
||||
private updateActivatedRouteProxy(component: any, activatedRoute: ActivatedRoute): void {
|
||||
const proxy = this.proxyMap.get(component);
|
||||
if (!proxy) {
|
||||
throw new Error(`Could not find activated route proxy for view`);
|
||||
}
|
||||
|
||||
(proxy as any)._futureSnapshot = (activatedRoute as any)._futureSnapshot;
|
||||
(proxy as any)._routerState = (activatedRoute as any)._routerState;
|
||||
proxy.snapshot = activatedRoute.snapshot;
|
||||
proxy.outlet = activatedRoute.outlet;
|
||||
proxy.component = activatedRoute.component;
|
||||
|
||||
this.currentActivatedRoute$.next({ component, activatedRoute });
|
||||
}
|
||||
}
|
||||
|
||||
class OutletInjector implements Injector {
|
||||
|
||||
@@ -58,6 +58,40 @@ export class StackController {
|
||||
animation = undefined;
|
||||
}
|
||||
const viewsSnapshot = this.views.slice();
|
||||
|
||||
let currentNavigation;
|
||||
|
||||
const router = (this.router as any);
|
||||
|
||||
// Angular >= 7.2.0
|
||||
if (router.getCurrentNavigation) {
|
||||
currentNavigation = router.getCurrentNavigation();
|
||||
|
||||
// Angular < 7.2.0
|
||||
} else if (
|
||||
router.navigations &&
|
||||
router.navigations.value
|
||||
) {
|
||||
currentNavigation = router.navigations.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the navigation action
|
||||
* sets `replaceUrl: true`
|
||||
* then we need to make sure
|
||||
* we remove the last item
|
||||
* from our views stack
|
||||
*/
|
||||
if (
|
||||
currentNavigation &&
|
||||
currentNavigation.extras &&
|
||||
currentNavigation.extras.replaceUrl
|
||||
) {
|
||||
if (this.views.length > 0) {
|
||||
this.views.splice(-1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const views = this.insertView(enteringView, direction);
|
||||
return this.wait(async () => {
|
||||
await this.transition(enteringView, leavingView, animation, this.canGoBack(1), false);
|
||||
@@ -82,7 +116,23 @@ export class StackController {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const view = views[views.length - deep - 1];
|
||||
return this.navCtrl.navigateBack(view.url).then(() => true);
|
||||
let url = view.url;
|
||||
|
||||
const viewSavedData = view.savedData;
|
||||
if (viewSavedData) {
|
||||
const primaryOutlet = viewSavedData.get('primary');
|
||||
if (
|
||||
primaryOutlet &&
|
||||
primaryOutlet.route &&
|
||||
primaryOutlet.route._routerState &&
|
||||
primaryOutlet.route._routerState.snapshot &&
|
||||
primaryOutlet.route._routerState.snapshot.url
|
||||
) {
|
||||
url = primaryOutlet.route._routerState.snapshot.url;
|
||||
}
|
||||
}
|
||||
|
||||
return this.navCtrl.navigateBack(url).then(() => true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -294,11 +294,12 @@ export declare interface IonImg extends StencilComponents<'IonImg'> {}
|
||||
@Component({ selector: 'ion-img', changeDetection: 0, template: '<ng-content></ng-content>', inputs: ['alt', 'src'] })
|
||||
export class IonImg {
|
||||
ionImgDidLoad!: EventEmitter<CustomEvent>;
|
||||
ionError!: EventEmitter<CustomEvent>;
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionImgDidLoad']);
|
||||
proxyOutputs(this, this.el, ['ionImgDidLoad', 'ionError']);
|
||||
}
|
||||
}
|
||||
proxyInputs(IonImg, ['alt', 'src']);
|
||||
@@ -586,7 +587,7 @@ export class IonRadioGroup {
|
||||
proxyInputs(IonRadioGroup, ['allowEmptySelection', 'name', 'value']);
|
||||
|
||||
export declare interface IonRange extends StencilComponents<'IonRange'> {}
|
||||
@Component({ selector: 'ion-range', changeDetection: 0, template: '<ng-content></ng-content>', inputs: ['color', 'mode', 'debounce', 'name', 'dualKnobs', 'min', 'max', 'pin', 'snaps', 'step', 'disabled', 'value'] })
|
||||
@Component({ selector: 'ion-range', changeDetection: 0, template: '<ng-content></ng-content>', inputs: ['color', 'mode', 'debounce', 'name', 'dualKnobs', 'min', 'max', 'pin', 'snaps', 'step', 'ticks', 'disabled', 'value'] })
|
||||
export class IonRange {
|
||||
ionChange!: EventEmitter<CustomEvent>;
|
||||
ionFocus!: EventEmitter<CustomEvent>;
|
||||
@@ -598,7 +599,7 @@ export class IonRange {
|
||||
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
|
||||
}
|
||||
}
|
||||
proxyInputs(IonRange, ['color', 'mode', 'debounce', 'name', 'dualKnobs', 'min', 'max', 'pin', 'snaps', 'step', 'disabled', 'value']);
|
||||
proxyInputs(IonRange, ['color', 'mode', 'debounce', 'name', 'dualKnobs', 'min', 'max', 'pin', 'snaps', 'step', 'ticks', 'disabled', 'value']);
|
||||
|
||||
export declare interface IonRefresher extends StencilComponents<'IonRefresher'> {}
|
||||
@Component({ selector: 'ion-refresher', changeDetection: 0, template: '<ng-content></ng-content>', inputs: ['pullMin', 'pullMax', 'closeDuration', 'snapbackDuration', 'disabled'] })
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Ionic Variables and Theming. */
|
||||
/* This is just a placeholder file For more info, please see: */
|
||||
/* https://beta.ionicframework.com/docs/theming/basics */
|
||||
/* https://ionicframework.com/docs/theming/basics */
|
||||
|
||||
/* To quickly generate your own theme, check out the color generator */
|
||||
/* https://beta.ionicframework.com/docs/theming/color-generator */
|
||||
/* https://ionicframework.com/docs/theming/color-generator */
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import {
|
||||
apply,
|
||||
chain,
|
||||
mergeWith,
|
||||
move,
|
||||
Rule,
|
||||
SchematicContext,
|
||||
SchematicsException,
|
||||
template,
|
||||
Tree,
|
||||
url
|
||||
} from '@angular-devkit/schematics';
|
||||
import { join, Path } from '@angular-devkit/core';
|
||||
import { apply, chain, mergeWith, move, Rule, SchematicContext, SchematicsException, template, Tree, url } from '@angular-devkit/schematics';
|
||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||
import { addPackageToPackageJson } from './../utils/package';
|
||||
import { addModuleImportToRootModule } from './../utils/ast';
|
||||
import { addArchitectBuilder, addStyle, getWorkspace } from './../utils/config';
|
||||
import { addArchitectBuilder, addStyle, getWorkspace, addAsset } from './../utils/config';
|
||||
import { addPackageToPackageJson } from './../utils/package';
|
||||
import { Schema as IonAddOptions } from './schema';
|
||||
|
||||
function addIonicAngularToPackageJson(): Rule {
|
||||
@@ -68,6 +57,18 @@ function addIonicStyles(): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicons(): Rule {
|
||||
return (host: Tree) => {
|
||||
const ioniconsGlob = {
|
||||
glob: '**/*.svg',
|
||||
input: 'node_modules/ionicons/dist/ionicons/svg',
|
||||
output: './svg'
|
||||
};
|
||||
addAsset(host, ioniconsGlob);
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicBuilder(): Rule {
|
||||
return (host: Tree) => {
|
||||
addArchitectBuilder(host, 'ionic-cordova-serve', {
|
||||
@@ -119,7 +120,7 @@ export default function ngAdd(options: IonAddOptions): Rule {
|
||||
|
||||
const sourcePath = join(project.root as Path, 'src');
|
||||
const rootTemplateSource = apply(url('./files/root'), [
|
||||
template({...options}),
|
||||
template({ ...options }),
|
||||
move(sourcePath)
|
||||
]);
|
||||
return chain([
|
||||
@@ -129,6 +130,7 @@ export default function ngAdd(options: IonAddOptions): Rule {
|
||||
addIonicAngularModuleToAppModule(sourcePath),
|
||||
addIonicBuilder(),
|
||||
addIonicStyles(),
|
||||
addIonicons(),
|
||||
mergeWith(rootTemplateSource),
|
||||
// install freshly added dependencies
|
||||
installNodeDeps()
|
||||
|
||||
@@ -64,6 +64,18 @@ export function addStyle(host: Tree, stylePath: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function addAsset(host: Tree, asset: string | {glob: string; input: string; output: string}) {
|
||||
const config = readConfig(host);
|
||||
const appConfig = getAngularAppConfig(config);
|
||||
|
||||
if (appConfig) {
|
||||
appConfig.architect.build.options.assets.push(asset);
|
||||
writeConfig(host, config);
|
||||
} else {
|
||||
throw new SchematicsException(`Cannot find valid app`);
|
||||
}
|
||||
}
|
||||
|
||||
export function addArchitectBuilder(host: Tree, builderName: string, builderOpts: any){
|
||||
const config = readConfig(host);
|
||||
const appConfig = getAngularAppConfig(config);
|
||||
|
||||
@@ -20,7 +20,7 @@ export class OverlayBaseController<Opts, Overlay> {
|
||||
/**
|
||||
* Returns the top overlay.
|
||||
*/
|
||||
getTop(): Promise<Overlay> {
|
||||
getTop(): Promise<Overlay | undefined> {
|
||||
return proxyMethod(this.ctrl, this.doc, 'getTop');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,25 @@ describe('tabs', () => {
|
||||
expect(await tab.$('ion-back-button').isDisplayed()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('enter url - /tabs/contact/one', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.get('/tabs/contact/one');
|
||||
});
|
||||
|
||||
it('should return to correct tab after going to page in different outlet', async () => {
|
||||
const tab = await getSelectedTab();
|
||||
await tab.$('#goto-nested-page1').click();
|
||||
|
||||
await testStack('app-nested-outlet ion-router-outlet', ['app-nested-outlet-page']);
|
||||
|
||||
const nestedOutlet = await element(by.css('app-nested-outlet'));
|
||||
const backButton = await nestedOutlet.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await testTabTitle('Tab 2 - Page 1');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
async function testState(count: number, tab: string) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>
|
||||
NESTED OUTLET
|
||||
</ion-title>
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
<p>
|
||||
<ion-button routerLink="/tabs/account" id="goto-tab1-page1">Go to Tab 1 - Page 1</ion-button>
|
||||
<ion-button routerLink="/tabs/account/nested/12" id="goto-tab1-page2">Go to Tab 1 - Page 2</ion-button>
|
||||
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go to nested</ion-button>
|
||||
</p>
|
||||
</ion-content>
|
||||
|
||||
@@ -23,8 +23,8 @@ The Ionic Core package contains the Web Components that make up the reusable UI
|
||||
Easiest way to start using Ionic Core is by adding a script tag to the CDN:
|
||||
|
||||
```html
|
||||
<link href="https://unpkg.com/@ionic/core@4.1.3/css/ionic.bundle.css" rel="stylesheet">
|
||||
<script src="https://unpkg.com/@ionic/core@4.1.3/dist/ionic.js"></script>
|
||||
<link href="https://unpkg.com/@ionic/core@4.3.1/css/ionic.bundle.css" rel="stylesheet">
|
||||
<script src="https://unpkg.com/@ionic/core@4.3.1/dist/ionic.js"></script>
|
||||
```
|
||||
|
||||
Any Ionic component added to the webpage will automatically load. This includes writing the component tag directly in HTML, or using JavaScript such as `document.createElement('ion-toggle')`.
|
||||
|
||||
@@ -7,7 +7,7 @@ ion-action-sheet-controller,method,getTop,getTop() => Promise<HTMLIonActionSheet
|
||||
ion-action-sheet,scoped
|
||||
ion-action-sheet,prop,animated,boolean,true,false,false
|
||||
ion-action-sheet,prop,backdropDismiss,boolean,true,false,false
|
||||
ion-action-sheet,prop,buttons,(string | ActionSheetButton)[],undefined,true,false
|
||||
ion-action-sheet,prop,buttons,(string | ActionSheetButton)[],[],false,false
|
||||
ion-action-sheet,prop,cssClass,string | string[] | undefined,undefined,false,false
|
||||
ion-action-sheet,prop,enterAnimation,((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) | undefined,undefined,false,false
|
||||
ion-action-sheet,prop,header,string | undefined,undefined,false,false
|
||||
@@ -380,6 +380,7 @@ ion-header,prop,translucent,boolean,false,false,false
|
||||
ion-img,shadow
|
||||
ion-img,prop,alt,string | undefined,undefined,false,false
|
||||
ion-img,prop,src,string | undefined,undefined,false,false
|
||||
ion-img,event,ionError,void,true
|
||||
ion-img,event,ionImgDidLoad,void,true
|
||||
|
||||
ion-infinite-scroll-content,none
|
||||
@@ -805,6 +806,7 @@ ion-range,prop,name,string,'',false,false
|
||||
ion-range,prop,pin,boolean,false,false,false
|
||||
ion-range,prop,snaps,boolean,false,false,false
|
||||
ion-range,prop,step,number,1,false,false
|
||||
ion-range,prop,ticks,boolean,true,false,false
|
||||
ion-range,prop,value,number | { lower: number; upper: number; },0,false,false
|
||||
ion-range,event,ionBlur,void,true
|
||||
ion-range,event,ionChange,RangeChangeEventDetail,true
|
||||
@@ -1133,11 +1135,13 @@ ion-toast-controller,method,getTop,getTop() => Promise<HTMLIonToastElement | und
|
||||
|
||||
ion-toast,shadow
|
||||
ion-toast,prop,animated,boolean,true,false,false
|
||||
ion-toast,prop,buttons,(string | ToastButton)[] | undefined,undefined,false,false
|
||||
ion-toast,prop,closeButtonText,string | undefined,undefined,false,false
|
||||
ion-toast,prop,color,string | undefined,undefined,false,false
|
||||
ion-toast,prop,cssClass,string | string[] | undefined,undefined,false,false
|
||||
ion-toast,prop,duration,number,0,false,false
|
||||
ion-toast,prop,enterAnimation,((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) | undefined,undefined,false,false
|
||||
ion-toast,prop,header,string | undefined,undefined,false,false
|
||||
ion-toast,prop,keyboardClose,boolean,false,false,false
|
||||
ion-toast,prop,leaveAnimation,((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) | undefined,undefined,false,false
|
||||
ion-toast,prop,message,string | undefined,undefined,false,false
|
||||
@@ -1161,11 +1165,13 @@ ion-toast,css-prop,--border-width
|
||||
ion-toast,css-prop,--box-shadow
|
||||
ion-toast,css-prop,--button-color
|
||||
ion-toast,css-prop,--color
|
||||
ion-toast,css-prop,--end
|
||||
ion-toast,css-prop,--height
|
||||
ion-toast,css-prop,--max-height
|
||||
ion-toast,css-prop,--max-width
|
||||
ion-toast,css-prop,--min-height
|
||||
ion-toast,css-prop,--min-width
|
||||
ion-toast,css-prop,--start
|
||||
ion-toast,css-prop,--width
|
||||
|
||||
ion-toggle,shadow
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "4.1.3",
|
||||
"version": "4.3.1",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -30,7 +30,7 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"ionicons": "4.5.5"
|
||||
"ionicons": "4.5.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@stencil/core": "0.17.3-0",
|
||||
|
||||
37
core/src/components.d.ts
vendored
37
core/src/components.d.ts
vendored
@@ -68,6 +68,7 @@ import {
|
||||
TabButtonLayout,
|
||||
TextareaChangeEventDetail,
|
||||
TextFieldTypes,
|
||||
ToastButton,
|
||||
ToastOptions,
|
||||
ToggleChangeEventDetail,
|
||||
TransitionDoneFn,
|
||||
@@ -175,7 +176,7 @@ export namespace Components {
|
||||
/**
|
||||
* An array of buttons for the action sheet.
|
||||
*/
|
||||
'buttons': (ActionSheetButton | string)[];
|
||||
'buttons'?: (ActionSheetButton | string)[];
|
||||
/**
|
||||
* Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces.
|
||||
*/
|
||||
@@ -1193,7 +1194,7 @@ export namespace Components {
|
||||
*/
|
||||
'pickerFormat'?: string;
|
||||
/**
|
||||
* Any additional options that the picker interface can accept. See the [Picker API docs](../../picker/Picker) for the picker options.
|
||||
* Any additional options that the picker interface can accept. See the [Picker API docs](../picker) for the picker options.
|
||||
*/
|
||||
'pickerOptions'?: DatetimeOptions;
|
||||
/**
|
||||
@@ -1299,7 +1300,7 @@ export namespace Components {
|
||||
*/
|
||||
'pickerFormat'?: string;
|
||||
/**
|
||||
* Any additional options that the picker interface can accept. See the [Picker API docs](../../picker/Picker) for the picker options.
|
||||
* Any additional options that the picker interface can accept. See the [Picker API docs](../picker) for the picker options.
|
||||
*/
|
||||
'pickerOptions'?: DatetimeOptions;
|
||||
/**
|
||||
@@ -1546,7 +1547,11 @@ export namespace Components {
|
||||
*/
|
||||
'alt'?: string;
|
||||
/**
|
||||
* Emitted when the img src is loaded
|
||||
* Emitted when the img fails to load
|
||||
*/
|
||||
'onIonError'?: (event: CustomEvent<void>) => void;
|
||||
/**
|
||||
* Emitted when the img src has been set
|
||||
*/
|
||||
'onIonImgDidLoad'?: (event: CustomEvent<void>) => void;
|
||||
/**
|
||||
@@ -3332,6 +3337,10 @@ export namespace Components {
|
||||
*/
|
||||
'step': number;
|
||||
/**
|
||||
* If `true`, tick marks are displayed based on the step value. Only applies when `snaps` is `true`.
|
||||
*/
|
||||
'ticks': boolean;
|
||||
/**
|
||||
* the value of the range.
|
||||
*/
|
||||
'value': RangeValue;
|
||||
@@ -3394,6 +3403,10 @@ export namespace Components {
|
||||
*/
|
||||
'step'?: number;
|
||||
/**
|
||||
* If `true`, tick marks are displayed based on the step value. Only applies when `snaps` is `true`.
|
||||
*/
|
||||
'ticks'?: boolean;
|
||||
/**
|
||||
* the value of the range.
|
||||
*/
|
||||
'value'?: RangeValue;
|
||||
@@ -4745,6 +4758,10 @@ export namespace Components {
|
||||
*/
|
||||
'animated': boolean;
|
||||
/**
|
||||
* An array of buttons for the toast.
|
||||
*/
|
||||
'buttons'?: (ToastButton | string)[];
|
||||
/**
|
||||
* Text to display in the close button.
|
||||
*/
|
||||
'closeButtonText'?: string;
|
||||
@@ -4769,6 +4786,10 @@ export namespace Components {
|
||||
*/
|
||||
'enterAnimation'?: AnimationBuilder;
|
||||
/**
|
||||
* Header to be shown in the toast.
|
||||
*/
|
||||
'header'?: string;
|
||||
/**
|
||||
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
||||
*/
|
||||
'keyboardClose': boolean;
|
||||
@@ -4816,6 +4837,10 @@ export namespace Components {
|
||||
*/
|
||||
'animated'?: boolean;
|
||||
/**
|
||||
* An array of buttons for the toast.
|
||||
*/
|
||||
'buttons'?: (ToastButton | string)[];
|
||||
/**
|
||||
* Text to display in the close button.
|
||||
*/
|
||||
'closeButtonText'?: string;
|
||||
@@ -4836,6 +4861,10 @@ export namespace Components {
|
||||
*/
|
||||
'enterAnimation'?: AnimationBuilder;
|
||||
/**
|
||||
* Header to be shown in the toast.
|
||||
*/
|
||||
'header'?: string;
|
||||
/**
|
||||
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
||||
*/
|
||||
'keyboardClose'?: boolean;
|
||||
|
||||
@@ -51,7 +51,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
|
||||
/**
|
||||
* An array of buttons for the action sheet.
|
||||
*/
|
||||
@Prop() buttons!: (ActionSheetButton | string)[];
|
||||
@Prop() buttons: (ActionSheetButton | string)[] = [];
|
||||
|
||||
/**
|
||||
* Additional classes to apply for custom CSS. If multiple classes are
|
||||
@@ -196,6 +196,8 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
|
||||
zIndex: 20000 + this.overlayIndex,
|
||||
},
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
...getClassMap(this.cssClass),
|
||||
'action-sheet-translucent': this.translucent
|
||||
}
|
||||
|
||||
@@ -257,19 +257,19 @@ export default {
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- |
|
||||
| `animated` | `animated` | If `true`, the action sheet will animate. | `boolean` | `true` |
|
||||
| `backdropDismiss` | `backdrop-dismiss` | If `true`, the action sheet will be dismissed when the backdrop is clicked. | `boolean` | `true` |
|
||||
| `buttons` _(required)_ | -- | An array of buttons for the action sheet. | `(string \| ActionSheetButton)[]` | `undefined` |
|
||||
| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` |
|
||||
| `enterAnimation` | -- | Animation to use when the action sheet is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `header` | `header` | Title for the action sheet. | `string \| undefined` | `undefined` |
|
||||
| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` |
|
||||
| `leaveAnimation` | -- | Animation to use when the action sheet is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `subHeader` | `sub-header` | Subtitle for the action sheet. | `string \| undefined` | `undefined` |
|
||||
| `translucent` | `translucent` | If `true`, the action sheet will be translucent. Only applies when the mode is `"ios"` and the device supports backdrop-filter. | `boolean` | `false` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ----------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- |
|
||||
| `animated` | `animated` | If `true`, the action sheet will animate. | `boolean` | `true` |
|
||||
| `backdropDismiss` | `backdrop-dismiss` | If `true`, the action sheet will be dismissed when the backdrop is clicked. | `boolean` | `true` |
|
||||
| `buttons` | -- | An array of buttons for the action sheet. | `(string \| ActionSheetButton)[]` | `[]` |
|
||||
| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` |
|
||||
| `enterAnimation` | -- | Animation to use when the action sheet is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `header` | `header` | Title for the action sheet. | `string \| undefined` | `undefined` |
|
||||
| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` |
|
||||
| `leaveAnimation` | -- | Animation to use when the action sheet is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `subHeader` | `sub-header` | Subtitle for the action sheet. | `string \| undefined` | `undefined` |
|
||||
| `translucent` | `translucent` | If `true`, the action sheet will be translucent. Only applies when the mode is `"ios"` and the device supports backdrop-filter. | `boolean` | `false` |
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
@@ -38,34 +38,34 @@ test('action-sheet: basic, scroll without cancel', async () => {
|
||||
* RTL Tests
|
||||
*/
|
||||
|
||||
test('action-sheet: basic', async () => {
|
||||
test('action-sheet:rtl: basic', async () => {
|
||||
await testActionSheet(DIRECTORY, '#basic', true);
|
||||
});
|
||||
|
||||
test('action-sheet: basic, alert from action sheet', async () => {
|
||||
test('action-sheet:rtl: basic, alert from action sheet', async () => {
|
||||
await testActionSheet(DIRECTORY, '#alertFromActionSheet', true, testActionSheetAlert);
|
||||
});
|
||||
|
||||
test('action-sheet: basic, cancel only', async () => {
|
||||
test('action-sheet:rtl: basic, cancel only', async () => {
|
||||
await testActionSheet(DIRECTORY, '#cancelOnly', true);
|
||||
});
|
||||
|
||||
test('action-sheet: basic, custom', async () => {
|
||||
test('action-sheet:rtl: basic, custom', async () => {
|
||||
await testActionSheet(DIRECTORY, '#custom', true);
|
||||
});
|
||||
|
||||
test('action-sheet: basic, icons', async () => {
|
||||
test('action-sheet:rtl: basic, icons', async () => {
|
||||
await testActionSheet(DIRECTORY, '#icons', true);
|
||||
});
|
||||
|
||||
test('action-sheet: basic, no backdrop dismiss', async () => {
|
||||
test('action-sheet:rtl: basic, no backdrop dismiss', async () => {
|
||||
await testActionSheet(DIRECTORY, '#noBackdropDismiss', true, testActionSheetBackdrop);
|
||||
});
|
||||
|
||||
test('action-sheet: basic, scrollable options', async () => {
|
||||
test('action-sheet:rtl: basic, scrollable options', async () => {
|
||||
await testActionSheet(DIRECTORY, '#scrollableOptions', true);
|
||||
});
|
||||
|
||||
test('action-sheet: basic, scroll without cancel', async () => {
|
||||
test('action-sheet:rtl: basic, scroll without cancel', async () => {
|
||||
await testActionSheet(DIRECTORY, '#scrollWithoutCancel', true);
|
||||
});
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { cleanScreenshotName, generateE2EUrl } from '../../../utils/test/utils';
|
||||
import { generateE2EUrl } from '../../../utils/test/utils';
|
||||
|
||||
export async function testActionSheet(
|
||||
type: string,
|
||||
selector: string,
|
||||
rtl = false,
|
||||
afterScreenshotHook = async (..._args: any[]): Promise<void> => {/**/},
|
||||
screenshotName: string = cleanScreenshotName(selector)
|
||||
afterScreenshotHook = async (..._args: any[]): Promise<void> => {/**/}
|
||||
) {
|
||||
try {
|
||||
const pageUrl = generateE2EUrl('action-sheet', type, rtl);
|
||||
if (rtl) {
|
||||
screenshotName = `${screenshotName} rtl`;
|
||||
}
|
||||
|
||||
const page = await newE2EPage({
|
||||
url: pageUrl
|
||||
@@ -27,14 +23,14 @@ export async function testActionSheet(
|
||||
let actionSheet = await page.find('ion-action-sheet');
|
||||
await actionSheet.waitForVisible();
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(screenshotName));
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
await afterScreenshotHook(page, screenshotName, screenshotCompares, actionSheet);
|
||||
await afterScreenshotHook(page, screenshotCompares, actionSheet);
|
||||
|
||||
await actionSheet.callMethod('dismiss');
|
||||
await actionSheet.waitForNotVisible();
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`dismissed ${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot('dismiss'));
|
||||
|
||||
actionSheet = await page.find('ion-action-sheet');
|
||||
expect(actionSheet).toBe(null);
|
||||
@@ -50,7 +46,6 @@ export async function testActionSheet(
|
||||
|
||||
export async function testActionSheetBackdrop(
|
||||
page: any,
|
||||
screenshotName: string,
|
||||
screenshotCompares: any,
|
||||
actionSheet: any
|
||||
) {
|
||||
@@ -59,7 +54,7 @@ export async function testActionSheetBackdrop(
|
||||
const backdrop = await page.find('ion-backdrop');
|
||||
await backdrop.click();
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`dismissed backdrop ${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot(`dismiss backdrop`));
|
||||
|
||||
const isVisible = await actionSheet.isVisible();
|
||||
expect(isVisible).toBe(true);
|
||||
@@ -70,7 +65,6 @@ export async function testActionSheetBackdrop(
|
||||
|
||||
export async function testActionSheetAlert(
|
||||
page: any,
|
||||
screenshotName: string,
|
||||
screenshotCompares: any
|
||||
) {
|
||||
try {
|
||||
@@ -81,7 +75,7 @@ export async function testActionSheetAlert(
|
||||
await alert.waitForVisible();
|
||||
await page.waitFor(250);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`alert open ${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot(`alert open`));
|
||||
|
||||
const alertOkayBtn = await page.find({ contains: 'Okay' });
|
||||
await alertOkayBtn.click();
|
||||
|
||||
@@ -399,6 +399,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
},
|
||||
class: {
|
||||
...getClassMap(this.cssClass),
|
||||
[`${this.mode}`]: true,
|
||||
'alert-translucent': this.translucent
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,34 +37,34 @@ test(`alert: basic, checkbox`, async () => {
|
||||
// Right to Left tests
|
||||
// ------------------------------------------------------
|
||||
|
||||
test(`alert: basic`, async () => {
|
||||
test(`alert:rtl: basic`, async () => {
|
||||
await testAlert(DIRECTORY, '#basic', true);
|
||||
});
|
||||
|
||||
test(`alert: basic, long message`, async () => {
|
||||
test(`alert:rtl: basic, long message`, async () => {
|
||||
await testAlert(DIRECTORY, '#longMessage', true);
|
||||
});
|
||||
|
||||
test(`alert: basic, multiple buttons`, async () => {
|
||||
test(`alert:rtl: basic, multiple buttons`, async () => {
|
||||
await testAlert(DIRECTORY, '#multipleButtons', true);
|
||||
});
|
||||
|
||||
test(`alert: basic, no message`, async () => {
|
||||
test(`alert:rtl: basic, no message`, async () => {
|
||||
await testAlert(DIRECTORY, '#noMessage', true);
|
||||
});
|
||||
|
||||
test(`alert: basic, confirm`, async () => {
|
||||
test(`alert:rtl: basic, confirm`, async () => {
|
||||
await testAlert(DIRECTORY, '#confirm', true);
|
||||
});
|
||||
|
||||
test(`alert: basic, prompt`, async () => {
|
||||
test(`alert:rtl: basic, prompt`, async () => {
|
||||
await testAlert(DIRECTORY, '#prompt', true);
|
||||
});
|
||||
|
||||
test(`alert: basic, radio`, async () => {
|
||||
test(`alert:rtl: basic, radio`, async () => {
|
||||
await testAlert(DIRECTORY, '#radio', true);
|
||||
});
|
||||
|
||||
test(`alert: basic, checkbox`, async () => {
|
||||
test(`alert:rtl: basic, checkbox`, async () => {
|
||||
await testAlert(DIRECTORY, '#checkbox', true);
|
||||
});
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { cleanScreenshotName, generateE2EUrl } from '../../../utils/test/utils';
|
||||
import { generateE2EUrl } from '../../../utils/test/utils';
|
||||
|
||||
export async function testAlert(
|
||||
type: string,
|
||||
selector: string,
|
||||
rtl = false,
|
||||
screenshotName: string = cleanScreenshotName(selector)
|
||||
rtl = false
|
||||
) {
|
||||
try {
|
||||
const pageUrl = generateE2EUrl('alert', type, rtl);
|
||||
if (rtl) {
|
||||
screenshotName = `${screenshotName} rtl`;
|
||||
}
|
||||
|
||||
const page = await newE2EPage({
|
||||
url: pageUrl
|
||||
@@ -28,12 +24,12 @@ export async function testAlert(
|
||||
expect(alert).not.toBe(null);
|
||||
await alert.waitForVisible();
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(screenshotName));
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
await alert.callMethod('dismiss');
|
||||
await alert.waitForNotVisible();
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`dismiss ${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot('dismiss'));
|
||||
|
||||
alert = await page.find('ion-alert');
|
||||
expect(alert).toBe(null);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ComponentInterface, Listen, Prop } from '@stencil/core';
|
||||
|
||||
import { Color, RouterDirection } from '../../interface';
|
||||
import { Color, Mode, RouterDirection } from '../../interface';
|
||||
import { createColorClasses, openURL } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
@@ -9,6 +9,7 @@ import { createColorClasses, openURL } from '../../utils/theme';
|
||||
shadow: true
|
||||
})
|
||||
export class Anchor implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
@Prop({ context: 'window' }) win!: Window;
|
||||
|
||||
@@ -40,6 +41,7 @@ export class Anchor implements ComponentInterface {
|
||||
return {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
'ion-activatable': true
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ComponentInterface, Element, Prop, QueueApi } from '@stencil/core';
|
||||
|
||||
import { Config } from '../../interface';
|
||||
import { Config, Mode } from '../../interface';
|
||||
import { rIC } from '../../utils/helpers';
|
||||
import { isPlatform } from '../../utils/platform';
|
||||
|
||||
@@ -9,6 +9,7 @@ import { isPlatform } from '../../utils/platform';
|
||||
styleUrl: 'app.scss'
|
||||
})
|
||||
export class App implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@@ -34,6 +35,7 @@ export class App implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'ion-page': true,
|
||||
'force-statusbar-padding': this.config.getBoolean('_forceStatusbarPadding')
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Component, ComponentInterface } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-avatar',
|
||||
styleUrls: {
|
||||
@@ -9,6 +11,15 @@ import { Component, ComponentInterface } from '@stencil/core';
|
||||
shadow: true
|
||||
})
|
||||
export class Avatar implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <slot></slot>;
|
||||
|
||||
@@ -62,6 +62,7 @@ export class BackButton implements ComponentInterface {
|
||||
return {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
'button': true, // ion-buttons target .button
|
||||
'ion-activatable': true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, ComponentInterface, Event, EventEmitter, Listen, Prop } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
import { GESTURE_CONTROLLER } from '../../utils/gesture';
|
||||
import { now } from '../../utils/helpers';
|
||||
|
||||
@@ -12,6 +13,7 @@ import { now } from '../../utils/helpers';
|
||||
shadow: true
|
||||
})
|
||||
export class Backdrop implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
private lastClick = -10000;
|
||||
private blocker = GESTURE_CONTROLLER.createBlocker({
|
||||
@@ -78,6 +80,7 @@ export class Backdrop implements ComponentInterface {
|
||||
return {
|
||||
tabindex: '-1',
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'backdrop-hide': !this.visible,
|
||||
'backdrop-no-tappable': !this.tappable,
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@ export class Badge implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: createColorClasses(this.color)
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ export class Button implements ComponentInterface {
|
||||
'aria-disabled': disabled ? 'true' : null,
|
||||
class: {
|
||||
...createColorClasses(color),
|
||||
[`${this.mode}`]: true,
|
||||
[buttonType]: true,
|
||||
[`${buttonType}-${expand}`]: expand !== undefined,
|
||||
[`${buttonType}-${size}`]: size !== undefined,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Component, ComponentInterface } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-buttons',
|
||||
styleUrls: {
|
||||
@@ -8,4 +10,14 @@ import { Component, ComponentInterface } from '@stencil/core';
|
||||
},
|
||||
scoped: true,
|
||||
})
|
||||
export class Buttons implements ComponentInterface {}
|
||||
export class Buttons implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, ComponentInterface, Prop } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-card-content',
|
||||
@@ -19,7 +18,12 @@ export class CardContent implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: createThemedClasses(this.mode, 'card-content')
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`card-content-${this.mode}`]: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export class CardHeader implements ComponentInterface {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
'card-header-translucent': this.translucent,
|
||||
[`${this.mode}`]: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@ export class CardSubtitle implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: createColorClasses(this.color),
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true
|
||||
},
|
||||
'role': 'heading',
|
||||
'aria-level': '3'
|
||||
};
|
||||
|
||||
@@ -26,7 +26,10 @@ export class CardTitle implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: createColorClasses(this.color),
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true
|
||||
},
|
||||
'role': 'heading',
|
||||
'aria-level': '2'
|
||||
};
|
||||
|
||||
@@ -27,7 +27,10 @@ export class Card implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: createColorClasses(this.color)
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ button {
|
||||
|
||||
background: var(--background);
|
||||
|
||||
contain: strict;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ export class Checkbox implements ComponentInterface {
|
||||
'aria-labelledby': labelId,
|
||||
class: {
|
||||
...createColorClasses(color),
|
||||
[`${this.mode}`]: true,
|
||||
'in-item': hostContext('ion-item', el),
|
||||
'checkbox-checked': checked,
|
||||
'checkbox-disabled': disabled,
|
||||
|
||||
@@ -33,6 +33,7 @@ export class Chip implements ComponentInterface {
|
||||
return {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
'chip-outline': this.outline,
|
||||
'ion-activatable': true,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, ComponentInterface, Element, Listen, Prop } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
import { matchBreakpoint } from '../../utils/media';
|
||||
|
||||
const win = window as any;
|
||||
@@ -12,6 +13,8 @@ const BREAKPOINTS = ['', 'xs', 'sm', 'md', 'lg', 'xl'];
|
||||
shadow: true
|
||||
})
|
||||
export class Col implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
@Prop({ context: 'window' }) win!: Window;
|
||||
|
||||
@Element() el!: HTMLStencilElement;
|
||||
@@ -247,6 +250,9 @@ export class Col implements ComponentInterface {
|
||||
hostData() {
|
||||
const isRTL = this.win.document.dir === 'rtl';
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true
|
||||
},
|
||||
style: {
|
||||
...this.calculateOffset(isRTL),
|
||||
...this.calculatePull(isRTL),
|
||||
|
||||
@@ -293,6 +293,7 @@ export class Content implements ComponentInterface {
|
||||
return {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
'content-sizing': hostContext('ion-popover', this.el),
|
||||
'overscroll': !!this.forceOverscroll,
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import { convertDataToISO, parseDate } from './datetime-util';
|
||||
|
||||
describe('datetime-util', () => {
|
||||
describe('convertDataToISO', () => {
|
||||
it('prints an emptry string for an empty datetime', () => {
|
||||
it('prints an empty string for an empty datetime', () => {
|
||||
expect(convertDataToISO({})).toEqual('');
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
export function getDateValue(date: DatetimeData, format: string): number {
|
||||
const getValue = getValueFromFormat(date, format);
|
||||
|
||||
if (getValue) { return getValue; }
|
||||
if (getValue !== undefined) { return getValue; }
|
||||
|
||||
const defaultDate = parseDate(new Date().toISOString());
|
||||
return getValueFromFormat((defaultDate as DatetimeData), format);
|
||||
@@ -248,8 +248,20 @@ export function parseDate(val: string | undefined | null): DatetimeData | undefi
|
||||
* such as "01:47"
|
||||
*/
|
||||
export const getLocalDateTime = (dateString: any = ''): Date => {
|
||||
const date = (typeof dateString === 'string' && dateString.length > 0) ? new Date(dateString) : new Date();
|
||||
/**
|
||||
* Ensures that YYYY-MM-DD, YYYY-MM,
|
||||
* YYYY-DD, etc does not get affected
|
||||
* by timezones and stays on the day/month
|
||||
* that the user provided
|
||||
*/
|
||||
if (
|
||||
dateString.length === 10 ||
|
||||
dateString.length === 7
|
||||
) {
|
||||
dateString += ' ';
|
||||
}
|
||||
|
||||
const date = (typeof dateString === 'string' && dateString.length > 0) ? new Date(dateString) : new Date();
|
||||
return new Date(
|
||||
Date.UTC(
|
||||
date.getFullYear(),
|
||||
@@ -267,7 +279,6 @@ export function updateDate(existingData: DatetimeData, newData: any): boolean {
|
||||
|
||||
if (!newData || typeof newData === 'string') {
|
||||
const localDateTime = getLocalDateTime(newData);
|
||||
|
||||
if (!Number.isNaN(localDateTime.getTime())) {
|
||||
newData = localDateTime.toISOString();
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
/**
|
||||
* Any additional options that the picker interface can accept.
|
||||
* See the [Picker API docs](../../picker/Picker) for the picker options.
|
||||
* See the [Picker API docs](../picker) for the picker options.
|
||||
*/
|
||||
@Prop() pickerOptions?: DatetimeOptions;
|
||||
|
||||
@@ -261,11 +261,36 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
const pickerOptions = this.generatePickerOptions();
|
||||
const picker = await this.pickerCtrl.create(pickerOptions);
|
||||
|
||||
this.isExpanded = true;
|
||||
picker.onDidDismiss().then(() => {
|
||||
this.isExpanded = false;
|
||||
this.setFocus();
|
||||
});
|
||||
picker.addEventListener('ionPickerColChange', async (event: any) => {
|
||||
const data = event.detail;
|
||||
|
||||
/**
|
||||
* Don't bother checking for non-dates as things like hours or minutes
|
||||
* are always going to have the same number of column options
|
||||
*/
|
||||
if (data.name !== 'month' && data.name !== 'day' && data.name !== 'year') { return; }
|
||||
|
||||
const colSelectedIndex = data.selectedIndex;
|
||||
const colOptions = data.options;
|
||||
|
||||
const changeData: any = {};
|
||||
changeData[data.name] = {
|
||||
value: colOptions[colSelectedIndex].value
|
||||
};
|
||||
|
||||
this.updateDatetimeValue(changeData);
|
||||
const columns = this.generateColumns();
|
||||
|
||||
picker.columns = columns;
|
||||
|
||||
await this.validate(picker);
|
||||
});
|
||||
await this.validate(picker);
|
||||
await picker.present();
|
||||
}
|
||||
@@ -300,6 +325,7 @@ export class Datetime implements ComponentInterface {
|
||||
text: this.cancelText,
|
||||
role: 'cancel',
|
||||
handler: () => {
|
||||
this.updateDatetimeValue(this.value);
|
||||
this.ionCancel.emit();
|
||||
}
|
||||
},
|
||||
@@ -589,6 +615,7 @@ export class Datetime implements ComponentInterface {
|
||||
'aria-haspopup': 'true',
|
||||
'aria-labelledby': labelId,
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'datetime-disabled': disabled,
|
||||
'datetime-readonly': readonly,
|
||||
'datetime-placeholder': addPlaceholderClass,
|
||||
|
||||
@@ -287,30 +287,28 @@ dates in JavaScript.
|
||||
</ion-item>
|
||||
```
|
||||
|
||||
```javascript
|
||||
this.customYearValues = [2020, 2016, 2008, 2004, 2000, 1996];
|
||||
```typescript
|
||||
@Component({…})
|
||||
export class MyComponent {
|
||||
customYearValues = [2020, 2016, 2008, 2004, 2000, 1996];
|
||||
customDayShortNames = ['s\u00f8n', 'man', 'tir', 'ons', 'tor', 'fre', 'l\u00f8r'];
|
||||
customPickerOptions: any;
|
||||
|
||||
this.customDayShortNames = [
|
||||
's\u00f8n',
|
||||
'man',
|
||||
'tir',
|
||||
'ons',
|
||||
'tor',
|
||||
'fre',
|
||||
'l\u00f8r'
|
||||
];
|
||||
|
||||
this.customPickerOptions = {
|
||||
buttons: [{
|
||||
text: 'Save',
|
||||
handler: () => console.log('Clicked Save!')
|
||||
}, {
|
||||
text: 'Log',
|
||||
handler: () => {
|
||||
console.log('Clicked Log. Do not Dismiss.');
|
||||
return false;
|
||||
constructor() {
|
||||
this.customPickerOptions = {
|
||||
buttons: [{
|
||||
text: 'Save',
|
||||
handler: () => console.log('Clicked Save!')
|
||||
}, {
|
||||
text: 'Log',
|
||||
handler: () => {
|
||||
console.log('Clicked Log. Do not Dismiss.');
|
||||
return false;
|
||||
}
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@@ -697,7 +695,7 @@ export default Example;
|
||||
| `monthValues` | `month-values` | Values used to create the list of selectable months. By default the month values range from `1` to `12`. However, to control exactly which months to display, the `monthValues` input can take a number, an array of numbers, or a string of comma separated numbers. For example, if only summer months should be shown, then this input value would be `monthValues="6,7,8"`. Note that month numbers do *not* have a zero-based index, meaning January's value is `1`, and December's is `12`. | `number \| number[] \| string \| undefined` | `undefined` |
|
||||
| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | `this.inputId` |
|
||||
| `pickerFormat` | `picker-format` | The format of the date and time picker columns the user selects. A datetime input can have one or many datetime parts, each getting their own column which allow individual selection of that particular datetime part. For example, year and month columns are two individually selectable columns which help choose an exact date from the datetime picker. Each column follows the string parse format. Defaults to use `displayFormat`. | `string \| undefined` | `undefined` |
|
||||
| `pickerOptions` | -- | Any additional options that the picker interface can accept. See the [Picker API docs](../../picker/Picker) for the picker options. | `undefined \| { columns?: PickerColumn[] \| undefined; buttons?: PickerButton[] \| undefined; cssClass?: string \| string[] \| undefined; backdropDismiss?: boolean \| undefined; animated?: boolean \| undefined; mode?: "ios" \| "md" \| undefined; keyboardClose?: boolean \| undefined; id?: string \| undefined; enterAnimation?: AnimationBuilder \| undefined; leaveAnimation?: AnimationBuilder \| undefined; }` | `undefined` |
|
||||
| `pickerOptions` | -- | Any additional options that the picker interface can accept. See the [Picker API docs](../picker) for the picker options. | `undefined \| { columns?: PickerColumn[] \| undefined; buttons?: PickerButton[] \| undefined; cssClass?: string \| string[] \| undefined; backdropDismiss?: boolean \| undefined; animated?: boolean \| undefined; mode?: "ios" \| "md" \| undefined; keyboardClose?: boolean \| undefined; id?: string \| undefined; enterAnimation?: AnimationBuilder \| undefined; leaveAnimation?: AnimationBuilder \| undefined; }` | `undefined` |
|
||||
| `placeholder` | `placeholder` | The text to display when there's no date selected yet. Using lowercase to match the input attribute | `null \| string \| undefined` | `undefined` |
|
||||
| `readonly` | `readonly` | If `true`, the datetime appears normal but is not interactive. | `boolean` | `false` |
|
||||
| `value` | `value` | The value of the datetime as a valid ISO 8601 datetime string. | `null \| string \| undefined` | `undefined` |
|
||||
|
||||
@@ -99,6 +99,11 @@
|
||||
<ion-datetime display-format="HH:mm"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>HH:mm (initial value 00:00)</ion-label>
|
||||
<ion-datetime display-format="HH:mm" value="00:00"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>h:mm a</ion-label>
|
||||
<ion-datetime display-format="h:mm a" value="01:47"></ion-datetime>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { DatetimeData, getDateValue, getLocalDateTime } from '../datetime-util';
|
||||
import { DatetimeData, daysInMonth, getDateValue, getLocalDateTime } from '../datetime-util';
|
||||
|
||||
describe('Datetime', () => {
|
||||
describe('getDateValue()', () => {
|
||||
it('it should return the date value for the current day', () => {
|
||||
const today = new Date();
|
||||
|
||||
|
||||
const dayValue = getDateValue({}, 'DD');
|
||||
const monthvalue = getDateValue({}, 'MM');
|
||||
const yearValue = getDateValue({}, 'YYYY');
|
||||
|
||||
|
||||
expect(dayValue).toEqual(today.getDate());
|
||||
expect(monthvalue).toEqual(today.getMonth() + 1);
|
||||
expect(yearValue).toEqual(today.getFullYear());
|
||||
});
|
||||
|
||||
|
||||
it('it should return the date value for a given day', () => {
|
||||
const date = new Date('15 October 1995');
|
||||
const dateTimeData: DatetimeData = {
|
||||
@@ -21,20 +21,20 @@ describe('Datetime', () => {
|
||||
month: date.getMonth() + 1,
|
||||
day: date.getDate()
|
||||
}
|
||||
|
||||
|
||||
const dayValue = getDateValue(dateTimeData, 'DD');
|
||||
const monthvalue = getDateValue(dateTimeData, 'MM');
|
||||
const yearValue = getDateValue(dateTimeData, 'YYYY');
|
||||
|
||||
|
||||
expect(dayValue).toEqual(date.getDate());
|
||||
expect(monthvalue).toEqual(date.getMonth() + 1);
|
||||
expect(yearValue).toEqual(date.getFullYear());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getLocalDateTime()', () => {
|
||||
it('should format a datetime string according to the local timezone', () => {
|
||||
|
||||
|
||||
const dateStringTests = [
|
||||
{ expectedHourUTC: 12, input: `2019-03-02T12:08:06.601-00:00`, expectedOutput: `2019-03-02T%HOUR%:08:06.601Z` },
|
||||
{ expectedHourUTC: 12, input: `2019-11-02T12:08:06.601-00:00`, expectedOutput: `2019-11-02T%HOUR%:08:06.601Z` },
|
||||
@@ -42,19 +42,54 @@ describe('Datetime', () => {
|
||||
{ expectedHourUTC: 18, input: `1994-12-15T13:47:20.789-05:00`, expectedOutput: `1994-12-15T%HOUR%:47:20.789Z` },
|
||||
{ expectedHourUTC: 9, input: `2019-02-14T09:00:00.000Z`, expectedOutput: `2019-02-14T%HOUR%:00:00.000Z` }
|
||||
];
|
||||
|
||||
|
||||
dateStringTests.forEach(test => {
|
||||
const convertToLocal = getLocalDateTime(test.input);
|
||||
|
||||
|
||||
const timeZoneOffset = convertToLocal.getTimezoneOffset() / 60;
|
||||
const expectedDateString = test.expectedOutput.replace('%HOUR%', padNumber(test.expectedHourUTC - timeZoneOffset));
|
||||
|
||||
|
||||
expect(convertToLocal.toISOString()).toEqual(expectedDateString);
|
||||
});
|
||||
});
|
||||
|
||||
it('should format a date string and not get affected by the timezone offset', () => {
|
||||
|
||||
const dateStringTests = [
|
||||
{ input: '2019-03-20', expectedOutput: '2019-03-20' },
|
||||
{ input: '1994-04-15', expectedOutput: '1994-04-15' },
|
||||
{ input: '2008-09-02', expectedOutput: '2008-09-02' },
|
||||
{ input: '1995-02', expectedOutput: '1995-02' },
|
||||
{ input: '1994-03-14', expectedOutput: '1994-03-14' },
|
||||
{ input: '9 01:47', expectedOutput: '09-01T01:47' }
|
||||
];
|
||||
|
||||
dateStringTests.forEach(test => {
|
||||
const convertToLocal = getLocalDateTime(test.input);
|
||||
expect(convertToLocal.toISOString()).toContain(test.expectedOutput);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('daysInMonth()', () => {
|
||||
it('should return correct days in month for month and year', () => {
|
||||
expect(daysInMonth(1, 2019)).toBe(31);
|
||||
expect(daysInMonth(2, 2019)).toBe(28);
|
||||
expect(daysInMonth(3, 2019)).toBe(31);
|
||||
expect(daysInMonth(4, 2019)).toBe(30);
|
||||
expect(daysInMonth(5, 2019)).toBe(31);
|
||||
expect(daysInMonth(6, 2019)).toBe(30);
|
||||
expect(daysInMonth(7, 2019)).toBe(31);
|
||||
expect(daysInMonth(8, 2019)).toBe(31);
|
||||
expect(daysInMonth(9, 2019)).toBe(30);
|
||||
expect(daysInMonth(10, 2019)).toBe(31);
|
||||
expect(daysInMonth(11, 2019)).toBe(30);
|
||||
expect(daysInMonth(12, 2019)).toBe(31);
|
||||
expect(daysInMonth(2, 2020)).toBe(29);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function padNumber(number: number, totalLength: number = 2): string {
|
||||
return number.toString().padStart(totalLength, '0');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,29 +78,27 @@
|
||||
</ion-item>
|
||||
```
|
||||
|
||||
```javascript
|
||||
this.customYearValues = [2020, 2016, 2008, 2004, 2000, 1996];
|
||||
```typescript
|
||||
@Component({…})
|
||||
export class MyComponent {
|
||||
customYearValues = [2020, 2016, 2008, 2004, 2000, 1996];
|
||||
customDayShortNames = ['s\u00f8n', 'man', 'tir', 'ons', 'tor', 'fre', 'l\u00f8r'];
|
||||
customPickerOptions: any;
|
||||
|
||||
this.customDayShortNames = [
|
||||
's\u00f8n',
|
||||
'man',
|
||||
'tir',
|
||||
'ons',
|
||||
'tor',
|
||||
'fre',
|
||||
'l\u00f8r'
|
||||
];
|
||||
|
||||
this.customPickerOptions = {
|
||||
buttons: [{
|
||||
text: 'Save',
|
||||
handler: () => console.log('Clicked Save!')
|
||||
}, {
|
||||
text: 'Log',
|
||||
handler: () => {
|
||||
console.log('Clicked Log. Do not Dismiss.');
|
||||
return false;
|
||||
constructor() {
|
||||
this.customPickerOptions = {
|
||||
buttons: [{
|
||||
text: 'Save',
|
||||
handler: () => console.log('Clicked Save!')
|
||||
}, {
|
||||
text: 'Log',
|
||||
handler: () => {
|
||||
console.log('Clicked Log. Do not Dismiss.');
|
||||
return false;
|
||||
}
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -95,6 +95,7 @@ export class FabButton implements ComponentInterface {
|
||||
'aria-disabled': disabled ? 'true' : null,
|
||||
class: {
|
||||
...createColorClasses(color),
|
||||
[`${this.mode}`]: true,
|
||||
'fab-button-in-list': inList,
|
||||
'fab-button-translucent-in-list': inList && translucent,
|
||||
'fab-button-close-active': activated,
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { Component, ComponentInterface, Element, Prop, Watch } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-fab-list',
|
||||
styleUrl: 'fab-list.scss',
|
||||
shadow: true
|
||||
})
|
||||
export class FabList implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
@Element() el!: HTMLIonFabElement;
|
||||
|
||||
/**
|
||||
@@ -32,6 +36,7 @@ export class FabList implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'fab-list-active': this.activated,
|
||||
[`fab-list-side-${this.side}`]: true
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Component, ComponentInterface, Element, Listen, Method, Prop, Watch } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-fab',
|
||||
styleUrl: 'fab.scss',
|
||||
shadow: true
|
||||
})
|
||||
export class Fab implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@@ -75,6 +78,7 @@ export class Fab implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
[`fab-horizontal-${this.horizontal}`]: this.horizontal !== undefined,
|
||||
[`fab-vertical-${this.vertical}`]: this.vertical !== undefined,
|
||||
'fab-edge': this.edge
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { cleanScreenshotName, generateE2EUrl } from '../../../utils/test/utils';
|
||||
import { generateE2EUrl } from '../../../utils/test/utils';
|
||||
|
||||
export async function testFab(
|
||||
type: string,
|
||||
selector: string,
|
||||
rtl = false,
|
||||
screenshotName: string = cleanScreenshotName(selector)
|
||||
rtl = false
|
||||
) {
|
||||
try {
|
||||
const pageUrl = generateE2EUrl('fab', type, rtl);
|
||||
if (rtl) {
|
||||
screenshotName = `${screenshotName} rtl`;
|
||||
}
|
||||
|
||||
const page = await newE2EPage({
|
||||
url: pageUrl
|
||||
});
|
||||
|
||||
const screenshotCompares = [];
|
||||
screenshotCompares.push(await page.compareScreenshot(`${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
const fab = await getFabComponent(page, selector);
|
||||
await fab.click();
|
||||
|
||||
await page.waitFor(250);
|
||||
|
||||
await ensureFabState(fab, 'active');
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`${screenshotName} open`));
|
||||
screenshotCompares.push(await page.compareScreenshot('open'));
|
||||
|
||||
const fabButton = await getFabButton(fab);
|
||||
await fabButton.click();
|
||||
|
||||
await page.waitFor(250);
|
||||
|
||||
await ensureFabState(fab, 'inactive');
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`${screenshotName} close`));
|
||||
screenshotCompares.push(await page.compareScreenshot('close'));
|
||||
|
||||
for (const screenshotCompare of screenshotCompares) {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
@@ -46,28 +46,24 @@ export async function testFab(
|
||||
export async function testDisabledFab(
|
||||
type: string,
|
||||
selector: string,
|
||||
rtl = false,
|
||||
screenshotName: string = cleanScreenshotName(selector)
|
||||
rtl = false
|
||||
) {
|
||||
try {
|
||||
const pageUrl = generateE2EUrl('fab', type, rtl);
|
||||
if (rtl) {
|
||||
screenshotName = `${screenshotName} rtl`;
|
||||
}
|
||||
|
||||
const page = await newE2EPage({
|
||||
url: pageUrl
|
||||
});
|
||||
|
||||
const screenshotCompares = [];
|
||||
screenshotCompares.push(await page.compareScreenshot(`disabled ${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot('disabled'));
|
||||
|
||||
const fab = await getFabComponent(page, selector);
|
||||
await fab.click();
|
||||
|
||||
await ensureFabState(fab, 'inactive');
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`disabled ${screenshotName} attempt open`));
|
||||
screenshotCompares.push(await page.compareScreenshot('disabled, attempt open'));
|
||||
|
||||
for (const screenshotCompare of screenshotCompares) {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, ComponentInterface, Prop } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-footer',
|
||||
@@ -25,13 +24,15 @@ export class Footer implements ComponentInterface {
|
||||
@Prop() translucent = false;
|
||||
|
||||
hostData() {
|
||||
const themedClasses = createThemedClasses(this.mode, 'footer');
|
||||
const translucentClasses = this.translucent ? createThemedClasses(this.mode, 'footer-translucent') : null;
|
||||
|
||||
return {
|
||||
class: {
|
||||
...themedClasses,
|
||||
...translucentClasses
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`footer-${this.mode}`]: true,
|
||||
|
||||
[`footer-translucent`]: this.translucent,
|
||||
[`footer-translucent-${this.mode}`]: this.translucent,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
14
core/src/components/footer/test/translucent/e2e.ts
Normal file
14
core/src/components/footer/test/translucent/e2e.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { checkComponentModeClasses } from '../../../../utils/test/utils';
|
||||
|
||||
test('footer: translucent', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/footer/test/translucent?ionic:_testing=true'
|
||||
});
|
||||
|
||||
await checkComponentModeClasses(await page.find('ion-footer'), 'footer-translucent');
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Component, ComponentInterface, Prop } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-grid',
|
||||
styleUrl: 'grid.scss',
|
||||
shadow: true
|
||||
})
|
||||
export class Grid implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
/**
|
||||
* If `true`, the grid will have a fixed width based on the screen size.
|
||||
@@ -15,6 +18,7 @@ export class Grid implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'grid-fixed': this.fixed
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, ComponentInterface, Prop } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-header',
|
||||
@@ -25,13 +24,15 @@ export class Header implements ComponentInterface {
|
||||
@Prop() translucent = false;
|
||||
|
||||
hostData() {
|
||||
const themedClasses = createThemedClasses(this.mode, 'header');
|
||||
const translucentClasses = this.translucent ? createThemedClasses(this.mode, 'header-translucent') : null;
|
||||
|
||||
return {
|
||||
class: {
|
||||
...themedClasses,
|
||||
...translucentClasses
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`header-${this.mode}`]: true,
|
||||
|
||||
[`header-translucent`]: this.translucent,
|
||||
[`header-translucent-${this.mode}`]: this.translucent,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
14
core/src/components/header/test/translucent/e2e.ts
Normal file
14
core/src/components/header/test/translucent/e2e.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { checkComponentModeClasses } from '../../../../utils/test/utils';
|
||||
|
||||
test('header: translucent', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/header/test/translucent?ionic:_testing=true'
|
||||
});
|
||||
|
||||
await checkComponentModeClasses(await page.find('ion-header'), 'header-translucent');
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-img',
|
||||
styleUrl: 'img.scss',
|
||||
shadow: true
|
||||
})
|
||||
export class Img implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
private io?: IntersectionObserver;
|
||||
|
||||
@@ -13,6 +16,8 @@ export class Img implements ComponentInterface {
|
||||
|
||||
@State() loadSrc?: string;
|
||||
|
||||
@State() loadError?: () => void;
|
||||
|
||||
/**
|
||||
* This attribute defines the alternative text describing the image.
|
||||
* Users will see this text displayed if the image URL is wrong,
|
||||
@@ -29,9 +34,12 @@ export class Img implements ComponentInterface {
|
||||
this.addIO();
|
||||
}
|
||||
|
||||
/** Emitted when the img src is loaded */
|
||||
/** Emitted when the img src has been set */
|
||||
@Event() ionImgDidLoad!: EventEmitter<void>;
|
||||
|
||||
/** Emitted when the img fails to load */
|
||||
@Event() ionError!: EventEmitter<void>;
|
||||
|
||||
componentDidLoad() {
|
||||
this.addIO();
|
||||
}
|
||||
@@ -60,10 +68,15 @@ export class Img implements ComponentInterface {
|
||||
}
|
||||
|
||||
private load() {
|
||||
this.loadError = this.onError;
|
||||
this.loadSrc = this.src;
|
||||
this.ionImgDidLoad.emit();
|
||||
}
|
||||
|
||||
private onError = () => {
|
||||
this.ionError.emit();
|
||||
}
|
||||
|
||||
private removeIO() {
|
||||
if (this.io) {
|
||||
this.io.disconnect();
|
||||
@@ -71,12 +84,21 @@ export class Img implements ComponentInterface {
|
||||
}
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<img
|
||||
src={this.loadSrc}
|
||||
alt={this.alt}
|
||||
decoding="async"
|
||||
onError={this.loadError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,9 +79,10 @@ export default Example
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description | Type |
|
||||
| --------------- | ---------------------------------- | ------------------- |
|
||||
| `ionImgDidLoad` | Emitted when the img src is loaded | `CustomEvent<void>` |
|
||||
| Event | Description | Type |
|
||||
| --------------- | ------------------------------------- | ------------------- |
|
||||
| `ionError` | Emitted when the img fails to load | `CustomEvent<void>` |
|
||||
| `ionImgDidLoad` | Emitted when the img src has been set | `CustomEvent<void>` |
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
@@ -31,6 +31,13 @@
|
||||
<f></f>
|
||||
<f></f>
|
||||
<ion-img id="hidden" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAARQAQMAAAA2ut43AAAABlBMVEXMzMz////TjRV2AAAEPUlEQVR42u3RMREAMBDDsPAn/eXhavOq87Ztu7u7+6eBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBf2hvgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgatgn4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGr7S0wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBwFewzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcLW9BQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq6CfQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgautrfAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAVbDPwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNX2FhgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGLgK9hkYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBi42t4CAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV8E+AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA1fbW2BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYOAq2GdgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGDgansLDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMXAX7DAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDFxtb4GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrYJ+BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBq+0tMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcBXsMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHC1vQUGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgaugn0GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGjvYD5WuYrpZqdmcAAAAASUVORK5CYII="></ion-img>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<ion-img id="hidden" src="data:image/png;base64,"></ion-img>
|
||||
|
||||
</ion-content>
|
||||
|
||||
@@ -63,6 +70,10 @@
|
||||
document.body.addEventListener('ionImgDidLoad', (event) => {
|
||||
console.log('image did load', event.target);
|
||||
});
|
||||
|
||||
document.body.addEventListener('ionError', (event) => {
|
||||
console.error('image error', event.target);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Component, ComponentInterface, Prop } from '@stencil/core';
|
||||
|
||||
import { Config, Mode, SpinnerTypes } from '../../interface';
|
||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-infinite-scroll-content',
|
||||
@@ -44,7 +43,12 @@ export class InfiniteScrollContent implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: createThemedClasses(this.mode, 'infinite-scroll-content')
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`infinite-scroll-content-${this.mode}`]: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, EventListenerEnable, Listen, Method, Prop, QueueApi, State, Watch } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-infinite-scroll',
|
||||
styleUrl: 'infinite-scroll.scss'
|
||||
})
|
||||
export class InfiniteScroll implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
private thrPx = 0;
|
||||
private thrPc = 0;
|
||||
@@ -212,6 +215,7 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'infinite-scroll-loading': this.isLoading,
|
||||
'infinite-scroll-enabled': !this.disabled
|
||||
}
|
||||
|
||||
@@ -307,7 +307,12 @@ export class Input implements ComponentInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private clearTextInput = () => {
|
||||
private clearTextInput = (ev?: Event) => {
|
||||
if (this.clearInput && !this.readonly && !this.disabled && ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
|
||||
this.value = '';
|
||||
}
|
||||
|
||||
@@ -327,6 +332,7 @@ export class Input implements ComponentInterface {
|
||||
'aria-disabled': this.disabled ? 'true' : null,
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
'has-value': this.hasValue(),
|
||||
'has-focus': this.hasFocus
|
||||
}
|
||||
|
||||
15
core/src/components/input/test/spec/e2e.ts
Normal file
15
core/src/components/input/test/spec/e2e.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('input: spec', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/input/test/spec?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const compares = [];
|
||||
|
||||
compares.push(await page.compareScreenshot());
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
61
core/src/components/input/test/spec/index.html
Normal file
61
core/src/components/input/test/spec/index.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Input - Spec</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script src="../../../../../dist/ionic.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-item>
|
||||
<ion-label position="floating">Standard</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Standard</ion-label>
|
||||
<ion-input value="value"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Stacked</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Stacked</ion-label>
|
||||
<ion-input value="value"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Floating</ion-label>
|
||||
<ion-textarea></ion-textarea>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Floating</ion-label>
|
||||
<ion-textarea value="value"></ion-textarea>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Stacked</ion-label>
|
||||
<ion-textarea></ion-textarea>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Stacked</ion-label>
|
||||
<ion-textarea value="value"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
</ion-content>
|
||||
|
||||
<style>
|
||||
ion-item {
|
||||
--background: #f5f5f5;
|
||||
}
|
||||
|
||||
</style>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -55,6 +55,7 @@ export class ItemDivider implements ComponentInterface {
|
||||
return {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
'item-divider-sticky': this.sticky,
|
||||
'item': true,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, ComponentInterface } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-item-group',
|
||||
@@ -18,8 +17,12 @@ export class ItemGroup implements ComponentInterface {
|
||||
return {
|
||||
'role': 'group',
|
||||
class: {
|
||||
...createThemedClasses(this.mode, 'item-group'),
|
||||
'item': true,
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`item-group-${this.mode}`]: true,
|
||||
|
||||
'item': true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -112,3 +112,17 @@
|
||||
transition-property: none;
|
||||
transition-timing-function: cubic-bezier(.65, .05, .36, 1);
|
||||
}
|
||||
|
||||
|
||||
// Item Disabled Styling
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.item-option-disabled) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:host(.item-option-disabled) .button-native {
|
||||
cursor: default;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -60,10 +60,14 @@ export class ItemOption implements ComponentInterface {
|
||||
}
|
||||
|
||||
hostData() {
|
||||
const { disabled, expandable } = this;
|
||||
return {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
'item-option-expandable': this.expandable,
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
'item-option-disabled': disabled,
|
||||
'item-option-expandable': expandable,
|
||||
'ion-activatable': true,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -77,6 +77,8 @@ ion-item-options {
|
||||
|
||||
ion-item-options {
|
||||
display: flex;
|
||||
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&.item-sliding-active-options-start .item-options-start,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop } from '@stencil/core';
|
||||
|
||||
import { Side } from '../../interface';
|
||||
import { Mode, Side } from '../../interface';
|
||||
import { isEndSide } from '../../utils/helpers';
|
||||
|
||||
@Component({
|
||||
@@ -11,6 +11,8 @@ import { isEndSide } from '../../utils/helpers';
|
||||
}
|
||||
})
|
||||
export class ItemOptions implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@Prop({ context: 'window' }) win!: Window;
|
||||
@@ -38,6 +40,11 @@ export class ItemOptions implements ComponentInterface {
|
||||
const isEnd = isEndSide(this.win, this.side);
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`item-options-${this.mode}`]: true,
|
||||
|
||||
'item-options-start': !isEnd,
|
||||
'item-options-end': isEnd
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ ion-item-sliding .item {
|
||||
.item-sliding-active-swipe-end .item-options-end .item-option-expandable {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
padding-left: 90%;
|
||||
padding-left: 100%;
|
||||
}
|
||||
|
||||
@include ltr() {
|
||||
@@ -51,7 +51,7 @@ ion-item-sliding .item {
|
||||
.item-sliding-active-swipe-start .item-options-start .item-option-expandable {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
padding-right: 90%;
|
||||
padding-right: 100%;
|
||||
}
|
||||
|
||||
@include ltr() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, QueueApi, State, Watch } from '@stencil/core';
|
||||
|
||||
import { Gesture, GestureDetail } from '../../interface';
|
||||
import { Gesture, GestureDetail, Mode } from '../../interface';
|
||||
|
||||
const SWIPE_MARGIN = 30;
|
||||
const ELASTIC_FACTOR = 0.55;
|
||||
@@ -29,6 +29,7 @@ let openSlidingItem: HTMLIonItemSlidingElement | undefined;
|
||||
styleUrl: 'item-sliding.scss'
|
||||
})
|
||||
export class ItemSliding implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
private item: HTMLIonItemElement | null = null;
|
||||
private openAmount = 0;
|
||||
@@ -307,6 +308,7 @@ export class ItemSliding implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'item-sliding-active-slide': (this.state !== SlidingState.Disabled),
|
||||
'item-sliding-active-options-end': (this.state & SlidingState.End) !== 0,
|
||||
'item-sliding-active-options-start': (this.state & SlidingState.Start) !== 0,
|
||||
|
||||
@@ -120,6 +120,30 @@
|
||||
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item100">
|
||||
<ion-item href="#">
|
||||
<ion-label>
|
||||
<h2>Disabled Buttons</h2>
|
||||
<p>Buttons should not be clickable</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option disabled>
|
||||
Disabled
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="danger" disabled>
|
||||
<ion-icon slot="icon-only" name="trash"></ion-icon>
|
||||
</ion-item-option>
|
||||
<ion-item-option disabled>
|
||||
<ion-icon slot="icon-only" name="star"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item0">
|
||||
<ion-item onclick="clickedItem('item0')">
|
||||
<ion-label text-wrap>
|
||||
@@ -286,6 +310,44 @@
|
||||
</ion-item>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item10">
|
||||
<ion-item detail>
|
||||
<ion-label text-wrap>
|
||||
<h2>RIGHT/LEFT side - many buttons</h2>
|
||||
<p>Use mobile emulator to check</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="start" class="sliding-enabled">
|
||||
<ion-item-option color="primary" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 1
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 2
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 3
|
||||
</ion-item-option>
|
||||
<ion-item-option color="tertiary" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 4
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="end" class="sliding-enabled">
|
||||
<ion-item-option color="primary" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 5
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 6
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 7
|
||||
</ion-item-option>
|
||||
<ion-item-option color="tertiary" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 8
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item>
|
||||
<ion-label text-wrap>
|
||||
<h2>Normal ion-item (no sliding)</h2>
|
||||
|
||||
@@ -245,7 +245,7 @@
|
||||
|
||||
:host(.item-label-floating),
|
||||
:host(.item-label-stacked) {
|
||||
--min-height: 65px;
|
||||
--min-height: 55px;
|
||||
}
|
||||
|
||||
// TODO: refactor, ion-item and ion-textarea have the same CSS
|
||||
|
||||
@@ -181,6 +181,9 @@ button, a {
|
||||
|
||||
display: flex;
|
||||
|
||||
/* This is required to work with an inset highlight */
|
||||
position: relative;
|
||||
|
||||
flex: 1;
|
||||
flex-direction: inherit;
|
||||
align-items: inherit;
|
||||
|
||||
@@ -136,10 +136,11 @@ export class Item implements ComponentInterface {
|
||||
class: {
|
||||
...childStyles,
|
||||
...createColorClasses(this.color),
|
||||
'item': true,
|
||||
[`${this.mode}`]: true,
|
||||
[`item-lines-${this.lines}`]: this.lines !== undefined,
|
||||
'item-disabled': this.disabled,
|
||||
'in-list': hostContext('ion-list', this.el),
|
||||
'item': true,
|
||||
'item-multiple-inputs': this.multipleInputs,
|
||||
'ion-activatable': this.isClickable(),
|
||||
'ion-focusable': true,
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
Items are elements that can contain text, icons, avatars, images, inputs, and any other native or custom elements. Generally they are placed in a list with other items. Items can be swiped, deleted, reordered, edited, and more.
|
||||
|
||||
## Clickable Items
|
||||
|
||||
An item is considered "clickable" if it has an `href` or `button` property set. Clickable items have a few visual differences that indicate they can be interacted with. For example, a clickable item receives the ripple effect upon activation in `md` mode, has a highlight when activated in `ios` mode, and has a [detail arrow](/#detail-arrows) by default in `ios` mode.
|
||||
|
||||
## Detail Arrows
|
||||
|
||||
By default, clickable items will display a right arrow icon on `ios` mode. A clickable item is an item that has an `href` or `button` property set. To hide the right arrow icon on clickable elements, set the `detail` property to `false`. To show the right arrow icon on an item that doesn't display it naturally, add the `detail` attribute to the item.
|
||||
By default [clickable items](/#clickable-items) will display a right arrow icon on `ios` mode. To hide the right arrow icon on clickable elements, set the `detail` property to `false`. To show the right arrow icon on an item that doesn't display it naturally, set the `detail` property to `true`.
|
||||
|
||||
<!--
|
||||
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
color: $label-ios-text-color-focused;
|
||||
}
|
||||
|
||||
:host-context(.item-has-focus).label-floating,
|
||||
:host-context(.item-has-placeholder).label-floating,
|
||||
:host-context(.item-has-value).label-floating {
|
||||
@include transform(translate3d(0, 0, 0), scale(.8));
|
||||
}
|
||||
|
||||
|
||||
// iOS Typography
|
||||
// --------------------------------------------------
|
||||
|
||||
@@ -13,19 +13,30 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.label-stacked) {
|
||||
font-size: 12.8px;
|
||||
@include transform-origin(start, top);
|
||||
@include transform(translate3d(0, 50%, 0), scale(.75));
|
||||
}
|
||||
|
||||
:host(.label-floating) {
|
||||
@include transform(translate3d(0, 27px, 0));
|
||||
@include transform(translate3d(0, 96%, 0));
|
||||
@include transform-origin(start, top);
|
||||
|
||||
transition: transform 150ms ease-in-out;
|
||||
transition: transform 150ms cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
|
||||
:host(.label-stacked),
|
||||
:host(.label-floating) {
|
||||
@include margin(null, null, 0, 0);
|
||||
@include margin(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
:host-context(.item-select).label-floating {
|
||||
@include transform(translate3d(0, 130%, 0));
|
||||
}
|
||||
|
||||
:host-context(.item-has-focus).label-floating,
|
||||
:host-context(.item-has-placeholder).label-floating,
|
||||
:host-context(.item-has-value).label-floating {
|
||||
@include transform(translate3d(0, 50%, 0), scale(.75));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:host-context(.item-textarea) {
|
||||
align-self: baseline;
|
||||
}
|
||||
|
||||
|
||||
// Fixed Inputs
|
||||
// --------------------------------------------------
|
||||
@@ -71,12 +75,6 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
:host-context(.item-has-focus).label-floating,
|
||||
:host-context(.item-has-placeholder).label-floating,
|
||||
:host-context(.item-has-value).label-floating {
|
||||
@include transform(translate3d(0, 0, 0), scale(.8));
|
||||
}
|
||||
|
||||
:host(.label-no-animate.label-floating) {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ export class Label implements ComponentInterface {
|
||||
return {
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
[`label-${position}`]: position !== undefined,
|
||||
[`label-no-animate`]: (this.noAnimate)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ export class ListHeader implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: createColorClasses(this.color)
|
||||
class: {
|
||||
...createColorClasses(this.color),
|
||||
[`${this.mode}`]: true,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, ComponentInterface, Element, Method, Prop } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-list',
|
||||
@@ -47,9 +46,13 @@ export class List implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
...createThemedClasses(this.mode, 'list'),
|
||||
[`list-lines-${this.lines}`]: this.lines !== undefined,
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`list-${this.mode}`]: true,
|
||||
|
||||
'list-inset': this.inset,
|
||||
[`list-lines-${this.lines}`]: this.lines !== undefined,
|
||||
[`list-${this.mode}-lines-${this.lines}`]: this.lines !== undefined
|
||||
}
|
||||
};
|
||||
|
||||
@@ -174,6 +174,7 @@ export class Loading implements ComponentInterface, OverlayInterface {
|
||||
},
|
||||
class: {
|
||||
...getClassMap(this.cssClass),
|
||||
[`${this.mode}`]: true,
|
||||
'loading-translucent': this.translucent
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { cleanScreenshotName, generateE2EUrl } from '../../../utils/test/utils';
|
||||
import { generateE2EUrl } from '../../../utils/test/utils';
|
||||
|
||||
export async function testLoading(
|
||||
type: string,
|
||||
selector: string,
|
||||
rtl = false,
|
||||
screenshotName: string = cleanScreenshotName(selector)
|
||||
rtl = false
|
||||
) {
|
||||
try {
|
||||
const pageUrl = generateE2EUrl('loading', type, rtl);
|
||||
if (rtl) {
|
||||
screenshotName = `${screenshotName} rtl`;
|
||||
}
|
||||
|
||||
const page = await newE2EPage({
|
||||
url: pageUrl
|
||||
@@ -28,12 +24,12 @@ export async function testLoading(
|
||||
|
||||
await loading.waitForVisible();
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(screenshotName));
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
await loading.callMethod('dismiss');
|
||||
await loading.waitForNotVisible();
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`dismiss ${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot('dismiss'));
|
||||
|
||||
loading = await page.find('ion-loading');
|
||||
expect(loading).toBeNull();
|
||||
|
||||
@@ -39,6 +39,7 @@ export class MenuButton implements ComponentInterface {
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'button': true, // ion-buttons target .button
|
||||
'ion-activatable': true,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Component, ComponentInterface, Listen, Prop, State } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-menu-toggle',
|
||||
styleUrl: 'menu-toggle.scss',
|
||||
shadow: true
|
||||
})
|
||||
export class MenuToggle implements ComponentInterface {
|
||||
mode!: Mode;
|
||||
|
||||
@Prop({ context: 'document' }) doc!: Document;
|
||||
|
||||
@@ -63,6 +66,7 @@ export class MenuToggle implements ComponentInterface {
|
||||
return {
|
||||
'aria-hidden': hidden ? 'true' : null,
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
'menu-toggle-hidden': hidden,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -510,6 +510,7 @@ export class Menu implements ComponentInterface, MenuI {
|
||||
return {
|
||||
role: 'complementary',
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
[`menu-type-${type}`]: true,
|
||||
'menu-enabled': !disabled,
|
||||
'menu-side-end': isEndSide,
|
||||
|
||||
@@ -3,11 +3,11 @@ import { testMenu } from '../test.utils';
|
||||
const DIRECTORY = 'basic';
|
||||
|
||||
test('menu: start menu', async () => {
|
||||
await testMenu(DIRECTORY, '#start-menu');
|
||||
await testMenu(DIRECTORY, '#start-menu', 'first');
|
||||
});
|
||||
|
||||
test('menu: start custom menu', async () => {
|
||||
await testMenu(DIRECTORY, '#custom-menu');
|
||||
await testMenu(DIRECTORY, '#custom-menu', 'custom');
|
||||
});
|
||||
|
||||
test('menu: end menu', async () => {
|
||||
@@ -19,13 +19,13 @@ test('menu: end menu', async () => {
|
||||
*/
|
||||
|
||||
test('menu:rtl: start menu', async () => {
|
||||
await testMenu(DIRECTORY, '#start-menu', true);
|
||||
await testMenu(DIRECTORY, '#start-menu', 'first', true);
|
||||
});
|
||||
|
||||
test('menu:rtl: start custom menu', async () => {
|
||||
await testMenu(DIRECTORY, '#custom-menu', true);
|
||||
await testMenu(DIRECTORY, '#custom-menu', 'custom', true);
|
||||
});
|
||||
|
||||
test('menu:rtl: end menu', async () => {
|
||||
await testMenu(DIRECTORY, '#end-menu', true);
|
||||
await testMenu(DIRECTORY, '#end-menu', '', true);
|
||||
});
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { cleanScreenshotName, generateE2EUrl } from '../../../utils/test/utils';
|
||||
import { generateE2EUrl } from '../../../utils/test/utils';
|
||||
|
||||
export async function testMenu(
|
||||
type: string,
|
||||
selector: string,
|
||||
rtl = false,
|
||||
screenshotName: string = cleanScreenshotName(selector)
|
||||
menuId = '',
|
||||
rtl = false
|
||||
) {
|
||||
try {
|
||||
const pageUrl = generateE2EUrl('menu', type, rtl);
|
||||
if (rtl) {
|
||||
screenshotName = `${screenshotName} rtl`;
|
||||
}
|
||||
|
||||
const page = await newE2EPage({
|
||||
url: pageUrl
|
||||
@@ -20,17 +17,23 @@ export async function testMenu(
|
||||
|
||||
const screenshotCompares = [];
|
||||
|
||||
if (menuId.length > 0) {
|
||||
const menuCtrl = await page.find('ion-menu-controller');
|
||||
await page.waitFor(250);
|
||||
await menuCtrl.callMethod('enable', true, menuId);
|
||||
}
|
||||
|
||||
const menu = await page.find(selector);
|
||||
|
||||
await menu.callMethod('open');
|
||||
await page.waitFor(250);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(screenshotName));
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
await menu.callMethod('close');
|
||||
await page.waitFor(250);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`dismiss ${screenshotName}`));
|
||||
screenshotCompares.push(await page.compareScreenshot('dismiss'));
|
||||
|
||||
for (const screenshotCompare of screenshotCompares) {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
|
||||
import { Animation, AnimationBuilder, ComponentProps, ComponentRef, Config, FrameworkDelegate, Mode, OverlayEventDetail, OverlayInterface } from '../../interface';
|
||||
import { attachComponent, detachComponent } from '../../utils/framework-delegate';
|
||||
import { BACKDROP, dismiss, eventMethod, present } from '../../utils/overlays';
|
||||
import { createThemedClasses, getClassMap } from '../../utils/theme';
|
||||
import { getClassMap } from '../../utils/theme';
|
||||
import { deepReady } from '../../utils/transition';
|
||||
|
||||
import { iosEnterAnimation } from './animations/ios.enter';
|
||||
@@ -193,7 +193,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
'no-router': true,
|
||||
'aria-modal': 'true',
|
||||
class: {
|
||||
...createThemedClasses(this.mode, 'modal'),
|
||||
[`${this.mode}`]: true,
|
||||
...getClassMap(this.cssClass)
|
||||
},
|
||||
style: {
|
||||
@@ -203,7 +203,10 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
}
|
||||
|
||||
render() {
|
||||
const dialogClasses = createThemedClasses(this.mode, 'modal-wrapper');
|
||||
const dialogClasses = {
|
||||
[`modal-wrapper`]: true,
|
||||
[`${this.mode}`]: true,
|
||||
};
|
||||
|
||||
return [
|
||||
<ion-backdrop visible={this.showBackdrop} tappable={this.backdropDismiss}/>,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user