Compare commits

..

10 Commits

Author SHA1 Message Date
Liam DeBeasi
ede6289bc6 remove package lock 2020-06-05 16:08:10 -04:00
Liam DeBeasi
f077f0aac7 sync with master 2020-06-05 16:06:40 -04:00
Liam DeBeasi
9c64308f0d add generic wrapper class 2020-06-05 16:05:43 -04:00
Liam DeBeasi
795783311a clean up animations: 2020-06-05 14:55:58 -04:00
Liam DeBeasi
f3e8d4c31d typo 2020-06-05 14:04:51 -04:00
Liam DeBeasi
7fb38d1e12 add support for backdrop to fade anim 2020-06-05 14:04:04 -04:00
Liam DeBeasi
9f9ba235ee add fade and fade through animations 2020-06-05 13:54:56 -04:00
Liam DeBeasi
28c5e14434 add multi axis anim 2020-05-20 11:25:30 -04:00
Liam DeBeasi
a4be67aeb8 add axis support 2020-05-18 17:39:12 -04:00
Liam DeBeasi
94c3d481e9 Add base motion package 2020-05-15 15:57:55 -04:00
1200 changed files with 10584 additions and 224838 deletions

View File

@@ -1,72 +1,51 @@
version: 2.1
orbs:
cypress: cypress-io/cypress@1.27.0
version: 2
aliases:
- &restore-cache
keys:
- dependency-cache-{{ checksum "package.json" }}-7
- dependency-cache-{{ checksum "package.json" }}-3
- &save-cache
key: dependency-cache-{{ checksum "package.json" }}-7
key: dependency-cache-{{ checksum "package.json" }}-3
paths:
- node_modules
- &restore-cache-core
keys:
- dependency-cache-{{ checksum "core/package.json" }}-7
- dependency-cache-{{ checksum "core/package.json" }}-3
- &save-cache-core
key: dependency-cache-{{ checksum "core/package.json" }}-7
key: dependency-cache-{{ checksum "core/package.json" }}-3
paths:
- core/node_modules
- &restore-cache-core-stencil
keys:
- stencil-cache-6
- stencil-cache-2
- &save-cache-core-stencil
key: stencil-cache-6
key: stencil-cache-2
paths:
- core/.stencil
- core/screenshot/images
defaults: &defaults
docker:
- image: circleci/node:latest-browsers
- image: circleci/node:10-browsers
working_directory: /tmp/workspace
environment:
NODE_ENV: development
jobs:
puppeteer-dependencies:
<<: *defaults
steps:
- run:
name: Install headless Chrome dependencies
command: |
sudo apt-get install -yq \
gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 \
libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates \
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
- run:
name: Install Puppeteer with Chromium
command: |
npm i puppeteer
build:
<<: *defaults
steps:
- checkout
- restore_cache: *restore-cache
- run: npm install --legacy-peer-deps
- run: npm install
- save_cache: *save-cache
- persist_to_workspace:
root: /tmp/workspace
paths:
- node_modules
- "*"
build-core:
<<: *defaults
@@ -77,7 +56,7 @@ jobs:
- restore_cache: *restore-cache-core
- restore_cache: *restore-cache-core-stencil
- run:
command: npm install --legacy-peer-deps
command: npm install
working_directory: /tmp/workspace/core
- save_cache: *save-cache-core
- run:
@@ -87,12 +66,7 @@ jobs:
- persist_to_workspace:
root: /tmp/workspace
paths:
- core/node_modules
- core/dist
- core/components
- core/css
- core/hydrate
- core/loader
- "*"
build-angular:
<<: *defaults
@@ -101,7 +75,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm install
working_directory: /tmp/workspace/angular
- run:
command: sudo npm link
@@ -115,8 +89,7 @@ jobs:
- persist_to_workspace:
root: /tmp/workspace
paths:
- angular/node_modules
- angular/dist
- "*"
build-angular-server:
<<: *defaults
@@ -125,7 +98,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm install
working_directory: /tmp/workspace/packages/angular-server
- run:
command: npm run build.prod
@@ -133,7 +106,7 @@ jobs:
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/angular-server/dist
- "*"
build-react:
<<: *defaults
@@ -142,7 +115,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm install
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link
@@ -156,9 +129,7 @@ jobs:
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/react/node_modules
- packages/react/dist
- packages/react/css
- "*"
build-react-router:
<<: *defaults
@@ -167,7 +138,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm install
working_directory: /tmp/workspace/packages/react-router
- run:
command: sudo npm link
@@ -187,56 +158,7 @@ jobs:
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/react-router/node_modules
- packages/react-router/dist
build-vue:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/vue
- run:
command: npm run build
working_directory: /tmp/workspace/packages/vue
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/vue/node_modules
- packages/vue/dist
- packages/vue/css
build-vue-router:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
working_directory: /tmp/workspace/packages/vue-router
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link @ionic/vue
working_directory: /tmp/workspace/packages/vue-router
- run:
command: npm run build
working_directory: /tmp/workspace/packages/vue-router
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/vue-router/dist
- "*"
test-core-clean-build:
<<: *defaults
@@ -266,7 +188,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run test.e2e -- --ci --no-build
command: npm run test.e2e --ci
working_directory: /tmp/workspace/core
test-core-spec:
@@ -276,7 +198,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run test.spec -- --ci
command: npm run test.spec --ci
working_directory: /tmp/workspace/core
test-core-treeshake:
@@ -286,7 +208,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run test.treeshake -- --ci
command: npm run test.treeshake --ci
working_directory: /tmp/workspace/core
test-core-screenshot:
@@ -297,7 +219,7 @@ jobs:
at: /tmp/workspace
- run:
name: Run Screenshot
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --no-build || true
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci || true
working_directory: /tmp/workspace/core
test-core-screenshot-master:
@@ -308,7 +230,7 @@ jobs:
at: /tmp/workspace
- run:
name: Run Screenshot
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --update-screenshot --no-build || true
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --update-screenshot || true
working_directory: /tmp/workspace/core
test-angular-lint:
@@ -341,26 +263,6 @@ jobs:
command: npm run lint
working_directory: /tmp/workspace/packages/react-router
test-vue-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/vue
test-vue-router-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/vue-router
test-react-spec:
<<: *defaults
steps:
@@ -399,55 +301,6 @@ jobs:
command: npm run test.spec
working_directory: /tmp/workspace/packages/react-router
install-react-test-app:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/react-router/test-app npm install
working_directory: /tmp/workspace/packages/react-router/test-app
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/react-router/test-app
test-react-e2e:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run sync
working_directory: /tmp/workspace/packages/react-router/test-app
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/react-router/test-app npm run e2e
working_directory: /tmp/workspace/packages/react-router/test-app
test-vue-router-spec:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link @ionic/vue
working_directory: /tmp/workspace/packages/vue-router
- run:
command: npm run test.spec
working_directory: /tmp/workspace/packages/vue-router
test-angular-e2e:
<<: *defaults
steps:
@@ -455,47 +308,16 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm install
working_directory: /tmp/workspace/angular/test/test-app
- run:
command: npm run test
command: npm test
working_directory: /tmp/workspace/angular/test/test-app
install-vue-test-app:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/vue/test-app npm install
working_directory: /tmp/workspace/packages/vue/test-app
- persist_to_workspace:
root: /tmp/workspace
paths:
- packages/vue/test-app
test-vue-spec-and-e2e:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run sync
working_directory: /tmp/workspace/packages/vue/test-app
- run:
command: npm run test:unit
working_directory: /tmp/workspace/packages/vue/test-app
- run:
command: CYPRESS_CACHE_FOLDER=/tmp/workspace/packages/vue/test-app npm run test:e2e
working_directory: /tmp/workspace/packages/vue/test-app
workflows:
version: 2
build:
jobs:
- puppeteer-dependencies
- build
- build-core:
requires: [build]
@@ -504,14 +326,11 @@ workflows:
- test-core-lint:
requires: [build-core]
- test-core-e2e:
requires: [puppeteer-dependencies, build-core]
requires: [build-core]
- test-core-spec:
requires: [build-core]
# Adam requested we skip this test for now
# since it is failing on ES5 code which
# will be removed in Ionic Framework v6
#- test-core-treeshake:
# requires: [build-core]
- test-core-treeshake:
requires: [build-core]
- test-core-screenshot:
requires: [build-core]
filters:
@@ -539,24 +358,6 @@ workflows:
requires: [build-react]
- test-react-router-spec:
requires: [build-react-router]
- install-react-test-app:
requires: [build-core]
- test-react-e2e:
requires: [install-react-test-app, build-react, build-react-router]
- build-vue:
requires: [build-core]
- build-vue-router:
requires: [build-vue]
- test-vue-lint:
requires: [build-vue]
- test-vue-router-lint:
requires: [build-vue-router]
- test-vue-router-spec:
requires: [build-vue-router]
- install-vue-test-app:
requires: [build-core]
- test-vue-spec-and-e2e:
requires: [install-vue-test-app, build-vue, build-vue-router]
- test-angular-lint:
requires: [build-angular]
- test-angular-e2e:

View File

@@ -9,14 +9,10 @@
* [Ripple Effect](#ripple-effect)
* [Example Components](#example-components)
* [References](#references)
- [Accessibility](#accessibility)
* [Checkbox](#checkbox)
* [Switch](#switch)
- [Rendering Anchor or Button](#rendering-anchor-or-button)
* [Example Components](#example-components-1)
* [Component Structure](#component-structure-1)
- [Converting Scoped to Shadow](#converting-scoped-to-shadow)
- [RTL](#rtl)
## Button States
@@ -366,265 +362,6 @@ ion-ripple-effect {
- [iOS Buttons](https://developer.apple.com/design/human-interface-guidelines/ios/controls/buttons/)
## Accessibility
### Checkbox
#### Example Components
- [ion-checkbox](https://github.com/ionic-team/ionic/tree/master/core/src/components/checkbox)
#### VoiceOver
In order for VoiceOver to work properly with a checkbox component there must be a native `input` with `type="checkbox"`, and `aria-checked` and `role="checkbox"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the checkbox is disabled, preventing iOS users from selecting it:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
<input
type="checkbox"
/>
...
</Host>
);
}
```
#### NVDA
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
<input
type="checkbox"
aria-checked={`${checked}`}
disabled={disabled}
/>
...
</Host>
);
}
```
#### Labels
A helper function has been created to get the proper `aria-label` for the checkbox. This can be imported as `getAriaLabel` like the following:
```tsx
const { label, labelId, labelText } = getAriaLabel(el, inputId);
```
where `el` and `inputId` are the following:
```tsx
export class Checkbox implements ComponentInterface {
private inputId = `ion-cb-${checkboxIds++}`;
@Element() el!: HTMLElement;
...
}
```
This can then be added to the `Host` like the following:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
```
In addition to that, the checkbox input should have a label added:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
<label htmlFor={inputId}>
{labelText}
</label>
<input
type="checkbox"
aria-checked={`${checked}`}
disabled={disabled}
id={inputId}
/>
```
#### Hidden Input
A helper function to render a hidden input has been added, it can be added in the `render`:
```tsx
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
```
> This is required for the checkbox to work with forms.
#### Known Issues
When using VoiceOver on macOS, Chrome will announce the following when you are focused on a checkbox:
```
currently on a checkbox inside of a checkbox
```
This is a compromise we have to make in order for it to work with the other screen readers & Safari.
### Switch
#### Example Components
- [ion-toggle](https://github.com/ionic-team/ionic/tree/master/core/src/components/toggle)
#### Voiceover
In order for VoiceOver to work properly with a switch component there must be a native `input` with `type="checkbox"` and `role="switch"`, and `aria-checked` and `role="switch"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the switch is disabled, preventing iOS users from selecting it:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
<input
type="checkbox"
role="switch"
/>
...
</Host>
);
}
```
#### NVDA
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
<input
type="checkbox"
role="switch"
aria-checked={`${checked}`}
disabled={disabled}
/>
...
</Host>
);
}
```
#### Labels
A helper function has been created to get the proper `aria-label` for the switch. This can be imported as `getAriaLabel` like the following:
```tsx
const { label, labelId, labelText } = getAriaLabel(el, inputId);
```
where `el` and `inputId` are the following:
```tsx
export class Toggle implements ComponentInterface {
private inputId = `ion-tg-${toggleIds++}`;
@Element() el!: HTMLElement;
...
}
```
This can then be added to the `Host` like the following:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
```
In addition to that, the checkbox input should have a label added:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
<label htmlFor={inputId}>
{labelText}
</label>
<input
type="checkbox"
role="switch"
aria-checked={`${checked}`}
disabled={disabled}
id={inputId}
/>
```
#### Hidden Input
A helper function to render a hidden input has been added, it can be added in the `render`:
```tsx
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
```
> This is required for the switch to work with forms.
#### Known Issues
When using VoiceOver on macOS or iOS, Chrome will announce the switch as a checked or unchecked `checkbox`:
```
You are currently on a switch. To select or deselect this checkbox, press Control-Option-Space.
```
There is a WebKit bug open for this: https://bugs.webkit.org/show_bug.cgi?id=196354
## Rendering Anchor or Button
Certain components can render an `<a>` or a `<button>` depending on the presence of an `href` attribute.
@@ -704,39 +441,3 @@ There will be some CSS issues when converting to shadow. Below are some of the d
/* IN SHADOW*/
:host-context(ion-toolbar:not(.ion-color)):host(:not(.ion-color)) ::slotted(ion-segment-button) {
```
## RTL
When you need to support both LTR and RTL modes, try to avoid using values such as `left` and `right`. For certain CSS properties, you can use the appropriate mixin to have this handled for you automatically.
For example, if you wanted `transform-origin` to be RTL-aware, you would use the `transform-origin` mixin:
```css
@include transform-origin(start, center);
```
This would output `transform-origin: left center` in LTR mode and `transform-origin: right center` in RTL mode.
These mixins depend on the `:host-context` pseudo-class when used inside of shadow components, which is not supported in WebKit. As a result, these mixins will not work in Safari for macOS and iOS when applied to shadow components.
To work around this, you should set an RTL class on the host of your component and set your RTL styles by targeting that class:
```tsx
<Host
class={{
'my-cmp-rtl': document.dir === 'rtl'
})
>
...
</Host>
```
```css
:host {
transform-origin: left center;
}
:host(.my-cmp-rtl) {
transform-origin: right center;
}
```

View File

@@ -10,7 +10,7 @@ assignees: ''
<!-- 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) -->
<!-- Please do not submit support requests or "How to" questions here. Instead, please use the Ionic Forum: https://forum.ionicframework.com/ -->
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
@@ -19,8 +19,7 @@ assignees: ''
**Ionic version:**
<!-- (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1) -->
<!-- (For Ionic 2.x & 3.x issues, please use https://github.com/ionic-team/ionic-v3) -->
[ ] **4.x**
[x] **5.x**
[x] **4.x**
**Current behavior:**
<!-- Describe how the bug manifests. -->

View File

@@ -8,4 +8,4 @@ assignees: ''
# Support Question
Please do not submit support requests or "How to" questions here. Instead, please use the Ionic Forum: https://forum.ionicframework.com/
Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/

12
.github/PROCESS.md vendored
View File

@@ -77,15 +77,7 @@ NOTE: be sure to perform those actions in the order stated. If you add the comme
If there is a response to the question, the bot will remove the `needs: reply` and apply the `triage` label. The issue will then go through the triage handling again.
If there is no response within 14 days, the issue will be closed and locked.
### Missing Code Reproduction
If the information the submitter has supplied is not enough for you to reproduce the issue, add the `ionitron: needs reproduction` label. An automated comment will be added to the thread asking the submitter to provide a code reproduction of the issue.
This label can also be added when the submitter has supplied some code, but not enough for you to reproduce the issue (i.e. code snippets).
Issues with this label are not automatically closed and locked, so we manually close and lock them if there is no response within 14 days.
If there is no response within 30 days, the issue will be closed and locked.
## Workflow
@@ -150,7 +142,7 @@ See the [steps for releasing](#releasing) below for detailed information on how
### 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 corresponding 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).
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.

View File

@@ -27,10 +27,7 @@ comment:
is added to issues that need a code reproduction.
Please reproduce this issue in an Ionic starter application and provide a way for us to access it (GitHub repo, StackBlitz, etc). Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
If you have already provided a code snippet and are seeing this message, it is likely that the code snippet was not enough for our team to reproduce the issue.
Please provide a reproduction with the minimum amount of code required to reproduce the issue. Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
For a guide on how to create a good reproduction, see our [Contributing Guide](https://ionicframework.com/docs/contributing/how-to-contribute#creating-a-good-code-reproduction).
@@ -133,6 +130,22 @@ noReproduction:
lock: true
dryRun: false
labelPullRequest:
labels:
- label: "package: angular"
branch: master
path: ^angular
- label: "package: core"
branch: master
path: ^core
- label: "package: react"
branch: master
path: ^react
- label: "package: vue"
branch: master
path: ^vue
dryRun: false
wrongRepo:
repos:
- label: "ionitron: capacitor"

21
.github/labeler.yml vendored
View File

@@ -1,21 +0,0 @@
# This is used with the label workflow which
# will triage pull requests and apply a label based on the
# paths that are modified in the pull request.
#
# For more information, see:
# https://github.com/actions/labeler
'package: core':
- core/**/*
'package: angular':
- angular/**/*
- packages/angular-*/**/*
'package: react':
- packages/react/**/*
- packages/react-*/**/*
'package: vue':
- packages/vue/**/*
- packages/vue-*/**/*

View File

@@ -1,19 +0,0 @@
# This workflow will triage pull requests and apply a label based on the
# paths that are modified in the pull request.
#
# To use this workflow, you will need to set up a .github/labeler.yml
# file with configuration. For more information, see:
# https://github.com/actions/labeler
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@main
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true

2
.gitignore vendored
View File

@@ -58,8 +58,6 @@ prerender-static.html
# stencil
angular/css/
packages/react/css/
packages/vue/css/
core/components/
core/css/
core/hydrate/
core/loader/

View File

@@ -14,9 +14,7 @@ const packages = [
'angular',
'packages/react',
'packages/react-router',
'packages/angular-server',
'packages/vue',
'packages/vue-router'
'packages/angular-server'
];
function readPkg(project) {
@@ -133,7 +131,7 @@ function preparePackage(tasks, package, version, install) {
title: `${pkg.name}: install npm dependencies`,
task: async () => {
await fs.remove(path.join(projectRoot, 'node_modules'));
await execa('npm', ['i', '--legacy-peer-deps'], { cwd: projectRoot });
await execa('npm', ['i'], { cwd: projectRoot });
}
});
}
@@ -143,13 +141,13 @@ function preparePackage(tasks, package, version, install) {
if (package !== 'core') {
projectTasks.push({
title: `${pkg.name}: npm link @ionic/core`,
task: () => execa('npm', ['link', '@ionic/core', '--legacy-peer-deps'], { cwd: projectRoot })
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
});
if (package === 'packages/react-router') {
projectTasks.push({
title: `${pkg.name}: npm link @ionic/react`,
task: () => execa('npm', ['link', '@ionic/react', '--legacy-peer-deps'], { cwd: projectRoot })
task: () => execa('npm', ['link', '@ionic/react'], { cwd: projectRoot })
});
}
}
@@ -209,15 +207,8 @@ function prepareDevPackage(tasks, package, version) {
if (package !== 'core') {
projectTasks.push({
title: `${pkg.name}: npm link @ionic/core`,
task: () => execa('npm', ['link', '@ionic/core', '--legacy-peer-deps'], { cwd: projectRoot })
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
});
if (package === 'packages/react-router') {
projectTasks.push({
title: `${pkg.name}: npm link @ionic/react`,
task: () => execa('npm', ['link', '@ionic/react', '--legacy-peer-deps'], { cwd: projectRoot })
});
}
}
projectTasks.push({

View File

@@ -78,9 +78,6 @@ async function setPackageVersionChanges(packages, version) {
if (package !== 'core') {
const pkg = common.readPkg(package);
common.updateDependency(pkg, '@ionic/core', version);
if(package === 'packages/react-router') {
common.updateDependency(pkg, '@ionic/react', version);
}
common.writePkg(package, pkg);
}
const projectRoot = common.projectPath(package);

View File

@@ -132,7 +132,7 @@ async function publishGithub(version, gitTag, changelog, npmTag) {
await octokit.repos.createRelease({
owner: 'ionic-team',
repo: 'ionic-framework',
repo: 'ionic',
target_commitish: branch,
tag_name: gitTag,
name: version,

View File

@@ -7,25 +7,7 @@ const fs = require('fs');
[
// core
{
files: [
'../core/css/core.css',
'../core/css/core.css.map',
'../core/css/normalize.css',
'../core/css/normalize.css.map',
'../core/components/index.js',
'../core/components/index.d.ts',
'../core/components/package.json',
'../core/dist/index.js',
'../core/dist/ionic/index.esm.js',
]
},
// hydrate
{
files: [
'../core/hydrate/index.js',
'../core/hydrate/index.d.ts',
'../core/hydrate/package.json'
]
files: ['../core/dist/index.js', '../core/dist/ionic/index.esm.js']
},
// angular
{

View File

@@ -1,586 +1,3 @@
## [5.6.9](https://github.com/ionic-team/ionic/compare/v5.6.8...v5.6.9) (2021-06-08)
### Bug Fixes
* **modal:** swipe to close modal is no longer swipeable on footer ([#23401](https://github.com/ionic-team/ionic/issues/23401)) ([ae96563](https://github.com/ionic-team/ionic/commit/ae96563fb3c4612cb8585292b389ee746f5759f7)), closes [#23398](https://github.com/ionic-team/ionic/issues/23398)
* **title:** inherit padding for iOS title in a toolbar ([#23343](https://github.com/ionic-team/ionic/issues/23343)) ([82cfa55](https://github.com/ionic-team/ionic/commit/82cfa5565347704b0e9f7dac792ed2aa6dd30505)), closes [#23072](https://github.com/ionic-team/ionic/issues/23072)
* **vue:** improve v-model integration for Vue 3.1.0+ ([#23420](https://github.com/ionic-team/ionic/issues/23420)) ([f008628](https://github.com/ionic-team/ionic/commit/f0086288512bd7f7d1929d79bfd8bf702efc732e))
* **vue:** prevent error from being thrown when testing on certain jest runners ([#23421](https://github.com/ionic-team/ionic/issues/23421)) ([60bedb5](https://github.com/ionic-team/ionic/commit/60bedb5599b286bffccfc54c4861a269d9b8df73)), closes [#23397](https://github.com/ionic-team/ionic/issues/23397)
## [5.6.8](https://github.com/ionic-team/ionic/compare/v5.6.7...v5.6.8) (2021-05-27)
### Bug Fixes
* **action-sheet:** subheader no longer overlaps action sheet buttons ([#23318](https://github.com/ionic-team/ionic/issues/23318)) ([d473a53](https://github.com/ionic-team/ionic/commit/d473a5385108ef5f39d7c9a2b2924e89fec631de)), closes [#23316](https://github.com/ionic-team/ionic/issues/23316)
* **all:** reflect color property as an attribute for vue ([#23345](https://github.com/ionic-team/ionic/issues/23345)) ([dc430af](https://github.com/ionic-team/ionic/commit/dc430af906c608f948c8d404ad73ae0e0ac36076)), closes [#23323](https://github.com/ionic-team/ionic/issues/23323)
* **range:** knob can now have an accessible name ([#23338](https://github.com/ionic-team/ionic/issues/23338)) ([881dcff](https://github.com/ionic-team/ionic/commit/881dcff40b8bdcb07b27d4ee812ce4ee64b6ea9a)), closes [#23295](https://github.com/ionic-team/ionic/issues/23295)
* **react:** remove @ionic/core dependency in @ionic/react-router to resolve yarn install warning ([#23351](https://github.com/ionic-team/ionic/issues/23351)) ([36bfa33](https://github.com/ionic-team/ionic/commit/36bfa3350354e09be6c62f4e4bee0c553c5981a3)), closes [#23346](https://github.com/ionic-team/ionic/issues/23346)
* **react:** support history@5 in preparation for react router 6 ([#23297](https://github.com/ionic-team/ionic/issues/23297)) ([4da5216](https://github.com/ionic-team/ionic/commit/4da5216b4f65f3d893cc81ebee77261835218f7f)), closes [#23294](https://github.com/ionic-team/ionic/issues/23294)
* **router:** guards are now triggered on initial navigation ([#23123](https://github.com/ionic-team/ionic/issues/23123)) ([56f6f56](https://github.com/ionic-team/ionic/commit/56f6f56c6665f40ea6bf41be463cd416883359f7)), closes [#22936](https://github.com/ionic-team/ionic/issues/22936)
* **router:** redirects now account for query string ([#23337](https://github.com/ionic-team/ionic/issues/23337)) ([08a9f3a](https://github.com/ionic-team/ionic/commit/08a9f3ac94685c5782dd2fa6b56bf3448729a768)), closes [#23136](https://github.com/ionic-team/ionic/issues/23136)
* **skeleton-text:** animation no longer jumps on large skeleton text elements ([#22697](https://github.com/ionic-team/ionic/issues/22697)) ([1a36922](https://github.com/ionic-team/ionic/commit/1a36922f41f2890c778feedbad9c5b74a72a3907)), closes [#22694](https://github.com/ionic-team/ionic/issues/22694)
* **slides:** resolve prototype pollution in swiper v5 ([#23344](https://github.com/ionic-team/ionic/issues/23344)) ([a708c41](https://github.com/ionic-team/ionic/commit/a708c412625cba475ec7863468dcc2146b8feb7a)), closes [#23342](https://github.com/ionic-team/ionic/issues/23342)
* **title:** large title scale animation is now correct in rtl mode ([#23372](https://github.com/ionic-team/ionic/issues/23372)) ([3d474ec](https://github.com/ionic-team/ionic/commit/3d474ec67ff4192fa3d08e370e20fecbee99a6aa)), closes [#23371](https://github.com/ionic-team/ionic/issues/23371)
## [5.6.7](https://github.com/ionic-team/ionic/compare/v5.6.6...v5.6.7) (2021-05-13)
### Bug Fixes
* **angular:** warnings are no longer generated when running tests with ng test ([#23292](https://github.com/ionic-team/ionic/issues/23292)) ([9cb6c80](https://github.com/ionic-team/ionic/commit/9cb6c80b3db4273e9c003a62a3065427995cb353)), closes [#19926](https://github.com/ionic-team/ionic/issues/19926)
* **overlays:** screen readers no longer read content behind overlays ([#23284](https://github.com/ionic-team/ionic/issues/23284)) ([a9b12a5](https://github.com/ionic-team/ionic/commit/a9b12a5aa4c150a1f8a80a826dda0df350bc0092)), closes [#22714](https://github.com/ionic-team/ionic/issues/22714)
* **refresher:** refresher now only activates when pulling down on MD ([#23283](https://github.com/ionic-team/ionic/issues/23283)) ([1e1596f](https://github.com/ionic-team/ionic/commit/1e1596f471e440085bf2d90e473f0cb0c0dcf6e2)), closes [#23245](https://github.com/ionic-team/ionic/issues/23245)
* **vue:** use correct history mode when doing ssr to avoid errors ([#23255](https://github.com/ionic-team/ionic/issues/23255)) ([2e00dab](https://github.com/ionic-team/ionic/commit/2e00dab95d3fefeab92c19cedb046ae2bb10879c)), closes [#23254](https://github.com/ionic-team/ionic/issues/23254)
## [5.6.6](https://github.com/ionic-team/ionic/compare/v5.6.5...v5.6.6) (2021-04-29)
### Bug Fixes
* **angular:** back button goes back to proper tab on angular 11.2.10 ([#23238](https://github.com/ionic-team/ionic/issues/23238)) ([e436439](https://github.com/ionic-team/ionic/commit/e436439e10c895b203ca4dc889cf307ffb9524b4)), closes [#23230](https://github.com/ionic-team/ionic/issues/23230)
* **react:** remove hardware back button event listener when NavManager is unmounted ([#23224](https://github.com/ionic-team/ionic/issues/23224)) ([c501da7](https://github.com/ionic-team/ionic/commit/c501da73be879db0fea818c507bae4386a47d42e)), closes [#23170](https://github.com/ionic-team/ionic/issues/23170)
* **slides:** undefined error is no longer thrown after destroying and quickly re-creating ion-slides ([#23239](https://github.com/ionic-team/ionic/issues/23239)) ([2ccaabb](https://github.com/ionic-team/ionic/commit/2ccaabb5b4d67ec4b1318e3ccb3edc1bd853ab3e)), closes [#22289](https://github.com/ionic-team/ionic/issues/22289)
* **vue:** components inside of ion-nav are now unmounted properly ([#23240](https://github.com/ionic-team/ionic/issues/23240)) ([f2f41e2](https://github.com/ionic-team/ionic/commit/f2f41e2af45ba9a36064d33e0b5c1b59da6b74ab)), closes [#23233](https://github.com/ionic-team/ionic/issues/23233)
* **vue:** overlay events can now be listened for without the "on" prefix, deprecated "on" prefix event listeners ([#23227](https://github.com/ionic-team/ionic/issues/23227)) ([dab927d](https://github.com/ionic-team/ionic/commit/dab927d2901658f4040c1d1aa6c777497d8714c8))
## [5.6.5](https://github.com/ionic-team/ionic/compare/v5.6.4...v5.6.5) (2021-04-22)
### Bug Fixes
* **content:** only render a main element when content is being used in primary view ([#23160](https://github.com/ionic-team/ionic/issues/23160)) ([2d07d82](https://github.com/ionic-team/ionic/commit/2d07d8216af908b181c5e7e438e79a049bb6d8c2))
* **datetime, input, textarea:** only add aria-labelledby if there is an adjacent label ([#23211](https://github.com/ionic-team/ionic/issues/23211)) ([a31fb55](https://github.com/ionic-team/ionic/commit/a31fb55bac1ef03e014f3d7f6c22c24eff20feb5))
* **radio-group:** pressing spacebar correctly unselects radio with allow-empty-selection ([#23194](https://github.com/ionic-team/ionic/issues/23194)) ([7139b3f](https://github.com/ionic-team/ionic/commit/7139b3f39e8eeef07ff7c595940fc5dafe062956)), closes [#22734](https://github.com/ionic-team/ionic/issues/22734)
* **react:** callback refs now work correctly with ionic components ([#23152](https://github.com/ionic-team/ionic/issues/23152)) ([0dd189e](https://github.com/ionic-team/ionic/commit/0dd189e2c05012659894a4c15cd3a9d407fe0a63)), closes [#23153](https://github.com/ionic-team/ionic/issues/23153)
* **segment, segment-button:** use tablist and tab roles ([#23145](https://github.com/ionic-team/ionic/issues/23145)) ([91ac340](https://github.com/ionic-team/ionic/commit/91ac340ae7e8928f7b0972a093dd9dd7fa727671))
* **vue:** dynamic tabs are now correctly recognized ([#23212](https://github.com/ionic-team/ionic/issues/23212)) ([004885b](https://github.com/ionic-team/ionic/commit/004885bfd4446487e6386876c868532a2795347f)), closes [#22847](https://github.com/ionic-team/ionic/issues/22847)
* **vue:** update props when navigating to new parameterized route ([#23189](https://github.com/ionic-team/ionic/issues/23189)) ([35c8802](https://github.com/ionic-team/ionic/commit/35c8802c22c1f4bf213a01e1c28398ad62d1b7ac))
## [5.6.4](https://github.com/ionic-team/ionic/compare/v5.6.3...v5.6.4) (2021-04-08)
### Bug Fixes
* **angular:** swiping back quickly no longer causes app to get stuck ([#23125](https://github.com/ionic-team/ionic/issues/23125)) ([28c52fd](https://github.com/ionic-team/ionic/commit/28c52fd4e3df3d96b4ec83075a322e110e938a1a)), closes [#15154](https://github.com/ionic-team/ionic/issues/15154)
* **input:** inherit aria-label to input ([#23159](https://github.com/ionic-team/ionic/issues/23159)) ([61f094d](https://github.com/ionic-team/ionic/commit/61f094d30665c9afec428028883a5d9a085892d8))
* **react:** overlays now correctly unmount any child components after dismissing ([#23149](https://github.com/ionic-team/ionic/issues/23149)) ([dee6eb3](https://github.com/ionic-team/ionic/commit/dee6eb30df370047bbc872b00ab6d801dd11fa81)), closes [#23140](https://github.com/ionic-team/ionic/issues/23140)
* **react, vue:** correct view now chosen when going back inside tabs ([#23154](https://github.com/ionic-team/ionic/issues/23154)) ([7203190](https://github.com/ionic-team/ionic/commit/72031902347dc279045e2e099f69852a23dd8436)), closes [#23087](https://github.com/ionic-team/ionic/issues/23087) [#23101](https://github.com/ionic-team/ionic/issues/23101)
* **toggle:** prevent click event from firing twice ([#23146](https://github.com/ionic-team/ionic/issues/23146)) ([42e6c90](https://github.com/ionic-team/ionic/commit/42e6c90c4632423386b165dddc4b94a55c075e2e)), closes [#23041](https://github.com/ionic-team/ionic/issues/23041)
* **vue:** account for event name changes in vue 3.0.6+ for overlay components ([#23100](https://github.com/ionic-team/ionic/issues/23100)) ([27318cf](https://github.com/ionic-team/ionic/commit/27318cf58563c4b38d0b7045fb61451f45954a8f))
* **vue:** components now integrate properly with vee-validate ([#23114](https://github.com/ionic-team/ionic/issues/23114)) ([ba51daf](https://github.com/ionic-team/ionic/commit/ba51daf17c4438aea6826882f82a04ebf8d6a5d8)), closes [#22886](https://github.com/ionic-team/ionic/issues/22886)
## [5.6.3](https://github.com/ionic-team/ionic/compare/v5.6.2...v5.6.3) (2021-03-23)
### Bug Fixes
* **all:** update tslib to resolve export errors ([#23092](https://github.com/ionic-team/ionic-framework/pull/23092)) ([0cdd326](https://github.com/ionic-team/ionic-framework/commit/0cdd326a4a02729a306bccfcadca7370475eae32)), closes [#23090](https://github.com/ionic-team/ionic-framework/issues/23090)
* **react:** correctly show ion-back-button when going back ([#23069](https://github.com/ionic-team/ionic/issues/23069)) ([1c93b75](https://github.com/ionic-team/ionic/commit/1c93b75e397961e374620eb43bee3d6bb4389836)), closes [#22692](https://github.com/ionic-team/ionic/issues/22692)
## [5.6.2](https://github.com/ionic-team/ionic/compare/v5.6.1...v5.6.2) (2021-03-22)
### Bug Fixes
* **item:** detail icon now respects rtl mode ([#23081](https://github.com/ionic-team/ionic/issues/23081)) ([b04fb6e](https://github.com/ionic-team/ionic/commit/b04fb6e849bc9d6283271aaadc2b8aaae1f3961d)), closes [#23078](https://github.com/ionic-team/ionic/issues/23078)
## [5.6.1](https://github.com/ionic-team/ionic/compare/v5.6.0...v5.6.1) (2021-03-17)
### Bug Fixes
* **custom-elements:** overlays now present correctly when using custom elements build ([#23039](https://github.com/ionic-team/ionic/issues/23039)) ([e4bf052](https://github.com/ionic-team/ionic/commit/e4bf052794af9aac07f887013b9250d2a045eba3)), closes [#23029](https://github.com/ionic-team/ionic/issues/23029)
* **item:** detail icon is no longer announced by screen readers ([#23055](https://github.com/ionic-team/ionic/issues/23055)) ([c877061](https://github.com/ionic-team/ionic/commit/c877061a328c6ab6fa7248b9880d0205c6c4f6c1)), closes [#23054](https://github.com/ionic-team/ionic/issues/23054)
* **label:** properly float labels for non-input items ([#23060](https://github.com/ionic-team/ionic/issues/23060)) ([c8a3999](https://github.com/ionic-team/ionic/commit/c8a3999da109b1719777f2acb791ab5388d371ea))
* **react:** only pass tab event props from IonTabs to IonTabBar if defined ([#23024](https://github.com/ionic-team/ionic/issues/23024)) ([f94e618](https://github.com/ionic-team/ionic/commit/f94e618a7b307b143eb39c061dc9e6b80e11f862)), closes [#23023](https://github.com/ionic-team/ionic/issues/23023)
* **refresher:** progressEnd no longer errors when pulling quickly in MD native refresher ([#23056](https://github.com/ionic-team/ionic/issues/23056)) ([67617fb](https://github.com/ionic-team/ionic/commit/67617fbc0f7ec825f1fa4c6e7e2da70e3fcd2d66))
* **virtual-scroll:** allow null in items property ([#23047](https://github.com/ionic-team/ionic/issues/23047)) ([2a253a1](https://github.com/ionic-team/ionic/commit/2a253a1d334ca2c6a478a5bc426e3115268a29af))
* **vue:** passing params as props are correctly updated when switching pages ([#23049](https://github.com/ionic-team/ionic/issues/23049)) ([2f54bc1](https://github.com/ionic-team/ionic/commit/2f54bc14699656e6905452a4233d982f83d0001f)), closes [#23043](https://github.com/ionic-team/ionic/issues/23043)
# [5.6.0 Argon](https://github.com/ionic-team/ionic/compare/v5.5.4...v5.6.0) (2021-03-04)
### Bug Fixes
* **all:** improve support for ids with special characters when getting label element ([#22680](https://github.com/ionic-team/ionic/issues/22680)) ([19d63f6](https://github.com/ionic-team/ionic/commit/19d63f62431ef9d8279f1726dd63fac2f0d4b46b)), closes [#22678](https://github.com/ionic-team/ionic/issues/22678)
* **header:** collapsed toolbar is no longer incorrectly shown when using ion-refresher ([#22937](https://github.com/ionic-team/ionic/issues/22937)) ([5300dcc](https://github.com/ionic-team/ionic/commit/5300dcc693caf51a726f8c346cfc9a44474fd3d1)), closes [#22829](https://github.com/ionic-team/ionic/issues/22829)
* **label:** only show placeholder with floating label when focused ([#22958](https://github.com/ionic-team/ionic/issues/22958)) ([9282aa6](https://github.com/ionic-team/ionic/commit/9282aa68715c088e9c8fcd915e78fb7ae91f551f)), closes [#17571](https://github.com/ionic-team/ionic/issues/17571)
* **progress-bar:** use correct theme colors in dark mode ([#22965](https://github.com/ionic-team/ionic/issues/22965)) ([b6b2714](https://github.com/ionic-team/ionic/commit/b6b2714d70f71255315510c5e49708944875db72)), closes [#20098](https://github.com/ionic-team/ionic/issues/20098)
* **radio-group:** pressing space no longer jumps screen to bottom of page ([#22892](https://github.com/ionic-team/ionic/issues/22892)) ([3a0465e](https://github.com/ionic-team/ionic/commit/3a0465e7d6f9e3cb01336a8bdbd7001e4ec34559)), closes [#22716](https://github.com/ionic-team/ionic/issues/22716)
* **react:** IonRouterOutlet now respects animated={false} prop ([#22905](https://github.com/ionic-team/ionic/issues/22905)) ([da1b7a0](https://github.com/ionic-team/ionic/commit/da1b7a0e7a9a5e6a9120dc4d5459c97d8bca5390)), closes [#22903](https://github.com/ionic-team/ionic/issues/22903)
* **react:** onIonTabsWillChange and onIonTabsDidChange event handlers are now properly bound to IonTabs ([#22233](https://github.com/ionic-team/ionic/issues/22233)) ([b064fde](https://github.com/ionic-team/ionic/commit/b064fdebef14018b77242b791914d5bb10863d39))
* **react, vue:** navigating using ion-back-button now selects correct page ([#22974](https://github.com/ionic-team/ionic/issues/22974)) ([cd8ffd8](https://github.com/ionic-team/ionic/commit/cd8ffd82a03ee69ef4cbd7922544bfc39680def9)), closes [#22830](https://github.com/ionic-team/ionic/issues/22830)
* **react, vue:** tab buttons no longer throw an error if href is undefined ([#22998](https://github.com/ionic-team/ionic/issues/22998)) ([943e3f6](https://github.com/ionic-team/ionic/commit/943e3f6ae37ecc56f21168f057dde77a05e4e144)), closes [#22997](https://github.com/ionic-team/ionic/issues/22997)
* **refresher:** add correct dark mode styles ([#22639](https://github.com/ionic-team/ionic/issues/22639)) ([c05476b](https://github.com/ionic-team/ionic/commit/c05476b88e3e6884b4c490461c9c67dee3dca83d)), closes [#22637](https://github.com/ionic-team/ionic/issues/22637)
* **vue:** correctly remove active state from tab button when navigating away from tab ([#23000](https://github.com/ionic-team/ionic/issues/23000)) ([a2763af](https://github.com/ionic-team/ionic/commit/a2763afe8e1fe1dc0decdbcb757a03bc5038045e)), closes [#22597](https://github.com/ionic-team/ionic/issues/22597)
* **vue:** prevent race conditions when opening overlays ([#22883](https://github.com/ionic-team/ionic/issues/22883)) ([68a9b80](https://github.com/ionic-team/ionic/commit/68a9b800532f9c0b308a3b74ed18a7068a942301)), closes [#22880](https://github.com/ionic-team/ionic/issues/22880)
### Features
* **custom-elements:** add experimental custom elements build ([#22863](https://github.com/ionic-team/ionic/issues/22863)) ([0de75af](https://github.com/ionic-team/ionic/commit/0de75afbefc521c1d76adcd587f77ba19c285a95))
* **progress-bar:** add parts for more design customization ([#22938](https://github.com/ionic-team/ionic/issues/22938)) ([e256d3f](https://github.com/ionic-team/ionic/commit/e256d3f09fd6f231c4d9e1d0f0927612a591466b)), closes [#20062](https://github.com/ionic-team/ionic/issues/20062) [#21820](https://github.com/ionic-team/ionic/issues/21820)
* **react:** add react hooks to control overlay components ([#22484](https://github.com/ionic-team/ionic/issues/22484)) ([b83e009](https://github.com/ionic-team/ionic/commit/b83e00934e794a936c9d3d23d7f94bbe89cedcd5))
* **searchbar:** add showClearIcon property ([#22759](https://github.com/ionic-team/ionic/issues/22759)) ([215eb5d](https://github.com/ionic-team/ionic/commit/215eb5d4efbb9ade942dba1687469caf61da21e7)), closes [#22738](https://github.com/ionic-team/ionic/issues/22738)
* **vue:** add composition API ionic lifecycle hooks ([#22970](https://github.com/ionic-team/ionic/issues/22970)) ([dd1c8db](https://github.com/ionic-team/ionic/commit/dd1c8dbf3b20fbd423f70c96846d9c366d90e7c5)), closes [#22769](https://github.com/ionic-team/ionic/issues/22769)
## [5.5.5](https://github.com/ionic-team/ionic/compare/v5.5.4...v5.5.5) (2021-02-26)
### Bug Fixes
* **vue:** account for event name changes in vue 3.0.6+ ([#22980](https://github.com/ionic-team/ionic/issues/22980)) ([7dd2e6d](https://github.com/ionic-team/ionic/commit/7dd2e6d287b47cca758e1d4a71928dd3dc9ac24d)), closes [#22977](https://github.com/ionic-team/ionic/issues/22977)
## [5.5.4](https://github.com/ionic-team/ionic/compare/v5.5.3...v5.5.4) (2021-02-04)
### Bug Fixes
* **angular:** update ngAdd schematic ([#22858](https://github.com/ionic-team/ionic/issues/22858)) ([487349f](https://github.com/ionic-team/ionic/commit/487349f02a41344db2478735d27bf79f2a1c99b3))
* **app:** keyboard no longer hides when using contenteditable ([#22857](https://github.com/ionic-team/ionic/issues/22857)) ([b6b2d34](https://github.com/ionic-team/ionic/commit/b6b2d34fd446feb06cf0143946a014d19231a78e)), closes [#22856](https://github.com/ionic-team/ionic/issues/22856)
* **ios**: scroll assist no longer prevents first click event from firing ([#22845](https://github.com/ionic-team/ionic/issues/22845)) ([f7d4c21](https://github.com/ionic-team/ionic/commit/f7d4c21b64e27f9b655bc1ab2522d6357dc6010f)), closes [#21871](https://github.com/ionic-team/ionic/issues/21871)
* **select:** class on component now indicates when select is open ([#22846](https://github.com/ionic-team/ionic/issues/22846)) ([1a5accc](https://github.com/ionic-team/ionic/commit/1a5accc5f707f84063469c0bd3e5e153489f1e5d)), closes [#22801](https://github.com/ionic-team/ionic/issues/22801)
* **vue:** ionChange events now propagate correctly ([#22872](https://github.com/ionic-team/ionic/issues/22872)) ([ff0f1da](https://github.com/ionic-team/ionic/commit/ff0f1da9f11915b48c4258af7c48c4513785f3fc)), closes [#22870](https://github.com/ionic-team/ionic/issues/22870)
## [5.5.3](https://github.com/ionic-team/ionic/compare/v5.5.2...v5.5.3) (2021-01-28)
### Bug Fixes
* **react:** do not unmount overlay inner component until overlay is dismissed ([#22813](https://github.com/ionic-team/ionic/issues/22813)) ([ab1fc8f](https://github.com/ionic-team/ionic/commit/ab1fc8f2311fd252146942c7a947ebc96efd054f)), closes [#22761](https://github.com/ionic-team/ionic/issues/22761)
* **react:** adding dynamic class to ion-page no longer hides component ([#22666](https://github.com/ionic-team/ionic/issues/22666)) ([a01bdb8](https://github.com/ionic-team/ionic/commit/a01bdb8c8dfee760721eeb35a8b556954f3b5b13)), closes [#22631](https://github.com/ionic-team/ionic/issues/22631)
* **react:** improve view matching logic ([#22569](https://github.com/ionic-team/ionic/issues/22569)) ([f891f66](https://github.com/ionic-team/ionic/commit/f891f667082d2deb5f1b5f0f27af46e46ed1ca0f))
* **react, vue:** do not show back button when replacing to root page ([#22750](https://github.com/ionic-team/ionic/issues/22750)) ([9e9a372](https://github.com/ionic-team/ionic/commit/9e9a3724979e95f3df1a340be21d16d8664a013c)), closes [#22528](https://github.com/ionic-team/ionic/issues/22528)
* **refresher:** correctly detect spinner when using native refresher ([#22800](https://github.com/ionic-team/ionic/issues/22800)) ([e2d8e5c](https://github.com/ionic-team/ionic/commit/e2d8e5c4dcf893ddd8aaa556c1dd8fcaf52411c9)), closes [#22706](https://github.com/ionic-team/ionic/issues/22706)
* **title:** only add large title transition when using collapsible header ([#22762](https://github.com/ionic-team/ionic/issues/22762)) ([348c50b](https://github.com/ionic-team/ionic/commit/348c50b7ea5d4c74498c5d26e40c1c4fe923ee55)), closes [#22760](https://github.com/ionic-team/ionic/issues/22760)
* **vue:** all ionic vue components can now use router link ([#22743](https://github.com/ionic-team/ionic/issues/22743)) ([3d6ac13](https://github.com/ionic-team/ionic/commit/3d6ac1382e23663a3d010fd253d3c6017d3923e4))
* **vue:** correctly determine leaving view when transitioning to a new instance of a previous page ([#22655](https://github.com/ionic-team/ionic/issues/22655)) ([e3a05bf](https://github.com/ionic-team/ionic/commit/e3a05bfeb55d8eaa38aa08a37859aa4df6ffa2d4)), closes [#22654](https://github.com/ionic-team/ionic/issues/22654) [#22658](https://github.com/ionic-team/ionic/issues/22658)
* **vue:** ensure v-model value is properly synced before ionChange event ([#22749](https://github.com/ionic-team/ionic/issues/22749)) ([e1d6627](https://github.com/ionic-team/ionic/commit/e1d6627bf0ef1f47f980db1573c6b2a3d16d7677)), closes [#22610](https://github.com/ionic-team/ionic/issues/22610)
* **vue:** improve path matching with tabs, deprecated adding additional pages as children of tabs without a router outlet ([#22807](https://github.com/ionic-team/ionic/issues/22807)) ([2a3ce9a](https://github.com/ionic-team/ionic/commit/2a3ce9a74e85111a2f1f470b9d8bfe2cda793ca5)), closes [#22519](https://github.com/ionic-team/ionic/issues/22519)
* **vue:** improve v-model binding sync between vue wrappers and web components ([#22745](https://github.com/ionic-team/ionic/issues/22745)) ([64719f4](https://github.com/ionic-team/ionic/commit/64719f49f979c0296a01827d3c02599a48ba93a6)), closes [#22731](https://github.com/ionic-team/ionic/issues/22731)
* **vue:** output commonjs format for node environments ([#22766](https://github.com/ionic-team/ionic/issues/22766)) ([7ecae2e](https://github.com/ionic-team/ionic/commit/7ecae2e4cb5d0eebc6041a8a7a5acc156132c2e1))
* **vue:** tab bar is now correctly hidden when keyboard is open ([#22687](https://github.com/ionic-team/ionic/issues/22687)) ([5c27dd8](https://github.com/ionic-team/ionic/commit/5c27dd8032d32ebb57c31e1f6c112dc513344b93))
## [5.5.2](https://github.com/ionic-team/ionic/compare/v5.5.1...v5.5.2) (2020-12-09)
### Bug Fixes
* **android:** setting hardwareBackButton: false in config now disables default webview behavior ([#22555](https://github.com/ionic-team/ionic/issues/22555)) ([dc9faa6](https://github.com/ionic-team/ionic/commit/dc9faa6a0fbebb64c83c107c79cfd486cc0c096a)), closes [#18237](https://github.com/ionic-team/ionic/issues/18237)
* **button:** allow aria-label to be inherited on inner button ([#22632](https://github.com/ionic-team/ionic/issues/22632)) ([818e387](https://github.com/ionic-team/ionic/commit/818e387fe81ac7026fb374d8865116dadd433c87)), closes [#22629](https://github.com/ionic-team/ionic/issues/22629)
* **react:** hardware back button now navigates correctly ([36939e1](https://github.com/ionic-team/ionic/commit/36939e10ae0b8ac9a9275ee06d8e0d345de7c64f))
* **react:** setting a ref now allows other props to be passed in ([31f45cd](https://github.com/ionic-team/ionic/commit/31f45cdcc953b08749d9db08321fa5ec6cbe2532)), closes [#22609](https://github.com/ionic-team/ionic/issues/22609)
* **refresher:** clean up old css if calling refresh method before native refresher is setup ([#22640](https://github.com/ionic-team/ionic/issues/22640)) ([8d5ed47](https://github.com/ionic-team/ionic/commit/8d5ed47a282f92a60a2c4126a673cc2a5733067e)), closes [#22636](https://github.com/ionic-team/ionic/issues/22636)
* **refresher:** refresher correctly detects native refresher when shown asynchronously ([#22623](https://github.com/ionic-team/ionic/issues/22623)) ([5ed73cd](https://github.com/ionic-team/ionic/commit/5ed73cdf4d63eeee25ef28d9676fcaa4f8e07b47)), closes [#22616](https://github.com/ionic-team/ionic/issues/22616)
* **vue:** adding non tab button elements inside ion-tab-bar no longer causes errors ([#22643](https://github.com/ionic-team/ionic/issues/22643)) ([61cf0c5](https://github.com/ionic-team/ionic/commit/61cf0c534e45ce09410be6bfb50bdc27b657d1bc)), closes [#22642](https://github.com/ionic-team/ionic/issues/22642)
* **vue:** correctly handle navigation failures ([#22621](https://github.com/ionic-team/ionic/issues/22621)) ([216f51b](https://github.com/ionic-team/ionic/commit/216f51b12a8c4ae7b410f47ce3d350ea513b68a1)), closes [#22591](https://github.com/ionic-team/ionic/issues/22591)
* **vue:** correctly remove old view when replacing route ([#22566](https://github.com/ionic-team/ionic/issues/22566)) ([4f4f31b](https://github.com/ionic-team/ionic/commit/4f4f31b65e48294c3130ff24ae00b1a2aa1f9d31)), closes [#22492](https://github.com/ionic-team/ionic/issues/22492)
* **vue:** pass in correct route to props function ([#22605](https://github.com/ionic-team/ionic/issues/22605)) ([01afdc4](https://github.com/ionic-team/ionic/commit/01afdc42e5b1598d4d15cb51761bbb3eb5d13893)), closes [#22602](https://github.com/ionic-team/ionic/issues/22602)
* **vue:** query strings are now correctly handled when navigating back ([#22615](https://github.com/ionic-team/ionic/issues/22615)) ([a94e2a8](https://github.com/ionic-team/ionic/commit/a94e2a87fb759e7b7daed2d0304c1199dbc7afd1)), closes [#22517](https://github.com/ionic-team/ionic/issues/22517)
* **vue:** swipe back gesture is properly disabled when swipeBackEnabled config is false ([#22568](https://github.com/ionic-team/ionic/issues/22568)) ([9d04c12](https://github.com/ionic-team/ionic/commit/9d04c127e817676983940b034a4c7efc92fdfbc6)), closes [#22567](https://github.com/ionic-team/ionic/issues/22567)
### For Ionic Vue Developers
Vue Router 4 has been released! Be sure to update from the release candidate to the latest stable version of Vue Router.
For more information on the changes in Vue Router 4, see https://github.com/vuejs/vue-router-next/releases/tag/v4.0.0.
```
npm install vue-router@4
```
## [5.5.1](https://github.com/ionic-team/ionic/compare/v5.5.0...v5.5.1) (2020-11-25)
### Bug Fixes
* **checkbox:** click handler now fires properly ([#22573](https://github.com/ionic-team/ionic/issues/22573)) ([0786835](https://github.com/ionic-team/ionic/commit/07868354aaf88deebf7472a5bf0f34d7c823de17)), closes [#22557](https://github.com/ionic-team/ionic/issues/22557)
* **radio:** properly announce radios on screen readers and resolve axe errors ([#22507](https://github.com/ionic-team/ionic/issues/22507)) ([afcc46e](https://github.com/ionic-team/ionic/commit/afcc46e1cc4d7f6e9d1a50f8b367da4b1d0c3143))
* **react:** eliminate use of deprecated `findDOMNode`, resolves [#20972](https://github.com/ionic-team/ionic/issues/20972) ([5275332](https://github.com/ionic-team/ionic/commit/5275332e43694f3ee8738a1726c0d202b16c3052))
* **router:** navigation guards now fire when navigating to a page with params ([#22521](https://github.com/ionic-team/ionic/issues/22521)) ([1956f98](https://github.com/ionic-team/ionic/commit/1956f9896883dc4687488e5418e50ce0f6cbe6c9)), closes [#22516](https://github.com/ionic-team/ionic/issues/22516)
* **select:** fix a11y issues with axe and screen readers ([#22494](https://github.com/ionic-team/ionic/issues/22494)) ([04b874e](https://github.com/ionic-team/ionic/commit/04b874e32a65588ca79eda9399ab7e9d86a3cb77)), closes [#21552](https://github.com/ionic-team/ionic/issues/21552) [#21548](https://github.com/ionic-team/ionic/issues/21548)
* **select:** improvements for announcing placeholder and value on screenreaders ([#22556](https://github.com/ionic-team/ionic/issues/22556)) ([ea52db6](https://github.com/ionic-team/ionic/commit/ea52db66f05a185fed6b2e849734a7ffa1c6c6ea))
* **vue:** onBeforeRouteLeave and onBeforeRouteUpdate hooks now fire properly ([#22542](https://github.com/ionic-team/ionic/issues/22542)) ([8002114](https://github.com/ionic-team/ionic/commit/8002114e720361e60d7a7fe2d15ab88b49a72e1b)), closes [#22540](https://github.com/ionic-team/ionic/issues/22540)
* **vue:** tabs now correctly fire lifecycle events ([#22479](https://github.com/ionic-team/ionic/issues/22479)) ([cdc2fb6](https://github.com/ionic-team/ionic/commit/cdc2fb652fe5aa149eaa751a77fb506ac1f64195)), closes [#22466](https://github.com/ionic-team/ionic/issues/22466)
* **vue:** unit testing a routerLink-capable component no longer warns of missing router dependency ([#22532](https://github.com/ionic-team/ionic/issues/22532)) ([4e23aad](https://github.com/ionic-team/ionic/commit/4e23aad3d911188e4a2706545463a81332c00ce9)), closes [#22506](https://github.com/ionic-team/ionic/issues/22506)
### For Ionic Vue Developers
When updating to Ionic Vue v5.5.1 make sure you are on the latest version of `vue-router@next` to take advantage of the bug fixes in this release:
```
npm install vue-router@next
```
# [5.5.0 Chlorine](https://github.com/ionic-team/ionic/compare/v5.4.4...v5.5.0) (2020-11-18)
### Bug Fixes
* **backdrop:** nvda no longer incorrectly announces backdrop ([#22481](https://github.com/ionic-team/ionic/issues/22481)) ([2d878fc](https://github.com/ionic-team/ionic/commit/2d878fc4f6c7a710dbfb722e188e3e402e1672f9)), closes [#22102](https://github.com/ionic-team/ionic/issues/22102)
* **checkbox:** use a native input to fix a11y issues with axe and screen readers ([#22402](https://github.com/ionic-team/ionic/issues/22402)) ([7214a84](https://github.com/ionic-team/ionic/commit/7214a8401b709e1353155304cf6e9f97b2b4d294)), closes [#21644](https://github.com/ionic-team/ionic/issues/21644) [#20517](https://github.com/ionic-team/ionic/issues/20517) [#17796](https://github.com/ionic-team/ionic/issues/17796)
* **input:** title attribute is now automatically inherited ([#22493](https://github.com/ionic-team/ionic/issues/22493)) ([abad12f](https://github.com/ionic-team/ionic/commit/abad12fbdb1378066282fe8e9b7761747951b685)), closes [#22055](https://github.com/ionic-team/ionic/issues/22055)
* **refresher:** ios native refresher now works in side menu ([#22449](https://github.com/ionic-team/ionic/issues/22449)) ([a4a6453](https://github.com/ionic-team/ionic/commit/a4a64530ff083b83187b293dfdacb0fa45ad9f51))
* **refresher:** md native refresher now works in side menu ([#22446](https://github.com/ionic-team/ionic/issues/22446)) ([6b817f2](https://github.com/ionic-team/ionic/commit/6b817f26b08d01d8367d16308db775b6192e7628)), closes [#20832](https://github.com/ionic-team/ionic/issues/20832)
* **toggle:** use a native input to fix a11y issues with axe and screen readers ([#22477](https://github.com/ionic-team/ionic/issues/22477)) ([813611a](https://github.com/ionic-team/ionic/commit/813611a61b664c9827760ccaa889d0e2fcae7d94)), closes [#22011](https://github.com/ionic-team/ionic/issues/22011) [#21552](https://github.com/ionic-team/ionic/issues/21552)
* **vue:** correctly pass route props to components ([#22476](https://github.com/ionic-team/ionic/issues/22476)) ([0956f8b](https://github.com/ionic-team/ionic/commit/0956f8bc5588836996c8c74f98166c347414a312)), closes [#22472](https://github.com/ionic-team/ionic/issues/22472)
* **vue:** tab bar now works with slot="top" ([#22461](https://github.com/ionic-team/ionic/issues/22461)) ([e17c822](https://github.com/ionic-team/ionic/commit/e17c822bfbc2a876226738b77a4c95c02e0b5953)), closes [#22456](https://github.com/ionic-team/ionic/issues/22456)
### Features
* **chip:** add disabled property ([#20658](https://github.com/ionic-team/ionic/issues/20658)) ([0a0cbd8](https://github.com/ionic-team/ionic/commit/0a0cbd8f2a505ad2b3d8afb60cb1e940ced52e0d)), closes [#19510](https://github.com/ionic-team/ionic/issues/19510)
* **segment:** add swipeGesture property to allow for disabling of the swipe gesture ([#22087](https://github.com/ionic-team/ionic/issues/22087)) ([65bc995](https://github.com/ionic-team/ionic/commit/65bc99577c44cce653dafd9937c4d8f9c45fff61)), closes [#22048](https://github.com/ionic-team/ionic/issues/22048)
* **vue:** composition api lifecycle methods ([#22241](https://github.com/ionic-team/ionic/issues/22241)) ([f5b0299](https://github.com/ionic-team/ionic/commit/f5b0299729c2c639e432612e62fb7eaa189ca969))
* **vue:** vetur support ([#22403](https://github.com/ionic-team/ionic/issues/22403)) ([e76f79d](https://github.com/ionic-team/ionic/commit/e76f79d0548c97edd193808f5e0a19889cffae5b))
* **vue:** web-types support ([#22428](https://github.com/ionic-team/ionic/issues/22428)) ([639314a](https://github.com/ionic-team/ionic/commit/639314ab218b65a9a2de6040417b0e1b363e47ef)), closes [#19522](https://github.com/ionic-team/ionic/issues/19522)
### Performance Improvements
* **ios:** move content to stacking context while preserving position: fixed behavior ([#22489](https://github.com/ionic-team/ionic/issues/22489)) ([d77a9d5](https://github.com/ionic-team/ionic/commit/d77a9d57ec02c69df43ec2a286eea674a85cae36)), closes [#22473](https://github.com/ionic-team/ionic/issues/22473)
## [5.4.4](https://github.com/ionic-team/ionic/compare/v5.4.3...v5.4.4) (2020-11-12)
### Bug Fixes
* **build:** add missing es5 output ([228d349](https://github.com/ionic-team/ionic/commit/228d349c6e29b62cbfee5d5502883682cfa5032f))
## [5.4.3](https://github.com/ionic-team/ionic/compare/v5.4.2...v5.4.3) (2020-11-06)
### Bug Fixes
* **all** add missing vendor prefixes to css ([0989ea5](https://github.com/ionic-team/ionic/commit/0989ea5ac897f528e8fce5434861ca080b9b4a56))
## [5.4.2](https://github.com/ionic-team/ionic/compare/v5.4.1...v5.4.2) (2020-11-05)
### Bug Fixes
* **alert:** correctly position alert when keyboard is open ([#22425](https://github.com/ionic-team/ionic/issues/22425)) ([9752cd6](https://github.com/ionic-team/ionic/commit/9752cd6371bc4a720e45871161e389e4a9ad8e8f))
* **ios:** contenteditable elements are now selectable on iOS ([#22404](https://github.com/ionic-team/ionic/issues/22404)) ([023fb18](https://github.com/ionic-team/ionic/commit/023fb1841259a61b361066ca369aeffd488efa3f)), closes [#18368](https://github.com/ionic-team/ionic/issues/18368)
* **item:** only add click event listener to items with inputs ([#22352](https://github.com/ionic-team/ionic/issues/22352)) ([9659ad6](https://github.com/ionic-team/ionic/commit/9659ad63349d5123ca2bd2548a43e37d5ee817e7)), closes [#22011](https://github.com/ionic-team/ionic/issues/22011)
* **range:** gesture is now properly re-created on connectedCallback ([#22407](https://github.com/ionic-team/ionic/issues/22407)) ([2fea36f](https://github.com/ionic-team/ionic/commit/2fea36fc98f772443a6560a9491f2f0e574366d1)), closes [#22335](https://github.com/ionic-team/ionic/issues/22335)
* **refresher:** work properly in modal by waiting for content to be ready ([#22390](https://github.com/ionic-team/ionic/issues/22390)) ([91d0414](https://github.com/ionic-team/ionic/commit/91d041485cb3565fa81fea24c1811e48108f277a)), closes [#22256](https://github.com/ionic-team/ionic/issues/22256)
* **segment-button:** color property is now reactive if previously undefined ([#22405](https://github.com/ionic-team/ionic/issues/22405)) ([04161c9](https://github.com/ionic-team/ionic/commit/04161c9512ed8e965c93698d7f5501a21485052f)), closes [#20831](https://github.com/ionic-team/ionic/issues/20831)
* **vue:** correctly switch tabs after going back ([#22309](https://github.com/ionic-team/ionic/issues/22309)) ([daf6a92](https://github.com/ionic-team/ionic/commit/daf6a92127d36c20f3445f83bd7bd3e739bb1b27)), closes [#22307](https://github.com/ionic-team/ionic/issues/22307)
* **vue:** ensure view is updated correctly when replacing a route outside of a nav guard ([#22429](https://github.com/ionic-team/ionic/issues/22429)) ([5a4d0c0](https://github.com/ionic-team/ionic/commit/5a4d0c0217ce93f98364bdd4d8d163679f82a6b3)), closes [#22412](https://github.com/ionic-team/ionic/issues/22412)
### Performance Improvements
* **ios:** move content to stacking context to improve scrolling performance on iOS devices ([#22180](https://github.com/ionic-team/ionic/issues/22180)) ([9f44966](https://github.com/ionic-team/ionic/commit/9f44966d8572a27d8296b38ae4f3e689c76c2e44))
## [5.4.1](https://github.com/ionic-team/ionic/compare/v5.4.0...v5.4.1) (2020-10-22)
### Bug Fixes
* **select:** properly align label with select in item in MD mode ([#22330](https://github.com/ionic-team/ionic/issues/22330)) ([1a2e532](https://github.com/ionic-team/ionic/commit/1a2e5322fb7ac0dd6bd3d0705b8e32f9d3649bfc)), closes [#19887](https://github.com/ionic-team/ionic/issues/19887)
* **vue:** going back with query params now goes to correct view ([#22350](https://github.com/ionic-team/ionic/issues/22350)) ([561a4ac](https://github.com/ionic-team/ionic/commit/561a4ac535432873860c3d0a4ac60481929d9089)), closes [#22324](https://github.com/ionic-team/ionic/issues/22324)
* **vue:** improve compatibility with route guards ([#22371](https://github.com/ionic-team/ionic/issues/22371)) ([31f9bc8](https://github.com/ionic-team/ionic/commit/31f9bc81d6d0fa81f9abe20172bb606651a2d75d)), closes [#22344](https://github.com/ionic-team/ionic/issues/22344)
* **vue:** improve handling of parameterized urls ([#22360](https://github.com/ionic-team/ionic/issues/22360)) ([6fad0fe](https://github.com/ionic-team/ionic/commit/6fad0fe42814cde1126e6df264b99c069849c87a)), closes [#22359](https://github.com/ionic-team/ionic/issues/22359)
* **vue:** lifecycle events are now fired in component context ([#22348](https://github.com/ionic-team/ionic/issues/22348)) ([bcef804](https://github.com/ionic-team/ionic/commit/bcef804deac4dea27def475460aff4cdf0d7d2fc)), closes [#22338](https://github.com/ionic-team/ionic/issues/22338)
# [5.4.0 Sulfur](https://github.com/ionic-team/ionic/compare/v5.3.0...v5.4.0) (2020-10-15)
> This is the first stable release of Ionic Vue.
Enjoy the Vue! :tada:
### New to Ionic Vue?
Check out our [Quickstart Guide](https://ionicframework.com/docs/vue/quickstart) to get up and running. Then be sure to check out our [Building Your First App Guide](https://ionicframework.com/docs/vue/your-first-app) to learn how build a cross platform Ionic Vue application from start to finish!
# [5.4.0-rc.3](https://github.com/ionic-team/ionic/compare/v5.4.0-rc.2...v5.4.0-rc.3) (2020-10-14)
> This version is dedicated to our upcoming Ionic Vue release.
### Bug Fixes
* **vue:** ion-page component is now properly shown with HMR ([#22319](https://github.com/ionic-team/ionic/issues/22319)) ([c5ab562](https://github.com/ionic-team/ionic/commit/c5ab562eaa098717407e6b3e8139abd2112246a2))
### Upgrade Steps
```
npm install @ionic/vue@5.4.0-rc.3 @ionic/vue-router@5.4.0-rc.3 --save-exact
```
### New to Ionic Vue?
Check out our [Quickstart Guide](https://ionicframework.com/docs/vue/quickstart) to get up and running. Then be sure to check out our [Building Your First App Guide](https://ionicframework.com/docs/vue/your-first-app) to learn how build a cross platform Ionic Vue application from start to finish!
# [5.4.0-rc.2](https://github.com/ionic-team/ionic/compare/v5.4.0-rc.1...v5.4.0-rc.2) (2020-10-13)
> This version is dedicated to our upcoming Ionic Vue release.
### Bug Fixes
* **vue:** do not hide page content when using ion-page in non-routing contexts ([#22302](https://github.com/ionic-team/ionic/issues/22302)) ([fff82d0](https://github.com/ionic-team/ionic/commit/fff82d0bdcd850e7c70947b39d554e88c4cdfd1e)), closes [#22300](https://github.com/ionic-team/ionic/issues/22300)
* **vue:** going back from tabs page to a non-tabs page now selects correct page ([#22275](https://github.com/ionic-team/ionic/issues/22275)) ([b06ae16](https://github.com/ionic-team/ionic/commit/b06ae165912cbab811fe4a3c35b4e2b3fe0b425b)), closes [#22258](https://github.com/ionic-team/ionic/issues/22258)
* **vue:** improve swipe to go back reliability ([#22288](https://github.com/ionic-team/ionic/issues/22288)) ([c74fd41](https://github.com/ionic-team/ionic/commit/c74fd4147b57e6b11c22dffdf6355568a763f30a)), closes [#22237](https://github.com/ionic-team/ionic/issues/22237)
* **vue:** modal, popover, and nav are now created within application context ([#22282](https://github.com/ionic-team/ionic/issues/22282)) ([6026c65](https://github.com/ionic-team/ionic/commit/6026c65b1ae80af0f8604e7a3bcb220153267955)), closes [#22079](https://github.com/ionic-team/ionic/issues/22079)
* **vue:** pages now render in correct outlet when using multiple nested outlets ([#22301](https://github.com/ionic-team/ionic/issues/22301)) ([52f655c](https://github.com/ionic-team/ionic/commit/52f655c9d40988cac36f88c88f24195b3f64c431)), closes [#22286](https://github.com/ionic-team/ionic/issues/22286)
### Upgrade Steps
```
npm install @ionic/vue@5.4.0-rc.2 @ionic/vue-router@5.4.0-rc.2 --save-exact
```
### New to Ionic Vue?
Check out our [Quickstart Guide](https://ionicframework.com/docs/vue/quickstart) to get up and running. Then be sure to check out our [Building Your First App Guide](https://ionicframework.com/docs/vue/your-first-app) to learn how build a cross platform Ionic Vue application from start to finish!
# [5.4.0-rc.1](https://github.com/ionic-team/ionic/compare/v5.3.5...v5.4.0-rc.1) (2020-10-08)
> This version is dedicated to our upcoming Ionic Vue release.
### Bug Fixes
* **vue:** correctly handle query params ([#22253](https://github.com/ionic-team/ionic/issues/22253)) ([6849dd3](https://github.com/ionic-team/ionic/commit/6849dd3483fb90aac1ff19834390a652c59a74de)), closes [#22229](https://github.com/ionic-team/ionic/issues/22229)
* **vue:** correctly show ion-back-button when going back ([#22260](https://github.com/ionic-team/ionic/issues/22260)) ([39d2530](https://github.com/ionic-team/ionic/commit/39d2530427b1cd86975fc95ab2c8da9f4b0b27b3)), closes [#22217](https://github.com/ionic-team/ionic/issues/22217)
* **vue:** hide layout shift on ion-page components ([#22254](https://github.com/ionic-team/ionic/issues/22254)) ([2bad1bb](https://github.com/ionic-team/ionic/commit/2bad1bb82e0fa3fe9e3db54403565d210f636120)), closes [#22052](https://github.com/ionic-team/ionic/issues/22052)
* **vue:** ion-tab-bar no longer throws undefined error when re-creating tabs ([#22261](https://github.com/ionic-team/ionic/issues/22261)) ([d746561](https://github.com/ionic-team/ionic/commit/d746561ea29e61db2cfb55d2765b5548fd8b5a78)), closes [#22255](https://github.com/ionic-team/ionic/issues/22255)
### Upgrade Steps
```
npm install @ionic/vue@5.4.0-rc.1 @ionic/vue-router@5.4.0-rc.1 --save-exact
```
### New to Ionic Vue?
Check out our [Quickstart Guide](https://ionicframework.com/docs/vue/quickstart) to get up and running. Then be sure to check out our [Building Your First App Guide](https://ionicframework.com/docs/vue/your-first-app) to learn how build a cross platform Ionic Vue application from start to finish!
## [5.3.5](https://github.com/ionic-team/ionic/compare/v5.3.4...v5.3.5) (2020-10-07)
### Bug Fixes
* **button:** allow any element type to use the "icon-only" slot ([#22168](https://github.com/ionic-team/ionic/issues/22168)) ([c454c84](https://github.com/ionic-team/ionic/commit/c454c84ef46322143467600334a0263d4e7df6cb))
* **datetime:** do not set ampm when the column doesn't exist ([#22220](https://github.com/ionic-team/ionic/issues/22220)) ([18fb885](https://github.com/ionic-team/ionic/commit/18fb8855e0c45fe65843b33811812c51c74de90f)), closes [#22149](https://github.com/ionic-team/ionic/issues/22149)
* **datetime:** remove the automatic switching from am to pm ([#22207](https://github.com/ionic-team/ionic/issues/22207)) ([f81d18c](https://github.com/ionic-team/ionic/commit/f81d18c6f9f1bce056afda1cac4cf6d6ace0a7ca)), closes [#18924](https://github.com/ionic-team/ionic/issues/18924) [#22171](https://github.com/ionic-team/ionic/issues/22171) [#22199](https://github.com/ionic-team/ionic/issues/22199)
* **item:** properly align datetime and select with fixed or no labels ([#22221](https://github.com/ionic-team/ionic/issues/22221)) ([f42c688](https://github.com/ionic-team/ionic/commit/f42c688f4630e3dc5d10b947e7f2bee9d5967d8c)), closes [#18773](https://github.com/ionic-team/ionic/issues/18773) [#18761](https://github.com/ionic-team/ionic/issues/18761) [#18779](https://github.com/ionic-team/ionic/issues/18779)
* **label:** keep color when focused on a floating or stacked label ([#18576](https://github.com/ionic-team/ionic/issues/18576)) ([992580a](https://github.com/ionic-team/ionic/commit/992580a3830321bdf9591681ebe38e823205389d)), closes [#18531](https://github.com/ionic-team/ionic/issues/18531)
* **select:** do not close popover or set value when switching with arrow keys ([#22210](https://github.com/ionic-team/ionic/issues/22210)) ([1878c8e](https://github.com/ionic-team/ionic/commit/1878c8e7e01c02f06bdc5f1562af0d45531539cf)), closes [#22179](https://github.com/ionic-team/ionic/issues/22179)
## [5.3.4](https://github.com/ionic-team/ionic/compare/v5.3.3...v5.3.4) (2020-09-25)
### Bug Fixes
* **alert:** follow accessibility guidelines outlined by wai-aria ([#22159](https://github.com/ionic-team/ionic/issues/22159)) ([e9b2cc8](https://github.com/ionic-team/ionic/commit/e9b2cc8453f5e1c45d44397df738f60ea5b32efd)), closes [#21744](https://github.com/ionic-team/ionic/issues/21744)
* **overlays:** return focus to presenting element after dismissal ([#22167](https://github.com/ionic-team/ionic/issues/22167)) ([cc45ad8](https://github.com/ionic-team/ionic/commit/cc45ad815c002c5d890f2e105c546b4c3b3a58c0)), closes [#21768](https://github.com/ionic-team/ionic/issues/21768)
* **picker-column:** add cancelable check to avoid intervention error in chrome ([#22140](https://github.com/ionic-team/ionic/issues/22140)) ([a24a041](https://github.com/ionic-team/ionic/commit/a24a041064fd9ce6ca161d3522083d50e585e9dd)), closes [#22137](https://github.com/ionic-team/ionic/issues/22137)
* **radio:** follow accessibility guidelines outlined by wai-aria ([#22113](https://github.com/ionic-team/ionic/issues/22113)) ([ea0e049](https://github.com/ionic-team/ionic/commit/ea0e0499e24865faad3d11f50f7037645f6cdcc8)), closes [#21743](https://github.com/ionic-team/ionic/issues/21743)
* **reorder:** allow click event propagation when reorder group is disabled ([#21947](https://github.com/ionic-team/ionic/issues/21947)) ([baafe08](https://github.com/ionic-team/ionic/commit/baafe08927b7b858170496605781e6fa682e0147)), closes [#21017](https://github.com/ionic-team/ionic/issues/21017)
* **segment:** do not allow text selection on desktop ([#22158](https://github.com/ionic-team/ionic/issues/22158)) ([1526bdf](https://github.com/ionic-team/ionic/commit/1526bdfb492c1fa8d71f8a1af8cd97abd9e62642))
### Performance Improvements
* **segment:** improve scrolling performance on ios when using segment ([#22110](https://github.com/ionic-team/ionic/issues/22110)) ([68afc49](https://github.com/ionic-team/ionic/commit/68afc49e9ed27acffb0b765b7be6b03e8574850d)), closes [#22095](https://github.com/ionic-team/ionic/issues/22095)
## [5.3.3](https://github.com/ionic-team/ionic/compare/v5.3.2...v5.3.3) (2020-09-17)
### Bug Fixes
* **datetime:** do not reset to am when changing hour and pm is set ([#21997](https://github.com/ionic-team/ionic/issues/21997)) ([8b85fe0](https://github.com/ionic-team/ionic/commit/8b85fe0d9eea39adfdcf790bf00d8ef91d5edbe7)), closes [#19175](https://github.com/ionic-team/ionic/issues/19175) [#19260](https://github.com/ionic-team/ionic/issues/19260) [#20026](https://github.com/ionic-team/ionic/issues/20026) [#16630](https://github.com/ionic-team/ionic/issues/16630)
* **input:** only focus the first input / textarea when clicking on the parent item ([#22049](https://github.com/ionic-team/ionic/issues/22049)) ([99f2532](https://github.com/ionic-team/ionic/commit/99f2532ee174da79e2b6a462cfa124673edc1170)), closes [#22037](https://github.com/ionic-team/ionic/issues/22037) [#22032](https://github.com/ionic-team/ionic/issues/22032)
* **react:** Keep a hold of previous routes when doing a redirect, closes [#22053](https://github.com/ionic-team/ionic/issues/22053) ([74af3cb](https://github.com/ionic-team/ionic/commit/74af3cb50b089a6bd60d515158e03b18b86455b8))
* **react:** redirect routes should unmount leaving component, fixes [#22022](https://github.com/ionic-team/ionic/issues/22022) ([#22029](https://github.com/ionic-team/ionic/issues/22029)) ([b11e06c](https://github.com/ionic-team/ionic/commit/b11e06cec1d3c28bab9f29185fe2c3a2975b092f))
* **textarea:** do not generate duplicate IDs between ion-input and ion-textarea ([#22074](https://github.com/ionic-team/ionic/issues/22074)) ([c72c7ff](https://github.com/ionic-team/ionic/commit/c72c7ffa983af8885dd93f9adfcb3f2af232d2d9)), closes [#21542](https://github.com/ionic-team/ionic/issues/21542)
## [5.3.2](https://github.com/ionic-team/ionic/compare/v5.3.1...v5.3.2) (2020-08-27)
### Bug Fixes
* **input:** improve reliability of scroll assist when accessory bar is enabled ([#21936](https://github.com/ionic-team/ionic/issues/21936)) ([22477fb](https://github.com/ionic-team/ionic/commit/22477fb9bf7c0637aa5c8d0aab34c8ccc521b0b9)), closes [#21912](https://github.com/ionic-team/ionic/issues/21912)
* **input:** properly focus the input when clicking the item padding in WebKit ([#21930](https://github.com/ionic-team/ionic/issues/21930)) ([e4964ff](https://github.com/ionic-team/ionic/commit/e4964ff77b317c92b201cf7c265787b55bdde4d4)), closes [#21509](https://github.com/ionic-team/ionic/issues/21509)
* **input:** remain focused in the input after pressing the clear button ([#21985](https://github.com/ionic-team/ionic/issues/21985)) ([6878fb9](https://github.com/ionic-team/ionic/commit/6878fb9eb99c17908f5630019efaa762b5b006e0)), closes [#21549](https://github.com/ionic-team/ionic/issues/21549)
* **label:** use translateY so input caret shows up due to webkit issue ([#21949](https://github.com/ionic-team/ionic/issues/21949)) ([00eac33](https://github.com/ionic-team/ionic/commit/00eac33053f49dbebf22ef95fddcb66570ed117a)), closes [#21943](https://github.com/ionic-team/ionic/issues/21943)
* **overlays:** prevent focus from being stolen when presenting another overlay from within a modal ([#21856](https://github.com/ionic-team/ionic/issues/21856)) ([5c177d7](https://github.com/ionic-team/ionic/commit/5c177d756f7755e766d5b619d49825c4799aee47)), closes [#21840](https://github.com/ionic-team/ionic/issues/21840)
* **range:** properly display stacked labels in an item with a range ([#21944](https://github.com/ionic-team/ionic/issues/21944)) ([9f4b01e](https://github.com/ionic-team/ionic/commit/9f4b01e17fd2f5e742d32bc9e080b6b394c43d37)), closes [#21625](https://github.com/ionic-team/ionic/issues/21625)
* **react:** export correct animation types ([#21950](https://github.com/ionic-team/ionic/issues/21950)) ([36e4bf7](https://github.com/ionic-team/ionic/commit/36e4bf7dd76e396f910d28445566b5503cc84c8c))
* **react:** removed exporting of ionRenderToString to decrease bundle size, closes [#21917](https://github.com/ionic-team/ionic/issues/21917) ([#21928](https://github.com/ionic-team/ionic/issues/21928)) ([434befe](https://github.com/ionic-team/ionic/commit/434befea5f31aa599ee5b1b7edf29238912c23d9))
* **react:** setting active tab properly on mount, closes [#21830](https://github.com/ionic-team/ionic/issues/21830) ([#21833](https://github.com/ionic-team/ionic/issues/21833)) ([f58424f](https://github.com/ionic-team/ionic/commit/f58424f62596b9eb82bebb8e07c211e1725c025a))
* **react:** fix tab currentHref when changing tabs, closes [#21834](https://github.com/ionic-team/ionic/issues/21834) ([#21835](https://github.com/ionic-team/ionic/issues/21835)) ([74468ab](https://github.com/ionic-team/ionic/commit/74468ab7972b174ba85bf239306c27080f253a4a))
## [5.3.1](https://github.com/ionic-team/ionic/compare/v5.3.0...v5.3.1) (2020-07-27)
### Bug Fixes
* **react:** properly extend HTMLElement for tabs ([bfddb17](https://github.com/ionic-team/ionic/commit/bfddb170659224d0f826762744dfe44a85813d36)), closes [#21803](https://github.com/ionic-team/ionic/issues/21803)
# [5.3.0 Phosphorus](https://github.com/ionic-team/ionic/compare/v5.2.3...v5.3.0) (2020-07-23)
### Bug Fixes
* **angular:** per-page animations now work with swipe to go back ([#21706](https://github.com/ionic-team/ionic/issues/21706)) ([2664587](https://github.com/ionic-team/ionic/commit/2664587749e45100a04f70796733de162b26cdf7)), closes [#21692](https://github.com/ionic-team/ionic/issues/21692)
* **datetime:** remove unneeded combobox role ([#21708](https://github.com/ionic-team/ionic/issues/21708)) ([f00ad8a](https://github.com/ionic-team/ionic/commit/f00ad8a8357ccd7fe85631dad0c841f2d4c72487))
* **input:** clear button can now be tabbed to ([#21633](https://github.com/ionic-team/ionic/issues/21633)) ([1dcd9de](https://github.com/ionic-team/ionic/commit/1dcd9de50ae16bfa102e98120a022de5b0287baa))
* **ios:** improve scroll assist reliability on password inputs ([#21703](https://github.com/ionic-team/ionic/issues/21703)) ([3cbf9e7](https://github.com/ionic-team/ionic/commit/3cbf9e7c4c225d6b02237d8ea8f16fb924ba360e)), closes [#21688](https://github.com/ionic-team/ionic/issues/21688)
* **keyboard:** keyboard events now consistently fire on android ([#21741](https://github.com/ionic-team/ionic/issues/21741)) ([020f3cc](https://github.com/ionic-team/ionic/commit/020f3cc56cb6dac23dd8766a3802422500b510e2)), closes [#21734](https://github.com/ionic-team/ionic/issues/21734)
* **nav:** insertPages method correctly inserts multiple pages with props ([#21725](https://github.com/ionic-team/ionic/issues/21725)) ([eb592b8](https://github.com/ionic-team/ionic/commit/eb592b8917b8a7412d8c346f41b47d3b79002b95)), closes [#21724](https://github.com/ionic-team/ionic/issues/21724)
* **overlays:** trap focus inside overlay components except toast ([#21716](https://github.com/ionic-team/ionic/issues/21716)) ([fff4aec](https://github.com/ionic-team/ionic/commit/fff4aec6cfbd48566594a05f4af57dd0578977a8)), closes [#21647](https://github.com/ionic-team/ionic/issues/21647)
* **segment-button:** allow min-width to be overridden ([#21722](https://github.com/ionic-team/ionic/issues/21722)) ([88f1828](https://github.com/ionic-team/ionic/commit/88f1828bd8f6b9a1c1f3dcb220d93067bed7f404)), closes [#21105](https://github.com/ionic-team/ionic/issues/21105)
* **title:** allow overriding of large title transform-origin ([#21770](https://github.com/ionic-team/ionic/issues/21770)) ([dbe6853](https://github.com/ionic-team/ionic/commit/dbe6853884bd76c3d8e229cd58e1571d9b3a7249)), closes [#21761](https://github.com/ionic-team/ionic/issues/21761)
* **virtual-scroll:** properly calculate top offset when nested ([#21581](https://github.com/ionic-team/ionic/issues/21581)) ([d297ecb](https://github.com/ionic-team/ionic/commit/d297ecb87ad3e1c8f0988f0571a475081ce368f8))
### Features
* **card:** expose global card css variables ([#21756](https://github.com/ionic-team/ionic/issues/21756)) ([096eef4](https://github.com/ionic-team/ionic/commit/096eef4a79c2d05c37eb224466c6d7d512d2be20)), closes [#21694](https://github.com/ionic-team/ionic/issues/21694)
* **input:** accept datetime-local, month, and week type values ([#21758](https://github.com/ionic-team/ionic/issues/21758)) ([fa93dff](https://github.com/ionic-team/ionic/commit/fa93dffdb4f350e8db8acc7f06b06761974eea8e)), closes [#21757](https://github.com/ionic-team/ionic/issues/21757)
* **input, textarea:** expose native events for ionBlur and ionFocus ([#21777](https://github.com/ionic-team/ionic/issues/21777)) ([a625c83](https://github.com/ionic-team/ionic/commit/a625c837a60abc07ad71c696196a89f1a25a4c27)), closes [#17363](https://github.com/ionic-team/ionic/issues/17363)
* **react:** add custom history to IonReactRouter ([#21775](https://github.com/ionic-team/ionic/issues/21775)) ([d4a5fbd](https://github.com/ionic-team/ionic/commit/d4a5fbd955e8ecccba8b77491943d81fdf5a5ef4)), closes [#20297](https://github.com/ionic-team/ionic/issues/20297)
* **react:** add new react router ([#21693](https://github.com/ionic-team/ionic/issues/21693)) ([c171ccb](https://github.com/ionic-team/ionic/commit/c171ccbd37c1ee4b4934758a3a759170ff357cb2))
* **router:** add navigation hooks ([#21709](https://github.com/ionic-team/ionic/issues/21709)) ([77464ef](https://github.com/ionic-team/ionic/commit/77464ef21aaaa5afa7a02e5417f3ec295b240601))
* **segment-button, toast:** expose additional shadow parts ([#21532](https://github.com/ionic-team/ionic/issues/21532)) ([a5e4669](https://github.com/ionic-team/ionic/commit/a5e4669c4bcbcb2cdd605ed17c35e42438bd5596))
* **select:** add optional generic typings ([#21514](https://github.com/ionic-team/ionic/issues/21514)) ([7c2d0c9](https://github.com/ionic-team/ionic/commit/7c2d0c981ab91930478c4b76220ce4ec4ed4e471))
## [5.2.3](https://github.com/ionic-team/ionic/compare/v5.2.2...v5.2.3) (2020-07-01)
### Bug Fixes
* **angular:** expose createAnimation in addition to AnimationController ([#21616](https://github.com/ionic-team/ionic/issues/21616)) ([a5b3750](https://github.com/ionic-team/ionic/commit/a5b3750ee2a7c005f80f8453b03c67dd1a5622ca)), closes [#21615](https://github.com/ionic-team/ionic/issues/21615)
* **select:** change role to listbox ([#21609](https://github.com/ionic-team/ionic/issues/21609)) ([8c79e2c](https://github.com/ionic-team/ionic/commit/8c79e2c5b58ad562967f2d559c6b548e57536936))
* **slides:** enable keyboard integration ([#21608](https://github.com/ionic-team/ionic/issues/21608)) ([26674f1](https://github.com/ionic-team/ionic/commit/26674f1dfa8c9a28f5525f1b16070e8ec494c232)), closes [#21554](https://github.com/ionic-team/ionic/issues/21554)
* **textarea:** add aria-labelledby to native textarea ([#21606](https://github.com/ionic-team/ionic/issues/21606)) ([88f23b1](https://github.com/ionic-team/ionic/commit/88f23b1626eb400336f2f52a3e0d34ac3c161e64)), closes [#21600](https://github.com/ionic-team/ionic/issues/21600)
## [5.2.2](https://github.com/ionic-team/ionic/compare/v5.2.1...v5.2.2) (2020-06-17)
### Bug Fixes
* **action-sheet, alert:** resolve double click issue when running in iOS mode on chrome ([#21506](https://github.com/ionic-team/ionic/issues/21506)) ([bcccc21](https://github.com/ionic-team/ionic/commit/bcccc217b8833a284a1781e287db5e46043b3548)), closes [#21503](https://github.com/ionic-team/ionic/issues/21503)
* **angular:** fix issue where navAnimation was being incorrectly overridden ([#21508](https://github.com/ionic-team/ionic/issues/21508)) ([e968bd0](https://github.com/ionic-team/ionic/commit/e968bd029a4fb37b4001d96a490c6091a948785a)), closes [#21495](https://github.com/ionic-team/ionic/issues/21495)
* **input:** add aria-label to clear button ([#21538](https://github.com/ionic-team/ionic/issues/21538)) ([d8b377f](https://github.com/ionic-team/ionic/commit/d8b377ffeb88eaae23b33eadeae5c8e54e1bc77c)), closes [#21537](https://github.com/ionic-team/ionic/issues/21537)
* **ios:** respect toolbar opacity when doing nav transition ([#21512](https://github.com/ionic-team/ionic/issues/21512)) ([24cfdc3](https://github.com/ionic-team/ionic/commit/24cfdc308f63b7a55969ac58806eafd67116b017))
* **segment:** ensure checked classes get set after not having a value ([#21547](https://github.com/ionic-team/ionic/issues/21547)) ([17308f2](https://github.com/ionic-team/ionic/commit/17308f247f8750029ece39548c9f457e15326189)), closes [#21546](https://github.com/ionic-team/ionic/issues/21546)
## [5.2.1](https://github.com/ionic-team/ionic/compare/v5.2.0...v5.2.1) (2020-06-11)
### Bug Fixes
* **angular:** resolve error when not using ngModel on components ([4083e32](https://github.com/ionic-team/ionic/commit/4083e32e103db71f6db86ed1ecd398fda407c28b))
# [5.2.0 Silicon](https://github.com/ionic-team/ionic/compare/v5.1.1...v5.2.0) (2020-06-10)
### Bug Fixes
* **action-sheet, toast:** allow button handler to return `Promise<void>` ([#21259](https://github.com/ionic-team/ionic/issues/21259)) ([7703da2](https://github.com/ionic-team/ionic/commit/7703da28f8181b02390b97a7d4d02df99b2ad34c))
* **angular:** patch FormControl methods to properly sync Ionic form classes ([#21429](https://github.com/ionic-team/ionic/issues/21429)) ([e95b481](https://github.com/ionic-team/ionic/commit/e95b481a53191582bca635f322ad07eadbd62d64))
* **datetime:** ensure year-only values are not affected by timezone when parsing ([#21309](https://github.com/ionic-team/ionic/issues/21309)) ([3937101](https://github.com/ionic-team/ionic/commit/3937101e5c2b181a6b7926eb8386c22b0f887716))
* **header:** large title transition now works on older versions of iOS ([#21339](https://github.com/ionic-team/ionic/issues/21339)) ([2dac12c](https://github.com/ionic-team/ionic/commit/2dac12c577e0c7a5310857389dbda2b2b3dfadd1))
* **img:** use setTimeout fallback on older versions of chrome ([#21358](https://github.com/ionic-team/ionic/issues/21358)) ([0bf9449](https://github.com/ionic-team/ionic/commit/0bf9449ee1f9b2498e35f61511cb3e018814c6ca))
* **ios:** add haptic drag gesture for action sheet and alert components ([#21060](https://github.com/ionic-team/ionic/issues/21060)) ([33be1f0](https://github.com/ionic-team/ionic/commit/33be1f061ebbe27ee22e357c394f112af42ec360))
* **item:** inherit align-items from parent item ([#19278](https://github.com/ionic-team/ionic/issues/19278)) ([882f8fe](https://github.com/ionic-team/ionic/commit/882f8fef07dfb6ebda17ca09046d1af281075098)), closes [#18703](https://github.com/ionic-team/ionic/issues/18703)
* **item:** input-wrapper now inherits overflow ([#21282](https://github.com/ionic-team/ionic/issues/21282)) ([29d208d](https://github.com/ionic-team/ionic/commit/29d208de88f340e216e22badb6366bba4eda8bfb))
* **menu-button:** screen readers now properly announce menu button ([#21324](https://github.com/ionic-team/ionic/issues/21324)) ([eaf4fb6](https://github.com/ionic-team/ionic/commit/eaf4fb6b2a6d68f5c3d8d49ef41b4885f096070d))
* **modal:** card style modal no longer gets stuck when swiping quickly ([#21224](https://github.com/ionic-team/ionic/issues/21224)) ([448dfa0](https://github.com/ionic-team/ionic/commit/448dfa0a6955008ce4dc73354f5b8319ae1a1cc2))
* **modal:** set card-style modal height using the --height css variable ([#21453](https://github.com/ionic-team/ionic/issues/21453)) ([a4f0bdb](https://github.com/ionic-team/ionic/commit/a4f0bdb4c3ceeeaf902d520e9aa72e04a6ec2302))
* **reorder-group:** revert item to original position when passing false to complete ([#21396](https://github.com/ionic-team/ionic/issues/21396)) ([5f2001c](https://github.com/ionic-team/ionic/commit/5f2001c43c0846ec8973cbb8eb5662976ba7e31a)), closes [#19128](https://github.com/ionic-team/ionic/issues/19128)
* **router:** use correct nav transition when going back ([#21301](https://github.com/ionic-team/ionic/issues/21301)) ([c8db0d5](https://github.com/ionic-team/ionic/commit/c8db0d5eeba6f10d1492e95e6df6b4d871d43400))
* **scroll-assist:** improve scroll detection accuracy ([#21416](https://github.com/ionic-team/ionic/issues/21416)) ([137c49d](https://github.com/ionic-team/ionic/commit/137c49d70be45e1b0ee73d41fed6e9d69a2caa32))
* **slides:** update Swiper dependency to resolve error when doing SSR ([#21350](https://github.com/ionic-team/ionic/issues/21350)) ([3290604](https://github.com/ionic-team/ionic/commit/32906048a491961ec7340ba2e085807ea8a9c118))
* **textarea:** native textarea inherits max/min width and height ([#21333](https://github.com/ionic-team/ionic/issues/21333)) ([2377480](https://github.com/ionic-team/ionic/commit/237748049c7644ae8a7a74101ece5cfd7a160470))
### Features
* **alert:** add destructive role to alert buttons ([#21269](https://github.com/ionic-team/ionic/issues/21269)) ([e53f024](https://github.com/ionic-team/ionic/commit/e53f0241e2bf91461c55097fde271ae85ca644ea))
* **alert:** add support for custom input attributes ([#21365](https://github.com/ionic-team/ionic/issues/21365)) ([1ed8169](https://github.com/ionic-team/ionic/commit/1ed81693f225b6801a0897ef1a8314999c122484))
* **all:** add all autocomplete values to input and searchbar ([#21297](https://github.com/ionic-team/ionic/issues/21297)) ([4fd7c0c](https://github.com/ionic-team/ionic/commit/4fd7c0cc5a6c97100fa380e4ff1be0bb84c7006b))
* **all:** add optional generic typings for overlay component methods ([#21393](https://github.com/ionic-team/ionic/issues/21393)) ([5bf83b8](https://github.com/ionic-team/ionic/commit/5bf83b80d7d2749719dd1a280ae8e205fbc4b2a9))
* **all:** add shadow parts to missing components ([#21436](https://github.com/ionic-team/ionic/issues/21436)) ([17375d2](https://github.com/ionic-team/ionic/commit/17375d232500b47ef1cacd7c028c38990d307984))
* **all:** add support for configuring animations on a per-page basis ([#21433](https://github.com/ionic-team/ionic/issues/21433)) ([f34d375](https://github.com/ionic-team/ionic/commit/f34d3752e3462c9d81487fc86af5ec185cc3d1e3))
* **angular:** expose activatedView ([#21302](https://github.com/ionic-team/ionic/issues/21302)) ([829a0d9](https://github.com/ionic-team/ionic/commit/829a0d9be5a20c5fc96b5c5f18fedc4eb5e5b9ec))
* **angular:** expose getPlatforms and isPlatform ([#21308](https://github.com/ionic-team/ionic/issues/21308)) ([4af54a2](https://github.com/ionic-team/ionic/commit/4af54a2fea5d9cef843b1ebce849fb4a5c206f0b))
* **angular:** add strongly typed Ionic lifecycle hooks ([#18044](https://github.com/ionic-team/ionic/issues/18044)) ([53fc8e3](https://github.com/ionic-team/ionic/commit/53fc8e37c89cea793d0e00246d52805166730108)), closes [#18043](https://github.com/ionic-team/ionic/issues/18043)
* **fab-button:** add close icon font size css variable ([#19628](https://github.com/ionic-team/ionic/issues/19628)) ([df408f9](https://github.com/ionic-team/ionic/commit/df408f91f1aef903adaa5e635fef9bc7542e8739))
* **fab-button:** add closeIcon property ([#19626](https://github.com/ionic-team/ionic/issues/19626)) ([698e526](https://github.com/ionic-team/ionic/commit/698e526b9f882e98efc4bf160999182c645b772c))
* **select-option:** pass class from the option to the interface for individual styling ([#21304](https://github.com/ionic-team/ionic/issues/21304)) ([5285824](https://github.com/ionic-team/ionic/commit/5285824da5258ea638420fc60c50e0a19952f89c))
## [5.1.1](https://github.com/ionic-team/ionic/compare/v5.1.0...v5.1.1) (2020-05-13)
@@ -781,9 +198,9 @@ Check out our [Quickstart Guide](https://ionicframework.com/docs/vue/quickstart)
* **modal:** presenting multiple card-style modals now adds border radius properly ([#20476](https://github.com/ionic-team/ionic/issues/20476)) ([abf594a](https://github.com/ionic-team/ionic/commit/abf594aa611562a76e3baecfa38456d41a1410f3)), closes [#20475](https://github.com/ionic-team/ionic/issues/20475)
* **modal:** prevent card style modal styles from being overridden ([#20470](https://github.com/ionic-team/ionic/issues/20470)) ([86ab77a](https://github.com/ionic-team/ionic/commit/86ab77a6e2cb124510c244110fc78a4bc0654cd1)), closes [#20469](https://github.com/ionic-team/ionic/issues/20469)
* **react:** do a better job matching up route to sync ([#20446](https://github.com/ionic-team/ionic/issues/20446)) ([c0aadd6](https://github.com/ionic-team/ionic/commit/c0aadd60077e5ba379959d93006e3a9c1418263c)), closes [#20363](https://github.com/ionic-team/ionic/issues/20363)
* **react:** do not remove pages when navigating between tabs ([#20431](https://github.com/ionic-team/ionic/issues/20431)) ([b6fbe98](https://github.com/ionic-team/ionic/commit/b6fbe98812fbab5ef9e0723802605c0711581dd2)), closes [#20398](https://github.com/ionic-team/ionic/issues/20398)
* **react:** do not remove pages when navigating between tabs ([#20431](https://github.com/ionic-team/ionic/issues/20431)) ([b6fbe98](https://github.com/ionic-team/ionic/commit/b6fbe98812fbab5ef9e0723802605c0711581dd2)), closes [#20398](https://github.com/ionic-team/ionic/issues/20398)
* **react:** icons with MD set should work in browser ([#20463](https://github.com/ionic-team/ionic/issues/20463)) ([82670fe](https://github.com/ionic-team/ionic/commit/82670fe4d0592451cbc243b3008beb3f8f483c30))
* **react:** update paths of tab buttons when href changes in ion buttons ([#20480](https://github.com/ionic-team/ionic/issues/20480)) ([45d03ba](https://github.com/ionic-team/ionic/commit/45d03baf981d0e10eb1fe689908532adef2ba31d)), closes [#20321](https://github.com/ionic-team/ionic/issues/20321)
* **react:** update paths of tab buttons when href changes in ion buttons ([#20480](https://github.com/ionic-team/ionic/issues/20480)) ([45d03ba](https://github.com/ionic-team/ionic/commit/45d03baf981d0e10eb1fe689908532adef2ba31d)), closes [#20321](https://github.com/ionic-team/ionic/issues/20321)
* **searchbar:** properly align placeholder ([#20460](https://github.com/ionic-team/ionic/issues/20460)) ([4d6e15a](https://github.com/ionic-team/ionic/commit/4d6e15ab18fc894c3826b89362163256adc227f4)), closes [#20456](https://github.com/ionic-team/ionic/issues/20456)
* **segment:** border radius applies to indicator on ios ([#20541](https://github.com/ionic-team/ionic/issues/20541)) ([9b5854d](https://github.com/ionic-team/ionic/commit/9b5854d79712356f8a3772442c0cc412db09d5e0)), closes [#20539](https://github.com/ionic-team/ionic/issues/20539)
* **segment:** do not show ripple effect if disabled via config ([#20542](https://github.com/ionic-team/ionic/issues/20542)) ([7a461c5](https://github.com/ionic-team/ionic/commit/7a461c59c5d9a23de0bcdd53397f452d17251fd6)), closes [#20533](https://github.com/ionic-team/ionic/issues/20533)

View File

@@ -1,129 +1,11 @@
# Contributor Code of Conduct
# Contributor Covenant Code of Conduct
As contributors and maintainers of the Ionic project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
## Our Pledge
Communication through any of Ionic's channels (GitHub, Slack, Forum, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Ionic project to do the same.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
If any member of the community violates this code of conduct, the maintainers of the Ionic project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include the ability to:
* Demonstrate empathy and kindness towards people
* Be respectful of differing opinions, viewpoints, and experiences
* Give and gracefully accept constructive feedback
* Accept responsibility and apologize to those affected by our mistakes,
and learn from the experience
* Focus on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[contact@ionic.io](mailto:contact@ionic.io).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [hi@ionicframework.com](mailto:hi@ionicframework.com).

View File

@@ -1,9 +1,9 @@
# Ionic Framework
# Ionic
[Ionic Framework](https://ionicframework.com/) is the open-source mobile app development framework that makes it easy to
[Ionic](https://ionicframework.com/) is the open-source mobile app development framework that makes it easy to
build top quality native and progressive web apps with web technologies.
Ionic Framework is based on [Web Components](https://www.webcomponents.org/introduction) and comes with many significant performance, usability, and feature improvements over the past versions.
Ionic is based on [Web Components](https://www.webcomponents.org/introduction) and comes with many significant performance, usability, and feature improvements over the past versions.
### Packages
@@ -12,7 +12,7 @@ Ionic Framework is based on [Web Components](https://www.webcomponents.org/intro
| ------- | ------- | ------- |:-----:|
| **Core** | [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) | [![version](https://img.shields.io/npm/v/@ionic/core/latest.svg)](https://www.npmjs.com/package/@ionic/core) | [`README.md`](core/README.md)
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [![version](https://img.shields.io/npm/v/@ionic/angular/latest.svg)](https://www.npmjs.com/package/@ionic/angular) | [`README.md`](angular/README.md)
| **Vue** | [`@ionic/vue`](https://www.npmjs.com/package/@ionic/vue) | [![version](https://img.shields.io/npm/v/@ionic/vue/latest.svg)](https://www.npmjs.com/package/@ionic/vue) | [`README.md`](packages/vue/README.md)
| **Vue** | [`@ionic/vue`](https://www.npmjs.com/package/@ionic/vue) | [![version](https://img.shields.io/npm/v/@ionic/vue/latest.svg)](https://www.npmjs.com/package/@ionic/vue) | [`README.md`](vue/README.md)
| **React** | [`@ionic/react`](https://www.npmjs.com/package/@ionic/react) | [![version](https://img.shields.io/npm/v/@ionic/react/latest.svg)](https://www.npmjs.com/package/@ionic/react) | [`README.md`](packages/react/README.md)
Looking for the `ionic-angular` package? Ionic 3 has been moved to the [`ionic-v3`](https://github.com/ionic-team/ionic-v3) repo. See [Earlier Versions](#earlier-versions).
@@ -31,8 +31,6 @@ Thanks for your interest in contributing! Read up on our guidelines for
and then look through our issues with a [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
label.
Please note that this project is released with a [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/master/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
### Examples
@@ -42,7 +40,7 @@ It is the perfect starting point for learning and building your own app.
### Future Goals
As Ionic Framework components migrate to the web component standard, a goal of ours is to have Ionic Framework easily work within all of the popular frameworks.
As Ionic components migrate to the web component standard, a goal of ours is to have Ionic components easily work within all of the popular frameworks.
### Earlier Versions

1
angular/.npmrc Normal file
View File

@@ -0,0 +1 @@
package-lock=false

View File

@@ -7,6 +7,7 @@ Ionic Angular specific building blocks on top of [@ionic/core](https://www.npmjs
* [Ionic Core Components](https://www.npmjs.com/package/@ionic/core)
* [Ionic Documentation](https://ionicframework.com/docs/)
* [Ionic Worldwide Slack](http://ionicworldwide.herokuapp.com/)
* [Ionic Forum](https://forum.ionicframework.com/)
* [Ionicons](http://ionicons.com/)
* [Stencil](https://stenciljs.com/)

9097
angular/package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "5.6.9",
"version": "5.1.1",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@@ -42,7 +42,7 @@
"validate": "npm i && npm run lint && npm run test && npm run build"
},
"dependencies": {
"@ionic/core": "5.6.9",
"@ionic/core": "5.1.1",
"tslib": "^1.9.3"
},
"peerDependencies": {
@@ -67,12 +67,12 @@
"ng-packagr": "^9.1.5",
"rollup": "~1.17.0",
"rollup-plugin-node-resolve": "~5.2.0",
"rxjs": "^6.6.2",
"tsickle": "^0.39.1",
"rxjs": "^6.5.2",
"tsickle": "^0.34.0",
"tslint": "^5.12.1",
"tslint-ionic-rules": "0.0.21",
"typescript": "3.4.5",
"zone.js": "^0.11.1"
"zone.js": "^0.8.28"
},
"schematics": "./schematics/collection.json",
"ngPackage": {

View File

@@ -1,4 +1,4 @@
import resolve from '@rollup/plugin-node-resolve';
import resolve from 'rollup-plugin-node-resolve';
export default {
input: 'build/es2015/core.js',

View File

@@ -5,10 +5,16 @@ import { Config } from './providers/config';
import { IonicWindow } from './types/interfaces';
import { raf } from './util/util';
let didInitialize = false;
export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
return (): any => {
const win: IonicWindow | undefined = doc.defaultView as any;
if (win && typeof (window as any) !== 'undefined') {
if (didInitialize) {
console.warn('Ionic Angular was already initialized. Make sure IonicModule.forRoot() is just called once.');
}
didInitialize = true;
const Ionic = win.Ionic = win.Ionic || {};
Ionic.config = {

View File

@@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { Directive, ElementRef, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor, setIonicClasses } from './value-accessor';
@@ -16,8 +16,8 @@ import { ValueAccessor, setIonicClasses } from './value-accessor';
})
export class BooleanValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
constructor(el: ElementRef) {
super(el);
}
writeValue(value: any) {

View File

@@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { Directive, ElementRef, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@@ -16,8 +16,8 @@ import { ValueAccessor } from './value-accessor';
})
export class NumericValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
constructor(el: ElementRef) {
super(el);
}
@HostListener('ionChange', ['$event.target'])

View File

@@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { Directive, ElementRef, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@@ -16,8 +16,8 @@ import { ValueAccessor } from './value-accessor';
})
export class RadioValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
constructor(el: ElementRef) {
super(el);
}
@HostListener('ionSelect', ['$event.target'])

View File

@@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { Directive, ElementRef, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@@ -16,8 +16,8 @@ import { ValueAccessor } from './value-accessor';
})
export class SelectValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
constructor(el: ElementRef) {
super(el);
}
@HostListener('ionChange', ['$event.target'])

View File

@@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { Directive, ElementRef, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@@ -16,8 +16,8 @@ import { ValueAccessor } from './value-accessor';
})
export class TextValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
constructor(el: ElementRef) {
super(el);
}
@HostListener('ionChange', ['$event.target'])

View File

@@ -1,17 +1,15 @@
import { AfterViewInit, ElementRef, HostListener, Injector, OnDestroy, Type } from '@angular/core';
import { ControlValueAccessor, NgControl } from '@angular/forms';
import { Subscription } from 'rxjs';
import { ElementRef, HostListener } from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { raf } from '../../util/util';
export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDestroy {
export class ValueAccessor implements ControlValueAccessor {
private onChange: (value: any) => void = () => {/**/};
private onTouched: () => void = () => {/**/};
protected lastValue: any;
private statusChanges?: Subscription;
constructor(protected injector: Injector, protected el: ElementRef) {}
constructor(protected el: ElementRef) {}
writeValue(value: any) {
/**
@@ -54,50 +52,6 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
setDisabledState(isDisabled: boolean) {
this.el.nativeElement.disabled = isDisabled;
}
ngOnDestroy() {
if (this.statusChanges) {
this.statusChanges.unsubscribe();
}
}
ngAfterViewInit() {
let ngControl;
try {
ngControl = this.injector.get<NgControl>(NgControl as Type<NgControl>);
} catch { /* No FormControl or ngModel binding */ }
if (!ngControl) { return; }
// Listen for changes in validity, disabled, or pending states
if (ngControl.statusChanges) {
this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.el));
}
/**
* TODO Remove this in favor of https://github.com/angular/angular/issues/10887
* whenever it is implemented. Currently, Ionic's form status classes
* do not react to changes when developers manually call
* Angular form control methods such as markAsTouched.
* This results in Ionic's form status classes being out
* of sync with the ng form status classes.
* This patches the methods to manually sync
* the classes until this feature is implemented in Angular.
*/
const formControl = ngControl.control;
if (formControl) {
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
methodsToPatch.forEach(method => {
if (formControl[method]) {
const oldFn = formControl[method].bind(formControl);
formControl[method] = (...params) => {
oldFn(...params);
setIonicClasses(this.el);
};
}
});
}
}
}
export const setIonicClasses = (element: ElementRef) => {

View File

@@ -1,5 +1,4 @@
import { Directive, HostListener, Optional } from '@angular/core';
import { AnimationBuilder } from '@ionic/core';
import { Config } from '../../providers/config';
import { NavController } from '../../providers/nav-controller';
@@ -8,12 +7,11 @@ import { IonRouterOutlet } from './ion-router-outlet';
@Directive({
selector: 'ion-back-button',
inputs: ['defaultHref', 'routerAnimation'],
inputs: ['defaultHref'],
})
export class IonBackButtonDelegate {
defaultHref: string | undefined | null;
routerAnimation?: AnimationBuilder;
constructor(
@Optional() private routerOutlet: IonRouterOutlet,
@@ -29,11 +27,10 @@ export class IonBackButtonDelegate {
const defaultHref = this.defaultHref || this.config.get('backButtonDefaultHref');
if (this.routerOutlet && this.routerOutlet.canGoBack()) {
this.navCtrl.setDirection('back', undefined, undefined, this.routerAnimation);
this.routerOutlet.pop();
ev.preventDefault();
} else if (defaultHref != null) {
this.navCtrl.navigateBack(defaultHref, { animation: this.routerAnimation });
this.navCtrl.navigateBack(defaultHref);
ev.preventDefault();
}
}

View File

@@ -1,7 +1,6 @@
import { Location } from '@angular/common';
import { Attribute, 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 { componentOnReady } from '@ionic/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
@@ -52,7 +51,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this._swipeGesture = swipe;
this.nativeEl.swipeHandler = swipe ? {
canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(),
canStart: () => this.stackCtrl.canGoBack(1),
onStart: () => this.stackCtrl.startBackTransition(),
onEnd: shouldContinue => this.stackCtrl.endBackTransition(shouldContinue)
} : undefined;
@@ -97,12 +96,13 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this.activateWith(context.route, context.resolver || null);
}
}
new Promise(resolve => componentOnReady(this.nativeEl, resolve)).then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
});
if ((this.nativeEl as any).componentOnReady) {
this.nativeEl.componentOnReady().then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
});
}
}
get isActivated(): boolean {
@@ -147,27 +147,15 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
deactivate(): void {
if (this.activated) {
if (this.activatedView) {
const context = this.getContext()!;
this.activatedView.savedData = new Map(context.children['contexts']);
/**
* Angular v11.2.10 introduced a change
* where this route context is cleared out when
* a router-outlet is deactivated, However,
* we need this route information in order to
* return a user back to the correct tab when
* leaving and then going back to the tab context.
*/
const primaryOutlet = this.activatedView.savedData.get('primary');
if (primaryOutlet && context.route) {
primaryOutlet.route = { ...context.route };
}
this.activatedView.savedData = new Map(this.getContext()!.children['contexts']);
/**
* Ensure we are saving the NavigationExtras
* data otherwise it will be lost
*/
this.activatedView.savedExtras = {};
const context = this.getContext()!;
if (context.route) {
const contextSnapshot = context.route.snapshot;

View File

@@ -1,21 +1,20 @@
import { LocationStrategy } from '@angular/common';
import { Directive, ElementRef, HostListener, Optional } from '@angular/core';
import { Router, RouterLink } from '@angular/router';
import { AnimationBuilder, RouterDirection } from '@ionic/core';
import { RouterDirection } from '@ionic/core';
import { Subscription } from 'rxjs';
import { NavController } from '../../providers/nav-controller';
@Directive({
selector: '[routerLink]',
inputs: ['routerDirection', 'routerAnimation']
inputs: ['routerDirection']
})
export class RouterLinkDelegate {
private subscription?: Subscription;
routerDirection: RouterDirection = 'forward';
routerAnimation?: AnimationBuilder;
constructor(
private locationStrategy: LocationStrategy,
@@ -51,7 +50,7 @@ export class RouterLinkDelegate {
*/
@HostListener('click', ['$event'])
onClick(ev: UIEvent) {
this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
this.navCtrl.setDirection(this.routerDirection);
ev.preventDefault();
}
}

View File

@@ -1,7 +1,7 @@
import { Location } from '@angular/common';
import { ComponentRef, NgZone } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AnimationBuilder, RouterDirection } from '@ionic/core';
import { RouterDirection } from '@ionic/core';
import { bindLifecycleEvents } from '../../providers/angular-delegate';
import { NavController } from '../../providers/nav-controller';
@@ -52,8 +52,7 @@ export class StackController {
}
setActive(enteringView: RouteView): Promise<StackEvent> {
const consumeResult = this.navCtrl.consumeTransition();
let { direction, animation, animationBuilder } = consumeResult;
let { direction, animation } = this.navCtrl.consumeTransition();
const leavingView = this.activeView;
const tabSwitch = isTabSwitch(enteringView, leavingView);
if (tabSwitch) {
@@ -106,31 +105,6 @@ export class StackController {
enteringView.ref.changeDetectorRef.detectChanges();
}
/**
* If we are going back from a page that
* was presented using a custom animation
* we should default to using that
* unless the developer explicitly
* provided another animation.
*/
const customAnimation = enteringView.animationBuilder;
if (
animationBuilder === undefined &&
direction === 'back' &&
!tabSwitch &&
customAnimation !== undefined
) {
animationBuilder = customAnimation;
}
/**
* Save any custom animation so that navigating
* back will use this custom animation by default.
*/
if (leavingView) {
leavingView.animationBuilder = animationBuilder;
}
// Wait until previous transitions finish
return this.zone.runOutsideAngular(() => {
return this.wait(() => {
@@ -142,7 +116,7 @@ export class StackController {
// In case the enteringView is the same as the leavingPage we need to reattach()
enteringView.ref.changeDetectorRef.reattach();
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false, animationBuilder)
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false)
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
.then(() => ({
enteringView,
@@ -180,8 +154,8 @@ export class StackController {
url = primaryOutlet.route._routerState.snapshot.url;
}
}
const { animationBuilder } = this.navCtrl.consumeTransition();
return this.navCtrl.navigateBack(url, { ...view.savedExtras, animation: animationBuilder }).then(() => true);
return this.navCtrl.navigateBack(url, view.savedExtras).then(() => true);
});
}
@@ -190,16 +164,13 @@ export class StackController {
if (leavingView) {
const views = this.getStack(leavingView.stackId);
const enteringView = views[views.length - 2];
const customAnimation = enteringView.animationBuilder;
return this.wait(() => {
return this.transition(
enteringView, // entering view
leavingView, // leaving view
'back',
this.canGoBack(2),
true,
customAnimation
true
);
});
}
@@ -232,10 +203,6 @@ export class StackController {
return this.activeView ? this.activeView.stackId : undefined;
}
hasRunningTask(): boolean {
return this.runningTask !== undefined;
}
destroy() {
this.containerEl = undefined!;
this.views.forEach(destroyView);
@@ -258,8 +225,7 @@ export class StackController {
leavingView: RouteView | undefined,
direction: 'forward' | 'back' | undefined,
showGoBack: boolean,
progressAnimation: boolean,
animationBuilder?: AnimationBuilder
progressAnimation: boolean
) {
if (this.skipTransition) {
this.skipTransition = false;
@@ -284,8 +250,7 @@ export class StackController {
duration: direction === undefined ? 0 : undefined,
direction,
showGoBack,
progressAnimation,
animationBuilder
progressAnimation
});
}
}
@@ -298,7 +263,6 @@ export class StackController {
this.runningTask = undefined;
}
const promise = this.runningTask = task();
promise.finally(() => this.runningTask = undefined);
return promise;
}
}

View File

@@ -1,6 +1,6 @@
import { ComponentRef } from '@angular/core';
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
import { NavDirection, RouterDirection } from '@ionic/core';
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection) => {
if (direction === 'root') {
@@ -96,5 +96,4 @@ export interface RouteView {
savedData?: any;
savedExtras?: NavigationExtras;
unlistenEvents: () => void;
animationBuilder?: AnimationBuilder;
}

View File

@@ -1,5 +1,5 @@
import type * as d from './proxies';
import * as d from './proxies';
export const DIRECTIVES = [
d.IonApp,

View File

File diff suppressed because it is too large Load Diff

View File

@@ -71,7 +71,7 @@ export declare interface IonVirtualScroll {
* entire virtual scroll is reset, which is an expensive operation and
* should be avoided if possible.
*/
items?: any[] | null;
items?: any[];
/**
* An optional function that maps each item within their height.

View File

@@ -43,7 +43,7 @@ export * from './types/ionic-lifecycle-hooks';
export { IonicModule } from './ionic-module';
// UTILS
export { IonicSafeString, getPlatforms, isPlatform, createAnimation } from '@ionic/core';
export { IonicSafeString, getPlatforms, isPlatform } from '@ionic/core';
// CORE TYPES
export { Animation, AnimationBuilder, AnimationCallbackOptions, AnimationDirection, AnimationFill, AnimationKeyFrames, AnimationLifecycle, Gesture, GestureConfig, GestureDetail, mdTransitionAnimation, iosTransitionAnimation, NavComponentWithProps } from '@ionic/core';
export { Animation, AnimationBuilder, AnimationCallbackOptions, AnimationDirection, AnimationFill, AnimationKeyFrames, AnimationLifecycle, Gesture, GestureConfig, GestureDetail, mdTransitionAnimation, iosTransitionAnimation } from '@ionic/core';

View File

@@ -1,7 +1,7 @@
import { Location } from '@angular/common';
import { Injectable, Optional } from '@angular/core';
import { NavigationExtras, NavigationStart, Router, UrlSerializer, UrlTree } from '@angular/router';
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
import { NavDirection, RouterDirection } from '@ionic/core';
import { IonRouterOutlet } from '../directives/navigation/ion-router-outlet';
@@ -9,7 +9,6 @@ import { Platform } from './platform';
export interface AnimationOptions {
animated?: boolean;
animation?: AnimationBuilder;
animationDirection?: 'forward' | 'back';
}
@@ -23,7 +22,6 @@ export class NavController {
private topOutlet?: IonRouterOutlet;
private direction: 'forward' | 'back' | 'root' | 'auto' = DEFAULT_DIRECTION;
private animated?: NavDirection = DEFAULT_ANIMATED;
private animationBuilder?: AnimationBuilder;
private guessDirection: RouterDirection = 'forward';
private guessAnimation?: NavDirection;
private lastNavId = -1;
@@ -67,7 +65,7 @@ export class NavController {
* ```
*/
navigateForward(url: string | UrlTree | any[], options: NavigationOptions = {}): Promise<boolean> {
this.setDirection('forward', options.animated, options.animationDirection, options.animation);
this.setDirection('forward', options.animated, options.animationDirection);
return this.navigate(url, options);
}
@@ -90,7 +88,7 @@ export class NavController {
* ```
*/
navigateBack(url: string | UrlTree | any[], options: NavigationOptions = {}): Promise<boolean> {
this.setDirection('back', options.animated, options.animationDirection, options.animation);
this.setDirection('back', options.animated, options.animationDirection);
return this.navigate(url, options);
}
@@ -113,7 +111,7 @@ export class NavController {
* ```
*/
navigateRoot(url: string | UrlTree | any[], options: NavigationOptions = {}): Promise<boolean> {
this.setDirection('root', options.animated, options.animationDirection, options.animation);
this.setDirection('root', options.animated, options.animationDirection);
return this.navigate(url, options);
}
@@ -123,7 +121,7 @@ export class NavController {
* by default.
*/
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }) {
this.setDirection('back', options.animated, options.animationDirection, options.animation);
this.setDirection('back', options.animated, options.animationDirection);
return this.location.back();
}
@@ -152,10 +150,9 @@ export class NavController {
*
* It's recommended to use `navigateForward()`, `navigateBack()` and `navigateRoot()` instead of `setDirection()`.
*/
setDirection(direction: RouterDirection, animated?: boolean, animationDirection?: 'forward' | 'back', animationBuilder?: AnimationBuilder) {
setDirection(direction: RouterDirection, animated?: boolean, animationDirection?: 'forward' | 'back') {
this.direction = direction;
this.animated = getAnimation(direction, animated, animationDirection);
this.animationBuilder = animationBuilder;
}
/**
@@ -171,7 +168,6 @@ export class NavController {
consumeTransition() {
let direction: RouterDirection = 'root';
let animation: NavDirection | undefined;
const animationBuilder = this.animationBuilder;
if (this.direction === 'auto') {
direction = this.guessDirection;
@@ -182,12 +178,10 @@ export class NavController {
}
this.direction = DEFAULT_DIRECTION;
this.animated = DEFAULT_ANIMATED;
this.animationBuilder = undefined;
return {
direction,
animation,
animationBuilder
animation
};
}

View File

@@ -209,7 +209,7 @@ export class Platform {
}
/**
* Returns `true` if the app is in portrait mode.
* Returns `true` if the app is in portait mode.
*/
isPortrait(): boolean {
return this.win.matchMedia && this.win.matchMedia('(orientation: portrait)').matches;

View File

@@ -40,23 +40,20 @@ function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
return (host: Tree) => {
const ionicStyles = [
'node_modules/@ionic/angular/css/core.css',
'node_modules/@ionic/angular/css/normalize.css',
'node_modules/@ionic/angular/css/structure.css',
'node_modules/@ionic/angular/css/typography.css',
'node_modules/@ionic/angular/css/display.css',
'node_modules/@ionic/angular/css/core.css',
'node_modules/@ionic/angular/css/padding.css',
'node_modules/@ionic/angular/css/float-elements.css',
'node_modules/@ionic/angular/css/text-alignment.css',
'node_modules/@ionic/angular/css/text-transformation.css',
'node_modules/@ionic/angular/css/flex-utils.css',
`${projectSourceRoot}/theme/variables.css`
]
ionicStyles.forEach(entry => {
].forEach(entry => {
addStyle(host, projectName, entry);
});
return host;
return host;
};
}
@@ -67,8 +64,7 @@ function addIonicons(projectName: string): Rule {
input: 'node_modules/ionicons/dist/ionicons/svg',
output: './svg'
};
addAsset(host, projectName, 'build', ioniconsGlob);
addAsset(host, projectName, 'test', ioniconsGlob);
addAsset(host, projectName, ioniconsGlob);
return host;
};
}

View File

@@ -62,10 +62,10 @@ export function addStyle(host: Tree, projectName: string, stylePath: string) {
writeConfig(host, config);
}
export function addAsset(host: Tree, projectName: string, architect: string, asset: string | {glob: string; input: string; output: string}) {
export function addAsset(host: Tree, projectName: string, asset: string | {glob: string; input: string; output: string}) {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect[architect].options.assets.push(asset);
appConfig.architect.build.options.assets.push(asset);
writeConfig(host, config);
}

View File

@@ -10,6 +10,6 @@ export interface IonicWindow extends Window {
}
export interface HTMLStencilElement extends HTMLElement {
componentOnReady?(): Promise<this>;
forceUpdate?(): void;
componentOnReady(): Promise<this>;
forceUpdate(): void;
}

View File

@@ -0,0 +1 @@
package-lock=false

View File

@@ -85,6 +85,33 @@
"browserTarget": "test-app:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"styles": ["src/styles.css"],
"scripts": [],
"assets": ["src/favicon.ico", "src/assets"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "test-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "test-app:serve:production"
},
"ci": {
"devServerTarget": "test-app:serve:ci"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {

View File

@@ -1,8 +0,0 @@
{
"integrationFolder": "./e2e",
"testFiles": "**/*.spec.ts",
"baseUrl": "http://localhost:4200/",
"ignoreTestFiles": "**/examples/*",
"video": false,
"screenshotOnRunFailure": false
}

View File

@@ -1,22 +0,0 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@@ -1,79 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
Cypress.Commands.add('ionSwipeToGoBack', (complete = false, selector = 'ion-router-outlet') => {
const increment = (complete) ? 60 : 25;
cy.get(selector)
.first()
.trigger('mousedown', 0, 275, { which: 1, force: true })
.trigger('mousemove', increment * 1, 275, { which: 1, force: true })
.wait(50)
.trigger('mousemove', increment * 2, 275, { which: 1, force: true })
.wait(50)
.trigger('mousemove', increment * 3, 275, { which: 1, force: true })
.wait(50)
.trigger('mousemove', increment * 4, 275, { which: 1, force: true })
.wait(50)
.trigger('mouseup', increment * 4, 275, { which: 1, force: true })
cy.wait(150);
})
Cypress.Commands.add('testStack', (selector, expected) => {
cy.document().then((doc) => {
const children = Array.from(
doc.querySelector(selector).children
).map(el => el.tagName.toLowerCase());
expect(children).to.deep.equal(expected);
})
})
Cypress.Commands.add('testLifeCycle', (selector, expected) => {
cy.get(`${selector} #ngOnInit`).invoke('text').should('equal', '1');
cy.get(`${selector} #ionViewWillEnter`).invoke('text').should('equal', expected.ionViewWillEnter.toString());
cy.get(`${selector} #ionViewDidEnter`).invoke('text').should('equal', expected.ionViewDidEnter.toString());
cy.get(`${selector} #ionViewWillLeave`).invoke('text').should('equal', expected.ionViewWillLeave.toString());
cy.get(`${selector} #ionViewDidLeave`).invoke('text').should('equal', expected.ionViewDidLeave.toString());
})
Cypress.Commands.add('ionPageVisible', (selector) => {
cy.get(selector)
.should('have.class', 'ion-page')
.should('not.have.class', 'ion-page-hidden')
.should('not.have.class', 'ion-page-invisible')
.should('have.length', 1)
})
Cypress.Commands.add('ionPageHidden', (selector) => {
cy.get(selector)
.should('have.class', 'ion-page')
.should('have.class', 'ion-page-hidden')
.should('have.length', 1)
})
Cypress.Commands.add('ionPageDoesNotExist', (selector) => {
cy.get(selector)
.should('not.exist')
});

View File

@@ -1,69 +0,0 @@
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable<Subject> {
/**
* Swipe to go back on the current selector or router outlet
* @example
* ```
* cy.ionSwipeToGoBack();
* cy.ionSwipeToGoBack(true);
* ```
*/
ionSwipeToGoBack(complete: boolean, selector: string): Chainable<any>
/**
* Test that the proper pages are in the navigation stack
* @example
* ```
* cy.testStack('ion-router-outlet', ['app-navigation-page2', 'app-navigation-page1']);
* cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested', 'app-tabs-tab2']);
* ```
*/
testStack(selector: string, expected: string[]): Chainable<any>
/**
* Test whether or not the lifecycle events fired
* @example
* ```
* cy.testLifeCycle('app-router-link-page', {
* ionViewWillEnter: 1,
* ionViewDidEnter: 1,
* ionViewWillLeave: 0,
* ionViewDidLeave: 0,
* });
* ```
*/
testLifeCycle(selector: string, expected: any): Chainable<any>
/**
* Test whether or not an .ion-page element is visible.
* Use this to test a page after navigating to it.
* @example
* ```
* cy.ionPageVisible('app-my-page');
* ```
*/
ionPageVisible(selector: string): Chainable<any>
/**
* Test whether or not an .ion-page element is hidden
* Use this to test a page after navigating away from it.
* @example
* ```
* cy.ionPageHidden('app-my-page');
* ```
*/
ionPageHidden(selector: string): Chainable<any>
/**
* Test whether or not an .ion-page element exists.
* Use this to test a page after popping it off the stack.
* @example
* ```
* cy.ionPageDoesNotExist('app-my-page');
* ```
*/
ionPageDoesNotExist(selector: string): Chainable<any>
}
}

View File

@@ -1,20 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -0,0 +1,35 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
chromeOptions: {
args: [ "--headless", "--disable-gpu", "--window-size=400,1000", "--start-maximized" ]
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 100000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@@ -0,0 +1,119 @@
import { browser, element, by } from 'protractor';
import { handleErrorMessages, setProperty, getText, waitTime } from './utils';
describe('form', () => {
afterEach(() => {
return handleErrorMessages();
});
describe('change', () => {
beforeEach(async () => {
await browser.get('/form');
await waitTime(30);
});
it('should have default values', async () => {
await testStatus('INVALID');
expect(await getText('#submit')).toEqual('false');
await testData({
'datetime': '2010-08-20',
'select': null,
'toggle': false,
'input': '',
'input2': 'Default Value',
'checkbox': false,
'range': 5
});
});
it('should become valid', async () => {
await setProperty('ion-input.required', 'value', 'Some value');
await testStatus('INVALID');
await setProperty('ion-select', 'value', 'nes');
await testStatus('INVALID');
await setProperty('ion-range', 'value', 40);
await testStatus('VALID');
await testData({
'datetime': '2010-08-20',
'select': 'nes',
'toggle': false,
'input': 'Some value',
'input2': 'Default Value',
'checkbox': false,
'range': 40
});
});
it('ion-toggle should change', async () => {
await element(by.css('form ion-toggle')).click();
await testData({
'datetime': '2010-08-20',
'select': null,
'toggle': true,
'input': '',
'input2': 'Default Value',
'checkbox': false,
'range': 5
});
});
it('ion-checkbox should change', async () => {
await element(by.css('ion-checkbox')).click();
await testData({
'datetime': '2010-08-20',
'select': null,
'toggle': false,
'input': '',
'input2': 'Default Value',
'checkbox': true,
'range': 5
});
});
it('should submit', async () => {
await element(by.css('#set-values')).click();
await waitTime(100);
await element(by.css('#submit-button')).click();
expect(await getText('#submit')).toEqual('true');
});
});
describe('blur', () => {
beforeEach(async () => {
await browser.get('/form#blur');
await waitTime(30);
});
it('ion-toggle should change only after blur', async () => {
await element(by.css('form ion-toggle')).click();
await testData({
'datetime': '2010-08-20',
'select': null,
'toggle': false,
'input': '',
'input2': 'Default Value',
'checkbox': false,
'range': 5
});
await element(by.css('ion-checkbox')).click();
await testData({
'datetime': '2010-08-20',
'select': null,
'toggle': true,
'input': '',
'input2': 'Default Value',
'checkbox': false,
'range': 5
});
});
});
});
async function testStatus(status: string) {
expect(await element(by.css('#status')).getText()).toEqual(status);
}
async function testData(data: any) {
expect(JSON.parse(await element(by.css('#data')).getText())).toEqual(data);
}

View File

@@ -1,118 +0,0 @@
describe('Form', () => {
beforeEach(() => {
cy.visit('/form');
})
describe('status updates', () => {
it('should update Ionic form classes when calling form methods programmatically', async () => {
cy.get('#input-touched').click();
cy.get('#touched-input-test').should('have.class', 'ion-touched');
});
});
describe('change', () => {
it('should have default values', () => {
testStatus('INVALID');
cy.get('#submit').should('have.text', 'false');
testData({
datetime: '2010-08-20',
select: null,
toggle: false,
input: '',
input2: 'Default Value',
checkbox: false,
range: 5
});
});
it('should become valid', () => {
cy.get('ion-input.required').invoke('prop', 'value', 'Some value');
testStatus('INVALID');
cy.get('ion-select').invoke('prop', 'value', 'nes');
testStatus('INVALID');
cy.get('ion-range').invoke('prop', 'value', 40);
testStatus('VALID');
testData({
datetime: '2010-08-20',
select: 'nes',
toggle: false,
input: 'Some value',
input2: 'Default Value',
checkbox: false,
range: 40
});
});
it('ion-toggle should change', () => {
cy.get('form ion-toggle').click();
testData({
datetime: '2010-08-20',
select: null,
toggle: true,
input: '',
input2: 'Default Value',
checkbox: false,
range: 5
});
});
it('ion-checkbox should change', () => {
cy.get('ion-checkbox').click();
testData({
datetime: '2010-08-20',
select: null,
toggle: false,
input: '',
input2: 'Default Value',
checkbox: true,
range: 5
});
});
it('should submit', () => {
cy.get('#set-values').click();
cy.get('#submit-button').click();
cy.get('#submit').should('have.text', 'true');
});
});
describe('blur', () => {
it('ion-toggle should change only after blur', () => {
cy.get('form ion-toggle').click();
testData({
datetime: '2010-08-20',
select: null,
toggle: true,
input: '',
input2: 'Default Value',
checkbox: false,
range: 5
});
cy.get('ion-checkbox').click();
testData({
datetime: '2010-08-20',
select: null,
toggle: true,
input: '',
input2: 'Default Value',
checkbox: true,
range: 5
});
});
});
});
function testStatus(status) {
cy.get('#status').should('have.text', status);
}
function testData(data) {
cy.get('#data').invoke('text').then(text => {
const value = JSON.parse(text);
console.log(value, data);
expect(value).to.deep.equal(data);
})
}

View File

@@ -0,0 +1,69 @@
import { browser, element, by } from 'protractor';
import { getProperty, setProperty, handleErrorMessages, waitTime } from './utils';
describe('inputs', () => {
beforeEach(async () => {
await browser.get('/inputs');
await waitTime(30);
});
afterEach(() => {
return handleErrorMessages();
});
it('should have default value', async () => {
expect(await getProperty('ion-checkbox', 'checked')).toEqual(true);
expect(await getProperty('ion-toggle', 'checked')).toEqual(true);
expect(await getProperty('ion-input', 'value')).toEqual('some text');
expect(await getProperty('ion-datetime', 'value')).toEqual('1994-03-15');
expect(await getProperty('ion-select', 'value')).toEqual('nes');
expect(await getProperty('ion-range', 'value')).toEqual(10);
});
it('should have reset value', async () => {
await element(by.css('#reset-button')).click();
expect(await getProperty('ion-checkbox', 'checked')).toEqual(false);
expect(await getProperty('ion-toggle', 'checked')).toEqual(false);
expect(await getProperty('ion-input', 'value')).toEqual('');
expect(await getProperty('ion-datetime', 'value')).toEqual('');
expect(await getProperty('ion-select', 'value')).toEqual('');
expect(await getProperty('ion-range', 'value')).toEqual(null);
});
it('should get some value', async () => {
await element(by.css('#reset-button')).click();
await element(by.css('#set-button')).click();
expect(await getProperty('ion-checkbox', 'checked')).toEqual(true);
expect(await getProperty('ion-toggle', 'checked')).toEqual(true);
expect(await getProperty('ion-input', 'value')).toEqual('some text');
expect(await getProperty('ion-datetime', 'value')).toEqual('1994-03-15');
expect(await getProperty('ion-select', 'value')).toEqual('nes');
expect(await getProperty('ion-range', 'value')).toEqual(10);
});
it('change values should update angular', async () => {
await element(by.css('#reset-button')).click();
await setProperty('ion-checkbox', 'checked', true);
await setProperty('ion-toggle', 'checked', true);
await setProperty('ion-input', 'value', 'hola');
await setProperty('ion-datetime', 'value', '1996-03-15');
await setProperty('ion-select', 'value', 'playstation');
await setProperty('ion-range', 'value', 20);
expect(await element(by.css('#checkbox-note')).getText()).toEqual('true');
expect(await element(by.css('#toggle-note')).getText()).toEqual('true');
expect(await element(by.css('#input-note')).getText()).toEqual('hola');
expect(await element(by.css('#datetime-note')).getText()).toEqual('1996-03-15');
expect(await element(by.css('#select-note')).getText()).toEqual('playstation');
expect(await element(by.css('#range-note')).getText()).toEqual('20');
});
it('nested components should not interfere with NgModel', async () => {
expect(await element(by.css('#range-note')).getText()).toEqual('10');
await element(by.css('#nested-toggle')).click();
expect(await element(by.css('#range-note')).getText()).toEqual('10');
});
});

View File

@@ -1,61 +0,0 @@
describe('Inputs', () => {
beforeEach(() => {
cy.visit('/inputs');
})
it('should have default value', () => {
cy.get('ion-checkbox').should('have.prop', 'checked').and('equal', true);
cy.get('ion-toggle').should('have.prop', 'checked').and('equal', true);
cy.get('ion-input').should('have.prop', 'value').and('equal', 'some text');
cy.get('ion-datetime').should('have.prop', 'value').and('equal', '1994-03-15');
cy.get('ion-select').should('have.prop', 'value').and('equal', 'nes');
cy.get('ion-range').should('have.prop', 'value').and('equal', 10);
});
it('should have reset value', () => {
cy.get('#reset-button').click();
cy.get('ion-checkbox').should('have.prop', 'checked').and('equal', false);
cy.get('ion-toggle').should('have.prop', 'checked').and('equal', false);
cy.get('ion-input').should('have.prop', 'value').and('equal', '');
cy.get('ion-datetime').should('have.prop', 'value').and('equal', '');
cy.get('ion-select').should('have.prop', 'value').and('equal', '');
cy.get('ion-range').should('have.prop', 'value').and('be.NaN');
});
it('should get some value', () => {
cy.get('#reset-button').click();
cy.get('#set-button').click();
cy.get('ion-checkbox').should('have.prop', 'checked').and('equal', true);
cy.get('ion-toggle').should('have.prop', 'checked').and('equal', true);
cy.get('ion-input').should('have.prop', 'value').and('equal', 'some text');
cy.get('ion-datetime').should('have.prop', 'value').and('equal', '1994-03-15');
cy.get('ion-select').should('have.prop', 'value').and('equal', 'nes');
cy.get('ion-range').should('have.prop', 'value').and('equal', 10);
});
it('change values should update angular', () => {
cy.get('#reset-button').click();
cy.get('ion-checkbox').invoke('prop', 'checked', true);
cy.get('ion-toggle').invoke('prop', 'checked', true);
cy.get('ion-input').invoke('prop', 'value', 'hola');
cy.get('ion-datetime').invoke('prop', 'value', '1996-03-15');
cy.get('ion-select').invoke('prop', 'value', 'playstation');
cy.get('ion-range').invoke('prop', 'value', 20);
cy.get('#checkbox-note').should('have.text', 'true');
cy.get('#toggle-note').should('have.text', 'true');
cy.get('#input-note').should('have.text', 'hola');
cy.get('#datetime-note').should('have.text', '1996-03-15');
cy.get('#select-note').should('have.text', 'playstation');
cy.get('#range-note').should('have.text', '20');
});
it('nested components should not interfere with NgModel', () => {
cy.get('#range-note').should('have.text', '10');
cy.get('#nested-toggle').click();
cy.get('#range-note').should('have.text', '10');
});
})

View File

@@ -0,0 +1,55 @@
import { browser, element, by } from 'protractor';
import { waitTime, getText, handleErrorMessages } from './utils';
describe('modals', () => {
beforeEach(async () => {
await browser.get('/modals');
await waitTime(30);
});
afterEach(() => {
return handleErrorMessages();
});
it('should open standalone modal and close', async () => {
await element(by.css('#action-button')).click();
await waitTime(800);
const modal = element(by.css('app-modal-example'));
expect(await modal.$('h2').getText()).toEqual('123');
expect(await modal.$('h3').getText()).toEqual('321');
expect(await getText('#onWillDismiss')).toEqual('false');
expect(await getText('#onDidDismiss')).toEqual('false');
await modal.$('#close-modal').click();
await waitTime(800);
expect(await getText('#onWillDismiss')).toEqual('true');
expect(await getText('#onDidDismiss')).toEqual('true');
});
it('should open nav modal and close', async () => {
await element(by.css('#action-button-2')).click();
await waitTime(800);
let page = element(by.css('ion-nav > *:last-child'));
expect(await page.$('h2').getText()).toEqual('123');
expect(await page.$('h3').getText()).toEqual('321');
await page.$('.push-page').click();
await waitTime(800);
page = element(by.css('ion-nav > *:last-child'));
expect(await page.$('h2').getText()).toEqual('pushed!');
expect(await page.$('h3').getText()).toEqual('');
await page.$('.pop-page').click();
await waitTime(800);
page = element(by.css('ion-nav > *:last-child'));
expect(await page.$('h2').getText()).toEqual('123');
});
});

View File

@@ -1,43 +0,0 @@
describe('Modals', () => {
beforeEach(() => {
cy.visit('/modals');
})
it('should open standalone modal and close', () => {
cy.get('#action-button').click();
cy.get('ion-modal').should('exist').should('be.visible');
cy.get('app-modal-example h2').should('have.text', '123');
cy.get('app-modal-example h3').should('have.text', '321');
cy.get('#onWillDismiss').should('have.text', 'false');
cy.get('#onDidDismiss').should('have.text', 'false');
cy.get('#close-modal').click();
cy.get('ion-modal').should('not.exist');
cy.get('#onWillDismiss').should('have.text', 'true');
cy.get('#onDidDismiss').should('have.text', 'true');
});
it('should open nav modal and close', () => {
cy.get('#action-button-2').click();
cy.get('ion-modal').should('exist').should('be.visible');
cy.get('ion-nav > *:last-child h2').should('have.text', '123');
cy.get('ion-nav > *:last-child h3').should('have.text', '321');
cy.get('ion-nav > *:last-child .push-page').click();
cy.get('ion-nav > *:last-child h2').should('have.text', 'pushed!');
cy.get('ion-nav > *:last-child h3').should('have.text', '');
cy.get('ion-nav > *:last-child .pop-page').click();
cy.get('ion-nav > *:last-child h2').should('have.text', '123');
});
});

View File

@@ -0,0 +1,73 @@
import { browser, element, by } from 'protractor';
import { handleErrorMessages, waitTime, testStack } from './utils';
describe('navigation', () => {
afterEach(() => {
return handleErrorMessages();
});
// TODO: Fix flaky tests
xit ('should swipe and abort', async () => {
await browser.get('/router-link?ionic:mode=ios');
await waitTime(500);
await element(by.css('#routerLink')).click();
await waitTime(500);
await swipeLeft(5);
await waitTime(500);
const pageHidden = element(by.css('app-router-link'));
expect(await pageHidden.getAttribute('aria-hidden')).toEqual('true');
expect(await pageHidden.getAttribute('class')).toEqual('ion-page ion-page-hidden');
const pageVisible = element(by.css('app-router-link-page'));
expect(await pageVisible.getAttribute('aria-hidden')).toEqual(null);
expect(await pageVisible.getAttribute('class')).toEqual('ion-page can-go-back');
});
xit ('should swipe and go back', async () => {
await browser.get('/router-link?ionic:mode=ios');
await waitTime(500);
await element(by.css('#routerLink')).click();
await waitTime(500);
await testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']);
await swipeLeft(300);
await waitTime(1000);
await testStack('ion-router-outlet', ['app-router-link']);
const page = element(by.css('app-router-link'));
expect(await page.getAttribute('aria-hidden')).toEqual(null);
expect(await page.getAttribute('class')).toEqual('ion-page');
})
it('should navigate correctly', async () => {
await browser.get('/navigation/page1');
await waitTime(2000);
await testStack('ion-router-outlet', ['app-navigation-page2', 'app-navigation-page1']);
const pageHidden = element(by.css('app-navigation-page2'));
expect(await pageHidden.getAttribute('aria-hidden')).toEqual('true');
expect(await pageHidden.getAttribute('class')).toEqual('ion-page ion-page-hidden');
const pageVisible = element(by.css('app-navigation-page1'));
expect(await pageVisible.getAttribute('aria-hidden')).toEqual(null);
expect(await pageVisible.getAttribute('class')).toEqual('ion-page can-go-back');
});
});
function swipeLeft(end: number) {
return browser.driver.touchActions()
.tapAndHold({x: 5, y: 1})
.move({x: 6, y: 1})
.move({x: 7, y: 1})
.move({x: 8, y: 1})
.move({x: 30, y: 1})
.move({x: 300, y: 1})
.move({x: end, y: 1})
.move({x: end, y: 1})
.release({x: end, y: 1})
.perform();
}

View File

@@ -1,18 +0,0 @@
describe('Navigation', () => {
beforeEach(() => {
cy.visit('/navigation');
})
it('should navigate correctly', () => {
cy.visit('/navigation/page1');
cy.wait(2000);
cy.testStack('ion-router-outlet', ['app-navigation-page2', 'app-navigation-page1']);
cy.get('app-navigation-page2').should('have.attr', 'aria-hidden').and('equal', 'true');
cy.get('app-navigation-page2').should('have.attr', 'class').and('equal', 'ion-page ion-page-hidden');
cy.get('app-navigation-page1').should('not.have.attr', 'aria-hidden');
cy.get('app-navigation-page1').should('have.attr', 'class').and('equal', 'ion-page can-go-back');
});
})

View File

@@ -0,0 +1,23 @@
import { browser, element, by } from 'protractor';
import { waitTime, handleErrorMessages, goBack } from './utils';
describe('nested-outlet', () => {
afterEach(() => {
return handleErrorMessages();
});
it('should navigate correctly', async () => {
await browser.get('/nested-outlet/page');
expect(await element(by.css('ion-router-outlet ion-router-outlet app-nested-outlet-page h1')).getText()).toEqual('Nested page 1');
await element(by.css('#goto-tabs')).click();
await waitTime(500);
await element(by.css('#goto-nested-page1')).click();
await waitTime(500);
await element(by.css('#goto-nested-page2')).click();
await waitTime(500);
expect(await element(by.css('ion-router-outlet ion-router-outlet app-nested-outlet-page2 h1')).getText()).toEqual('Nested page 2');
});
});

View File

@@ -1,25 +0,0 @@
describe('Nested Outlet', () => {
beforeEach(() => {
cy.visit('/nested-outlet/page');
})
it('should navigate correctly', () => {
cy.get('ion-router-outlet ion-router-outlet app-nested-outlet-page h1').should('have.text', 'Nested page 1');
cy.get('#goto-tabs').click();
cy.ionPageVisible('app-tabs');
cy.ionPageVisible('app-tabs-tab1');
cy.get('#goto-nested-page1').click();
cy.ionPageVisible('app-nested-outlet-page');
cy.ionPageDoesNotExist('app-tabs');
cy.get('#goto-nested-page2').click();
cy.ionPageVisible('app-nested-outlet-page2');
cy.get('ion-router-outlet ion-router-outlet app-nested-outlet-page2 h1').should('have.text', 'Nested page 2');
});
});

View File

@@ -0,0 +1,29 @@
import { browser, element, by } from 'protractor';
import { handleErrorMessages, waitTime } from './utils';
describe('providers', () => {
afterEach(() => {
return handleErrorMessages();
});
it('should load all providers', async () => {
await browser.get('/providers');
expect(await element(by.css('#is-loaded')).getText()).toEqual('true');
expect(await element(by.css('#is-ready')).getText()).toEqual('true');
expect(await element(by.css('#is-paused')).getText()).toEqual('true');
expect(await element(by.css('#is-resumed')).getText()).toEqual('true');
expect(await element(by.css('#is-resized')).getText()).toEqual('true');
expect(await element(by.css('#is-testing')).getText()).toEqual('false');
expect(await element(by.css('#is-desktop')).getText()).toEqual('true');
expect(await element(by.css('#is-mobile')).getText()).toEqual('false');
expect(await element(by.css('#keyboard-height')).getText()).toEqual('12345');
});
it('should detect testing mode', async () => {
await browser.get('/providers?ionic:_testing=true');
expect(await element(by.css('#is-testing')).getText()).toEqual('true');
});
});

View File

@@ -1,24 +0,0 @@
describe('Providers', () => {
beforeEach(() => {
cy.visit('/providers');
})
it('should load all providers', () => {
cy.get('#is-loaded').should('have.text', 'true');
cy.get('#is-ready').should('have.text', 'true');
cy.get('#is-paused').should('have.text', 'true');
cy.get('#is-resumed').should('have.text', 'true');
cy.get('#is-resized').should('have.text', 'true');
cy.get('#is-testing').should('have.text', 'false');
cy.get('#is-desktop').should('have.text', 'true');
cy.get('#is-mobile').should('have.text', 'false');
cy.get('#keyboard-height').should('have.text', '12345');
});
it('should detect testing mode', () => {
cy.visit('/providers?ionic:_testing=true');
cy.get('#is-testing').should('have.text', 'true');
});
});

View File

@@ -0,0 +1,194 @@
import { browser, element, by, protractor } from 'protractor';
import { waitTime, testStack, testLifeCycle, handleErrorMessages, getText } from './utils';
const EC = protractor.ExpectedConditions;
describe('router-link params and fragments', () => {
const queryParam = 'A&=#Y';
const fragment = 'myDiv1';
const id = 'MyPageID==';
afterEach(() => {
return handleErrorMessages();
});
it('should go to a page with properly encoded values', async () => {
await browser.get('/router-link?ionic:_testing=true');
await element(by.css('#queryParamsFragment')).click();
const expectedRoute = `${encodeURIComponent(id)}?token=${encodeURIComponent(queryParam)}#${encodeURIComponent(fragment)}`;
browser.wait(EC.urlContains(expectedRoute), 5000);
});
it('should return to a page with preserved query param and fragment', async () => {
await browser.get('/router-link?ionic:_testing=true');
await waitTime(30);
await element(by.css('#queryParamsFragment')).click();
await waitTime(400);
await element(by.css('#goToPage3')).click();
browser.wait(EC.urlContains('router-link-page3'), 5000);
await waitTime(400);
await element(by.css('#goBackFromPage3')).click();
const expectedRoute = `${encodeURIComponent(id)}?token=${encodeURIComponent(queryParam)}#${encodeURIComponent(fragment)}`;
browser.wait(EC.urlContains(expectedRoute), 5000);
});
it('should preserve query param and fragment with defaultHref string', async () => {
await browser.get('/router-link-page3?ionic:_testing=true');
await waitTime(30);
await element(by.css('#goBackFromPage3')).click();
const expectedRoute = '?token=ABC#fragment';
browser.wait(EC.urlContains(expectedRoute), 5000);
});
});
describe('router-link', () => {
beforeEach(async () => {
await browser.get('/router-link');
await waitTime(30);
});
afterEach(() => {
return handleErrorMessages();
});
it('should have correct lifecycle counts', async () => {
await testLifeCycle('app-router-link', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
});
describe('forward', () => {
it('should go forward with ion-button[routerLink]', async () => {
await element(by.css('#routerLink')).click();
await testForward();
});
it('should go forward with a[routerLink]', async () => {
await element(by.css('#a')).click();
await testForward();
});
it('should go forward with button + navigateByUrl()', async () => {
await element(by.css('#button')).click();
await testForward();
});
it('should go forward with button + navigateForward()', async () => {
await element(by.css('#button-forward')).click();
await testForward();
});
});
describe('root', () => {
it('should go root with ion-button[routerLink][routerDirection=root]', async () => {
await element(by.css('#routerLink-root')).click();
await testRoot();
});
it('should go root with a[routerLink][routerDirection=root]', async () => {
await element(by.css('#a-root')).click();
await testRoot();
});
it('should go root with button + navigateRoot', async () => {
await element(by.css('#button-root')).click();
await testRoot();
});
});
describe('back', () => {
it('should go back with ion-button[routerLink][routerDirection=back]', async () => {
await element(by.css('#routerLink-back')).click();
});
it('should go back with a[routerLink][routerDirection=back]', async () => {
await element(by.css('#a-back')).click();
await testBack();
});
it('should go back with button + navigateBack', async () => {
await element(by.css('#button-back')).click();
await testBack();
});
});
});
async function testForward() {
await waitTime(2500);
await testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']);
await testLifeCycle('app-router-link-page', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('true');
await browser.navigate().back();
await waitTime(100);
await testStack('ion-router-outlet', ['app-router-link']);
await testLifeCycle('app-router-link', {
ionViewWillEnter: 2,
ionViewDidEnter: 2,
ionViewWillLeave: 1,
ionViewDidLeave: 1,
});
}
async function testRoot() {
await waitTime(200);
await testStack('ion-router-outlet', ['app-router-link-page']);
await testLifeCycle('app-router-link-page', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('false');
await browser.navigate().back();
await waitTime(100);
await testStack('ion-router-outlet', ['app-router-link']);
await testLifeCycle('app-router-link', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
}
async function testBack() {
await waitTime(500);
await testStack('ion-router-outlet', ['app-router-link-page']);
await testLifeCycle('app-router-link-page', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('false');
await browser.navigate().back();
await waitTime(100);
await testStack('ion-router-outlet', ['app-router-link']);
await testLifeCycle('app-router-link', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
}

View File

@@ -1,192 +0,0 @@
describe('Router Link', () => {
beforeEach(() => {
cy.visit('/router-link');
});
describe('router-link params and fragments', () => {
const queryParam = 'A&=#Y';
const fragment = 'myDiv1';
const id = 'MyPageID==';
it('should go to a page with properly encoded values', () => {
cy.visit('/router-link?ionic:_testing=true');
cy.get('#queryParamsFragment').click();
const expectedPath = `${encodeURIComponent(id)}`;
const expectedSearch = `?token=${encodeURIComponent(queryParam)}`;
const expectedHash = `#${encodeURIComponent(fragment)}`;
cy.location().should((location) => {
expect(location.pathname).to.contain(expectedPath);
expect(location.search).to.eq(expectedSearch);
expect(location.hash).to.eq(expectedHash);
});
});
it('should return to a page with preserved query param and fragment', () => {
cy.visit('/router-link?ionic:_testing=true');
cy.get('#queryParamsFragment').click();
cy.get('#goToPage3').click();
cy.location().should((location) => {
expect(location.pathname).to.contain('router-link-page3');
});
cy.get('#goBackFromPage3').click();
const expectedPath = `${encodeURIComponent(id)}`;
const expectedSearch = `?token=${encodeURIComponent(queryParam)}`;
const expectedHash = `#${encodeURIComponent(fragment)}`;
cy.location().should((location) => {
expect(location.pathname).to.contain(expectedPath);
expect(location.search).to.eq(expectedSearch);
expect(location.hash).to.eq(expectedHash);
});
});
it('should preserve query param and fragment with defaultHref string', () => {
cy.visit('/router-link-page3?ionic:_testing=true');
cy.get('#goBackFromPage3').click();
const expectedSearch = '?token=ABC';
const expectedHash = '#fragment';
cy.location().should((location) => {
expect(location.search).to.eq(expectedSearch);
expect(location.hash).to.eq(expectedHash);
});
});
});
describe('router-link', () => {
it('should have correct lifecycle counts', () => {
cy.testLifeCycle('app-router-link', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
});
});
describe('forward', () => {
it('should go forward with ion-button[routerLink]', () => {
cy.get('#routerLink').click();
testForward();
});
it('should go forward with a[routerLink]', () => {
cy.get('#a').click();
testForward();
});
it('should go forward with button + navigateByUrl()', () => {
cy.get('#button').click();
testForward();
});
it('should go forward with button + navigateForward()', () => {
cy.get('#button-forward').click();
testForward();
});
});
describe('root', () => {
it('should go root with ion-button[routerLink][routerDirection=root]', () => {
cy.get('#routerLink-root').click();
testRoot();
});
it('should go root with a[routerLink][routerDirection=root]', () => {
cy.get('#a-root').click();
testRoot();
});
it('should go root with button + navigateRoot', () => {
cy.get('#button-root').click();
testRoot();
});
});
describe('back', () => {
it('should go back with ion-button[routerLink][routerDirection=back]', () => {
cy.get('#routerLink-back').click();
});
it('should go back with a[routerLink][routerDirection=back]', () => {
cy.get('#a-back').click();
testBack();
});
it('should go back with button + navigateBack', () => {
cy.get('#button-back').click();
testBack();
});
});
});
function testForward() {
cy.testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']);
cy.testLifeCycle('app-router-link-page', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
cy.get('app-router-link-page #canGoBack').should('have.text', 'true');
cy.go('back');
cy.testStack('ion-router-outlet', ['app-router-link']);
cy.testLifeCycle('app-router-link', {
ionViewWillEnter: 2,
ionViewDidEnter: 2,
ionViewWillLeave: 1,
ionViewDidLeave: 1,
});
}
function testRoot() {
cy.wait(200);
cy.testStack('ion-router-outlet', ['app-router-link-page']);
cy.testLifeCycle('app-router-link-page', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
cy.get('app-router-link-page #canGoBack').should('have.text', 'false');
cy.go('back');
cy.wait(100);
cy.testStack('ion-router-outlet', ['app-router-link']);
cy.testLifeCycle('app-router-link', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
}
function testBack() {
cy.wait(500);
cy.testStack('ion-router-outlet', ['app-router-link-page']);
cy.testLifeCycle('app-router-link-page', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
cy.get('app-router-link-page #canGoBack').should('have.text', 'false');
cy.go('back');
cy.wait(100);
cy.testStack('ion-router-outlet', ['app-router-link']);
cy.testLifeCycle('app-router-link', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
}

View File

@@ -1,36 +0,0 @@
describe('Routing', () => {
beforeEach(() => {
cy.visit('/router-link?ionic:mode=ios');
})
it('should swipe and abort', () => {
cy.get('#routerLink').click();
cy.ionSwipeToGoBack();
cy.get('app-router-link').should('have.attr', 'aria-hidden').and('equal', 'true');
cy.get('app-router-link').should('have.attr', 'class').and('equal', 'ion-page ion-page-hidden');
cy.get('app-router-link-page').should('not.have.attr', 'aria-hidden');
cy.get('app-router-link-page').should('have.attr', 'class').and('equal', 'ion-page can-go-back');
});
it('should swipe and go back', () => {
cy.get('#routerLink').click();
cy.ionPageHidden('app-router-link');
cy.ionPageVisible('app-router-link-page');
cy.testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']);
cy.ionSwipeToGoBack(true);
cy.ionPageVisible('app-router-link');
cy.ionPageDoesNotExist('app-router-link-page');
cy.testStack('ion-router-outlet', ['app-router-link']);
cy.get('app-router-link').should('not.have.attr', 'aria-hidden');
cy.get('app-router-link').should('have.attr', 'class').and('equal', 'ion-page');
});
})

View File

@@ -0,0 +1,51 @@
import { browser, element, by } from 'protractor';
import { handleErrorMessages, waitTime } from './utils';
describe('slides', () => {
beforeEach(async () => {
await browser.get('/slides');
await waitTime(30);
});
afterEach(() => {
return handleErrorMessages();
});
it('should change index on slide change', async () => {
expect(await element.all(by.css('ion-slide')).count()).toEqual(0);
await addSlides();
expect(await element.all(by.css('ion-slide')).count()).toEqual(3);
await checkIndex('0');
await nextSlide();
await checkIndex('1');
await nextSlide();
await checkIndex('2');
await prevSlide();
await checkIndex('1');
});
});
async function checkIndex(index: string) {
expect(await element(by.css('#slide-index')).getText()).toEqual(index);
expect(await element(by.css('#slide-index-2')).getText()).toEqual(index);
}
async function addSlides() {
await element(by.css('#add-slides')).click();
await waitTime(800);
}
async function nextSlide() {
await element(by.css('#btn-next')).click();
await waitTime(800);
}
async function prevSlide() {
await element(by.css('#btn-prev')).click();
await waitTime(800);
}

View File

@@ -1,44 +0,0 @@
describe('Slides', () => {
beforeEach(() => {
cy.visit('/slides');
cy.wait(30);
})
it('should change index on slide change', () => {
cy.get('ion-slide').should('have.length', 0);
cy.get('#add-slides').click();
cy.get('ion-slide').should('have.length', 3);
// Should be on the first slide
checkIndex('0');
// Swipe to the second slide
nextSlide();
checkIndex('1');
// Swipe to the third slide
nextSlide();
checkIndex('2');
// Go back to the second slide
prevSlide();
checkIndex('1');
});
});
function checkIndex(index) {
cy.get('#slide-index').should('have.text', index);
cy.get('#slide-index-2').should('have.text', index);
}
function nextSlide() {
cy.get('#btn-next').click();
cy.wait(800);
}
function prevSlide() {
cy.get('#btn-prev').click();
cy.wait(800);
}

View File

@@ -0,0 +1,334 @@
import { browser, by, element, ElementFinder, ExpectedConditions } from 'protractor';
import { handleErrorMessages, testStack, waitTime } from './utils';
describe('tabs', () => {
afterEach(() => {
return handleErrorMessages();
});
describe('entry url - /tabs', () => {
beforeEach(async () => {
await browser.get('/tabs');
await waitTime(30);
});
it('should redirect and load tab-account', async () => {
await testTabTitle('Tab 1 - Page 1');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1']);
await testState(1, 'account');
});
it('should navigate between tabs and ionChange events should be dispatched ', async () => {
let tab = await testTabTitle('Tab 1 - Page 1');
expect(await tab.$('.segment-changed').getText()).toEqual('false');
await element(by.css('#tab-button-contact')).click();
tab = await testTabTitle('Tab 2 - Page 1');
expect(await tab.$('.segment-changed').getText()).toEqual('false');
});
it('should simulate stack + double tab click', async () => {
let tab = await getSelectedTab() as ElementFinder;
await tab.$('#goto-tab1-page2').click();
await testTabTitle('Tab 1 - Page 2 (1)');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested']);
await testState(1, 'account');
expect(await tab.$('ion-back-button').isDisplayed()).toBe(true);
await element(by.css('#tab-button-contact')).click();
await testTabTitle('Tab 2 - Page 1');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested', 'app-tabs-tab2']);
await testState(2, 'contact');
await element(by.css('#tab-button-account')).click();
tab = await testTabTitle('Tab 1 - Page 2 (1)');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested', 'app-tabs-tab2']);
await testState(3, 'account');
expect(await tab.$('ion-back-button').isDisplayed()).toBe(true);
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 1');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab2']);
await testState(3, 'account');
});
it('should simulate stack + back button click', async () => {
const tab = await getSelectedTab();
await tab.$('#goto-tab1-page2').click();
await testTabTitle('Tab 1 - Page 2 (1)');
await testState(1, 'account');
await element(by.css('#tab-button-contact')).click();
await testTabTitle('Tab 2 - Page 1');
await testState(2, 'contact');
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 2 (1)');
await testState(3, 'account');
await element(by.css('ion-back-button')).click();
await testTabTitle('Tab 1 - Page 1');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab2']);
await testState(3, 'account');
});
it('should navigate deep then go home', async () => {
let tab = await getSelectedTab();
await tab.$('#goto-tab1-page2').click();
tab = await testTabTitle('Tab 1 - Page 2 (1)');
await tab.$('#goto-next').click();
tab = await testTabTitle('Tab 1 - Page 2 (2)');
await element(by.css('#tab-button-contact')).click();
tab = await testTabTitle('Tab 2 - Page 1');
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 2 (2)');
await testStack('ion-tabs ion-router-outlet', [
'app-tabs-tab1',
'app-tabs-tab1-nested',
'app-tabs-tab1-nested',
'app-tabs-tab2'
]);
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 1');
await testStack('ion-tabs ion-router-outlet', [
'app-tabs-tab1',
'app-tabs-tab2'
]);
});
it('should switch tabs and go back', async () => {
await element(by.css('#tab-button-contact')).click();
const tab = await testTabTitle('Tab 2 - Page 1');
await tab.$('#goto-tab1-page1').click();
await testTabTitle('Tab 1 - Page 1');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab2']);
});
it('should switch tabs and go to nested', async () => {
await element(by.css('#tab-button-contact')).click();
const tab = await testTabTitle('Tab 2 - Page 1');
await tab.$('#goto-tab1-page2').click();
await testTabTitle('Tab 1 - Page 2 (1)');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab2', 'app-tabs-tab1-nested']);
});
it('should load lazy loaded tab', async () => {
await element(by.css('#tab-button-lazy')).click();
await testTabTitle('Tab 3 - Page 1');
});
it('should use ion-back-button defaultHref', async () => {
let tab = await getSelectedTab() as ElementFinder;
await tab.$('#goto-tab3-page2').click();
tab = await testTabTitle('Tab 3 - Page 2');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab3-nested']);
await tab.$('ion-back-button').click();
await testTabTitle('Tab 3 - Page 1');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab3']);
});
it('should preserve navigation extras when switching tabs', async () => {
const expectUrlToContain = 'search=hello#fragment';
let tab = await getSelectedTab() as ElementFinder;
await tab.$('#goto-nested-page1-with-query-params').click();
await testTabTitle('Tab 1 - Page 2 (1)');
await testUrlContains(expectUrlToContain);
await element(by.css('#tab-button-contact')).click();
await testTabTitle('Tab 2 - Page 1');
await element(by.css('#tab-button-account')).click();
tab = await testTabTitle('Tab 1 - Page 2 (1)');
await testUrlContains(expectUrlToContain);
});
it('should set root when clicking on an active tab to navigate to the root', async () => {
const expectNestedTabUrlToContain = 'search=hello#fragment';
let tab = await getSelectedTab() as ElementFinder;
const initialUrl = await browser.getCurrentUrl();
await tab.$('#goto-nested-page1-with-query-params').click();
await testTabTitle('Tab 1 - Page 2 (1)');
await testUrlContains(expectNestedTabUrlToContain);
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 1');
await testUrlEquals(initialUrl);
});
});
describe('entry tab contains navigation extras', () => {
const expectNestedTabUrlToContain = 'search=hello#fragment';
const rootUrlParams = 'test=123#rootFragment';
const rootUrl = `/tabs/account?${rootUrlParams}`;
beforeEach(async () => {
await browser.get(rootUrl);
await waitTime(30);
});
it('should preserve root url navigation extras when clicking on an active tab to navigate to the root', async () => {
await browser.get(rootUrl);
let tab = await getSelectedTab() as ElementFinder;
await tab.$('#goto-nested-page1-with-query-params').click();
await testTabTitle('Tab 1 - Page 2 (1)');
await testUrlContains(expectNestedTabUrlToContain);
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 1');
await testUrlContains(rootUrl);
});
it('should preserve root url navigation extras when changing tabs', async () => {
await browser.get(rootUrl);
let tab = await getSelectedTab() as ElementFinder;
await element(by.css('#tab-button-contact')).click();
tab = await testTabTitle('Tab 2 - Page 1');
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 1');
await testUrlContains(rootUrl);
});
it('should navigate deep then go home and preserve navigation extras', async () => {
let tab = await getSelectedTab();
await tab.$('#goto-tab1-page2').click();
tab = await testTabTitle('Tab 1 - Page 2 (1)');
await tab.$('#goto-next').click();
tab = await testTabTitle('Tab 1 - Page 2 (2)');
await element(by.css('#tab-button-contact')).click();
tab = await testTabTitle('Tab 2 - Page 1');
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 2 (2)');
await element(by.css('#tab-button-account')).click();
await testTabTitle('Tab 1 - Page 1');
await testUrlContains(rootUrl);
});
});
describe('entry url - /tabs/account/nested/1', () => {
beforeEach(async () => {
await browser.get('/tabs/account/nested/1');
await waitTime(30);
});
it('should only display the back-button when there is a page in the stack', async () => {
let tab = await testTabTitle('Tab 1 - Page 2 (1)') as ElementFinder;
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1-nested']);
expect(await tab.$('ion-back-button').isDisplayed()).toBe(false);
await element(by.css('#tab-button-account')).click();
tab = await testTabTitle('Tab 1 - Page 1');
await tab.$('#goto-tab1-page2').click();
tab = await testTabTitle('Tab 1 - Page 2 (1)');
expect(await tab.$('ion-back-button').isDisplayed()).toBe(true);
});
it('should not reuse the same page', async () => {
let tab = await testTabTitle('Tab 1 - Page 2 (1)') as ElementFinder;
await tab.$('#goto-next').click();
tab = await testTabTitle('Tab 1 - Page 2 (2)');
await tab.$('#goto-next').click();
tab = await testTabTitle('Tab 1 - Page 2 (3)');
await testStack('ion-tabs ion-router-outlet', [
'app-tabs-tab1-nested',
'app-tabs-tab1-nested',
'app-tabs-tab1-nested'
]);
await tab.$('ion-back-button').click();
tab = await testTabTitle('Tab 1 - Page 2 (2)');
await tab.$('ion-back-button').click();
tab = await testTabTitle('Tab 1 - Page 2 (1)');
expect(await tab.$('ion-back-button').isDisplayed()).toBe(false);
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1-nested']);
});
});
describe('entry url - /tabs/lazy', () => {
beforeEach(async () => {
await browser.get('/tabs/lazy');
await waitTime(30);
});
it('should not display the back-button if coming from a different stack', async () => {
let tab = await testTabTitle('Tab 3 - Page 1') as ElementFinder;
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab3']);
await tab.$('#goto-tab1-page2').click();
tab = await testTabTitle('Tab 1 - Page 2 (1)');
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab3', 'app-tabs-tab1-nested']);
expect(await tab.$('ion-back-button').isDisplayed()).toBe(false);
});
});
describe('enter url - /tabs/contact/one', () => {
beforeEach(async () => {
await browser.get('/tabs/contact/one');
await waitTime(30);
});
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 waitTime(600);
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) {
expect(await element(by.css('#tabs-state')).getText()).toEqual(`${count}.${tab}`);
}
async function testTabTitle(title: string) {
await waitTime(1000);
const tab = await getSelectedTab();
expect(await tab.$('ion-title').getText()).toEqual(title);
return tab;
}
async function testUrlContains(urlFragment: string) {
await browser.wait(ExpectedConditions.urlContains(urlFragment),
5000,
`expected ${browser.getCurrentUrl()} to contain ${urlFragment}`);
}
async function testUrlEquals(url: string) {
await browser.wait(ExpectedConditions.urlIs(url),
5000,
`expected ${browser.getCurrentUrl()} to equal ${url}`);
}
async function getSelectedTab(): Promise<ElementFinder> {
const tabs = element.all(by.css('ion-tabs ion-router-outlet > *:not(.ion-page-hidden)'));
expect(await tabs.count()).toEqual(1);
const tab = tabs.first();
return tab;
}

View File

@@ -1,329 +0,0 @@
describe('Tabs', () => {
beforeEach(() => {
cy.visit('/tabs');
})
describe('entry url - /tabs', () => {
it('should redirect and load tab-account', () => {
testTabTitle('Tab 1 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1']);
testState(1, 'account');
});
it('should navigate between tabs and ionChange events should be dispatched', () => {
let tab = testTabTitle('Tab 1 - Page 1');
tab.find('.segment-changed').should('have.text', 'false');
cy.get('#tab-button-contact').click();
tab = testTabTitle('Tab 2 - Page 1');
tab.find('.segment-changed').should('have.text', 'false');
});
it('should simulate stack + double tab click', () => {
let tab = getSelectedTab();
tab.find('#goto-tab1-page2').click();
testTabTitle('Tab 1 - Page 2 (1)');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested']);
testState(1, 'account');
// When you call find on tab above it changes the value of tab
// so we need to redefine it
tab = getSelectedTab();
tab.find('ion-back-button').should('be.visible');
cy.get('#tab-button-contact').click();
testTabTitle('Tab 2 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested', 'app-tabs-tab2']);
testState(2, 'contact');
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 2 (1)');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested', 'app-tabs-tab2']);
testState(3, 'account');
tab = getSelectedTab();
tab.find('ion-back-button').should('be.visible');
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab2']);
testState(3, 'account');
});
it('should simulate stack + back button click', () => {
const tab = getSelectedTab();
tab.find('#goto-tab1-page2').click();
testTabTitle('Tab 1 - Page 2 (1)');
testState(1, 'account');
cy.get('#tab-button-contact').click();
testTabTitle('Tab 2 - Page 1');
testState(2, 'contact');
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 2 (1)');
testState(3, 'account');
cy.get('ion-back-button').click();
testTabTitle('Tab 1 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab2']);
testState(3, 'account');
});
it('should navigate deep then go home', () => {
const tab = getSelectedTab();
tab.find('#goto-tab1-page2').click();
testTabTitle('Tab 1 - Page 2 (1)');
cy.get('#goto-next').click();
testTabTitle('Tab 1 - Page 2 (2)');
cy.get('#tab-button-contact').click();
testTabTitle('Tab 2 - Page 1');
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 2 (2)');
cy.testStack('ion-tabs ion-router-outlet', [
'app-tabs-tab1',
'app-tabs-tab1-nested',
'app-tabs-tab1-nested',
'app-tabs-tab2'
]);
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', [
'app-tabs-tab1',
'app-tabs-tab2'
]);
});
it('should switch tabs and go back', () => {
cy.get('#tab-button-contact').click();
const tab = testTabTitle('Tab 2 - Page 1');
tab.find('#goto-tab1-page1').click();
testTabTitle('Tab 1 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab2']);
});
it('should switch tabs and go to nested', () => {
cy.get('#tab-button-contact').click();
const tab = testTabTitle('Tab 2 - Page 1');
tab.find('#goto-tab1-page2').click();
testTabTitle('Tab 1 - Page 2 (1)');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab2', 'app-tabs-tab1-nested']);
});
it('should load lazy loaded tab', () => {
cy.get('#tab-button-lazy').click();
cy.ionPageVisible('app-tabs-tab3');
testTabTitle('Tab 3 - Page 1');
});
it('should use ion-back-button defaultHref', () => {
let tab = getSelectedTab();
tab.find('#goto-tab3-page2').click();
testTabTitle('Tab 3 - Page 2');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab3-nested']);
tab = getSelectedTab();
tab.find('ion-back-button').click();
testTabTitle('Tab 3 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab3']);
});
it('should preserve navigation extras when switching tabs', () => {
const expectUrlToContain = 'search=hello#fragment';
let tab = getSelectedTab();
tab.find('#goto-nested-page1-with-query-params').click();
testTabTitle('Tab 1 - Page 2 (1)');
testUrlContains(expectUrlToContain);
cy.get('#tab-button-contact').click();
testTabTitle('Tab 2 - Page 1');
cy.get('#tab-button-account').click();
tab = testTabTitle('Tab 1 - Page 2 (1)');
testUrlContains(expectUrlToContain);
});
it('should set root when clicking on an active tab to navigate to the root', () => {
const expectNestedTabUrlToContain = 'search=hello#fragment';
cy.url().then(url => {
const tab = getSelectedTab();
tab.find('#goto-nested-page1-with-query-params').click();
testTabTitle('Tab 1 - Page 2 (1)');
testUrlContains(expectNestedTabUrlToContain);
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 1');
testUrlEquals(url);
})
});
})
describe('entry tab contains navigation extras', () => {
const expectNestedTabUrlToContain = 'search=hello#fragment';
const rootUrlParams = 'test=123#rootFragment';
const rootUrl = `/tabs/account?${rootUrlParams}`;
beforeEach(() => {
cy.visit(rootUrl);
})
it('should preserve root url navigation extras when clicking on an active tab to navigate to the root', () => {
const tab = getSelectedTab();
tab.find('#goto-nested-page1-with-query-params').click();
testTabTitle('Tab 1 - Page 2 (1)');
testUrlContains(expectNestedTabUrlToContain);
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 1');
testUrlContains(rootUrl);
});
it('should preserve root url navigation extras when changing tabs', () => {
getSelectedTab();
cy.get('#tab-button-contact').click();
testTabTitle('Tab 2 - Page 1');
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 1');
testUrlContains(rootUrl);
});
it('should navigate deep then go home and preserve navigation extras', () => {
let tab = getSelectedTab();
tab.find('#goto-tab1-page2').click();
tab = testTabTitle('Tab 1 - Page 2 (1)');
tab.find('#goto-next').click();
testTabTitle('Tab 1 - Page 2 (2)');
cy.get('#tab-button-contact').click();
testTabTitle('Tab 2 - Page 1');
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 2 (2)');
cy.get('#tab-button-account').click();
testTabTitle('Tab 1 - Page 1');
testUrlContains(rootUrl);
});
})
describe('entry url - /tabs/account/nested/1', () => {
beforeEach(() => {
cy.visit('/tabs/account/nested/1');
})
it('should only display the back-button when there is a page in the stack', () => {
let tab = getSelectedTab();
tab.find('ion-back-button').should('not.be.visible');
testTabTitle('Tab 1 - Page 2 (1)');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1-nested']);
cy.get('#tab-button-account').click();
tab = testTabTitle('Tab 1 - Page 1');
tab.find('#goto-tab1-page2').click();
tab = testTabTitle('Tab 1 - Page 2 (1)');
tab.find('ion-back-button').should('be.visible');
});
it('should not reuse the same page', () => {
let tab = testTabTitle('Tab 1 - Page 2 (1)');
tab.find('#goto-next').click();
tab = testTabTitle('Tab 1 - Page 2 (2)');
tab.find('#goto-next').click();
tab = testTabTitle('Tab 1 - Page 2 (3)');
cy.testStack('ion-tabs ion-router-outlet', [
'app-tabs-tab1-nested',
'app-tabs-tab1-nested',
'app-tabs-tab1-nested'
]);
tab = getSelectedTab();
tab.find('ion-back-button').click();
tab = testTabTitle('Tab 1 - Page 2 (2)');
tab.find('ion-back-button').click();
tab = testTabTitle('Tab 1 - Page 2 (1)');
tab.find('ion-back-button').should('not.be.visible');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1-nested']);
});
})
describe('entry url - /tabs/lazy', () => {
beforeEach(() => {
cy.visit('/tabs/lazy');
});
it('should not display the back-button if coming from a different stack', () => {
let tab = testTabTitle('Tab 3 - Page 1');
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab3']);
tab = getSelectedTab();
tab.find('#goto-tab1-page2').click();
cy.testStack('ion-tabs ion-router-outlet', ['app-tabs-tab3', 'app-tabs-tab1-nested']);
tab = testTabTitle('Tab 1 - Page 2 (1)');
tab.find('ion-back-button').should('not.be.visible');
});
})
describe('enter url - /tabs/contact/one', () => {
beforeEach(() => {
cy.visit('/tabs/contact/one');
});
it('should return to correct tab after going to page in different outlet', () => {
const tab = getSelectedTab();
tab.find('#goto-nested-page1').click();
cy.testStack('app-nested-outlet ion-router-outlet', ['app-nested-outlet-page']);
const nestedOutlet = cy.get('app-nested-outlet');
nestedOutlet.find('ion-back-button').click();
testTabTitle('Tab 2 - Page 1');
});
})
})
function testTabTitle(title) {
const tab = getSelectedTab();
// Find is used to get a direct descendant instead of get
tab.find('ion-title').should('have.text', title);
return getSelectedTab();
}
function getSelectedTab() {
cy.get('ion-tabs ion-router-outlet > *:not(.ion-page-hidden)').should('have.length', 1);
return cy.get('ion-tabs ion-router-outlet > *:not(.ion-page-hidden)').first();
}
function testState(count, tab) {
cy.get('#tabs-state').should('have.text', `${count}.${tab}`);
}
function testUrlContains(urlFragment) {
cy.location().should((location) => {
expect(location.href).to.contain(urlFragment);
});
}
function testUrlEquals(url) {
cy.url().should('eq', url);
}

View File

@@ -0,0 +1,73 @@
import { browser } from 'protractor';
export function goBack() {
return browser.executeScript(`return window.history.back()`);
}
export function getProperty(selector: string, property: string) {
return browser.executeScript(`
return document.querySelector('${selector}')['${property}'];
`);
}
export function getText(selector: string) {
return browser.executeScript(`
return document.querySelector('${selector}').textContent;
`);
}
export function setProperty(selector: string, property: string, value: any) {
const text = JSON.stringify(value);
return browser.executeScript(`
document.querySelector('${selector}')['${property}'] = ${text};
`);
}
export function waitTime(time: number) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
export interface LifeCycleCount {
ionViewWillEnter: number;
ionViewDidEnter: number;
ionViewWillLeave: number;
ionViewDidLeave: number;
}
export function handleErrorMessages() {
return browser.manage().logs().get('browser').then(function (browserLog) {
for (let i = 0; i <= browserLog.length - 1; i++) {
if (browserLog[i].level.name_ === 'SEVERE') {
fail(browserLog[i].message);
}
}
});
}
export async function testLifeCycle(selector: string, expected: LifeCycleCount) {
await waitTime(50);
const results = await Promise.all([
getText(`${selector} #ngOnInit`),
getText(`${selector} #ionViewWillEnter`),
getText(`${selector} #ionViewDidEnter`),
getText(`${selector} #ionViewWillLeave`),
getText(`${selector} #ionViewDidLeave`),
]);
expect(results[0]).toEqual('1');
expect(results[1]).toEqual(expected.ionViewWillEnter.toString());
expect(results[2]).toEqual(expected.ionViewDidEnter.toString());
expect(results[3]).toEqual(expected.ionViewWillLeave.toString());
expect(results[4]).toEqual(expected.ionViewDidLeave.toString());
}
export async function testStack(selector: string, expected: string[]) {
const children = await browser.executeScript(`
return Array.from(
document.querySelector('${selector}').children
).map(el => el.tagName.toLowerCase());
`);
expect(children).toEqual(expected);
}

View File

@@ -0,0 +1,22 @@
import { browser, element, by } from 'protractor';
import { handleErrorMessages, waitTime } from './utils';
describe('view-child', () => {
beforeEach(async () => {
await browser.get('/view-child');
await waitTime(30);
});
afterEach(() => {
return handleErrorMessages();
});
it('should get a reference to all children', async () => {
// button should be red
expect(await element(by.css('#color-button.ion-color-danger')).isPresent()).toBeTruthy();
// tabs should be found
expect(await element(by.css('#tabs-result')).getText()).toEqual('all found');
});
});

View File

@@ -1,14 +0,0 @@
describe('View Child', () => {
beforeEach(() => {
cy.visit('/view-child');
})
it('should get a reference to all children', () => {
// button should be red
cy.get('#color-button').should('have.class', 'ion-color-danger');
// tabs should be found
cy.get('#tabs-result').should('have.text', 'all found');
});
});

View File

@@ -0,0 +1,18 @@
import { browser, element, by } from 'protractor';
import { waitTime, handleErrorMessages } from './utils';
describe('virtual-scroll', () => {
afterEach(() => {
return handleErrorMessages();
});
beforeEach(async () => {
await browser.get('/virtual-scroll');
await waitTime(30);
});
it('should open virtual-scroll', () => {
const virtualElements = element.all(by.css('ion-virtual-scroll > *'));
expect(virtualElements.count()).toBeGreaterThan(0);
});
});

View File

@@ -1,14 +0,0 @@
describe('Virtual Scroll', () => {
beforeEach(() => {
cy.visit('/virtual-scroll');
cy.wait(30);
})
it('should open virtual-scroll', () => {
cy.document().then((doc) => {
const virtualElements = doc.querySelectorAll('ion-virtual-scroll > *');
expect(virtualElements.length).to.be.greaterThan(0);
});
});
});

View File

@@ -4,17 +4,14 @@
"strictMetadataEmit" : true
},
"extends": "../tsconfig.json",
"include": [
"src/**spec.ts",
"../cypress/support/index.d.ts"
],
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"cypress",
"jasmine",
"jasminewd2",
"node"
]
}
}
}

View File

@@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

File diff suppressed because it is too large Load Diff

View File

@@ -8,53 +8,56 @@
"sync:build": "sh scripts/build-ionic.sh",
"sync": "sh scripts/sync.sh",
"build": "npm run sync && ng build --prod --no-progress",
"test": "ng e2e --prod",
"test.dev": "npm run sync && ng e2e",
"lint": "ng lint",
"postinstall": "npm run sync && ngcc",
"serve:ssr": "node dist/test-app/server/main.js",
"build:ssr": "ng build --prod && ng run test-app:server:production",
"dev:ssr": "ng run test-app:serve-ssr",
"prerender": "ng run test-app:prerender",
"cy.open": "cypress open",
"cy.run": "cypress run",
"test": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.run\" --kill-others --success first",
"test.watch": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.open\" --kill-others --success first"
"prerender": "ng run test-app:prerender"
},
"dependencies": {
"@angular/animations": "^11.2.11",
"@angular/common": "^11.2.11",
"@angular/compiler": "^11.2.11",
"@angular/core": "^11.2.11",
"@angular/forms": "^11.2.11",
"@angular/platform-browser": "^11.2.11",
"@angular/platform-browser-dynamic": "^11.2.11",
"@angular/platform-server": "^11.2.11",
"@angular/router": "^11.2.11",
"@ionic/angular": "^5.3.1",
"@ionic/angular-server": "^5.3.1",
"@nguniversal/express-engine": "^11.2.1",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"@angular/animations": "^9.0.0-rc.2",
"@angular/common": "^9.0.0-rc.2",
"@angular/compiler": "^9.0.0-rc.2",
"@angular/core": "^9.0.0-rc.2",
"@angular/forms": "^9.0.0-rc.2",
"@angular/platform-browser": "^9.0.0-rc.2",
"@angular/platform-browser-dynamic": "^9.0.0-rc.2",
"@angular/platform-server": "^9.0.0-rc.2",
"@angular/router": "^9.0.0-rc.2",
"@ionic/angular": "^4.7.0",
"@ionic/angular-server": "^0.0.2",
"@nguniversal/express-engine": "9.0.0-next.9",
"core-js": "^2.6.2",
"express": "^4.15.2",
"rxjs": "^6.5.5",
"tslib": "^2.0.0",
"zone.js": "^0.10.3"
"rxjs": "^6.5.3",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.1102.10",
"@angular/cli": "^11.2.10",
"@angular/compiler-cli": "^11.2.11",
"@angular/language-service": "^11.2.11",
"@nguniversal/builders": "^11.2.1",
"@types/express": "^4.17.7",
"@types/node": "^12.12.54",
"codelyzer": "^6.0.1",
"concurrently": "^6.0.0",
"cypress": "^6.7.1",
"ts-loader": "^6.2.2",
"ts-node": "^8.3.0",
"tslint": "~6.1.0",
"typescript": "^4.0.7",
"wait-on": "^5.2.1",
"webpack-cli": "^3.3.12"
"@angular-devkit/build-angular": "~0.900.0-rc.2",
"@angular/cli": "^9.0.0-rc.2",
"@angular/compiler-cli": "^9.0.0-rc.2",
"@angular/language-service": "^9.0.0-rc.2",
"@nguniversal/builders": "9.0.0-next.9",
"@types/express": "^4.17.0",
"@types/jasmine": "3.4.1",
"@types/node": "^12.11.1",
"codelyzer": "^5.1.2",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.1.0",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"protractor": "~5.4.2",
"ts-loader": "^6.1.2",
"ts-node": "8.4.1",
"tslint": "~5.18.0",
"typescript": "~3.6.4",
"webpack-cli": "^3.3.9"
}
}

View File

@@ -1,8 +1,5 @@
<ion-header>
<ion-toolbar>
<ion-buttons>
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>
Alert test
</ion-title>

View File

@@ -35,11 +35,9 @@
<ion-item>
<ion-label>Input (required)</ion-label>
<ion-input formControlName="input" class="required" id="touched-input-test"></ion-input>
<ion-input formControlName="input" class="required"></ion-input>
</ion-item>
<ion-button id="input-touched" (click)="setTouched()">Set Input Touched</ion-button>
<ion-item>
<ion-label>Input</ion-label>
<ion-input formControlName="input2"></ion-input>

View File

@@ -25,11 +25,6 @@ export class FormComponent {
});
}
setTouched() {
const formControl = this.profileForm.get('input');
formControl.markAsTouched();
}
onSubmit(_ev) {
this.submitted = 'true';
}

View File

@@ -7,7 +7,7 @@
</ion-header>
<ion-content>
<ion-list>
<ion-item routerLink="/alerts" [routerAnimation]="routerAnimation">
<ion-item routerLink="/alerts">
<ion-label>
Alerts test
</ion-label>

View File

@@ -1,25 +1,9 @@
import { Component } from '@angular/core';
import { AnimationBuilder, AnimationController } from '@ionic/angular';
@Component({
selector: 'app-home-page',
templateUrl: './home-page.component.html',
})
export class HomePageComponent {
routerAnimation: AnimationBuilder = (_, opts) => {
const { direction, enteringEl, leavingEl } = opts;
const animation = this.animationCtrl.create().duration(500).easing('ease-out');
const enteringAnimation = this.animationCtrl.create().addElement(enteringEl).beforeRemoveClass(['ion-page-invisible']);
const leavingAnimation = this.animationCtrl.create().addElement(leavingEl).beforeRemoveClass(['ion-page-invisible']);
if (direction === 'back') {
enteringAnimation.fromTo('transform', 'translateX(-100%)', 'translateX(0%)');
leavingAnimation.fromTo('transform', 'translateX(0%)', 'translateX(100%)');
} else {
enteringAnimation.fromTo('transform', 'translateX(100%)', 'translateX(0%)');
leavingAnimation.fromTo('transform', 'translateX(0%)', 'translateX(-100%)');
}
return animation.addAnimation([enteringAnimation, leavingAnimation]);
};
constructor(private animationCtrl: AnimationController) {}
}

View File

@@ -27,7 +27,6 @@ export class ModalComponent {
async open(TheModalComponent: any) {
const modal = await this.modalCtrl.create({
component: TheModalComponent,
animated: false,
componentProps: {
value: '123',
prop: '321'
@@ -41,7 +40,7 @@ export class ModalComponent {
modal.onDidDismiss().then(() => {
NgZone.assertInAngularZone();
if (!this.onWillDismiss) {
throw new Error('onWillDismiss should be emitted first');
throw new Error('onWillDismiss should be emited first');
}
this.onDidDismiss = true;
});

View File

@@ -0,0 +1,20 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -6,7 +6,7 @@
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"module": "es2020",
"module": "esnext",
"target": "es2015",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,

View File

@@ -2,7 +2,7 @@
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "./out-tsc/app-server",
"target": "es2016",
"module": "commonjs",
"types": [
"node"
]

View File

@@ -3,6 +3,7 @@
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},

View File

@@ -2,37 +2,16 @@
"extends": "tslint:recommended",
"rulesDirectory": ["codelyzer"],
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-parens": false,
"arrow-return-shorthand": true,
"deprecation": {
"severity": "warn"
},
"import-blacklist": [
true,
"rxjs/Rx"
],
"curly": true,
"import-blacklist": [true, "rxjs/Rx"],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"eofline": true,
"max-line-length": [true, 140],
"member-access": false,
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"member-ordering": [
true,
{
@@ -51,6 +30,7 @@
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [true, "as-needed"],
"object-literal-sort-keys": false,
@@ -66,68 +46,9 @@
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"one-variable-per-declaration": false,
"component-class-suffix": [
true,
"Page",
"Component"
],
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"component-class-suffix": [true, "Page", "Component"],
"directive-class-suffix": true,
"typedef-whitespace": {
"options": [
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
]
},
"directive-selector": [true, "attribute", "app", "camelCase"],
"component-selector": [
true,
"element",
"app",
"page",
"kebab-case"
],
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
}
"component-selector": [true, "element", "app", "page", "kebab-case"]
}
}

1
core/.npmrc Normal file
View File

@@ -0,0 +1 @@
package-lock=false

121
core/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,121 @@
# Contribution guide
## Contribute to Ionic Core
Ionic Core is the fundation of Ionic v4. It's based in [Stencil](https://stenciljs.com) and consist in a set of web components a long of JS and CSS utils.
### Installing dependencies
Before you can build ionic, we assume the following list of software is already installed in your system
- Git
- Node 8 or higher
- Npm 6.0 or higher
### Fork repository
In order to contributo to Ionic, you must have a github account so you can push code and create a new Pull Request (PR).
Once you are all setup, following the Github's guide of how to fork a repository: https://guides.github.com/activities/forking/
### Build Ionic Core
```bash
# Clone your Github repository:
git clone https://github.com/<github username>/ionic.git
# Go to the core directory:
cd ionic/core
# Install npm dependencies
npm install
# Run Ionic dev server
npm start
```
### Development Workflow
#### 1. Run Dev Server
```bash
# Move to the core folder
cd core
# Run dev server
npm start
```
You should be able to navigate to `http://localhost:3333` which will look like a file browser.
E2E tests are located inside the `src/components` folder, in the following way: `http://localhost:3333/src/components/{COMPONENT}/test/`
**Path examples:**
- ActionSheet basic test: http://localhost:3333/src/components/action-sheet/test/basic
- Nav basic test: http://localhost:3333/src/components/nav/test/basic
- Button basic test:
http://localhost:3333/src/components/button/test/basic
**IMPORTANT**
Leave the dev server running in the background while you make changes. The dev server listen for changes and automatically recompile Ionic for you.
#### 2. Open `core` folder in your IDE
Components implementations live inside the `core/src/components` folder.
You can find each ionic component inside their directory.
#### 3. Run test suite
Before commiting your changes make sure tests are passing:
```
npm run validate
```
#### 4. Create a branch and commit
```bash
# Create a git branch
git checkout -b my-improvement
# Add changes
git add .
# Create commit
git commit -m "fix(component): message"
```
Create a PR:
https://guides.github.com/activities/forking/
### Summary
```bash
# Clone repo
git clone git@github.com:ionic-team/ionic.git
# Move to ionic/core folder
cd ionic/core
# Install npm dependencies
npm i
# Run dev server
npm start
# Run test suite
npm run validate
```

View File

@@ -40,24 +40,6 @@ The `@ionic/core` package can by used in simple HTML, or by vanilla JavaScript w
* [@ionic/angular](https://www.npmjs.com/package/@ionic/angular)
## Custom Elements Build (Experimental)
In addition to the default, self lazy-loading components built by Stencil, this package also comes with each component exported as a stand-alone custom element within `@ionic/core/components`. Each component extends `HTMLElement`, and does not lazy-load itself. Instead, this package is useful for projects already using a bundler such as Webpack or Rollup. While all components are available to be imported, the custom elements build also ensures bundlers only import what's used, and tree-shakes any unused components.
Below is an example of importing `ion-toggle`, and initializing Ionic so it's able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
```typescript
import { IonBadge } from "@ionic/core/components/ion-badge";
import { initialize } from "@ionic/core/components";
initialize();
customElements.define("ion-badge", IonBadge);
```
Notice how `IonBadge` is imported from `@ionic/core/components/ion-badge` rather than just `@ionic/core/components`. Additionally, the `initialize` function is imported from `@ionic/core/components` rather than `@ionic/core`. All of this helps to ensure bundlers do not pull in more code than is needed.
## How to contribute
[Check out the CONTRIBUTE guide](CONTRIBUTING.md)
@@ -65,6 +47,7 @@ Notice how `IonBadge` is imported from `@ionic/core/components/ion-badge` rather
## Related
* [Ionic Documentation](https://ionicframework.com/docs/)
* [Ionic Worldwide Slack](http://ionicworldwide.herokuapp.com/)
* [Ionic Forum](https://forum.ionicframework.com/)
* [Ionicons](http://ionicons.com/)
* [Stencil](https://stenciljs.com/)

View File

@@ -80,12 +80,11 @@ ion-avatar,shadow
ion-avatar,css-prop,--border-radius
ion-back-button,shadow
ion-back-button,prop,color,string | undefined,undefined,false,true
ion-back-button,prop,color,string | undefined,undefined,false,false
ion-back-button,prop,defaultHref,string | undefined,undefined,false,false
ion-back-button,prop,disabled,boolean,false,false,true
ion-back-button,prop,icon,null | string | undefined,undefined,false,false
ion-back-button,prop,mode,"ios" | "md",undefined,false,false
ion-back-button,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-back-button,prop,text,null | string | undefined,undefined,false,false
ion-back-button,prop,type,"button" | "reset" | "submit",'button',false,false
ion-back-button,css-prop,--background
@@ -120,9 +119,6 @@ ion-back-button,css-prop,--padding-start
ion-back-button,css-prop,--padding-top
ion-back-button,css-prop,--ripple-color
ion-back-button,css-prop,--transition
ion-back-button,part,icon
ion-back-button,part,native
ion-back-button,part,text
ion-backdrop,shadow
ion-backdrop,prop,stopPropagation,boolean,true,false,false
@@ -131,7 +127,7 @@ ion-backdrop,prop,visible,boolean,true,false,false
ion-backdrop,event,ionBackdropTap,void,true
ion-badge,shadow
ion-badge,prop,color,string | undefined,undefined,false,true
ion-badge,prop,color,string | undefined,undefined,false,false
ion-badge,prop,mode,"ios" | "md",undefined,false,false
ion-badge,css-prop,--background
ion-badge,css-prop,--color
@@ -142,7 +138,7 @@ ion-badge,css-prop,--padding-top
ion-button,shadow
ion-button,prop,buttonType,string,'button',false,false
ion-button,prop,color,string | undefined,undefined,false,true
ion-button,prop,color,string | undefined,undefined,false,false
ion-button,prop,disabled,boolean,false,false,true
ion-button,prop,download,string | undefined,undefined,false,false
ion-button,prop,expand,"block" | "full" | undefined,undefined,false,true
@@ -150,7 +146,6 @@ ion-button,prop,fill,"clear" | "default" | "outline" | "solid" | undefined,undef
ion-button,prop,href,string | undefined,undefined,false,false
ion-button,prop,mode,"ios" | "md",undefined,false,false
ion-button,prop,rel,string | undefined,undefined,false,false
ion-button,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-button,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-button,prop,shape,"round" | undefined,undefined,false,true
ion-button,prop,size,"default" | "large" | "small" | undefined,undefined,false,true
@@ -182,48 +177,45 @@ ion-button,css-prop,--padding-start
ion-button,css-prop,--padding-top
ion-button,css-prop,--ripple-color
ion-button,css-prop,--transition
ion-button,part,native
ion-buttons,scoped
ion-buttons,prop,collapse,boolean,false,false,false
ion-card,shadow
ion-card,prop,button,boolean,false,false,false
ion-card,prop,color,string | undefined,undefined,false,true
ion-card,prop,color,string | undefined,undefined,false,false
ion-card,prop,disabled,boolean,false,false,false
ion-card,prop,download,string | undefined,undefined,false,false
ion-card,prop,href,string | undefined,undefined,false,false
ion-card,prop,mode,"ios" | "md",undefined,false,false
ion-card,prop,rel,string | undefined,undefined,false,false
ion-card,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-card,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-card,prop,target,string | undefined,undefined,false,false
ion-card,prop,type,"button" | "reset" | "submit",'button',false,false
ion-card,css-prop,--background
ion-card,css-prop,--color
ion-card,part,native
ion-card-content,none
ion-card-content,prop,mode,"ios" | "md",undefined,false,false
ion-card-header,shadow
ion-card-header,prop,color,string | undefined,undefined,false,true
ion-card-header,prop,color,string | undefined,undefined,false,false
ion-card-header,prop,mode,"ios" | "md",undefined,false,false
ion-card-header,prop,translucent,boolean,false,false,false
ion-card-subtitle,shadow
ion-card-subtitle,prop,color,string | undefined,undefined,false,true
ion-card-subtitle,prop,color,string | undefined,undefined,false,false
ion-card-subtitle,prop,mode,"ios" | "md",undefined,false,false
ion-card-subtitle,css-prop,--color
ion-card-title,shadow
ion-card-title,prop,color,string | undefined,undefined,false,true
ion-card-title,prop,color,string | undefined,undefined,false,false
ion-card-title,prop,mode,"ios" | "md",undefined,false,false
ion-card-title,css-prop,--color
ion-checkbox,shadow
ion-checkbox,prop,checked,boolean,false,false,false
ion-checkbox,prop,color,string | undefined,undefined,false,true
ion-checkbox,prop,color,string | undefined,undefined,false,false
ion-checkbox,prop,disabled,boolean,false,false,false
ion-checkbox,prop,indeterminate,boolean,false,false,false
ion-checkbox,prop,mode,"ios" | "md",undefined,false,false
@@ -247,8 +239,7 @@ ion-checkbox,part,container
ion-checkbox,part,mark
ion-chip,shadow
ion-chip,prop,color,string | undefined,undefined,false,true
ion-chip,prop,disabled,boolean,false,false,false
ion-chip,prop,color,string | undefined,undefined,false,false
ion-chip,prop,mode,"ios" | "md",undefined,false,false
ion-chip,prop,outline,boolean,false,false,false
ion-chip,css-prop,--background
@@ -288,7 +279,7 @@ ion-col,css-prop,--ion-grid-column-padding-xs
ion-col,css-prop,--ion-grid-columns
ion-content,shadow
ion-content,prop,color,string | undefined,undefined,false,true
ion-content,prop,color,string | undefined,undefined,false,false
ion-content,prop,forceOverscroll,boolean | undefined,undefined,false,false
ion-content,prop,fullscreen,boolean,false,false,false
ion-content,prop,scrollEvents,boolean,false,false,false
@@ -297,7 +288,7 @@ ion-content,prop,scrollY,boolean,true,false,false
ion-content,method,getScrollElement,getScrollElement() => Promise<HTMLElement>
ion-content,method,scrollByPoint,scrollByPoint(x: number, y: number, duration: number) => Promise<void>
ion-content,method,scrollToBottom,scrollToBottom(duration?: number) => Promise<void>
ion-content,method,scrollToPoint,scrollToPoint(x: number | undefined | null, y: number | undefined | null, duration?: number) => Promise<void>
ion-content,method,scrollToPoint,scrollToPoint(x: number | null | undefined, y: number | null | undefined, duration?: number) => Promise<void>
ion-content,method,scrollToTop,scrollToTop(duration?: number) => Promise<void>
ion-content,event,ionScroll,ScrollDetail,true
ion-content,event,ionScrollEnd,ScrollBaseDetail,true
@@ -333,7 +324,7 @@ ion-datetime,prop,monthShortNames,string | string[] | undefined,undefined,false,
ion-datetime,prop,monthValues,number | number[] | string | undefined,undefined,false,false
ion-datetime,prop,name,string,this.inputId,false,false
ion-datetime,prop,pickerFormat,string | undefined,undefined,false,false
ion-datetime,prop,pickerOptions,undefined | { columns?: PickerColumn[] | undefined; buttons?: PickerButton[] | undefined; cssClass?: string | string[] | undefined; showBackdrop?: boolean | undefined; backdropDismiss?: boolean | undefined; animated?: boolean | undefined; mode?: Mode | undefined; keyboardClose?: boolean | undefined; id?: string | undefined; enterAnimation?: AnimationBuilder | undefined; leaveAnimation?: AnimationBuilder | undefined; },undefined,false,false
ion-datetime,prop,pickerOptions,undefined | { columns?: PickerColumn[] | undefined; buttons?: PickerButton[] | undefined; cssClass?: string | string[] | undefined; showBackdrop?: boolean | undefined; backdropDismiss?: boolean | undefined; animated?: boolean | undefined; mode?: "ios" | "md" | undefined; keyboardClose?: boolean | undefined; id?: string | undefined; enterAnimation?: AnimationBuilder | undefined; leaveAnimation?: AnimationBuilder | undefined; },undefined,false,false
ion-datetime,prop,placeholder,null | string | undefined,undefined,false,false
ion-datetime,prop,readonly,boolean,false,false,false
ion-datetime,prop,value,null | string | undefined,undefined,false,false
@@ -360,14 +351,12 @@ ion-fab,method,close,close() => Promise<void>
ion-fab-button,shadow
ion-fab-button,prop,activated,boolean,false,false,false
ion-fab-button,prop,closeIcon,string,'close',false,false
ion-fab-button,prop,color,string | undefined,undefined,false,true
ion-fab-button,prop,color,string | undefined,undefined,false,false
ion-fab-button,prop,disabled,boolean,false,false,false
ion-fab-button,prop,download,string | undefined,undefined,false,false
ion-fab-button,prop,href,string | undefined,undefined,false,false
ion-fab-button,prop,mode,"ios" | "md",undefined,false,false
ion-fab-button,prop,rel,string | undefined,undefined,false,false
ion-fab-button,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-fab-button,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-fab-button,prop,show,boolean,false,false,false
ion-fab-button,prop,size,"small" | undefined,undefined,false,false
@@ -388,7 +377,6 @@ ion-fab-button,css-prop,--border-radius
ion-fab-button,css-prop,--border-style
ion-fab-button,css-prop,--border-width
ion-fab-button,css-prop,--box-shadow
ion-fab-button,css-prop,--close-icon-font-size
ion-fab-button,css-prop,--color
ion-fab-button,css-prop,--color-activated
ion-fab-button,css-prop,--color-focused
@@ -399,8 +387,6 @@ ion-fab-button,css-prop,--padding-start
ion-fab-button,css-prop,--padding-top
ion-fab-button,css-prop,--ripple-color
ion-fab-button,css-prop,--transition
ion-fab-button,part,close-icon
ion-fab-button,part,native
ion-fab-list,shadow
ion-fab-list,prop,activated,boolean,false,false,false
@@ -457,7 +443,7 @@ ion-input,prop,autocorrect,"off" | "on",'off',false,false
ion-input,prop,autofocus,boolean,false,false,false
ion-input,prop,clearInput,boolean,false,false,false
ion-input,prop,clearOnEdit,boolean | undefined,undefined,false,false
ion-input,prop,color,string | undefined,undefined,false,true
ion-input,prop,color,string | undefined,undefined,false,false
ion-input,prop,debounce,number,0,false,false
ion-input,prop,disabled,boolean,false,false,false
ion-input,prop,enterkeyhint,"done" | "enter" | "go" | "next" | "previous" | "search" | "send" | undefined,undefined,false,false
@@ -476,13 +462,13 @@ ion-input,prop,required,boolean,false,false,false
ion-input,prop,size,number | undefined,undefined,false,false
ion-input,prop,spellcheck,boolean,false,false,false
ion-input,prop,step,string | undefined,undefined,false,false
ion-input,prop,type,"date" | "datetime-local" | "email" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week",'text',false,false
ion-input,prop,type,"date" | "email" | "number" | "password" | "search" | "tel" | "text" | "time" | "url",'text',false,false
ion-input,prop,value,null | number | string | undefined,'',false,false
ion-input,method,getInputElement,getInputElement() => Promise<HTMLInputElement>
ion-input,method,setFocus,setFocus() => Promise<void>
ion-input,event,ionBlur,FocusEvent,true
ion-input,event,ionBlur,void,true
ion-input,event,ionChange,InputChangeEventDetail,true
ion-input,event,ionFocus,FocusEvent,true
ion-input,event,ionFocus,void,true
ion-input,event,ionInput,KeyboardEvent,true
ion-input,css-prop,--background
ion-input,css-prop,--color
@@ -497,7 +483,7 @@ ion-input,css-prop,--placeholder-opacity
ion-item,shadow
ion-item,prop,button,boolean,false,false,false
ion-item,prop,color,string | undefined,undefined,false,true
ion-item,prop,color,string | undefined,undefined,false,false
ion-item,prop,detail,boolean | undefined,undefined,false,false
ion-item,prop,detailIcon,string,'chevron-forward',false,false
ion-item,prop,disabled,boolean,false,false,false
@@ -506,7 +492,6 @@ ion-item,prop,href,string | undefined,undefined,false,false
ion-item,prop,lines,"full" | "inset" | "none" | undefined,undefined,false,false
ion-item,prop,mode,"ios" | "md",undefined,false,false
ion-item,prop,rel,string | undefined,undefined,false,false
ion-item,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-item,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-item,prop,target,string | undefined,undefined,false,false
ion-item,prop,type,"button" | "reset" | "submit",'button',false,false
@@ -546,10 +531,9 @@ ion-item,css-prop,--padding-top
ion-item,css-prop,--ripple-color
ion-item,css-prop,--transition
ion-item,part,detail-icon
ion-item,part,native
ion-item-divider,shadow
ion-item-divider,prop,color,string | undefined,undefined,false,true
ion-item-divider,prop,color,string | undefined,undefined,false,false
ion-item-divider,prop,mode,"ios" | "md",undefined,false,false
ion-item-divider,prop,sticky,boolean,false,false,false
ion-item-divider,css-prop,--background
@@ -566,7 +550,7 @@ ion-item-divider,css-prop,--padding-top
ion-item-group,none
ion-item-option,shadow
ion-item-option,prop,color,string | undefined,undefined,false,true
ion-item-option,prop,color,string | undefined,undefined,false,false
ion-item-option,prop,disabled,boolean,false,false,false
ion-item-option,prop,download,string | undefined,undefined,false,false
ion-item-option,prop,expandable,boolean,false,false,false
@@ -577,7 +561,6 @@ ion-item-option,prop,target,string | undefined,undefined,false,false
ion-item-option,prop,type,"button" | "reset" | "submit",'button',false,false
ion-item-option,css-prop,--background
ion-item-option,css-prop,--color
ion-item-option,part,native
ion-item-options,none
ion-item-options,prop,side,"end" | "start",'end',false,false
@@ -589,11 +572,11 @@ ion-item-sliding,method,close,close() => Promise<void>
ion-item-sliding,method,closeOpened,closeOpened() => Promise<boolean>
ion-item-sliding,method,getOpenAmount,getOpenAmount() => Promise<number>
ion-item-sliding,method,getSlidingRatio,getSlidingRatio() => Promise<number>
ion-item-sliding,method,open,open(side: Side | undefined) => Promise<void>
ion-item-sliding,method,open,open(side: "start" | "end" | undefined) => Promise<void>
ion-item-sliding,event,ionDrag,any,true
ion-label,scoped
ion-label,prop,color,string | undefined,undefined,false,true
ion-label,prop,color,string | undefined,undefined,false,false
ion-label,prop,mode,"ios" | "md",undefined,false,false
ion-label,prop,position,"fixed" | "floating" | "stacked" | undefined,undefined,false,false
ion-label,css-prop,--color
@@ -605,7 +588,7 @@ ion-list,prop,mode,"ios" | "md",undefined,false,false
ion-list,method,closeSlidingItems,closeSlidingItems() => Promise<boolean>
ion-list-header,shadow
ion-list-header,prop,color,string | undefined,undefined,false,true
ion-list-header,prop,color,string | undefined,undefined,false,false
ion-list-header,prop,lines,"full" | "inset" | "none" | undefined,undefined,false,false
ion-list-header,prop,mode,"ios" | "md",undefined,false,false
ion-list-header,css-prop,--background
@@ -676,10 +659,9 @@ ion-menu,part,container
ion-menu-button,shadow
ion-menu-button,prop,autoHide,boolean,true,false,false
ion-menu-button,prop,color,string | undefined,undefined,false,true
ion-menu-button,prop,color,string | undefined,undefined,false,false
ion-menu-button,prop,disabled,boolean,false,false,false
ion-menu-button,prop,menu,string | undefined,undefined,false,false
ion-menu-button,prop,mode,"ios" | "md",undefined,false,false
ion-menu-button,prop,type,"button" | "reset" | "submit",'button',false,false
ion-menu-button,css-prop,--background
ion-menu-button,css-prop,--background-focused
@@ -694,8 +676,6 @@ ion-menu-button,css-prop,--padding-bottom
ion-menu-button,css-prop,--padding-end
ion-menu-button,css-prop,--padding-start
ion-menu-button,css-prop,--padding-top
ion-menu-button,part,icon
ion-menu-button,part,native
ion-menu-toggle,shadow
ion-menu-toggle,prop,autoHide,boolean,true,false,false
@@ -746,13 +726,13 @@ ion-nav,method,getActive,getActive() => Promise<ViewController | undefined>
ion-nav,method,getByIndex,getByIndex(index: number) => Promise<ViewController | undefined>
ion-nav,method,getPrevious,getPrevious(view?: ViewController | undefined) => Promise<ViewController | undefined>
ion-nav,method,insert,insert<T extends NavComponent>(insertIndex: number, component: T, componentProps?: ComponentProps<T> | null | undefined, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,insertPages,insertPages(insertIndex: number, insertComponents: NavComponent[] | NavComponentWithProps[], opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,insertPages,insertPages(insertIndex: number, insertComponents: NavComponent[], opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,pop,pop(opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,popTo,popTo(indexOrViewCtrl: number | ViewController, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,popToRoot,popToRoot(opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,push,push<T extends NavComponent>(component: T, componentProps?: ComponentProps<T> | null | undefined, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,removeIndex,removeIndex(startIndex: number, removeCount?: number, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,setPages,setPages(views: NavComponent[] | NavComponentWithProps[], opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,setPages,setPages(views: any[], opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,method,setRoot,setRoot<T extends NavComponent>(component: T, componentProps?: ComponentProps<T> | null | undefined, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>
ion-nav,event,ionNavDidChange,void,false
ion-nav,event,ionNavWillChange,void,false
@@ -760,11 +740,10 @@ ion-nav,event,ionNavWillChange,void,false
ion-nav-link,none
ion-nav-link,prop,component,Function | HTMLElement | ViewController | null | string | undefined,undefined,false,false
ion-nav-link,prop,componentProps,undefined | { [key: string]: any; },undefined,false,false
ion-nav-link,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-nav-link,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-note,shadow
ion-note,prop,color,string | undefined,undefined,false,true
ion-note,prop,color,string | undefined,undefined,false,false
ion-note,prop,mode,"ios" | "md",undefined,false,false
ion-note,css-prop,--color
@@ -836,7 +815,7 @@ ion-popover,css-prop,--width
ion-progress-bar,shadow
ion-progress-bar,prop,buffer,number,1,false,false
ion-progress-bar,prop,color,string | undefined,undefined,false,true
ion-progress-bar,prop,color,string | undefined,undefined,false,false
ion-progress-bar,prop,mode,"ios" | "md",undefined,false,false
ion-progress-bar,prop,reversed,boolean,false,false,false
ion-progress-bar,prop,type,"determinate" | "indeterminate",'determinate',false,false
@@ -844,12 +823,9 @@ ion-progress-bar,prop,value,number,0,false,false
ion-progress-bar,css-prop,--background
ion-progress-bar,css-prop,--buffer-background
ion-progress-bar,css-prop,--progress-background
ion-progress-bar,part,progress
ion-progress-bar,part,stream
ion-progress-bar,part,track
ion-radio,shadow
ion-radio,prop,color,string | undefined,undefined,false,true
ion-radio,prop,color,string | undefined,undefined,false,false
ion-radio,prop,disabled,boolean,false,false,false
ion-radio,prop,mode,"ios" | "md",undefined,false,false
ion-radio,prop,name,string,this.inputId,false,false
@@ -870,7 +846,7 @@ ion-radio-group,prop,value,any,undefined,false,false
ion-radio-group,event,ionChange,RadioGroupChangeEventDetail,true
ion-range,shadow
ion-range,prop,color,string | undefined,undefined,false,true
ion-range,prop,color,string | undefined,undefined,false,false
ion-range,prop,debounce,number,0,false,false
ion-range,prop,disabled,boolean,false,false,false
ion-range,prop,dualKnobs,boolean,false,false,false
@@ -937,8 +913,6 @@ ion-ripple-effect,prop,type,"bounded" | "unbounded",'bounded',false,false
ion-ripple-effect,method,addRipple,addRipple(x: number, y: number) => Promise<() => void>
ion-route,none
ion-route,prop,beforeEnter,(() => NavigationHookResult | Promise<NavigationHookResult>) | undefined,undefined,false,false
ion-route,prop,beforeLeave,(() => NavigationHookResult | Promise<NavigationHookResult>) | undefined,undefined,false,false
ion-route,prop,component,string,undefined,true,false
ion-route,prop,componentProps,undefined | { [key: string]: any; },undefined,false,false
ion-route,prop,url,string,'',false,false
@@ -953,15 +927,14 @@ ion-router,none
ion-router,prop,root,string,'/',false,false
ion-router,prop,useHash,boolean,true,false,false
ion-router,method,back,back() => Promise<void>
ion-router,method,push,push(url: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>
ion-router,method,push,push(url: string, direction?: RouterDirection) => Promise<boolean>
ion-router,event,ionRouteDidChange,RouterEventDetail,true
ion-router,event,ionRouteWillChange,RouterEventDetail,true
ion-router-link,shadow
ion-router-link,prop,color,string | undefined,undefined,false,true
ion-router-link,prop,color,string | undefined,undefined,false,false
ion-router-link,prop,href,string | undefined,undefined,false,false
ion-router-link,prop,rel,string | undefined,undefined,false,false
ion-router-link,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-router-link,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-router-link,prop,target,string | undefined,undefined,false,false
ion-router-link,css-prop,--background
@@ -981,7 +954,7 @@ ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
ion-searchbar,prop,cancelButtonIcon,string,config.get('backButtonIcon', 'arrow-back-sharp') as string,false,false
ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false
ion-searchbar,prop,clearIcon,string | undefined,undefined,false,false
ion-searchbar,prop,color,string | undefined,undefined,false,true
ion-searchbar,prop,color,string | undefined,undefined,false,false
ion-searchbar,prop,debounce,number,250,false,false
ion-searchbar,prop,disabled,boolean,false,false,false
ion-searchbar,prop,enterkeyhint,"done" | "enter" | "go" | "next" | "previous" | "search" | "send" | undefined,undefined,false,false
@@ -990,7 +963,6 @@ ion-searchbar,prop,mode,"ios" | "md",undefined,false,false
ion-searchbar,prop,placeholder,string,'Search',false,false
ion-searchbar,prop,searchIcon,string | undefined,undefined,false,false
ion-searchbar,prop,showCancelButton,"always" | "focus" | "never",'never',false,false
ion-searchbar,prop,showClearButton,"always" | "focus" | "never",'focus',false,false
ion-searchbar,prop,spellcheck,boolean,false,false,false
ion-searchbar,prop,type,"email" | "number" | "password" | "search" | "tel" | "text" | "url",'search',false,false
ion-searchbar,prop,value,null | string | undefined,'',false,false
@@ -1015,11 +987,10 @@ ion-searchbar,css-prop,--placeholder-font-weight
ion-searchbar,css-prop,--placeholder-opacity
ion-segment,shadow
ion-segment,prop,color,string | undefined,undefined,false,true
ion-segment,prop,color,string | undefined,undefined,false,false
ion-segment,prop,disabled,boolean,false,false,false
ion-segment,prop,mode,"ios" | "md",undefined,false,false
ion-segment,prop,scrollable,boolean,false,false,false
ion-segment,prop,swipeGesture,boolean,true,false,false
ion-segment,prop,value,null | string | undefined,undefined,false,false
ion-segment,event,ionChange,SegmentChangeEventDetail,true
ion-segment,css-prop,--background
@@ -1058,9 +1029,6 @@ ion-segment-button,css-prop,--padding-end
ion-segment-button,css-prop,--padding-start
ion-segment-button,css-prop,--padding-top
ion-segment-button,css-prop,--transition
ion-segment-button,part,indicator
ion-segment-button,part,indicator-background
ion-segment-button,part,native
ion-select,shadow
ion-select,prop,cancelText,string,'Cancel',false,false
@@ -1078,7 +1046,7 @@ ion-select,prop,value,any,undefined,false,false
ion-select,method,open,open(event?: UIEvent | undefined) => Promise<any>
ion-select,event,ionBlur,void,true
ion-select,event,ionCancel,void,true
ion-select,event,ionChange,SelectChangeEventDetail<any>,true
ion-select,event,ionChange,SelectChangeEventDetail,true
ion-select,event,ionFocus,void,true
ion-select,css-prop,--padding-bottom
ion-select,css-prop,--padding-end
@@ -1147,7 +1115,7 @@ ion-slides,css-prop,--scroll-bar-background
ion-slides,css-prop,--scroll-bar-background-active
ion-spinner,shadow
ion-spinner,prop,color,string | undefined,undefined,false,true
ion-spinner,prop,color,string | undefined,undefined,false,false
ion-spinner,prop,duration,number | undefined,undefined,false,false
ion-spinner,prop,name,"bubbles" | "circles" | "circular" | "crescent" | "dots" | "lines" | "lines-small" | undefined,undefined,false,false
ion-spinner,prop,paused,boolean,false,false,false
@@ -1169,7 +1137,7 @@ ion-tab,prop,tab,string,undefined,true,false
ion-tab,method,setActive,setActive() => Promise<void>
ion-tab-bar,shadow
ion-tab-bar,prop,color,string | undefined,undefined,false,true
ion-tab-bar,prop,color,string | undefined,undefined,false,false
ion-tab-bar,prop,mode,"ios" | "md",undefined,false,false
ion-tab-bar,prop,selectedTab,string | undefined,undefined,false,false
ion-tab-bar,prop,translucent,boolean,false,false,false
@@ -1198,7 +1166,6 @@ ion-tab-button,css-prop,--padding-end
ion-tab-button,css-prop,--padding-start
ion-tab-button,css-prop,--padding-top
ion-tab-button,css-prop,--ripple-color
ion-tab-button,part,native
ion-tabs,shadow
ion-tabs,method,getSelected,getSelected() => Promise<string | undefined>
@@ -1208,7 +1175,7 @@ ion-tabs,event,ionTabsDidChange,{ tab: string; },false
ion-tabs,event,ionTabsWillChange,{ tab: string; },false
ion-text,shadow
ion-text,prop,color,string | undefined,undefined,false,true
ion-text,prop,color,string | undefined,undefined,false,false
ion-text,prop,mode,"ios" | "md",undefined,false,false
ion-textarea,scoped
@@ -1216,7 +1183,7 @@ ion-textarea,prop,autoGrow,boolean,false,false,false
ion-textarea,prop,autocapitalize,string,'none',false,false
ion-textarea,prop,autofocus,boolean,false,false,false
ion-textarea,prop,clearOnEdit,boolean,false,false,false
ion-textarea,prop,color,string | undefined,undefined,false,true
ion-textarea,prop,color,string | undefined,undefined,false,false
ion-textarea,prop,cols,number | undefined,undefined,false,false
ion-textarea,prop,debounce,number,0,false,false
ion-textarea,prop,disabled,boolean,false,false,false
@@ -1235,9 +1202,9 @@ ion-textarea,prop,value,null | string | undefined,'',false,false
ion-textarea,prop,wrap,"hard" | "off" | "soft" | undefined,undefined,false,false
ion-textarea,method,getInputElement,getInputElement() => Promise<HTMLTextAreaElement>
ion-textarea,method,setFocus,setFocus() => Promise<void>
ion-textarea,event,ionBlur,FocusEvent,true
ion-textarea,event,ionBlur,void,true
ion-textarea,event,ionChange,TextareaChangeEventDetail,true
ion-textarea,event,ionFocus,FocusEvent,true
ion-textarea,event,ionFocus,void,true
ion-textarea,event,ionInput,KeyboardEvent,true
ion-textarea,css-prop,--background
ion-textarea,css-prop,--border-radius
@@ -1256,14 +1223,14 @@ ion-thumbnail,css-prop,--border-radius
ion-thumbnail,css-prop,--size
ion-title,shadow
ion-title,prop,color,string | undefined,undefined,false,true
ion-title,prop,color,string | undefined,undefined,false,false
ion-title,prop,size,"large" | "small" | undefined,undefined,false,false
ion-title,css-prop,--color
ion-toast,shadow
ion-toast,prop,animated,boolean,true,false,false
ion-toast,prop,buttons,(string | ToastButton)[] | undefined,undefined,false,false
ion-toast,prop,color,string | undefined,undefined,false,true
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,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
@@ -1299,14 +1266,10 @@ ion-toast,css-prop,--min-width
ion-toast,css-prop,--start
ion-toast,css-prop,--white-space
ion-toast,css-prop,--width
ion-toast,part,button
ion-toast,part,container
ion-toast,part,header
ion-toast,part,message
ion-toggle,shadow
ion-toggle,prop,checked,boolean,false,false,false
ion-toggle,prop,color,string | undefined,undefined,false,true
ion-toggle,prop,color,string | undefined,undefined,false,false
ion-toggle,prop,disabled,boolean,false,false,false
ion-toggle,prop,mode,"ios" | "md",undefined,false,false
ion-toggle,prop,name,string,this.inputId,false,false
@@ -1330,7 +1293,7 @@ ion-toggle,part,handle
ion-toggle,part,track
ion-toolbar,shadow
ion-toolbar,prop,color,string | undefined,undefined,false,true
ion-toolbar,prop,color,string | undefined,undefined,false,false
ion-toolbar,prop,mode,"ios" | "md",undefined,false,false
ion-toolbar,css-prop,--background
ion-toolbar,css-prop,--border-color

25249
core/package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "5.6.9",
"version": "5.1.1",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -15,59 +15,54 @@
"progressive web app",
"pwa"
],
"main": "dist/index.cjs.js",
"module": "dist/index.js",
"es2015": "dist/esm/index.js",
"es2017": "dist/esm/index.js",
"jsnext:main": "dist/esm/index.js",
"main": "dist/index.js",
"module": "dist/index.mjs",
"es2015": "dist/esm/index.mjs",
"es2017": "dist/esm/index.mjs",
"jsnext:main": "dist/esm/index.mjs",
"collection:main": "dist/collection/index.js",
"collection": "dist/collection/collection-manifest.json",
"types": "dist/types/interface.d.ts",
"files": [
"components/",
"css/",
"dist/",
"css/",
"hydrate/",
"loader/"
],
"dependencies": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.1",
"tslib": "^2.1.0"
"ionicons": "^5.0.1",
"tslib": "^1.10.0"
},
"devDependencies": {
"@axe-core/puppeteer": "^4.1.1",
"@jest/core": "^26.6.3",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/sass": "1.3.2",
"@stencil/vue-output-target": "^0.4.3",
"@types/jest": "^26.0.20",
"@types/node": "^14.6.0",
"@types/puppeteer": "5.4.3",
"@types/swiper": "5.4.0",
"aws-sdk": "^2.738.0",
"@stencil/core": "1.13.0",
"@stencil/sass": "1.3.1",
"@types/jest": "24.9.1",
"@types/node": "12.12.3",
"@types/puppeteer": "1.19.1",
"@types/swiper": "5.3.1",
"aws-sdk": "^2.497.0",
"clean-css-cli": "^4.1.11",
"domino": "^2.1.6",
"execa": "^5.0.0",
"fs-extra": "^9.0.1",
"jest": "^26.4.1",
"jest-cli": "^26.4.1",
"np": "^6.4.0",
"domino": "^2.1.3",
"fs-extra": "^8.0.1",
"jest": "24.9.0",
"jest-cli": "24.9.0",
"np": "^5.0.3",
"pixelmatch": "4.0.2",
"puppeteer": "^7.0.1",
"rollup": "^2.26.4",
"sass": "^1.26.10",
"stylelint": "^13.6.1",
"stylelint-order": "^4.1.0",
"puppeteer": "1.20.0",
"rollup": "1.32.0",
"rollup-plugin-node-resolve": "5.2.0",
"rollup-plugin-virtual": "^1.0.1",
"sass": "^1.22.9",
"stylelint": "10.1.0",
"stylelint-order": "3.0.1",
"swiper": "5.4.1",
"tslint": "^6.1.3",
"tslint": "^5.10.0",
"tslint-ionic-rules": "0.0.21",
"tslint-react": "^5.0.0",
"typescript": "^4.0.5"
"tslint-react": "^3.6.0"
},
"scripts": {
"build": "npm run clean && npm run build.css && stencil build --docs --es5 --docs-json dist/docs.json && npm run cdnloader",
"build": "npm run clean && npm run build.css && npm run build.vendor && stencil build --docs && npm run cdnloader",
"build.vendor": "rollup --config ./scripts/swiper.rollup.config.js",
"build.css": "npm run css.sass && npm run css.minify",
"build.debug": "npm run clean && stencil build --debug",
"build.docs": "stencil build --docs",
@@ -78,7 +73,7 @@
"css.sass": "sass src/css:./css",
"lint": "npm run lint.ts && npm run lint.sass",
"lint.fix": "npm run lint.ts.fix && npm run lint.sass.fix",
"lint.sass": "stylelint \"src/**/*.scss\"",
"lint.sass": "stylelint 'src/**/*.scss'",
"lint.sass.fix": "npm run lint.sass -- --fix",
"lint.ts": "tslint --project .",
"lint.ts.fix": "tslint --project . --fix",
@@ -92,7 +87,7 @@
"test.screenshot": "stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/dev.js",
"test.screenshot.ci": "stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci",
"test.watch": "jest --watch --no-cache",
"test.treeshake": "node scripts/treeshaking.js dist/index.js",
"test.treeshake": "node scripts/treeshaking.js dist/index.mjs",
"validate": "npm run lint && npm run test && npm run build && npm run test.treeshake"
},
"author": "Ionic Team",

View File

@@ -1,2 +0,0 @@
export * from './index';
export * from '../dist/types/interface';

View File

@@ -1,9 +0,0 @@
{
"name": "@ionic/core/components",
"version": "0.0.0",
"description": "Ionic Components exported as custom elements, extending HTMLElement.",
"main": "./index.js",
"types": "./custom-elements.d.ts",
"private": true,
"sideEffects": false
}

View File

@@ -13,7 +13,7 @@ const s3 = new S3({ apiVersion: '2006-03-01' });
class CIScreenshotConnector extends IonicConnector {
async initBuild(opts) {
const { stdout: result } = await execa('git', ['log', '-1', '--format=%H%n%an <%ae>%n%ct%n%s']);
const result = await execa.stdout('git', ['log', '-1', '--format=%H%n%an <%ae>%n%ct%n%s']);
const [ sha1, author, timestamp, msg ] = result.split('\n');
const sha1short = sha1.slice(0, 7);
@@ -125,6 +125,8 @@ class CIScreenshotConnector extends IonicConnector {
uploadPaths.push(path.join(appRoot, 'scripts', 'testing', 'styles.css'));
const distDir = path.join(appRoot, 'dist');
uploadPaths.push(path.join(distDir, 'ionic.js'));
const distIonicDir = path.join(distDir, 'ionic');
fs.readdirSync(distIonicDir).forEach(distIonicFile => {
uploadPaths.push(path.join(distIonicDir, distIonicFile));

View File

@@ -0,0 +1,12 @@
import resolve from 'rollup-plugin-node-resolve';
export default {
input: 'src/components/slides/swiper/swiper.js',
output: {
file: 'src/components/slides/swiper/swiper.bundle.js',
format: 'es'
},
plugins: [
resolve()
]
};

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