mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
190 Commits
v6.0.1
...
fix/transl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cefcd02fb9 | ||
|
|
a296ca875c | ||
|
|
c52f3ad2c2 | ||
|
|
8424210049 | ||
|
|
c35077884d | ||
|
|
df71a0e625 | ||
|
|
2909b080b7 | ||
|
|
a8fd2d9199 | ||
|
|
9e84ef7f91 | ||
|
|
63842a25c3 | ||
|
|
0d0740161f | ||
|
|
43ac6ac471 | ||
|
|
02b7baf6a6 | ||
|
|
66e125bc5e | ||
|
|
fc0d7282f5 | ||
|
|
babe7d63ef | ||
|
|
8a97f6b5c9 | ||
|
|
4534c8bc0b | ||
|
|
d46e1e8506 | ||
|
|
981eeba0e1 | ||
|
|
c666deca4d | ||
|
|
8e8783190f | ||
|
|
d5efa11331 | ||
|
|
3e7dfd5f73 | ||
|
|
c45b38f28a | ||
|
|
44a6f032e7 | ||
|
|
b193b83c7c | ||
|
|
11a5edfd9d | ||
|
|
77a697ccd7 | ||
|
|
805907af4e | ||
|
|
8ed948e647 | ||
|
|
f6cde30d3e | ||
|
|
2ac9105796 | ||
|
|
331ce6d676 | ||
|
|
65b43aae2b | ||
|
|
9154ce3bdc | ||
|
|
c7f7db915a | ||
|
|
479c1bac48 | ||
|
|
c87366b186 | ||
|
|
56b550e220 | ||
|
|
7131e449e5 | ||
|
|
4b5843ed5d | ||
|
|
aacb58a322 | ||
|
|
e4ec572043 | ||
|
|
9e0917597c | ||
|
|
5faf51caef | ||
|
|
5baeeb1172 | ||
|
|
ea4a9bb694 | ||
|
|
836c01c73e | ||
|
|
3d0f99904f | ||
|
|
c35b898f1d | ||
|
|
33292fbd92 | ||
|
|
1ac8ffb1b7 | ||
|
|
1f86b4ee5a | ||
|
|
62878238fc | ||
|
|
b6d7e1c757 | ||
|
|
b0ac7de168 | ||
|
|
7f1086740b | ||
|
|
cd05961ab1 | ||
|
|
1c3b3791d0 | ||
|
|
8246112ca1 | ||
|
|
19ac2389eb | ||
|
|
243f67362f | ||
|
|
0a8f44359a | ||
|
|
2fc2de5177 | ||
|
|
99c91eff87 | ||
|
|
16647b2c72 | ||
|
|
fca3f56ca4 | ||
|
|
866f261f07 | ||
|
|
cd486e71ff | ||
|
|
fd031aa1c3 | ||
|
|
a093544fdf | ||
|
|
82c41510de | ||
|
|
38627ff277 | ||
|
|
b401de1ab3 | ||
|
|
abc36ae80b | ||
|
|
7b3838cc67 | ||
|
|
be2205e5a2 | ||
|
|
c40ff12052 | ||
|
|
bae49ec5d9 | ||
|
|
87eab37ca8 | ||
|
|
ef46eafc94 | ||
|
|
0580d65821 | ||
|
|
da4f750f8d | ||
|
|
34a6ce6d7e | ||
|
|
6ee7d159ec | ||
|
|
59bbd52e35 | ||
|
|
596aad435b | ||
|
|
c6381ce4f9 | ||
|
|
4ff9524e10 | ||
|
|
721a461073 | ||
|
|
8c22646d66 | ||
|
|
d40c0c3a09 | ||
|
|
231d6df622 | ||
|
|
aab4d306f8 | ||
|
|
df84d155ea | ||
|
|
f5c5c3cffa | ||
|
|
34cae57acc | ||
|
|
2a27befe46 | ||
|
|
897ae4a454 | ||
|
|
b0c9f097d2 | ||
|
|
928c5fbfcb | ||
|
|
0b18260da6 | ||
|
|
bf9b4dfb4e | ||
|
|
7530143634 | ||
|
|
b40fc4632e | ||
|
|
6d4a07d05c | ||
|
|
43aa6c11f4 | ||
|
|
484de5074d | ||
|
|
bdb5c421d2 | ||
|
|
6d7b1444b6 | ||
|
|
1f918835f4 | ||
|
|
a34ab420e3 | ||
|
|
43b19cf536 | ||
|
|
378c632643 | ||
|
|
7d5c6afd18 | ||
|
|
6f66d08ba8 | ||
|
|
525f01f086 | ||
|
|
632dafcd57 | ||
|
|
88ce010418 | ||
|
|
90a9a9c3e8 | ||
|
|
fde35a361f | ||
|
|
034d049209 | ||
|
|
94d033c421 | ||
|
|
d3311df967 | ||
|
|
43c5977d48 | ||
|
|
5925e7608e | ||
|
|
f295134624 | ||
|
|
af0135ce7d | ||
|
|
360643d96a | ||
|
|
35e5235645 | ||
|
|
2940e73a45 | ||
|
|
3e2d04dcc6 | ||
|
|
353dbc0537 | ||
|
|
5dba4e5ce0 | ||
|
|
bbbe778a8a | ||
|
|
29f1140384 | ||
|
|
54db1a1e7c | ||
|
|
eb7905cac9 | ||
|
|
5e448d9c74 | ||
|
|
be022f7de8 | ||
|
|
1a9be747f2 | ||
|
|
af01a8b307 | ||
|
|
e00c9cbd4c | ||
|
|
e284d7a2c7 | ||
|
|
c8a392aef5 | ||
|
|
273ae2cc08 | ||
|
|
9a15753fd9 | ||
|
|
a753d3438a | ||
|
|
7704ac3a37 | ||
|
|
8c442059b5 | ||
|
|
3d20959221 | ||
|
|
88602a9acf | ||
|
|
f5b4382fd5 | ||
|
|
c4745d24ac | ||
|
|
bce849c5f3 | ||
|
|
bb9e5f68b4 | ||
|
|
3057afdf82 | ||
|
|
d520f78d21 | ||
|
|
d81c4f9a35 | ||
|
|
2d9ec00745 | ||
|
|
c90ce311a8 | ||
|
|
5bb1414f7f | ||
|
|
90458da406 | ||
|
|
f539ff4788 | ||
|
|
77f8412b74 | ||
|
|
388622f973 | ||
|
|
5c54593dde | ||
|
|
bd82b5dc1d | ||
|
|
987d46cfa6 | ||
|
|
32fad3d02c | ||
|
|
1462cef692 | ||
|
|
b39003a4c6 | ||
|
|
a3724e6a56 | ||
|
|
8f000089c2 | ||
|
|
430439191d | ||
|
|
e6955a26b9 | ||
|
|
948a3b4914 | ||
|
|
ee488ff27a | ||
|
|
4aacccb112 | ||
|
|
4715b83abb | ||
|
|
87c51c4a82 | ||
|
|
fc64715e2a | ||
|
|
94a781cb6a | ||
|
|
ac3b110248 | ||
|
|
b8c3b49d70 | ||
|
|
77f13d5cbb | ||
|
|
90b0fdc4eb | ||
|
|
4d185404bd | ||
|
|
1405761d08 |
13
.github/CONTRIBUTING.md
vendored
13
.github/CONTRIBUTING.md
vendored
@@ -6,6 +6,7 @@ Thanks for your interest in contributing to the Ionic Framework! :tada:
|
||||
- [Creating an Issue](#creating-an-issue)
|
||||
* [Creating a Good Code Reproduction](#creating-a-good-code-reproduction)
|
||||
- [Creating a Pull Request](#creating-a-pull-request)
|
||||
* [Requirements](#requirements)
|
||||
* [Setup](#setup)
|
||||
* [Core](#core)
|
||||
+ [Modifying Components](#modifying-components)
|
||||
@@ -41,7 +42,7 @@ Please see our [Contributor Code of Conduct](https://github.com/ionic-team/ionic
|
||||
|
||||
## Creating an Issue
|
||||
|
||||
* If you have a question about using the framework, please ask on the [Ionic Forum](http://forum.ionicframework.com/) or in the [Ionic Worldwide Slack](http://ionicworldwide.herokuapp.com/) group.
|
||||
* If you have a question about using the framework, please ask on the [Ionic Forum](http://forum.ionicframework.com/) or in the [Ionic Discord](https://ionic.link/discord).
|
||||
|
||||
* It is required that you clearly describe the steps necessary to reproduce the issue you are running into. Although we would love to help our users as much as possible, diagnosing issues without clear reproduction steps is extremely time-consuming and simply not sustainable.
|
||||
|
||||
@@ -83,10 +84,16 @@ Without a reliable code reproduction, it is unlikely we will be able to resolve
|
||||
|
||||
## Creating a Pull Request
|
||||
|
||||
* We appreciate you taking the time to contribute! Before submitting a pull request, we ask that you please [create an issue](#creating-an-issue) that explains the bug or feature request and let us know that you plan on creating a pull request for it. If an issue already exists, please comment on that issue letting us know you would like to submit a pull request for it. This helps us to keep track of the pull request and make sure there isn't duplicated effort.
|
||||
Before creating a pull request, please read our requirements that explains the minimal details to have your PR considered and merged into the codebase.
|
||||
|
||||
* Looking for an issue to fix? Make sure to look through our issues with the [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label!
|
||||
### Requirements
|
||||
1. PRs must reference an existing issue that describes the issue or feature being submitted.
|
||||
2. PRs must have a reproduction app or the issue must include a reproduction app to verify changes against.
|
||||
3. PRs must include tests covering the changed behavior or a description of why tests cannot be written.
|
||||
|
||||
> Note: We appreciate you taking the time to contribute! Before submitting a pull request, please take the time to comment on the issue you are wanting to resolve. This helps us prevent duplicate effort or advise if the team is already addressing the issue.
|
||||
|
||||
* Looking for an issue to fix? Look through our issues with the [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label!
|
||||
|
||||
### Setup
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -49,7 +49,7 @@ body:
|
||||
label: Ionic Info
|
||||
description: Please run `ionic info` from within your Ionic Framework project directory and paste the output below.
|
||||
validations:
|
||||
requred: true
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
|
||||
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -26,9 +26,11 @@ Please check the type of change your PR introduces:
|
||||
|
||||
|
||||
## What is the current behavior?
|
||||
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
|
||||
<!-- Please describe the current behavior that you are modifying. -->
|
||||
|
||||
Issue Number: N/A
|
||||
|
||||
<!-- Issues are required for both bug fixes and features. -->
|
||||
Issue URL:
|
||||
|
||||
|
||||
## What is the new behavior?
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
node-version: 16
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
@@ -33,6 +33,10 @@ runs:
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./angular/test/test-app
|
||||
- name: Sync Built Changes
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./angular/test/test-app
|
||||
- name: Run Tests
|
||||
run: npm run test
|
||||
shell: bash
|
||||
|
||||
@@ -29,9 +29,19 @@ runs:
|
||||
name: ionic-react-router
|
||||
path: ./packages/react-router
|
||||
filename: ReactRouterBuild.zip
|
||||
- uses: cypress-io/github-action@v2
|
||||
with:
|
||||
browser: chrome
|
||||
headless: true
|
||||
start: npm run start.ci
|
||||
working-directory: ./packages/react/test-app
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
- name: Run E2E Tests
|
||||
run: npm run e2e
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
|
||||
@@ -29,9 +29,19 @@ runs:
|
||||
name: ionic-react-router
|
||||
path: ./packages/react-router
|
||||
filename: ReactRouterBuild.zip
|
||||
- uses: cypress-io/github-action@v2
|
||||
with:
|
||||
browser: chrome
|
||||
headless: true
|
||||
start: npm run start.ci
|
||||
working-directory: ./packages/react-router/test-app
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
- name: Run E2E Tests
|
||||
run: npm run e2e
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
|
||||
11
.github/workflows/dev-build.yml
vendored
11
.github/workflows/dev-build.yml
vendored
@@ -21,15 +21,18 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
# A 1 is required before the timestamp
|
||||
# as lerna will fail when there is a leading 0
|
||||
# See https://github.com/lerna/lerna/issues/2840
|
||||
- name: Create Dev Hash
|
||||
run: |
|
||||
echo "HASH=$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_ENV
|
||||
echo "TIMESTAMP=$(date +%s)" >> $GITHUB_ENV
|
||||
echo "CURRENT_VERSION=$(node -p -e "require('./core/package.json').version")" >> $GITHUB_ENV
|
||||
echo "HASH=1$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_ENV
|
||||
echo "TIMESTAMP=1$(date +%s)" >> $GITHUB_ENV
|
||||
echo "CURRENT_VERSION=$(node ./.scripts/bump-version.js)" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Create Dev Build
|
||||
run: |
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}") --no-verify-access --yes --force-publish='*' --dist-tag dev --no-git-tag-version --no-push
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}") --no-verify-access --yes --force-publish='*' --dist-tag dev --no-git-tag-version --no-push --exact
|
||||
shell: bash
|
||||
- id: dev-build
|
||||
run: echo "::set-output name=version::$(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}")"
|
||||
|
||||
48
.github/workflows/pre-release.yml
vendored
Normal file
48
.github/workflows/pre-release.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: 'Ionic Pre-Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: choice
|
||||
description: Which version should be published?
|
||||
options:
|
||||
- prepatch
|
||||
- preminor
|
||||
- premajor
|
||||
prefix:
|
||||
required: true
|
||||
type: choice
|
||||
description: What kind of pre-release is this?
|
||||
default: beta
|
||||
options:
|
||||
- alpha
|
||||
- beta
|
||||
- rc
|
||||
|
||||
jobs:
|
||||
build-ionic:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Configure Identity
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
shell: bash
|
||||
- name: Install Dependencies
|
||||
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
|
||||
shell: bash
|
||||
- name: Prepare NPM Token
|
||||
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
|
||||
shell: bash
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Release
|
||||
run: |
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ github.event.inputs.version }}") --no-verify-access --yes --force-publish='*' --dist-tag next --no-git-tag-version --no-push --skip-npm --preid $(echo "${{ github.events.inputs.prefix }}")
|
||||
shell: bash
|
||||
31
.github/workflows/release.yml
vendored
31
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: 'Ionic Release'
|
||||
name: 'Ionic Production Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -11,33 +11,30 @@ on:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
- prepatch
|
||||
- preminor
|
||||
- premajor
|
||||
tag:
|
||||
required: true
|
||||
type: choice
|
||||
description: Which npm tag should this be published to?
|
||||
options:
|
||||
- latest
|
||||
- next
|
||||
- dev
|
||||
prefix:
|
||||
type: choice
|
||||
description: For pre-releases, what kind of pre-release is this?
|
||||
default: ''
|
||||
options:
|
||||
- alpha
|
||||
- beta
|
||||
- rc
|
||||
- v5-lts
|
||||
- v4-lts
|
||||
|
||||
jobs:
|
||||
dev-build:
|
||||
build-ionic:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Configure Identity
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
shell: bash
|
||||
- name: Install Dependencies
|
||||
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
|
||||
shell: bash
|
||||
@@ -48,5 +45,7 @@ jobs:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Release
|
||||
run: |
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ github.event.inputs.version }}") --no-verify-access --yes --force-publish='*' --dist-tag $(echo "${{ github.events.inputs.tag }}") --no-git-tag-version --no-push --skip-npm --preid $(echo "${{ github.events.inputs.prefix }}")
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ github.event.inputs.version }}") --no-verify-access --yes --force-publish='*' --dist-tag $(echo "${{ github.event.inputs.tag }}") --conventional-commits --create-release github
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
|
||||
11
.github/workflows/update-screenshots.yml
vendored
Normal file
11
.github/workflows/update-screenshots.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: 'Update Screenshot References'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stub:
|
||||
steps:
|
||||
- name: Stub
|
||||
run: echo 'This is a stub'
|
||||
shell: bash
|
||||
@@ -1,26 +0,0 @@
|
||||
# Build Scripts
|
||||
|
||||
## Release
|
||||
|
||||
The deploy scripts at the root, make a new release of all the packages in this monorepo.
|
||||
All packages will be released with the same version.
|
||||
|
||||
In order to make a new release:
|
||||
|
||||
1. `npm run release.prepare`
|
||||
2. Review/update changelog
|
||||
3. Commit updates using the package name and version number as the commit message.
|
||||
4. `npm run release`
|
||||
5. :tada:
|
||||
|
||||
|
||||
## Prerelease
|
||||
|
||||
It's also possible to make prereleases of individual packages (@ionic/core, @ionic/angular).
|
||||
In order to do so, move to the package you want to make a new release and execute:
|
||||
```
|
||||
npm run prerelease
|
||||
```
|
||||
|
||||
It will publish a new prerelease in NPM, but it will not create any new git tag
|
||||
or update the CHANGELOG.
|
||||
@@ -1,15 +0,0 @@
|
||||
const common = require('./common');
|
||||
const Listr = require('listr');
|
||||
|
||||
|
||||
async function main() {
|
||||
const tasks = [];
|
||||
common.packages.forEach(package => {
|
||||
common.preparePackage(tasks, package, false, false);
|
||||
});
|
||||
|
||||
const listr = new Listr(tasks, { showSubtasks: true });
|
||||
await listr.run();
|
||||
}
|
||||
|
||||
main();
|
||||
10
.scripts/bump-version.js
Normal file
10
.scripts/bump-version.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const semver = require('semver');
|
||||
|
||||
const getDevVersion = () => {
|
||||
const originalVersion = require('../lerna.json').version;
|
||||
const baseVersion = semver.inc(originalVersion, 'patch');
|
||||
|
||||
return baseVersion;
|
||||
}
|
||||
|
||||
console.log(getDevVersion());
|
||||
@@ -1,404 +0,0 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const Listr = require('listr');
|
||||
const semver = require('semver');
|
||||
const { bold, cyan, dim } = require('colorette');
|
||||
|
||||
const rootDir = path.join(__dirname, '../');
|
||||
|
||||
const packages = [
|
||||
'core',
|
||||
'docs',
|
||||
'angular',
|
||||
'packages/react',
|
||||
'packages/react-router',
|
||||
'packages/angular-server',
|
||||
'packages/vue',
|
||||
'packages/vue-router'
|
||||
];
|
||||
|
||||
function readPkg(project) {
|
||||
const packageJsonPath = packagePath(project);
|
||||
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||
}
|
||||
|
||||
function writePkg(project, pkg) {
|
||||
const packageJsonPath = packagePath(project);
|
||||
const text = JSON.stringify(pkg, null, 2);
|
||||
return fs.writeFileSync(packageJsonPath, `${text}\n`);
|
||||
}
|
||||
|
||||
function packagePath(project) {
|
||||
return path.join(rootDir, project, 'package.json');
|
||||
}
|
||||
|
||||
function projectPath(project) {
|
||||
return path.join(rootDir, project);
|
||||
}
|
||||
|
||||
async function askNpmTag(version) {
|
||||
const prompts = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'npmTag',
|
||||
message: 'Select npm tag or specify a new tag',
|
||||
choices: ['latest', 'next', 'v4-lts']
|
||||
.concat([
|
||||
new inquirer.Separator(),
|
||||
{
|
||||
name: 'Other (specify)',
|
||||
value: null
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: answers => {
|
||||
return `Will publish ${cyan(version)} to ${cyan(answers.npmTag)}. Continue?`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { npmTag, confirm } = await inquirer.prompt(prompts);
|
||||
return { npmTag, confirm };
|
||||
}
|
||||
|
||||
function checkGit(tasks) {
|
||||
tasks.push(
|
||||
{
|
||||
title: 'Check current branch',
|
||||
task: () =>
|
||||
execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
|
||||
if (branch.indexOf('release') === -1 && branch.indexOf('hotfix') === -1) {
|
||||
throw new Error(`Must be on a "release" or "hotfix" branch.`);
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Check local working tree',
|
||||
task: () =>
|
||||
execa.stdout('git', ['status', '--porcelain']).then(status => {
|
||||
if (status !== '') {
|
||||
throw new Error(`Unclean working tree. Commit or stash changes first.`);
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Check remote history',
|
||||
task: () =>
|
||||
execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
|
||||
if (result !== '0') {
|
||||
throw new Error(`Remote history differs. Please pull changes.`);
|
||||
}
|
||||
})
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function checkTestDist(tasks) {
|
||||
tasks.push({
|
||||
title: 'Check dist folders for required files',
|
||||
task: () =>
|
||||
execa.stdout('node', ['.scripts/test-dist.js']).then(status => {
|
||||
if (status.indexOf('✅ test.dist') === -1) {
|
||||
throw new Error(`Test Dist did not find some required files`);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const isValidVersion = input => Boolean(semver.valid(input));
|
||||
|
||||
function preparePackage(tasks, package, version, install) {
|
||||
const projectRoot = projectPath(package);
|
||||
const pkg = readPkg(package);
|
||||
|
||||
const projectTasks = [];
|
||||
if (version) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: validate new version`,
|
||||
task: () => {
|
||||
if (!isVersionGreater(pkg.version, version)) {
|
||||
throw new Error(
|
||||
`New version \`${version}\` should be higher than current version \`${pkg.version}\``
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (install) {
|
||||
projectTasks.push({
|
||||
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 });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (package !== 'docs') {
|
||||
if (package !== 'core') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link @ionic/core`,
|
||||
task: () => execa('npm', ['link', '@ionic/core', '--legacy-peer-deps'], { 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 })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Lint, Test, Bump Core dependency
|
||||
if (version) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: lint`,
|
||||
task: () => execa('npm', ['run', 'lint'], { cwd: projectRoot })
|
||||
});
|
||||
// TODO will not work due to https://github.com/ionic-team/ionic/issues/20136
|
||||
// projectTasks.push({
|
||||
// title: `${pkg.name}: test`,
|
||||
// task: async () => await execa('npm', ['test'], { cwd: projectRoot })
|
||||
// });
|
||||
}
|
||||
|
||||
// Build
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: build`,
|
||||
task: () => execa('npm', ['run', 'build'], { cwd: projectRoot })
|
||||
});
|
||||
|
||||
// Link core or react for sub projects
|
||||
if (package === 'core' || package === 'packages/react') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link`,
|
||||
task: () => execa('npm', ['link'], { cwd: projectRoot })
|
||||
});
|
||||
}
|
||||
|
||||
if (version) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||
task: () => {
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add project tasks
|
||||
tasks.push({
|
||||
title: `Prepare ${bold(pkg.name)}`,
|
||||
task: () => new Listr(projectTasks)
|
||||
});
|
||||
}
|
||||
|
||||
function prepareDevPackage(tasks, package, version) {
|
||||
const projectRoot = projectPath(package);
|
||||
const pkg = readPkg(package);
|
||||
|
||||
const projectTasks = [];
|
||||
|
||||
if (package !== 'docs') {
|
||||
if (package !== 'core') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link @ionic/core`,
|
||||
task: () => execa('npm', ['link', '@ionic/core', '--legacy-peer-deps'], { 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({
|
||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||
task: () => {
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: build`,
|
||||
task: () => execa('npm', ['run', 'build'], { cwd: projectRoot })
|
||||
});
|
||||
|
||||
if (package === 'core' || package === 'packages/react') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link`,
|
||||
task: () => execa('npm', ['link'], { cwd: projectRoot })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add project tasks
|
||||
tasks.push({
|
||||
title: `Prepare dev build: ${bold(pkg.name)}`,
|
||||
task: () => new Listr(projectTasks)
|
||||
});
|
||||
}
|
||||
|
||||
function updatePackageVersions(tasks, packages, version) {
|
||||
packages.forEach(package => {
|
||||
updatePackageVersion(tasks, package, version);
|
||||
|
||||
tasks.push({
|
||||
title: `${package} update @ionic/core dependency, if present ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
if (package !== 'core') {
|
||||
const pkg = readPkg(package);
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// angular & angular-server need to update their dist versions
|
||||
if (package === 'angular' || package === 'packages/angular-server') {
|
||||
const distPackage = path.join(package, 'dist');
|
||||
|
||||
updatePackageVersion(tasks, distPackage, version);
|
||||
|
||||
tasks.push({
|
||||
title: `${package} update @ionic/core dependency, if present ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
const pkg = readPkg(distPackage);
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(distPackage, pkg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (package === 'packages/react-router') {
|
||||
tasks.push({
|
||||
title: `${package} update @ionic/react dependency, if present ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
const pkg = readPkg(package);
|
||||
updateDependency(pkg, '@ionic/react', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updatePackageVersion(tasks, package, version) {
|
||||
const projectRoot = projectPath(package);
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: update package.json ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
await execa('npm', ['version', version], { cwd: projectRoot });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function copyPackageToDist(tasks, packages) {
|
||||
packages.forEach(package => {
|
||||
const projectRoot = projectPath(package);
|
||||
|
||||
// angular and angular-server are the only packages that publish dist
|
||||
if (package !== 'angular' && package !== 'packages/angular-server') {
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: Copy package.json to dist`,
|
||||
task: () => execa('node', ['copy-package.js', package], { cwd: path.join(rootDir, '.scripts') })
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function publishPackages(tasks, packages, version, npmTag = 'latest') {
|
||||
// first verify version
|
||||
packages.forEach(package => {
|
||||
if (package === 'core') {
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: check version (must match: ${version})`,
|
||||
task: () => {
|
||||
const pkg = readPkg(package);
|
||||
|
||||
if (version !== pkg.version) {
|
||||
throw new Error(`${pkg.name} version ${pkg.version} must match ${version}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Publish
|
||||
packages.forEach(package => {
|
||||
let projectRoot = projectPath(package);
|
||||
|
||||
if (package === 'packages/angular-server' || package === 'angular') {
|
||||
projectRoot = path.join(projectRoot, 'dist')
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: publish to ${npmTag} tag`,
|
||||
task: async () => {
|
||||
await execa('npm', ['publish', '--tag', npmTag], { cwd: projectRoot });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateDependency(pkg, dependency, version) {
|
||||
if (pkg.dependencies && pkg.dependencies[dependency]) {
|
||||
pkg.dependencies[dependency] = version;
|
||||
}
|
||||
if (pkg.devDependencies && pkg.devDependencies[dependency]) {
|
||||
pkg.devDependencies[dependency] = version;
|
||||
}
|
||||
if (pkg.peerDependencies && pkg.peerDependencies[dependency]) {
|
||||
pkg.peerDependencies[dependency] = version;
|
||||
}
|
||||
}
|
||||
|
||||
function isVersionGreater(oldVersion, newVersion) {
|
||||
if (!isValidVersion(newVersion)) {
|
||||
throw new Error('Version should be a valid semver version.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function copyCDNLoader(tasks, version) {
|
||||
tasks.push({
|
||||
title: `Copy CDN loader`,
|
||||
task: () => execa('node', ['copy-cdn-loader.js', version], { cwd: path.join(rootDir, 'core', 'scripts') })
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkTestDist,
|
||||
checkGit,
|
||||
askNpmTag,
|
||||
isValidVersion,
|
||||
isVersionGreater,
|
||||
copyCDNLoader,
|
||||
copyPackageToDist,
|
||||
packages,
|
||||
packagePath,
|
||||
prepareDevPackage,
|
||||
preparePackage,
|
||||
projectPath,
|
||||
publishPackages,
|
||||
readPkg,
|
||||
rootDir,
|
||||
updateDependency,
|
||||
updatePackageVersion,
|
||||
updatePackageVersions,
|
||||
writePkg
|
||||
};
|
||||
@@ -1,214 +0,0 @@
|
||||
/**
|
||||
* Deploy script adopted from https://github.com/sindresorhus/np
|
||||
* MIT License (c) Sindre Sorhus (sindresorhus.com)
|
||||
*/
|
||||
const { cyan, dim, red, reset } = require('colorette');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const Listr = require('listr');
|
||||
const semver = require('semver');
|
||||
const common = require('./common');
|
||||
const path = require('path');
|
||||
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
if (!process.env.GH_TOKEN) {
|
||||
throw new Error('env.GH_TOKEN is undefined');
|
||||
}
|
||||
|
||||
const { version, confirm } = await askVersion();
|
||||
const install = process.argv.indexOf('--no-install') < 0;
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
// compile and verify packages
|
||||
await preparePackages(common.packages, version, install);
|
||||
|
||||
console.log(`\nionic ${version} prepared 🤖\n`);
|
||||
console.log(`Next steps:`);
|
||||
console.log(` Verify CHANGELOG.md`);
|
||||
console.log(` git commit -m "${version}"`);
|
||||
console.log(` npm run release\n`);
|
||||
|
||||
} catch(err) {
|
||||
console.log('\n', red(err), '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function askVersion() {
|
||||
const pkg = common.readPkg('core');
|
||||
const oldVersion = pkg.version;
|
||||
|
||||
const prompts = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'version',
|
||||
message: 'Select semver increment or specify new version',
|
||||
pageSize: SEMVER_INCREMENTS.length + 2,
|
||||
choices: SEMVER_INCREMENTS
|
||||
.map(inc => ({
|
||||
name: `${inc} ${prettyVersionDiff(oldVersion, inc)}`,
|
||||
value: inc
|
||||
}))
|
||||
.concat([
|
||||
new inquirer.Separator(),
|
||||
{
|
||||
name: 'Other (specify)',
|
||||
value: null
|
||||
}
|
||||
]),
|
||||
filter: input => isValidVersionInput(input) ? getNewVersion(oldVersion, input) : input
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: 'Version',
|
||||
when: answers => !answers.version,
|
||||
filter: input => isValidVersionInput(input) ? getNewVersion(pkg.version, input) : input,
|
||||
validate: input => {
|
||||
if (!isValidVersionInput(input)) {
|
||||
return 'Please specify a valid semver, for example, `1.2.3`. See http://semver.org';
|
||||
} else if (!common.isVersionGreater(oldVersion, input)) {
|
||||
return `Version must be greater than ${oldVersion}`;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: answers => {
|
||||
return `Will bump from ${cyan(oldVersion)} to ${cyan(answers.version)}. Continue?`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { version, confirm } = await inquirer.prompt(prompts);
|
||||
return { version, confirm };
|
||||
}
|
||||
|
||||
|
||||
async function preparePackages(packages, version, install) {
|
||||
// execution order matters
|
||||
const tasks = [];
|
||||
|
||||
// check git is nice and clean local and remote
|
||||
common.checkGit(tasks);
|
||||
|
||||
// test we're good with git
|
||||
validateGit(tasks, version);
|
||||
|
||||
// add all the prepare scripts
|
||||
// run all these tasks before updating package.json version
|
||||
packages.forEach(package => {
|
||||
common.preparePackage(tasks, package, version, install);
|
||||
});
|
||||
|
||||
// add update package.json of each project
|
||||
common.updatePackageVersions(tasks, packages, version);
|
||||
|
||||
// generate changelog
|
||||
generateChangeLog(tasks);
|
||||
|
||||
// check dist folders
|
||||
common.checkTestDist(tasks);
|
||||
|
||||
// update core readme with version number
|
||||
updateCoreReadme(tasks, version);
|
||||
common.copyCDNLoader(tasks, version);
|
||||
|
||||
const listr = new Listr(tasks, { showSubtasks: true });
|
||||
await listr.run();
|
||||
}
|
||||
|
||||
|
||||
function validateGit(tasks, version) {
|
||||
tasks.push(
|
||||
{
|
||||
title: `Validate git tag ${dim(`(v${version})`)}`,
|
||||
task: () => execa('git', ['fetch'])
|
||||
.then(() => {
|
||||
return execa.stdout('npm', ['config', 'get', 'tag-version-prefix']);
|
||||
})
|
||||
.then(
|
||||
output => {
|
||||
tagPrefix = output;
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
.then(() => execa.stdout('git', ['rev-parse', '--quiet', '--verify', `refs/tags/${tagPrefix}${version}`]))
|
||||
.then(
|
||||
output => {
|
||||
if (output) {
|
||||
throw new Error(`Git tag \`${tagPrefix}${version}\` already exists.`);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
// Command fails with code 1 and no output if the tag does not exist, even though `--quiet` is provided
|
||||
// https://github.com/sindresorhus/np/pull/73#discussion_r72385685
|
||||
if (err.stdout !== '' || err.stderr !== '') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function generateChangeLog(tasks) {
|
||||
tasks.push({
|
||||
title: `Generate CHANGELOG.md`,
|
||||
task: () => execa('npm', ['run', 'changelog'], { cwd: common.rootDir }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateCoreReadme(tasks, version) {
|
||||
tasks.push({
|
||||
title: `Update core README.md`,
|
||||
task: () => execa('node', ['update-readme.js', version], { cwd: path.join(common.rootDir, 'core', 'scripts') }),
|
||||
});
|
||||
}
|
||||
|
||||
const SEMVER_INCREMENTS = ['patch', 'minor', 'major'];
|
||||
|
||||
const isValidVersionInput = input => SEMVER_INCREMENTS.indexOf(input) !== -1 || common.isValidVersion(input);
|
||||
|
||||
function getNewVersion(oldVersion, input) {
|
||||
if (!isValidVersionInput(input)) {
|
||||
throw new Error(`Version should be either ${SEMVER_INCREMENTS.join(', ')} or a valid semver version.`);
|
||||
}
|
||||
|
||||
return SEMVER_INCREMENTS.indexOf(input) === -1 ? input : semver.inc(oldVersion, input);
|
||||
};
|
||||
|
||||
|
||||
function prettyVersionDiff(oldVersion, inc) {
|
||||
const newVersion = getNewVersion(oldVersion, inc).split('.');
|
||||
oldVersion = oldVersion.split('.');
|
||||
let firstVersionChange = false;
|
||||
const output = [];
|
||||
|
||||
for (let i = 0; i < newVersion.length; i++) {
|
||||
if ((newVersion[i] !== oldVersion[i] && !firstVersionChange)) {
|
||||
output.push(`${dim(cyan(newVersion[i]))}`);
|
||||
firstVersionChange = true;
|
||||
} else if (newVersion[i].indexOf('-') >= 1) {
|
||||
let preVersion = [];
|
||||
preVersion = newVersion[i].split('-');
|
||||
output.push(`${dim(cyan(`${preVersion[0]}-${preVersion[1]}`))}`);
|
||||
} else {
|
||||
output.push(reset(dim(newVersion[i])));
|
||||
}
|
||||
}
|
||||
return output.join(reset(dim('.')));
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,107 +0,0 @@
|
||||
const { cyan, red } = require('colorette');
|
||||
const semver = require('semver');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const Listr = require('listr');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
const common = require('./common');
|
||||
|
||||
const DIST_NPM_TAG = 'dev';
|
||||
const DIST_TAG = 'dev';
|
||||
|
||||
async function main() {
|
||||
const { packages } = common;
|
||||
|
||||
const orgPkg = packages.map(package => {
|
||||
const packageJsonPath = common.packagePath(package);
|
||||
return {
|
||||
filePath: packageJsonPath,
|
||||
packageContent: fs.readFileSync(packageJsonPath, 'utf-8')
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const originalVersion = common.readPkg('core').version;
|
||||
const devVersion = await getDevVersion(originalVersion);
|
||||
|
||||
const confirm = await askDevVersion(devVersion);
|
||||
if (!confirm) {
|
||||
console.log(``);
|
||||
return;
|
||||
}
|
||||
|
||||
const tasks = [];
|
||||
|
||||
await setPackageVersionChanges(packages, devVersion);
|
||||
|
||||
packages.forEach(package => {
|
||||
common.prepareDevPackage(tasks, package, devVersion);
|
||||
});
|
||||
common.copyCDNLoader(tasks, devVersion);
|
||||
common.publishPackages(tasks, packages, devVersion, DIST_NPM_TAG);
|
||||
|
||||
const listr = new Listr(tasks);
|
||||
await listr.run();
|
||||
|
||||
console.log(`\nionic ${devVersion} published!! 🎉\n`);
|
||||
|
||||
} catch (err) {
|
||||
console.log('\n', red(err), '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
orgPkg.forEach(pkg => {
|
||||
fs.writeFileSync(pkg.filePath, pkg.packageContent);
|
||||
});
|
||||
}
|
||||
|
||||
async function askDevVersion(devVersion) {
|
||||
|
||||
const prompts = [
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
value: true,
|
||||
message: () => {
|
||||
return `Publish the dev build ${cyan(devVersion)}?`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { confirm } = await inquirer.prompt(prompts);
|
||||
return confirm;
|
||||
}
|
||||
|
||||
async function setPackageVersionChanges(packages, version) {
|
||||
await Promise.all(packages.map(async package => {
|
||||
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);
|
||||
await execa('npm', ['version', version], { cwd: projectRoot });
|
||||
}));
|
||||
}
|
||||
|
||||
async function getDevVersion(originalVersion) {
|
||||
const { stdout: sha } = await execa('git', ['log', '-1', '--format=%H']);
|
||||
const shortSha = sha.substring(0, 7);
|
||||
const baseVersion = semver.inc(originalVersion, 'minor');
|
||||
|
||||
const d = new Date();
|
||||
|
||||
let timestamp = d.getUTCFullYear().toString();
|
||||
timestamp += (d.getUTCMonth() + 1).toString().padStart(2, '0');
|
||||
timestamp += d.getUTCDate().toString().padStart(2, '0');
|
||||
timestamp += d.getUTCHours().toString().padStart(2, '0');
|
||||
timestamp += d.getUTCMinutes().toString().padStart(2, '0');
|
||||
|
||||
return `${baseVersion}-${DIST_TAG}.${timestamp}.${shortSha}`;
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,144 +0,0 @@
|
||||
/**
|
||||
* Deploy script adopted from https://github.com/sindresorhus/np
|
||||
* MIT License (c) Sindre Sorhus (sindresorhus.com)
|
||||
*/
|
||||
const { cyan, dim, green, red, yellow } = require('colorette');
|
||||
const execa = require('execa');
|
||||
const Listr = require('listr');
|
||||
const path = require('path');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const common = require('./common');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const dryRun = process.argv.indexOf('--dry-run') > -1;
|
||||
|
||||
if (!process.env.GH_TOKEN) {
|
||||
throw new Error('env.GH_TOKEN is undefined');
|
||||
}
|
||||
|
||||
checkProductionRelease();
|
||||
|
||||
const tasks = [];
|
||||
const { version } = common.readPkg('core');
|
||||
const changelog = findChangelog();
|
||||
|
||||
// repo must be clean
|
||||
common.checkGit(tasks);
|
||||
|
||||
const { npmTag, confirm } = await common.askNpmTag(version);
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!dryRun) {
|
||||
// publish each package in NPM
|
||||
common.publishPackages(tasks, common.packages, version, npmTag);
|
||||
|
||||
// push tag to git remote
|
||||
publishGit(tasks, version, changelog, npmTag);
|
||||
}
|
||||
|
||||
const listr = new Listr(tasks);
|
||||
await listr.run();
|
||||
|
||||
// Dry run doesn't publish to npm or git
|
||||
if (dryRun) {
|
||||
console.log(`
|
||||
\n${yellow('Did not publish. Remove the "--dry-run" flag to publish:')}\n${green(version)} to ${cyan(npmTag)}\n
|
||||
`);
|
||||
} else {
|
||||
console.log(`\nionic ${version} published to ${npmTag}!! 🎉\n`);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log('\n', red(err), '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function checkProductionRelease() {
|
||||
const corePath = common.projectPath('core');
|
||||
const hasEsm = fs.existsSync(path.join(corePath, 'dist', 'esm'));
|
||||
const hasEsmEs5 = fs.existsSync(path.join(corePath, 'dist', 'esm-es5'));
|
||||
const hasCjs = fs.existsSync(path.join(corePath, 'dist', 'cjs'));
|
||||
if (!hasEsm || !hasEsmEs5 || !hasCjs) {
|
||||
throw new Error('core build is not a production build');
|
||||
}
|
||||
}
|
||||
|
||||
function publishGit(tasks, version, changelog, npmTag) {
|
||||
const gitTag = `v${version}`;
|
||||
|
||||
tasks.push(
|
||||
{
|
||||
title: `Tag latest commit ${dim(`(${gitTag})`)}`,
|
||||
task: () => execa('git', ['tag', `${gitTag}`], { cwd: common.rootDir })
|
||||
},
|
||||
{
|
||||
title: 'Push branches to remote',
|
||||
task: () => execa('git', ['push'], { cwd: common.rootDir })
|
||||
},
|
||||
{
|
||||
title: 'Push tags to remove',
|
||||
task: () => execa('git', ['push', '--follow-tags'], { cwd: common.rootDir })
|
||||
},
|
||||
{
|
||||
title: 'Publish Github release',
|
||||
task: () => publishGithub(version, gitTag, changelog, npmTag)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function findChangelog() {
|
||||
const lines = fs.readFileSync('CHANGELOG.md', 'utf-8').toString().split('\n');
|
||||
let start = -1;
|
||||
let end = -1;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (line.startsWith('## [') || line.startsWith('# [')) {
|
||||
if (start === -1) {
|
||||
start = i + 1;
|
||||
} else {
|
||||
end = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(start === -1 || end === -1) {
|
||||
throw new Error('changelog diff was not found');
|
||||
}
|
||||
return lines.slice(start, end).join('\n').trim();
|
||||
}
|
||||
|
||||
async function publishGithub(version, gitTag, changelog, npmTag) {
|
||||
// If the npm tag is next then publish as a prerelease
|
||||
const prerelease = npmTag === 'next' ? true : false;
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GH_TOKEN
|
||||
});
|
||||
|
||||
let branch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']);
|
||||
|
||||
if (!branch) {
|
||||
branch = 'main';
|
||||
}
|
||||
|
||||
await octokit.repos.createRelease({
|
||||
owner: 'ionic-team',
|
||||
repo: 'ionic-framework',
|
||||
target_commitish: branch,
|
||||
tag_name: gitTag,
|
||||
name: version,
|
||||
body: changelog,
|
||||
prerelease: prerelease
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,113 +0,0 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// Test dist build:
|
||||
// Double-triple check all the packages
|
||||
// and files are good to go before publishing
|
||||
[
|
||||
// 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'
|
||||
]
|
||||
},
|
||||
// angular
|
||||
{
|
||||
files: [
|
||||
'../angular/dist/schematics/collection.json',
|
||||
'../angular/dist/fesm2015/ionic-angular.js',
|
||||
'../angular/dist/esm2015/ionic-angular.js',
|
||||
'../angular/dist/ionic-angular.d.ts'
|
||||
]
|
||||
},
|
||||
// angular-server
|
||||
{
|
||||
files: [
|
||||
'../packages/angular-server/dist/esm2015/ionic-angular-server.js',
|
||||
'../packages/angular-server/dist/fesm2015/ionic-angular-server.js',
|
||||
'../packages/angular-server/dist/ionic-angular-server.d.ts'
|
||||
]
|
||||
},
|
||||
// react
|
||||
{
|
||||
files: ['../packages/react/dist/index.js']
|
||||
},
|
||||
// react-router
|
||||
{
|
||||
files: ['../packages/react-router/dist/index.js']
|
||||
}
|
||||
].forEach(testPackage);
|
||||
|
||||
function testPackage(testPkg) {
|
||||
if (testPkg.packageJson) {
|
||||
const pkgDir = path.dirname(testPkg.packageJson);
|
||||
const pkgJson = require(testPkg.packageJson);
|
||||
|
||||
if (!pkgJson.name) {
|
||||
throw new Error('missing package.json name: ' + testPkg.packageJson);
|
||||
}
|
||||
|
||||
if (!pkgJson.main) {
|
||||
throw new Error('missing package.json main: ' + testPkg.packageJson);
|
||||
}
|
||||
|
||||
const pkgPath = path.join(pkgDir, pkgJson.main);
|
||||
const pkgImport = require(pkgPath);
|
||||
|
||||
if (testPkg.files) {
|
||||
if (!Array.isArray(pkgJson.files)) {
|
||||
throw new Error(testPkg.packageJson + ' missing "files" property');
|
||||
}
|
||||
testPkg.files.forEach(testPkgFile => {
|
||||
if (!pkgJson.files.includes(testPkgFile)) {
|
||||
throw new Error(testPkg.packageJson + ' missing file ' + testPkgFile);
|
||||
}
|
||||
|
||||
const filePath = path.join(__dirname, pkgDir, testPkgFile);
|
||||
fs.accessSync(filePath);
|
||||
});
|
||||
}
|
||||
|
||||
if (pkgJson.module) {
|
||||
const moduleIndex = path.join(__dirname, pkgDir, pkgJson.module);
|
||||
fs.accessSync(moduleIndex);
|
||||
}
|
||||
|
||||
if (pkgJson.types) {
|
||||
const pkgTypes = path.join(__dirname, pkgDir, pkgJson.types);
|
||||
fs.accessSync(pkgTypes);
|
||||
}
|
||||
|
||||
if (testPkg.exports) {
|
||||
testPkg.exports.forEach(exportName => {
|
||||
const m = pkgImport[exportName];
|
||||
if (!m) {
|
||||
throw new Error('export "' + exportName + '" not found in: ' + testPkg.packageJson);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (testPkg.files) {
|
||||
testPkg.files.forEach(file => {
|
||||
const filePath = path.join(__dirname, file);
|
||||
fs.statSync(filePath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ test.dist`);
|
||||
265
CHANGELOG.md
265
CHANGELOG.md
@@ -1,3 +1,266 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.0.13](https://github.com/ionic-team/ionic-framework/compare/v6.0.12...v6.0.13) (2022-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ngOnDestroy runs inside angular zone ([#24949](https://github.com/ionic-team/ionic-framework/issues/24949)) ([a8fd2d9](https://github.com/ionic-team/ionic-framework/commit/a8fd2d9199ca92d62bce6abf8caccc7709fa5ca1)), closes [#22571](https://github.com/ionic-team/ionic-framework/issues/22571)
|
||||
* **datetime:** presentation time emits ionChange once ([#24968](https://github.com/ionic-team/ionic-framework/issues/24968)) ([2909b08](https://github.com/ionic-team/ionic-framework/commit/2909b080b7020299a4554c1459b4b190ff739085)), closes [#24967](https://github.com/ionic-team/ionic-framework/issues/24967)
|
||||
* **popover:** dismissing nested popover via backdrop now works ([#24957](https://github.com/ionic-team/ionic-framework/issues/24957)) ([9e84ef7](https://github.com/ionic-team/ionic-framework/commit/9e84ef7f91d76ca5a1ecaffc7592287267d5368b)), closes [#24954](https://github.com/ionic-team/ionic-framework/issues/24954)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.12](https://github.com/ionic-team/ionic-framework/compare/v6.0.11...v6.0.12) (2022-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** reinit behavior on presentation change ([#24828](https://github.com/ionic-team/ionic-framework/issues/24828)) ([d46e1e8](https://github.com/ionic-team/ionic-framework/commit/d46e1e8506ca5095817b421e9edb37d41451e885))
|
||||
* **tabs:** angular, fire willChange event before selected tab changes ([#24910](https://github.com/ionic-team/ionic-framework/issues/24910)) ([d5efa11](https://github.com/ionic-team/ionic-framework/commit/d5efa113317eaf874712134dc9b8e4502aa4760f))
|
||||
* **toast:** screen readers now announce toasts when presented ([#24937](https://github.com/ionic-team/ionic-framework/issues/24937)) ([8a97f6b](https://github.com/ionic-team/ionic-framework/commit/8a97f6b5c9ca1e77c1790abd1e924955b6b6ea27)), closes [#22333](https://github.com/ionic-team/ionic-framework/issues/22333)
|
||||
* **vue:** tapping the active tab button now correctly resets the tab stack ([#24935](https://github.com/ionic-team/ionic-framework/issues/24935)) ([4534c8b](https://github.com/ionic-team/ionic-framework/commit/4534c8bc0b2bca7ab6eecd9886243116e9a039b7)), closes [#24934](https://github.com/ionic-team/ionic-framework/issues/24934)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.11](https://github.com/ionic-team/ionic-framework/compare/v6.0.10...v6.0.11) (2022-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** time picker now scrolls to correct value ([#24879](https://github.com/ionic-team/ionic-framework/issues/24879)) ([331ce6d](https://github.com/ionic-team/ionic-framework/commit/331ce6d6769900e2aec9e30d35b52cfd40cbb889)), closes [#24878](https://github.com/ionic-team/ionic-framework/issues/24878)
|
||||
* **ios:** swipe to go back now works in rtl mode ([#24866](https://github.com/ionic-team/ionic-framework/issues/24866)) ([2ac9105](https://github.com/ionic-team/ionic-framework/commit/2ac9105796a0765fabc48592b5b44ac58c568579)), closes [#19488](https://github.com/ionic-team/ionic-framework/issues/19488)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* improve treeshaking functionality ([#24895](https://github.com/ionic-team/ionic-framework/issues/24895)) ([805907a](https://github.com/ionic-team/ionic-framework/commit/805907af4e78179f1acc9cb02263b1ea10d4e3df)), closes [#24280](https://github.com/ionic-team/ionic-framework/issues/24280)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.10](https://github.com/ionic-team/ionic-framework/compare/v6.0.9...v6.0.10) (2022-03-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** confirm method now uses selected date ([#24827](https://github.com/ionic-team/ionic-framework/issues/24827)) ([c35b898](https://github.com/ionic-team/ionic-framework/commit/c35b898f1dc0fb706446b6971982df48fd72fe6d)), closes [#24823](https://github.com/ionic-team/ionic-framework/issues/24823)
|
||||
* **datetime:** persist minutes column on hour change ([#24829](https://github.com/ionic-team/ionic-framework/issues/24829)) ([aacb58a](https://github.com/ionic-team/ionic-framework/commit/aacb58a3224e3cc51c731d0c9aa52f52c9276692)), closes [#24821](https://github.com/ionic-team/ionic-framework/issues/24821)
|
||||
* **item-sliding:** close() will maintain disabled state ([#24847](https://github.com/ionic-team/ionic-framework/issues/24847)) ([ea4a9bb](https://github.com/ionic-team/ionic-framework/commit/ea4a9bb69465f8e97746b36638f0b3a26e45da18)), closes [#24747](https://github.com/ionic-team/ionic-framework/issues/24747)
|
||||
* **modal:** .ion-page element is now correctly added ([#24811](https://github.com/ionic-team/ionic-framework/issues/24811)) ([3d0f999](https://github.com/ionic-team/ionic-framework/commit/3d0f99904fe192fcb5f529780858a0f25f076af7)), closes [#24809](https://github.com/ionic-team/ionic-framework/issues/24809)
|
||||
* **modal:** re-enable swipe gestures when modal is dismissed ([#24846](https://github.com/ionic-team/ionic-framework/issues/24846)) ([836c01c](https://github.com/ionic-team/ionic-framework/commit/836c01c73e42caab0101ac4988f0a9b27cf96a5b)), closes [#24817](https://github.com/ionic-team/ionic-framework/issues/24817)
|
||||
* **modal:** sheet modal now allows input focusing when backdrop disabled ([#24840](https://github.com/ionic-team/ionic-framework/issues/24840)) ([e4ec572](https://github.com/ionic-team/ionic-framework/commit/e4ec572043e22bd2626dbcfd204fc22a7335282c)), closes [#24581](https://github.com/ionic-team/ionic-framework/issues/24581)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.9](https://github.com/ionic-team/ionic-framework/compare/v6.0.8...v6.0.9) (2022-02-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** improve datetime sizing in modals ([#24762](https://github.com/ionic-team/ionic-framework/issues/24762)) ([b0ac7de](https://github.com/ionic-team/ionic-framework/commit/b0ac7de168c353ba4899cb74a2b38e25fd0cc0f1)), closes [#23992](https://github.com/ionic-team/ionic-framework/issues/23992)
|
||||
* **datetime:** month and year column order is now locale aware ([#24802](https://github.com/ionic-team/ionic-framework/issues/24802)) ([16647b2](https://github.com/ionic-team/ionic-framework/commit/16647b2c7290389755a4093145788f281c84b7e2)), closes [#24548](https://github.com/ionic-team/ionic-framework/issues/24548)
|
||||
* **datetime:** month picker no longer gives duplicate months on ios 14 and older ([#24792](https://github.com/ionic-team/ionic-framework/issues/24792)) ([b6d7e1c](https://github.com/ionic-team/ionic-framework/commit/b6d7e1c75740a61dcd02c21692e4d4632fb8df5c)), closes [#24663](https://github.com/ionic-team/ionic-framework/issues/24663)
|
||||
* **img:** draggable attribute is now inherited to inner img element ([#24781](https://github.com/ionic-team/ionic-framework/issues/24781)) ([19ac238](https://github.com/ionic-team/ionic-framework/commit/19ac2389eb0843173f51a12de41ac808cd8f0569)), closes [#21325](https://github.com/ionic-team/ionic-framework/issues/21325)
|
||||
* **modal:** backdropBreakpoint allows interactivity behind sheet ([#24798](https://github.com/ionic-team/ionic-framework/issues/24798)) ([fca3f56](https://github.com/ionic-team/ionic-framework/commit/fca3f56ca4568e63fd493debda088263caa86c64)), closes [#24797](https://github.com/ionic-team/ionic-framework/issues/24797)
|
||||
* **popover:** default alignment to 'center' for ios mode ([#24815](https://github.com/ionic-team/ionic-framework/issues/24815)) ([243f673](https://github.com/ionic-team/ionic-framework/commit/243f67362f25699bdb373be6b72cb9c14dc95a32))
|
||||
* **react, vue:** scroll is no longer interrupted on ios ([#24791](https://github.com/ionic-team/ionic-framework/issues/24791)) ([99c91ef](https://github.com/ionic-team/ionic-framework/commit/99c91eff8764c9a1630adedab6a9765dd9754f7d)), closes [#24435](https://github.com/ionic-team/ionic-framework/issues/24435)
|
||||
* **select:** interface components now show correctly ([#24810](https://github.com/ionic-team/ionic-framework/issues/24810)) ([2fc2de5](https://github.com/ionic-team/ionic-framework/commit/2fc2de51771f4a5c3f20c6071284096e5bf31ec8)), closes [#24807](https://github.com/ionic-team/ionic-framework/issues/24807)
|
||||
* **toast:** toast is now correctly excluded from focus trapping ([#24816](https://github.com/ionic-team/ionic-framework/issues/24816)) ([8246112](https://github.com/ionic-team/ionic-framework/commit/8246112ca12f90edb98629ab82e27a792a1fafad)), closes [#24733](https://github.com/ionic-team/ionic-framework/issues/24733)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.8](https://github.com/ionic-team/ionic-framework/compare/v6.0.7...v6.0.8) (2022-02-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **back-button, breadcrumb, item:** flip chevron icons on RTL ([#24705](https://github.com/ionic-team/ionic-framework/issues/24705)) ([a093544](https://github.com/ionic-team/ionic-framework/commit/a093544fdfc438ed03024285b2a35c5f645ea011))
|
||||
* **datetime:** navigate to month within min range ([#24759](https://github.com/ionic-team/ionic-framework/issues/24759)) ([7b3838c](https://github.com/ionic-team/ionic-framework/commit/7b3838cc670de7845bb5937d204e86cdba93b6e6)), closes [#24757](https://github.com/ionic-team/ionic-framework/issues/24757)
|
||||
* **input:** only set native input value if different ([#24758](https://github.com/ionic-team/ionic-framework/issues/24758)) ([fd031aa](https://github.com/ionic-team/ionic-framework/commit/fd031aa1c3f05b7bfa3e0a0ee2a4793e29e22df5)), closes [#24753](https://github.com/ionic-team/ionic-framework/issues/24753)
|
||||
* **router-outlet:** getRouteId() returns the params set in setRouteId(). ([#24656](https://github.com/ionic-team/ionic-framework/issues/24656)) ([be2205e](https://github.com/ionic-team/ionic-framework/commit/be2205e5a2b2f8778bd1f7b4ea5cae0bf96f9ef3)), closes [#24652](https://github.com/ionic-team/ionic-framework/issues/24652)
|
||||
* **router-outlet:** navigating to same route with different params now activates component ([#24760](https://github.com/ionic-team/ionic-framework/issues/24760)) ([abc36ae](https://github.com/ionic-team/ionic-framework/commit/abc36ae80b060a659f7557ad90efe98b78f5ead9)), closes [#24653](https://github.com/ionic-team/ionic-framework/issues/24653)
|
||||
* **vue:** preserve custom classes on IonPage ([#24776](https://github.com/ionic-team/ionic-framework/issues/24776)) ([b401de1](https://github.com/ionic-team/ionic-framework/commit/b401de1ab3385c67cc476ff90971ce131cefcc3f)), closes [#24772](https://github.com/ionic-team/ionic-framework/issues/24772)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.7](https://github.com/ionic-team/ionic-framework/compare/v6.0.6...v6.0.7) (2022-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** inline modals now add .ion-page class correctly ([#24751](https://github.com/ionic-team/ionic-framework/issues/24751)) ([ef46eaf](https://github.com/ionic-team/ionic-framework/commit/ef46eafc9476a85ea3369e542f528d01d3cca0a8)), closes [#24750](https://github.com/ionic-team/ionic-framework/issues/24750)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.6](https://github.com/ionic-team/ionic-framework/compare/v6.0.5...v6.0.6) (2022-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **action-sheet:** background includes safe area margin ([#24700](https://github.com/ionic-team/ionic-framework/issues/24700)) ([8c22646](https://github.com/ionic-team/ionic-framework/commit/8c22646d66e2077fc88aaacf350330097733bb9b)), closes [#24699](https://github.com/ionic-team/ionic-framework/issues/24699)
|
||||
* **angular-server:** publish only the dist directory to avoid import errors ([#24701](https://github.com/ionic-team/ionic-framework/issues/24701)) ([2a27bef](https://github.com/ionic-team/ionic-framework/commit/2a27befe463832b9ca7709ba22421abbdaa4cfa4)), closes [#24605](https://github.com/ionic-team/ionic-framework/issues/24605)
|
||||
* **angular, react, vue:** overlays no longer throw errors when used inside tests ([#24681](https://github.com/ionic-team/ionic-framework/issues/24681)) ([897ae4a](https://github.com/ionic-team/ionic-framework/commit/897ae4a4546ac0dd811125d5513ef23d133a1589)), closes [#24549](https://github.com/ionic-team/ionic-framework/issues/24549) [#24590](https://github.com/ionic-team/ionic-framework/issues/24590)
|
||||
* **datetime:** disable intersection observer during month update ([#24713](https://github.com/ionic-team/ionic-framework/issues/24713)) ([aab4d30](https://github.com/ionic-team/ionic-framework/commit/aab4d306f80851bfd8a02a6361e26d60faeaadb4)), closes [#24712](https://github.com/ionic-team/ionic-framework/issues/24712)
|
||||
* **datetime:** minutes only filtered when max hour matches current hour ([#24710](https://github.com/ionic-team/ionic-framework/issues/24710)) ([231d6df](https://github.com/ionic-team/ionic-framework/commit/231d6df622c1f5dd9ecdff6fed8f61a4bff332df)), closes [#24702](https://github.com/ionic-team/ionic-framework/issues/24702)
|
||||
* **input:** cursor position does not jump to end ([#24736](https://github.com/ionic-team/ionic-framework/issues/24736)) ([4ff9524](https://github.com/ionic-team/ionic-framework/commit/4ff9524e1057aa487069b5982c5f1ecdf51d982b)), closes [#24727](https://github.com/ionic-team/ionic-framework/issues/24727)
|
||||
* **input:** IME composition mode ([#24735](https://github.com/ionic-team/ionic-framework/issues/24735)) ([c6381ce](https://github.com/ionic-team/ionic-framework/commit/c6381ce4f90707774d1c8662bba874f7b306bd1c)), closes [#24669](https://github.com/ionic-team/ionic-framework/issues/24669)
|
||||
* **modal, popover:** quickly opening modal/popover no longer presents duplicates ([#24697](https://github.com/ionic-team/ionic-framework/issues/24697)) ([928c5fb](https://github.com/ionic-team/ionic-framework/commit/928c5fbfcbf3ef1b2c3074464fc20dcf1fe143ae))
|
||||
* **modal:** inline modals inherit ion-page styling ([#24723](https://github.com/ionic-team/ionic-framework/issues/24723)) ([596aad4](https://github.com/ionic-team/ionic-framework/commit/596aad435b5102307da89dd626ca4682b78db452)), closes [#24706](https://github.com/ionic-team/ionic-framework/issues/24706)
|
||||
* **popover:** use alignment with popover options ([#24711](https://github.com/ionic-team/ionic-framework/issues/24711)) ([f5c5c3c](https://github.com/ionic-team/ionic-framework/commit/f5c5c3cffa4f34046b0e9471a9f193db3772180e)), closes [#24709](https://github.com/ionic-team/ionic-framework/issues/24709)
|
||||
* **router:** router push with relative path ([#24719](https://github.com/ionic-team/ionic-framework/issues/24719)) ([d40c0c3](https://github.com/ionic-team/ionic-framework/commit/d40c0c3a0993eaefbe5107e98958c6b0699a62c2)), closes [#24718](https://github.com/ionic-team/ionic-framework/issues/24718)
|
||||
* **select:** value is selected when given array ([#24687](https://github.com/ionic-team/ionic-framework/issues/24687)) ([6ee7d15](https://github.com/ionic-team/ionic-framework/commit/6ee7d159ecfff3382fadb524c5c430172d40c267)), closes [#24742](https://github.com/ionic-team/ionic-framework/issues/24742)
|
||||
* **vue:** replacing routes now updates location state correctly ([#24721](https://github.com/ionic-team/ionic-framework/issues/24721)) ([721a461](https://github.com/ionic-team/ionic-framework/commit/721a461073bbd8e7218cd5ce02965d673f5a03e8)), closes [#24432](https://github.com/ionic-team/ionic-framework/issues/24432)
|
||||
* **vue:** routing history is correctly replaced when overwriting browser history ([#24670](https://github.com/ionic-team/ionic-framework/issues/24670)) ([0b18260](https://github.com/ionic-team/ionic-framework/commit/0b18260da64334d8211c5a0cd806f7416274fc5e)), closes [#23873](https://github.com/ionic-team/ionic-framework/issues/23873)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.5](https://github.com/ionic-team/ionic-framework/compare/v6.0.4...v6.0.5) (2022-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** prevent navigating to disabled months ([#24421](https://github.com/ionic-team/ionic-framework/issues/24421)) ([b40fc46](https://github.com/ionic-team/ionic-framework/commit/b40fc4632efcdc01f1062d9bcec826afff5cf4ea)), closes [#24208](https://github.com/ionic-team/ionic-framework/issues/24208) [#24482](https://github.com/ionic-team/ionic-framework/issues/24482)
|
||||
* **input:** min/max compatibility with react-hook-form ([#24657](https://github.com/ionic-team/ionic-framework/issues/24657)) ([1f91883](https://github.com/ionic-team/ionic-framework/commit/1f918835f437a59f7a70fc59d9305647aa9c298d)), closes [#24489](https://github.com/ionic-team/ionic-framework/issues/24489)
|
||||
* **react-router:** remove page transition flicker on outlet mounting ([#24667](https://github.com/ionic-team/ionic-framework/issues/24667)) ([bdb5c42](https://github.com/ionic-team/ionic-framework/commit/bdb5c421d2d1f72c123c254e50c6d31b3c1a8f42)), closes [#24666](https://github.com/ionic-team/ionic-framework/issues/24666)
|
||||
* **react:** nested router outlets will not reanimate entered views ([#24672](https://github.com/ionic-team/ionic-framework/issues/24672)) ([43aa6c1](https://github.com/ionic-team/ionic-framework/commit/43aa6c11f42fd5cf455c50419d5f5fbb327b2901)), closes [#24107](https://github.com/ionic-team/ionic-framework/issues/24107)
|
||||
* **vue:** going back to a tabs outlet no loger causes classList error ([#24665](https://github.com/ionic-team/ionic-framework/issues/24665)) ([6d7b144](https://github.com/ionic-team/ionic-framework/commit/6d7b1444b63cca03158789fcd41b33a527f6abac)), closes [#24654](https://github.com/ionic-team/ionic-framework/issues/24654)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **various:** don't use lazy-loaded icon names in components ([#24671](https://github.com/ionic-team/ionic-framework/issues/24671)) ([484de50](https://github.com/ionic-team/ionic-framework/commit/484de5074de212dffb4bf4f73bade7acec103fe8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.4](https://github.com/ionic-team/ionic-framework/compare/v6.0.3...v6.0.4) (2022-01-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **accordion:** items inside of the content now correct display borders ([#24618](https://github.com/ionic-team/ionic-framework/issues/24618)) ([d3311df](https://github.com/ionic-team/ionic-framework/commit/d3311df96765948d0a395e4ba99fb9117b44adcb)), closes [#24613](https://github.com/ionic-team/ionic-framework/issues/24613)
|
||||
* **angular:** routerLink with null value works with Angular 13 ([#24622](https://github.com/ionic-team/ionic-framework/issues/24622)) ([90a9a9c](https://github.com/ionic-team/ionic-framework/commit/90a9a9c3e813c8db0a9d6b3b25c152929bea80fe)), closes [#24586](https://github.com/ionic-team/ionic-framework/issues/24586)
|
||||
* **col:** col no longer errors when running in non-browser environment ([#24603](https://github.com/ionic-team/ionic-framework/issues/24603)) ([af0135c](https://github.com/ionic-team/ionic-framework/commit/af0135ce7dbe737b2df46094fd3dc8a41bdb60ae)), closes [#24446](https://github.com/ionic-team/ionic-framework/issues/24446)
|
||||
* **datetime:** datetime locale with h23 will respect max time range ([#24610](https://github.com/ionic-team/ionic-framework/issues/24610)) ([5925e76](https://github.com/ionic-team/ionic-framework/commit/5925e7608ef04f8877e4dd8a80b2c8bdc1cfd4bd)), closes [#24588](https://github.com/ionic-team/ionic-framework/issues/24588)
|
||||
* **datetime:** timepicker popover will position relative to click target ([#24616](https://github.com/ionic-team/ionic-framework/issues/24616)) ([378c632](https://github.com/ionic-team/ionic-framework/commit/378c63264366964e6ea11e1a2ff8791a40f182d4)), closes [#24531](https://github.com/ionic-team/ionic-framework/issues/24531) [#24415](https://github.com/ionic-team/ionic-framework/issues/24415)
|
||||
* **input:** ion-input accepts any string value ([#24606](https://github.com/ionic-team/ionic-framework/issues/24606)) ([43c5977](https://github.com/ionic-team/ionic-framework/commit/43c5977d48cb12331c1d3107eb73f29b92c5e049)), closes [#19884](https://github.com/ionic-team/ionic-framework/issues/19884)
|
||||
* **item:** label text aligns with input text ([#24620](https://github.com/ionic-team/ionic-framework/issues/24620)) ([94d033c](https://github.com/ionic-team/ionic-framework/commit/94d033c4216ae9978aed6346c1c0ea2aec4d375b)), closes [#24404](https://github.com/ionic-team/ionic-framework/issues/24404)
|
||||
* **item:** match material design character counter ([#24335](https://github.com/ionic-team/ionic-framework/issues/24335)) ([54db1a1](https://github.com/ionic-team/ionic-framework/commit/54db1a1e7c71c78e843370848fc768582768333e)), closes [#24334](https://github.com/ionic-team/ionic-framework/issues/24334)
|
||||
* **menu:** focus trapping with menu and overlays no longer results in errors ([#24611](https://github.com/ionic-team/ionic-framework/issues/24611)) ([632dafc](https://github.com/ionic-team/ionic-framework/commit/632dafcd57de5086deebdc7d586b01710aa1a3ce)), closes [#24361](https://github.com/ionic-team/ionic-framework/issues/24361) [#24481](https://github.com/ionic-team/ionic-framework/issues/24481)
|
||||
* **modal:** native keyboard will dismiss when bottom sheet is dragged ([#24642](https://github.com/ionic-team/ionic-framework/issues/24642)) ([525f01f](https://github.com/ionic-team/ionic-framework/commit/525f01f086ebf95ab62af0162b876a25f50a3d4b)), closes [#23878](https://github.com/ionic-team/ionic-framework/issues/23878)
|
||||
* **range:** setting dir on ion-range to enable rtl mode now supported ([#24597](https://github.com/ionic-team/ionic-framework/issues/24597)) ([5dba4e5](https://github.com/ionic-team/ionic-framework/commit/5dba4e5ce0a07f69a08f2b427e8010b311910f88)), closes [#20201](https://github.com/ionic-team/ionic-framework/issues/20201)
|
||||
* **react:** setupIonicReact no longer crashes in SSR environment ([#24604](https://github.com/ionic-team/ionic-framework/issues/24604)) ([360643d](https://github.com/ionic-team/ionic-framework/commit/360643d96a03b345c83b88c9ff06e9aa254dee81))
|
||||
* **searchbar:** setting dir on ion-searchbar to enable rtl mode now supported ([#24602](https://github.com/ionic-team/ionic-framework/issues/24602)) ([35e5235](https://github.com/ionic-team/ionic-framework/commit/35e523564561c0f3323efa761c4654016b97ef69))
|
||||
* **segment:** setting dir on ion-segment to enable rtl mode now supported ([#24601](https://github.com/ionic-team/ionic-framework/issues/24601)) ([2940e73](https://github.com/ionic-team/ionic-framework/commit/2940e73a4504247eecb0de6c433104f529530850)), closes [#23978](https://github.com/ionic-team/ionic-framework/issues/23978)
|
||||
* **spinner:** ensure transform doesn't overwrite circular animation ([#24643](https://github.com/ionic-team/ionic-framework/issues/24643)) ([88ce010](https://github.com/ionic-team/ionic-framework/commit/88ce010418eaca38786b51720c696860b417ab6d))
|
||||
* **toast:** allow input focus while toast is visible ([#24572](https://github.com/ionic-team/ionic-framework/issues/24572)) ([29f1140](https://github.com/ionic-team/ionic-framework/commit/29f1140384ae7e1402b09c3760e168cf79833619)), closes [#24571](https://github.com/ionic-team/ionic-framework/issues/24571)
|
||||
* **toggle:** setting dir on ion-toggle to enable rtl mode now supported ([#24600](https://github.com/ionic-team/ionic-framework/issues/24600)) ([353dbc0](https://github.com/ionic-team/ionic-framework/commit/353dbc0537ef3b46b9ba13a365ebc5da269de4c7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.3](https://github.com/ionic-team/ionic-framework/compare/v6.0.2...v6.0.3) (2022-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular-server:** use correct @ionic/angular dependency version ([#24593](https://github.com/ionic-team/ionic-framework/issues/24593)) ([be022f7](https://github.com/ionic-team/ionic-framework/commit/be022f7de8df85ae842b0e111722b03448d60387)), closes [#24592](https://github.com/ionic-team/ionic-framework/issues/24592)
|
||||
* **angular:** apply touch, dirty and pristine form control classes ([#24558](https://github.com/ionic-team/ionic-framework/issues/24558)) ([273ae2c](https://github.com/ionic-team/ionic-framework/commit/273ae2cc087b2a5a30fb50a1b0eaeb0a221900fc)), closes [#24483](https://github.com/ionic-team/ionic-framework/issues/24483)
|
||||
* **datetime:** showing calendar grid no longer causes month to switch on ios 15 ([#24554](https://github.com/ionic-team/ionic-framework/issues/24554)) ([3d20959](https://github.com/ionic-team/ionic-framework/commit/3d2095922147ea3763e977412977edd9586fec5d)), closes [#24405](https://github.com/ionic-team/ionic-framework/issues/24405)
|
||||
* **item:** error slot visible in Safari ([#24579](https://github.com/ionic-team/ionic-framework/issues/24579)) ([af01a8b](https://github.com/ionic-team/ionic-framework/commit/af01a8b3073dce784cc042923d712b9492638d32)), closes [#24575](https://github.com/ionic-team/ionic-framework/issues/24575)
|
||||
* **menu:** remove main attribute that was supposed to removed in v5 ([#24565](https://github.com/ionic-team/ionic-framework/issues/24565)) ([7704ac3](https://github.com/ionic-team/ionic-framework/commit/7704ac3a3710396248590daecb945b76825a0539)), closes [#24563](https://github.com/ionic-team/ionic-framework/issues/24563)
|
||||
* **modal:** life cycle events for controller modals ([#24508](https://github.com/ionic-team/ionic-framework/issues/24508)) ([9a15753](https://github.com/ionic-team/ionic-framework/commit/9a15753fd95e32155abdeb490ec57cb72385ad1a)), closes [#24460](https://github.com/ionic-team/ionic-framework/issues/24460)
|
||||
* **overlays:** getTop now returns the top-most presented overlay ([#24547](https://github.com/ionic-team/ionic-framework/issues/24547)) ([f5b4382](https://github.com/ionic-team/ionic-framework/commit/f5b4382fd5728365e4badf39bc1dd0c149b45c2c)), closes [#19111](https://github.com/ionic-team/ionic-framework/issues/19111)
|
||||
* **react:** add useRef wrapper to useIonOverlay state to avoid stale references ([#24553](https://github.com/ionic-team/ionic-framework/issues/24553)) ([bce849c](https://github.com/ionic-team/ionic-framework/commit/bce849c5f324522002eff7f8a5e5023150e9201c))
|
||||
* **react:** prevent errors when dismissing inline popover after containing element is removed ([#24569](https://github.com/ionic-team/ionic-framework/issues/24569)) ([c8a392a](https://github.com/ionic-team/ionic-framework/commit/c8a392aef5fbf25f59a573897d970c41abac04d2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.2](https://github.com/ionic-team/ionic-framework/compare/v6.0.1...v6.0.2) (2022-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** attach change detector ref for inline overlays ([#24521](https://github.com/ionic-team/ionic-framework/issues/24521)) ([5c54593](https://github.com/ionic-team/ionic-framework/commit/5c54593dde64ae61347568405ebf74502cfff370)), closes [#24502](https://github.com/ionic-team/ionic-framework/issues/24502)
|
||||
* **angular:** popover will respect side attribute value ([#24470](https://github.com/ionic-team/ionic-framework/issues/24470)) ([e6955a2](https://github.com/ionic-team/ionic-framework/commit/e6955a26b92fc536c5c73b60b5943881c7d58ee1)), closes [#24466](https://github.com/ionic-team/ionic-framework/issues/24466)
|
||||
* **breadcrumb:** support routerLink on breadcrumb ([#24509](https://github.com/ionic-team/ionic-framework/issues/24509)) ([5bb1414](https://github.com/ionic-team/ionic-framework/commit/5bb1414f7fa04ea07954cb3f68883ee2f162586a)), closes [#24493](https://github.com/ionic-team/ionic-framework/issues/24493)
|
||||
* **css:** inline css source in source maps ([#24514](https://github.com/ionic-team/ionic-framework/issues/24514)) ([987d46c](https://github.com/ionic-team/ionic-framework/commit/987d46cfa6e48a932330f04f2e8eb7054b11baf8)), closes [#24441](https://github.com/ionic-team/ionic-framework/issues/24441)
|
||||
* **datetime:** add top padding to MD calendar month grid ([#24522](https://github.com/ionic-team/ionic-framework/issues/24522)) ([bd82b5d](https://github.com/ionic-team/ionic-framework/commit/bd82b5dc1d06ba22a5410858802d57735fdcf450)), closes [#24408](https://github.com/ionic-team/ionic-framework/issues/24408)
|
||||
* **datetime:** RTL will no longer infinitely scroll ([#24475](https://github.com/ionic-team/ionic-framework/issues/24475)) ([8f00008](https://github.com/ionic-team/ionic-framework/commit/8f000089c2986f292147c7f501f23c8c7d1df457)), closes [#24472](https://github.com/ionic-team/ionic-framework/issues/24472)
|
||||
* **datetime:** time picker format with hourCycle h23 ([#24476](https://github.com/ionic-team/ionic-framework/issues/24476)) ([a3724e6](https://github.com/ionic-team/ionic-framework/commit/a3724e6a5662c5bc1b724d80540530472827506e)), closes [#24474](https://github.com/ionic-team/ionic-framework/issues/24474)
|
||||
* **datetime:** update active day styling when day is selected ([#24454](https://github.com/ionic-team/ionic-framework/issues/24454)) ([4304391](https://github.com/ionic-team/ionic-framework/commit/430439191dba824c11290d7f8622fea10ced6c40)), closes [#24414](https://github.com/ionic-team/ionic-framework/issues/24414) [#24451](https://github.com/ionic-team/ionic-framework/issues/24451)
|
||||
* **datetime:** wheel picker shows correct column order in rtl ([#24546](https://github.com/ionic-team/ionic-framework/issues/24546)) ([c90ce31](https://github.com/ionic-team/ionic-framework/commit/c90ce311a86ccb7c06b1cde91a4659f6682df04d)), closes [#24378](https://github.com/ionic-team/ionic-framework/issues/24378)
|
||||
* **overlays:** define custom element children ([#24439](https://github.com/ionic-team/ionic-framework/issues/24439)) ([4715b83](https://github.com/ionic-team/ionic-framework/commit/4715b83abb30ec5930710d16e5bfe8fc88a940ce)), closes [#24393](https://github.com/ionic-team/ionic-framework/issues/24393)
|
||||
* **popover:** allow arrow configuration with controller approach ([#24512](https://github.com/ionic-team/ionic-framework/issues/24512)) ([b39003a](https://github.com/ionic-team/ionic-framework/commit/b39003a4c67cd7e01d09be012c9e12d99ca1730a)), closes [#24487](https://github.com/ionic-team/ionic-framework/issues/24487)
|
||||
* **radio:** fix radio not showing checked state when not in a group ([#24423](https://github.com/ionic-team/ionic-framework/issues/24423)) ([94a781c](https://github.com/ionic-team/ionic-framework/commit/94a781cb6a3d92c5e6cab1a7603bfe25826a753c))
|
||||
* **react,vue:** backdrop for inline modal/popover overlay ([#24453](https://github.com/ionic-team/ionic-framework/issues/24453)) ([77f8412](https://github.com/ionic-team/ionic-framework/commit/77f8412b746222793cd9d17f12f50d512ab5e886)), closes [#24449](https://github.com/ionic-team/ionic-framework/issues/24449)
|
||||
* **react:** building app for production now works correctly with vite ([#24515](https://github.com/ionic-team/ionic-framework/issues/24515)) ([32fad3d](https://github.com/ionic-team/ionic-framework/commit/32fad3d02cb6b012a772de03eafe3e3a6b1300e0)), closes [#24229](https://github.com/ionic-team/ionic-framework/issues/24229)
|
||||
* **react:** scrolling to bottom of modal contents ([#24510](https://github.com/ionic-team/ionic-framework/issues/24510)) ([1462cef](https://github.com/ionic-team/ionic-framework/commit/1462cef69225e20582e2f9a0b8fd655ca2066b79)), closes [#24478](https://github.com/ionic-team/ionic-framework/issues/24478)
|
||||
* **refresher:** import icons to avoid errors in react and vue ([#24525](https://github.com/ionic-team/ionic-framework/issues/24525)) ([388622f](https://github.com/ionic-team/ionic-framework/commit/388622f9734b7b832bca3ede99820a7124faa618)), closes [#24480](https://github.com/ionic-team/ionic-framework/issues/24480)
|
||||
* **vue:** correct route is replaced when using router.replace ([#24533](https://github.com/ionic-team/ionic-framework/issues/24533)) ([90458da](https://github.com/ionic-team/ionic-framework/commit/90458da406e2f7a6675be185409ea78595a35128)), closes [#24226](https://github.com/ionic-team/ionic-framework/issues/24226)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.1](https://github.com/ionic-team/ionic/compare/v6.0.0...v6.0.1) (2021-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** datetime now appears correctly when presented in modal ([#24385](https://github.com/ionic-team/ionic/issues/24385)) ([e7d0674](https://github.com/ionic-team/ionic/commit/e7d06743ae2e09864510940bf8a97bc312ef1cf8)), closes [#24112](https://github.com/ionic-team/ionic-framework/issues/24112)
|
||||
* **item:** remove empty padding space for item bottom ([#24323](https://github.com/ionic-team/ionic/issues/24323)) ([500985c](https://github.com/ionic-team/ionic/commit/500985ce04783f502a1f5c50fbd8b4c5e93294d7)), closes [#23892](https://github.com/ionic-team/ionic/issues/23892)
|
||||
* **modal:** fix timing issue when rapidly closing and opening controller modal ([#24380](https://github.com/ionic-team/ionic/issues/24380)) ([732f8e1](https://github.com/ionic-team/ionic/commit/732f8e10ce604f1a3e98518ae9c3a4afd7803e9a)), closes [#24230](https://github.com/ionic-team/ionic-framework/issues/24230)
|
||||
* **overlays:** define children custom elements for picker ([#24372](https://github.com/ionic-team/ionic/issues/24372)) ([7c700b4](https://github.com/ionic-team/ionic/commit/7c700b4caa35d7eb50c877d794f9db9fad6ed88b)), closes [#24366](https://github.com/ionic-team/ionic/issues/24366)
|
||||
* **vue:** improve query params handling in tabs ([#24355](https://github.com/ionic-team/ionic/issues/24355)) ([6309d5d](https://github.com/ionic-team/ionic/commit/6309d5ddbaa7da5e37eda4e19866baf380069578)), closes [#24353](https://github.com/ionic-team/ionic/issues/24353)
|
||||
* **vue:** strongly typed controller methods ([#24388](https://github.com/ionic-team/ionic/issues/24388)) ([a5d56b3](https://github.com/ionic-team/ionic/commit/a5d56b3d5a0a64fd4c62f4beab69a3a1681c0b70)), closes [#24387](https://github.com/ionic-team/ionic/issues/24387)
|
||||
* **vue:** tabs no longer get unmounted when navigating back to a tabs context ([#24337](https://github.com/ionic-team/ionic/issues/24337)) ([bf8e436](https://github.com/ionic-team/ionic/commit/bf8e436ee3f7441ebbc7eaf53ec8d04545dab476)), closes [#24332](https://github.com/ionic-team/ionic/issues/24332)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **content:** remove global click listener to improve interaction performance ([#24360](https://github.com/ionic-team/ionic/issues/24360)) ([1bfac52](https://github.com/ionic-team/ionic/commit/1bfac52331d3f296e5721b2a6c3fd94a97450a1d)), closes [#24359](https://github.com/ionic-team/ionic/issues/24359)
|
||||
|
||||
|
||||
|
||||
## [5.9.3](https://github.com/ionic-team/ionic/compare/v5.9.2...v5.9.3) (2021-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **vue:** improve query params handling in tabs ([#24355](https://github.com/ionic-team/ionic/issues/24355)) ([1c28750](https://github.com/ionic-team/ionic/commit/1c2875044ad4d93fdca866017159a89f4dc8872d)), closes [#24353](https://github.com/ionic-team/ionic/issues/24353)
|
||||
* **vue:** tabs no longer get unmounted when navigating back to a tabs context ([#24337](https://github.com/ionic-team/ionic/issues/24337)) ([4aab72b](https://github.com/ionic-team/ionic/commit/4aab72b06159729d2dcd18b2ef0b76f693e5a74e)), closes [#24332](https://github.com/ionic-team/ionic/issues/24332)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **content:** remove global click listener to improve interaction performance ([#24360](https://github.com/ionic-team/ionic/issues/24360)) ([9c9e28c](https://github.com/ionic-team/ionic/commit/9c9e28ccc9f899c403c757d911ac02d9099415af)), closes [#24359](https://github.com/ionic-team/ionic/issues/24359)
|
||||
|
||||
|
||||
|
||||
# [6.0.0 Titanium](https://github.com/ionic-team/ionic/compare/v6.0.0-rc.4...v6.0.0) (2021-12-08)
|
||||
|
||||
Enjoy! 🚀
|
||||
@@ -4670,4 +4933,4 @@ The following dependencies need to be updated to resolve build errors
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
|
||||
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)
|
||||
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)
|
||||
|
||||
118
angular/CHANGELOG.md
Normal file
118
angular/CHANGELOG.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.0.13](https://github.com/ionic-team/ionic/compare/v6.0.12...v6.0.13) (2022-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ngOnDestroy runs inside angular zone ([#24949](https://github.com/ionic-team/ionic/issues/24949)) ([a8fd2d9](https://github.com/ionic-team/ionic/commit/a8fd2d9199ca92d62bce6abf8caccc7709fa5ca1)), closes [#22571](https://github.com/ionic-team/ionic/issues/22571)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.12](https://github.com/ionic-team/ionic/compare/v6.0.11...v6.0.12) (2022-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tabs:** angular, fire willChange event before selected tab changes ([#24910](https://github.com/ionic-team/ionic/issues/24910)) ([d5efa11](https://github.com/ionic-team/ionic/commit/d5efa113317eaf874712134dc9b8e4502aa4760f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.11](https://github.com/ionic-team/ionic/compare/v6.0.10...v6.0.11) (2022-03-09)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.10](https://github.com/ionic-team/ionic/compare/v6.0.9...v6.0.10) (2022-03-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** .ion-page element is now correctly added ([#24811](https://github.com/ionic-team/ionic/issues/24811)) ([3d0f999](https://github.com/ionic-team/ionic/commit/3d0f99904fe192fcb5f529780858a0f25f076af7)), closes [#24809](https://github.com/ionic-team/ionic/issues/24809)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.9](https://github.com/ionic-team/ionic/compare/v6.0.8...v6.0.9) (2022-02-23)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.8](https://github.com/ionic-team/ionic/compare/v6.0.7...v6.0.8) (2022-02-15)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.7](https://github.com/ionic-team/ionic/compare/v6.0.6...v6.0.7) (2022-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** inline modals now add .ion-page class correctly ([#24751](https://github.com/ionic-team/ionic/issues/24751)) ([ef46eaf](https://github.com/ionic-team/ionic/commit/ef46eafc9476a85ea3369e542f528d01d3cca0a8)), closes [#24750](https://github.com/ionic-team/ionic/issues/24750)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.6](https://github.com/ionic-team/ionic/compare/v6.0.5...v6.0.6) (2022-02-09)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.5](https://github.com/ionic-team/ionic/compare/v6.0.4...v6.0.5) (2022-02-02)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.4](https://github.com/ionic-team/ionic/compare/v6.0.3...v6.0.4) (2022-01-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** routerLink with null value works with Angular 13 ([#24622](https://github.com/ionic-team/ionic/issues/24622)) ([90a9a9c](https://github.com/ionic-team/ionic/commit/90a9a9c3e813c8db0a9d6b3b25c152929bea80fe)), closes [#24586](https://github.com/ionic-team/ionic/issues/24586)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** apply touch, dirty and pristine form control classes ([#24558](https://github.com/ionic-team/ionic/issues/24558)) ([273ae2c](https://github.com/ionic-team/ionic/commit/273ae2cc087b2a5a30fb50a1b0eaeb0a221900fc)), closes [#24483](https://github.com/ionic-team/ionic/issues/24483)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** attach change detector ref for inline overlays ([#24521](https://github.com/ionic-team/ionic/issues/24521)) ([5c54593](https://github.com/ionic-team/ionic/commit/5c54593dde64ae61347568405ebf74502cfff370)), closes [#24502](https://github.com/ionic-team/ionic/issues/24502)
|
||||
* **angular:** popover will respect side attribute value ([#24470](https://github.com/ionic-team/ionic/issues/24470)) ([e6955a2](https://github.com/ionic-team/ionic/commit/e6955a26b92fc536c5c73b60b5943881c7d58ee1)), closes [#24466](https://github.com/ionic-team/ionic/issues/24466)
|
||||
18
angular/package-lock.json
generated
18
angular/package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "6.0.0",
|
||||
"version": "6.0.13",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/angular",
|
||||
"version": "6.0.0",
|
||||
"version": "6.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ionic/core": "6.0.0-rc.4",
|
||||
"@ionic/core": "6.0.0",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
@@ -1023,9 +1023,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@ionic/core": {
|
||||
"version": "6.0.0-rc.4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.0-rc.4.tgz",
|
||||
"integrity": "sha512-bQmF2U07/HwN9x6xquNeXQyRDWDUfLmkDV5F/foOxdQk0qd78v9x4xdX3Yr9ypsn2sxlu6U83wALYqmw6fF8Fg==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.0.tgz",
|
||||
"integrity": "sha512-3e5EJhDebK4pCiFREpNB95o2kBSAdhRb3eMsBDOCYWYuFlcZEOGOpiGx+kYF/klYVQnB45UXAmR8nCX1indmHQ==",
|
||||
"dependencies": {
|
||||
"@stencil/core": "~2.11.0-0",
|
||||
"ionicons": "^6.0.0",
|
||||
@@ -7951,9 +7951,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "6.0.0-rc.4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.0-rc.4.tgz",
|
||||
"integrity": "sha512-bQmF2U07/HwN9x6xquNeXQyRDWDUfLmkDV5F/foOxdQk0qd78v9x4xdX3Yr9ypsn2sxlu6U83wALYqmw6fF8Fg==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.0.tgz",
|
||||
"integrity": "sha512-3e5EJhDebK4pCiFREpNB95o2kBSAdhRb3eMsBDOCYWYuFlcZEOGOpiGx+kYF/klYVQnB45UXAmR8nCX1indmHQ==",
|
||||
"requires": {
|
||||
"@stencil/core": "~2.11.0-0",
|
||||
"ionicons": "^6.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "6.0.0",
|
||||
"version": "6.0.13",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -44,7 +44,7 @@
|
||||
"validate": "npm i && npm run lint && npm run test && npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/core": "6.0.0",
|
||||
"@ionic/core": "^6.0.13",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
|
||||
@@ -96,7 +96,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
if (formControl) {
|
||||
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
|
||||
methodsToPatch.forEach((method) => {
|
||||
if (formControl.get(method)) {
|
||||
if (typeof formControl[method] !== 'undefined') {
|
||||
const oldFn = formControl[method].bind(formControl);
|
||||
formControl[method] = (...params: any[]) => {
|
||||
oldFn(...params);
|
||||
|
||||
@@ -57,10 +57,10 @@ export class IonTabs {
|
||||
onPageSelected(detail: StackEvent): void {
|
||||
const stackId = detail.enteringView.stackId;
|
||||
if (detail.tabSwitch && stackId !== undefined) {
|
||||
this.ionTabsWillChange.emit({ tab: stackId });
|
||||
if (this.tabBar) {
|
||||
this.tabBar.selectedTab = stackId;
|
||||
}
|
||||
this.ionTabsWillChange.emit({ tab: stackId });
|
||||
this.ionTabsDidChange.emit({ tab: stackId });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { LocationStrategy } from '@angular/common';
|
||||
import { ElementRef, OnChanges, OnDestroy, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { ElementRef, OnChanges, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { AnimationBuilder, RouterDirection } from '@ionic/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
@Directive({
|
||||
selector: '[routerLink]',
|
||||
})
|
||||
export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy {
|
||||
private subscription?: Subscription;
|
||||
|
||||
export class RouterLinkDelegateDirective implements OnInit, OnChanges {
|
||||
@Input()
|
||||
routerDirection: RouterDirection = 'forward';
|
||||
|
||||
@@ -34,14 +31,8 @@ export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy
|
||||
this.updateTargetUrlAndHref();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private updateTargetUrlAndHref() {
|
||||
if (this.routerLink) {
|
||||
if (this.routerLink?.urlTree) {
|
||||
const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree));
|
||||
this.elementRef.nativeElement.href = href;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ export class StackController {
|
||||
enteringView.ref.changeDetectorRef.reattach();
|
||||
|
||||
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false, animationBuilder)
|
||||
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
|
||||
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location, this.zone))
|
||||
.then(() => ({
|
||||
enteringView,
|
||||
direction,
|
||||
@@ -201,7 +201,7 @@ export class StackController {
|
||||
this.skipTransition = true;
|
||||
this.pop(1);
|
||||
} else if (this.activeView) {
|
||||
cleanup(this.activeView, this.views, this.views, this.location);
|
||||
cleanup(this.activeView, this.views, this.views, this.location, this.zone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,11 +294,17 @@ export class StackController {
|
||||
}
|
||||
}
|
||||
|
||||
const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
|
||||
const cleanupAsync = (
|
||||
activeRoute: RouteView,
|
||||
views: RouteView[],
|
||||
viewsSnapshot: RouteView[],
|
||||
location: Location,
|
||||
zone: NgZone
|
||||
) => {
|
||||
if (typeof (requestAnimationFrame as any) === 'function') {
|
||||
return new Promise<void>((resolve) => {
|
||||
requestAnimationFrame(() => {
|
||||
cleanup(activeRoute, views, viewsSnapshot, location);
|
||||
cleanup(activeRoute, views, viewsSnapshot, location, zone);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
@@ -306,8 +312,18 @@ const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot:
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const cleanup = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
|
||||
viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView);
|
||||
const cleanup = (
|
||||
activeRoute: RouteView,
|
||||
views: RouteView[],
|
||||
viewsSnapshot: RouteView[],
|
||||
location: Location,
|
||||
zone: NgZone
|
||||
) => {
|
||||
/**
|
||||
* Re-enter the Angular zone when destroying page components. This will allow
|
||||
* lifecycle events (`ngOnDestroy`) to be run inside the Angular zone.
|
||||
*/
|
||||
zone.run(() => viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView));
|
||||
|
||||
views.forEach((view) => {
|
||||
/**
|
||||
|
||||
@@ -73,7 +73,7 @@ export declare interface IonModal extends Components.IonModal {
|
||||
@Component({
|
||||
selector: 'ion-modal',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
|
||||
template: `<div class="ion-page" *ngIf="isCmpOpen"><ng-container [ngTemplateOutlet]="template"></ng-container></div>`,
|
||||
inputs: [
|
||||
'animated',
|
||||
'backdropBreakpoint',
|
||||
@@ -103,7 +103,6 @@ export class IonModal {
|
||||
protected el: HTMLElement;
|
||||
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
|
||||
this.el.addEventListener('willPresent', () => {
|
||||
|
||||
@@ -66,6 +66,7 @@ export declare interface IonPopover extends Components.IonPopover {
|
||||
'triggerAction',
|
||||
'reference',
|
||||
'size',
|
||||
'side',
|
||||
],
|
||||
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
|
||||
})
|
||||
@@ -92,6 +93,7 @@ export declare interface IonPopover extends Components.IonPopover {
|
||||
'triggerAction',
|
||||
'reference',
|
||||
'size',
|
||||
'side',
|
||||
],
|
||||
})
|
||||
export class IonPopover {
|
||||
@@ -102,7 +104,6 @@ export class IonPopover {
|
||||
protected el: HTMLElement;
|
||||
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
|
||||
this.el.addEventListener('willPresent', () => {
|
||||
|
||||
1
angular/test/test-app/.gitignore
vendored
1
angular/test/test-app/.gitignore
vendored
@@ -29,6 +29,7 @@ speed-measure-plugin.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
|
||||
@@ -8,6 +8,16 @@ describe('Form', () => {
|
||||
cy.get('#input-touched').click();
|
||||
cy.get('#touched-input-test').should('have.class', 'ion-touched');
|
||||
});
|
||||
|
||||
describe('markAllAsTouched', () => {
|
||||
it('should apply .ion-touched to nearest ion-item', () => {
|
||||
cy.get('#mark-all-touched-button').click();
|
||||
cy.get('form ion-item').each(item => {
|
||||
cy.wrap(item).should('have.class', 'ion-touched');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('change', () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ describe('Modals', () => {
|
||||
|
||||
cy.get('app-modal-example h2').should('have.text', '123');
|
||||
cy.get('app-modal-example h3').should('have.text', '321');
|
||||
cy.get('#modalInstance').should('have.text', 'true');
|
||||
|
||||
cy.get('#onWillDismiss').should('have.text', 'false');
|
||||
cy.get('#onDidDismiss').should('have.text', 'false');
|
||||
@@ -41,3 +42,38 @@ describe('Modals', () => {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('Modals: Inline', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/modal-inline');
|
||||
});
|
||||
|
||||
it('should initially have no items', () => {
|
||||
cy.get('ion-list ion-item').should('not.exist');
|
||||
});
|
||||
|
||||
it('should have items after opening', () => {
|
||||
cy.get('#open-modal').click();
|
||||
|
||||
cy.get('ion-list ion-item:nth-child(1)').should('have.text', 'A');
|
||||
cy.get('ion-list ion-item:nth-child(2)').should('have.text', 'B');
|
||||
cy.get('ion-list ion-item:nth-child(3)').should('have.text', 'C');
|
||||
cy.get('ion-list ion-item:nth-child(4)').should('have.text', 'D');
|
||||
});
|
||||
|
||||
it('should have a div with .ion-page after opening', () => {
|
||||
cy.get('#open-modal').click();
|
||||
|
||||
cy.get('ion-modal').children('.ion-page').should('exist');
|
||||
});
|
||||
|
||||
it('should remove .ion-page when closing the modal', () => {
|
||||
cy.get('#open-modal').click();
|
||||
|
||||
cy.get('ion-modal').children('.ion-page').should('exist');
|
||||
cy.get('ion-modal').trigger('click', 20, 20);
|
||||
|
||||
cy.get('ion-modal').children('.ion-page').should('not.exist');
|
||||
})
|
||||
});
|
||||
|
||||
@@ -20,6 +20,12 @@ describe('Nested Outlet', () => {
|
||||
cy.ionPageVisible('app-nested-outlet-page2');
|
||||
|
||||
cy.get('ion-router-outlet ion-router-outlet app-nested-outlet-page2 h1').should('have.text', 'Nested page 2');
|
||||
|
||||
cy.get('#goto-nested-page1').click();
|
||||
cy.ionPageVisible('app-nested-outlet-page');
|
||||
|
||||
cy.get('#goto-nested-page2').click();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
18
angular/test/test-app/e2e/src/popover.spec.ts
Normal file
18
angular/test/test-app/e2e/src/popover.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
describe('Popovers: Inline', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/popover-inline');
|
||||
});
|
||||
|
||||
it('should initially have no items', () => {
|
||||
cy.get('ion-list ion-item').should('not.exist');
|
||||
});
|
||||
|
||||
it('should have items after 1500ms', () => {
|
||||
cy.wait(1500);
|
||||
|
||||
cy.get('ion-list ion-item:nth-child(1)').should('have.text', 'A');
|
||||
cy.get('ion-list ion-item:nth-child(2)').should('have.text', 'B');
|
||||
cy.get('ion-list ion-item:nth-child(3)').should('have.text', 'C');
|
||||
cy.get('ion-list ion-item:nth-child(4)').should('have.text', 'D');
|
||||
});
|
||||
});
|
||||
@@ -19,6 +19,30 @@ describe('Tabs', () => {
|
||||
tab.find('.segment-changed').should('have.text', 'false');
|
||||
});
|
||||
|
||||
describe('when navigating between tabs', () => {
|
||||
|
||||
it('should emit ionTabsWillChange before setting the selected tab', () => {
|
||||
cy.get('#ionTabsWillChangeCounter').should('have.text', '1');
|
||||
cy.get('#ionTabsWillChangeEvent').should('have.text', 'account');
|
||||
cy.get('#ionTabsWillChangeSelectedTab').should('have.text', '');
|
||||
|
||||
cy.get('#ionTabsDidChangeCounter').should('have.text', '1');
|
||||
cy.get('#ionTabsDidChangeEvent').should('have.text', 'account');
|
||||
cy.get('#ionTabsDidChangeSelectedTab').should('have.text', 'account');
|
||||
|
||||
cy.get('#tab-button-contact').click();
|
||||
|
||||
cy.get('#ionTabsWillChangeCounter').should('have.text', '2');
|
||||
cy.get('#ionTabsWillChangeEvent').should('have.text', 'contact');
|
||||
cy.get('#ionTabsWillChangeSelectedTab').should('have.text', 'account');
|
||||
|
||||
cy.get('#ionTabsDidChangeCounter').should('have.text', '2');
|
||||
cy.get('#ionTabsDidChangeEvent').should('have.text', 'contact');
|
||||
cy.get('#ionTabsDidChangeSelectedTab').should('have.text', 'contact');
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it('should simulate stack + double tab click', () => {
|
||||
let tab = getSelectedTab();
|
||||
tab.find('#goto-tab1-page2').click();
|
||||
|
||||
15151
angular/test/test-app/package-lock.json
generated
15151
angular/test/test-app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,33 +4,33 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "npm run sync && ng serve",
|
||||
"start": "ng serve",
|
||||
"sync:build": "sh scripts/build-ionic.sh",
|
||||
"sync": "sh scripts/sync.sh",
|
||||
"build": "npm run sync && ng build --configuration production --no-progress",
|
||||
"build": "ng build --configuration production --no-progress",
|
||||
"lint": "ng lint",
|
||||
"postinstall": "npm run sync && ngcc",
|
||||
"postinstall": "ngcc",
|
||||
"serve:ssr": "node dist/test-app/server/main.js",
|
||||
"build:ssr": "ng build --prod && ng run test-app:server:production",
|
||||
"dev:ssr": "ng run test-app:serve-ssr",
|
||||
"prerender": "ng run test-app:prerender",
|
||||
"cy.open": "cypress open",
|
||||
"cy.run": "cypress run",
|
||||
"test": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.run\" --kill-others --success first",
|
||||
"test": "concurrently \"npm run start -- --configuration production\" \"wait-on http-get://localhost:4200 && npm run cy.run\" --kill-others --success first",
|
||||
"test.watch": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.open\" --kill-others --success first"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^12.2.8",
|
||||
"@angular/common": "^12.2.8",
|
||||
"@angular/compiler": "^12.2.8",
|
||||
"@angular/core": "^12.2.8",
|
||||
"@angular/forms": "^12.2.8",
|
||||
"@angular/platform-browser": "^12.2.8",
|
||||
"@angular/platform-browser-dynamic": "^12.2.8",
|
||||
"@angular/platform-server": "^12.2.8",
|
||||
"@angular/router": "^12.2.8",
|
||||
"@ionic/angular": "^5.3.1",
|
||||
"@ionic/angular-server": "^5.3.1",
|
||||
"@angular/animations": "^13.1.3",
|
||||
"@angular/common": "^13.1.3",
|
||||
"@angular/compiler": "^13.1.3",
|
||||
"@angular/core": "^13.1.3",
|
||||
"@angular/forms": "^13.1.3",
|
||||
"@angular/platform-browser": "^13.1.3",
|
||||
"@angular/platform-browser-dynamic": "^13.1.3",
|
||||
"@angular/platform-server": "^13.1.3",
|
||||
"@angular/router": "^13.1.3",
|
||||
"@ionic/angular": "^6.0.12",
|
||||
"@ionic/angular-server": "^6.0.12",
|
||||
"@nguniversal/express-engine": "^12.1.1",
|
||||
"angular-in-memory-web-api": "^0.11.0",
|
||||
"core-js": "^2.6.11",
|
||||
@@ -41,16 +41,16 @@
|
||||
"zone.js": "^0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^12.2.8",
|
||||
"@angular-eslint/builder": "12.5.0",
|
||||
"@angular-eslint/eslint-plugin": "12.5.0",
|
||||
"@angular-eslint/eslint-plugin-template": "12.5.0",
|
||||
"@angular-eslint/schematics": "12.5.0",
|
||||
"@angular-eslint/template-parser": "12.5.0",
|
||||
"@angular/cli": "^12.2.8",
|
||||
"@angular/compiler-cli": "^12.2.8",
|
||||
"@angular/language-service": "^12.2.8",
|
||||
"@nguniversal/builders": "^12.1.1",
|
||||
"@angular-devkit/build-angular": "^13.1.4",
|
||||
"@angular-eslint/builder": "13.0.1",
|
||||
"@angular-eslint/eslint-plugin": "13.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "13.0.1",
|
||||
"@angular-eslint/schematics": "13.0.1",
|
||||
"@angular-eslint/template-parser": "13.0.1",
|
||||
"@angular/cli": "^13.1.4",
|
||||
"@angular/compiler-cli": "^13.1.3",
|
||||
"@angular/language-service": "^13.1.3",
|
||||
"@nguniversal/builders": "^13.0.2",
|
||||
"@types/express": "^4.17.7",
|
||||
"@types/node": "^12.12.54",
|
||||
"@typescript-eslint/eslint-plugin": "4.28.2",
|
||||
@@ -60,7 +60,7 @@
|
||||
"eslint": "^7.26.0",
|
||||
"ts-loader": "^6.2.2",
|
||||
"ts-node": "^8.3.0",
|
||||
"typescript": "^4.3.5",
|
||||
"typescript": "^4.5.5",
|
||||
"wait-on": "^5.2.1",
|
||||
"webpack": "^5.61.0",
|
||||
"webpack-cli": "^3.3.12"
|
||||
|
||||
@@ -27,7 +27,9 @@ const routes: Routes = [
|
||||
{ path: 'inputs', component: InputsComponent },
|
||||
{ path: 'form', component: FormComponent },
|
||||
{ path: 'modals', component: ModalComponent },
|
||||
{ path: 'modal-inline', loadChildren: () => import('./modal-inline').then(m => m.ModalInlineModule) },
|
||||
{ path: 'view-child', component: ViewChildComponent },
|
||||
{ path: 'popover-inline', loadChildren: () => import('./popover-inline').then(m => m.PopoverInlineModule) },
|
||||
{ path: 'providers', component: ProvidersComponent },
|
||||
{ path: 'router-link', component: RouterLinkComponent },
|
||||
{ path: 'router-link-page', component: RouterLinkPageComponent },
|
||||
|
||||
@@ -65,10 +65,6 @@ import { AlertComponent } from './alert/alert.component';
|
||||
ReactiveFormsModule,
|
||||
IonicModule.forRoot({ keyboardHeight: 12345 }),
|
||||
],
|
||||
entryComponents: [
|
||||
ModalExampleComponent,
|
||||
NavComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
],
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
<ion-item>
|
||||
<ion-label>DateTime</ion-label>
|
||||
<ion-datetime formControlName="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY"></ion-datetime>
|
||||
<ion-datetime formControlName="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY">
|
||||
</ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
@@ -65,6 +66,7 @@
|
||||
<p>
|
||||
Form Submit: <span id="submit">{{submitted}}</span>
|
||||
</p>
|
||||
<ion-button id="mark-all-touched-button" (click)="markAllAsTouched()">Mark all as touched</ion-button>
|
||||
<ion-button id="submit-button" type="submit" [disabled]="!profileForm.valid">Submit</ion-button>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -46,4 +46,8 @@ export class FormComponent {
|
||||
});
|
||||
}
|
||||
|
||||
markAllAsTouched() {
|
||||
this.profileForm.markAllAsTouched();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<h1>Value</h1>
|
||||
<h2>{{value}}</h2>
|
||||
<h3>{{valueFromParams}}</h3>
|
||||
<p>modal is defined: <span id="modalInstance">{{ !!modal }}</span></p>
|
||||
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
|
||||
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
|
||||
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
|
||||
|
||||
@@ -16,6 +16,8 @@ export class ModalExampleComponent implements OnInit, ViewWillLeave, ViewDidEnte
|
||||
willLeave = 0;
|
||||
didLeave = 0;
|
||||
|
||||
modal: HTMLElement;
|
||||
|
||||
constructor(
|
||||
private modalCtrl: ModalController,
|
||||
@Optional() public nav: IonNav,
|
||||
|
||||
2
angular/test/test-app/src/app/modal-inline/index.ts
Normal file
2
angular/test/test-app/src/app/modal-inline/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './modal-inline.component';
|
||||
export * from './modal-inline.module';
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { ModalInlineComponent } from ".";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: ModalInlineComponent
|
||||
}
|
||||
])
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class ModalInlineRoutingModule { }
|
||||
@@ -0,0 +1,13 @@
|
||||
<ion-button id="open-modal">Open Modal</ion-button>
|
||||
|
||||
<ion-modal [animated]="false" trigger="open-modal" [breakpoints]="[0.1, 0.5, 1]" [initialBreakpoint]="0.5">
|
||||
<ng-template>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item *ngFor="let item of items">
|
||||
<ion-label>{{ item }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
@@ -0,0 +1,21 @@
|
||||
import { AfterViewInit, Component } from "@angular/core";
|
||||
|
||||
/**
|
||||
* Validates that inline modals will correctly display
|
||||
* dynamic contents that are updated after the modal is
|
||||
* display.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-modal-inline',
|
||||
templateUrl: 'modal-inline.component.html'
|
||||
})
|
||||
export class ModalInlineComponent implements AfterViewInit {
|
||||
|
||||
items: string[] = [];
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setTimeout(() => {
|
||||
this.items = ['A', 'B', 'C', 'D'];
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { IonicModule } from "@ionic/angular";
|
||||
import { ModalInlineRoutingModule } from "./modal-inline-routing.module";
|
||||
import { ModalInlineComponent } from "./modal-inline.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, IonicModule, ModalInlineRoutingModule],
|
||||
declarations: [ModalInlineComponent],
|
||||
exports: [ModalInlineComponent]
|
||||
})
|
||||
export class ModalInlineModule { }
|
||||
@@ -1,8 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nested-outlet-page',
|
||||
templateUrl: './nested-outlet-page.component.html',
|
||||
})
|
||||
export class NestedOutletPageComponent {
|
||||
export class NestedOutletPageComponent implements OnDestroy, OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
NgZone.assertInAngularZone();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
NgZone.assertInAngularZone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<ion-content>
|
||||
<h1>Nested page 2</h1>
|
||||
<p>
|
||||
<ion-button routerLink="/nested-outlet/page">Go To FIRST</ion-button>
|
||||
</p>
|
||||
</ion-content>
|
||||
<h1>Nested page 2</h1>
|
||||
<p>
|
||||
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go To FIRST</ion-button>
|
||||
</p>
|
||||
</ion-content>
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nested-outlet-page2',
|
||||
templateUrl: './nested-outlet-page2.component.html',
|
||||
})
|
||||
export class NestedOutletPage2Component {
|
||||
export class NestedOutletPage2Component implements OnDestroy, OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
NgZone.assertInAngularZone();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
NgZone.assertInAngularZone();
|
||||
}
|
||||
}
|
||||
|
||||
1
angular/test/test-app/src/app/popover-inline/index.ts
Normal file
1
angular/test/test-app/src/app/popover-inline/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './popover-inline.module';
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { PopoverInlineComponent } from "./popover-inline.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: PopoverInlineComponent
|
||||
}
|
||||
])],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class PopoverInlineRoutingModule { }
|
||||
@@ -0,0 +1,11 @@
|
||||
<ion-popover [isOpen]="true">
|
||||
<ng-template>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item *ngFor="let item of items">
|
||||
<ion-label>{{ item }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</ion-popover>
|
||||
@@ -0,0 +1,22 @@
|
||||
import { AfterViewInit, Component } from "@angular/core";
|
||||
|
||||
/**
|
||||
* Validates that inline popovers will correctly display
|
||||
* dynamic contents that are updated after the modal is
|
||||
* display.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-popover-inline',
|
||||
templateUrl: 'popover-inline.component.html'
|
||||
})
|
||||
export class PopoverInlineComponent implements AfterViewInit {
|
||||
|
||||
items: string[] = [];
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setTimeout(() => {
|
||||
this.items = ['A', 'B', 'C', 'D'];
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { IonicModule } from "@ionic/angular";
|
||||
import { PopoverInlineRoutingModule } from "./popover-inline-routing.module";
|
||||
import { PopoverInlineComponent } from "./popover-inline.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, IonicModule, PopoverInlineRoutingModule],
|
||||
declarations: [PopoverInlineComponent],
|
||||
})
|
||||
export class PopoverInlineModule { }
|
||||
@@ -6,6 +6,7 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
|
||||
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
|
||||
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
|
||||
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
|
||||
@@ -14,11 +15,15 @@
|
||||
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
|
||||
|
||||
<p>
|
||||
<ion-button routerLink="/router-link-page" expand="block" color="dark" id="routerLink">ion-button[routerLink]</ion-button>
|
||||
<ion-button routerLink="/router-link-page" routerDirection="root" expand="block" color="dark" id="routerLink-root">ion-button[routerLink] (direction:root)</ion-button>
|
||||
<ion-button routerLink="/router-link-page" routerDirection="back" expand="block" color="dark" id="routerLink-back">ion-button[routerLink] (direction:back)</ion-button>
|
||||
<ion-button routerLink="/router-link-page" expand="block" color="dark" id="routerLink">ion-button[routerLink]
|
||||
</ion-button>
|
||||
<ion-button routerLink="/router-link-page" routerDirection="root" expand="block" color="dark" id="routerLink-root">
|
||||
ion-button[routerLink] (direction:root)</ion-button>
|
||||
<ion-button routerLink="/router-link-page" routerDirection="back" expand="block" color="dark" id="routerLink-back">
|
||||
ion-button[routerLink] (direction:back)</ion-button>
|
||||
</p>
|
||||
|
||||
<p><a [routerLink]="null">null router link</a></p>
|
||||
<p><a routerLink="/router-link-page" id="a">a[routerLink]</a></p>
|
||||
<p><a routerLink="/router-link-page" routerDirection="root" id="a-root">a[routerLink] (direction:root)</a></p>
|
||||
<p><a routerLink="/router-link-page" routerDirection="back" id="a-back">a[routerLink] (direction:back)</a></p>
|
||||
@@ -28,6 +33,7 @@
|
||||
<p><button (click)="navigateRoot()" id="button-root">navigateForward</button></p>
|
||||
<p><button (click)="navigateBack()" id="button-back">navigateBack</button></p>
|
||||
|
||||
<p><button id="queryParamsFragment" routerLink="/router-link-page2/MyPageID==" [queryParams]="{ token: 'A&=#Y' }" fragment="myDiv1">Query Params and Fragment</button></p>
|
||||
<p><button id="queryParamsFragment" routerLink="/router-link-page2/MyPageID==" [queryParams]="{ token: 'A&=#Y' }"
|
||||
fragment="myDiv1">Query Params and Fragment</button></p>
|
||||
|
||||
</ion-content>
|
||||
|
||||
5
angular/test/test-app/src/app/tabs/tabs.component.css
Normal file
5
angular/test/test-app/src/app/tabs/tabs.component.css
Normal file
@@ -0,0 +1,5 @@
|
||||
#test {
|
||||
position: absolute;
|
||||
bottom: 100px;
|
||||
left: 0;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<ion-tabs (ionTabsDidChange)="tabChanged($event)">
|
||||
<ion-tabs (ionTabsDidChange)="tabChanged($event)" (ionTabsWillChange)="tabsWillChange($event)">
|
||||
<ion-tab-bar>
|
||||
<ion-tab-button tab="account">
|
||||
<ion-label>Tab One</ion-label>
|
||||
@@ -18,5 +18,29 @@
|
||||
</ion-tab-bar>
|
||||
</ion-tabs>
|
||||
<ion-fab horizontal="end" vertical="top">
|
||||
<ion-fab-button id="tabs-state" style="width: 100px;">{{tabCounter}}.{{tabEvent}}</ion-fab-button>
|
||||
<ion-fab-button id="tabs-state" style="width: 100px;">{{tabsDidChangeCounter}}.{{tabsDidChangeEvent}}</ion-fab-button>
|
||||
</ion-fab>
|
||||
<div id="test">
|
||||
<ul>
|
||||
<li>
|
||||
ionTabsWillChange counter: <span id="ionTabsWillChangeCounter">{{ tabsWillChangeCounter }}</span>
|
||||
</li>
|
||||
<li>
|
||||
ionTabsWillChange event: <span id="ionTabsWillChangeEvent">{{ tabsWillChangeEvent }}</span>
|
||||
</li>
|
||||
<li>
|
||||
ionTabsWillChange selectedTab: <span id="ionTabsWillChangeSelectedTab">{{ tabsWillChangeSelectedTab }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
ionTabsDidChange counter: <span id="ionTabsDidChangeCounter">{{ tabsDidChangeCounter }}</span>
|
||||
</li>
|
||||
<li>
|
||||
ionTabsDidChange event: <span id="ionTabsDidChangeEvent">{{ tabsDidChangeEvent }}</span>
|
||||
</li>
|
||||
<li>
|
||||
ionTabsDidChange selectedTab: <span id="ionTabsDidChangeSelectedTab">{{ tabsDidChangeSelectedTab }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { IonTabBar } from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tabs',
|
||||
templateUrl: './tabs.component.html',
|
||||
styleUrls: ['./tabs.component.css']
|
||||
})
|
||||
export class TabsComponent {
|
||||
tabCounter = 0;
|
||||
tabEvent = '';
|
||||
tabsDidChangeCounter = 0;
|
||||
tabsDidChangeEvent = '';
|
||||
tabsDidChangeSelectedTab = '';
|
||||
|
||||
tabChanged(ev: {tab: string}) {
|
||||
this.tabCounter++;
|
||||
this.tabEvent = ev.tab;
|
||||
tabsWillChangeCounter = 0;
|
||||
tabsWillChangeEvent = '';
|
||||
tabsWillChangeSelectedTab = '';
|
||||
|
||||
@ViewChild(IonTabBar) tabBar: IonTabBar;
|
||||
|
||||
tabChanged(ev: { tab: string }) {
|
||||
console.log('ionTabsDidChange', this.tabBar.selectedTab);
|
||||
this.tabsDidChangeCounter++;
|
||||
this.tabsDidChangeEvent = ev.tab;
|
||||
this.tabsDidChangeSelectedTab = this.tabBar.selectedTab;
|
||||
}
|
||||
|
||||
tabsWillChange(ev: { tab: string }) {
|
||||
console.log('ionTabsWillChange', this.tabBar.selectedTab);
|
||||
this.tabsWillChangeCounter++;
|
||||
this.tabsWillChangeEvent = ev.tab;
|
||||
this.tabsWillChangeSelectedTab = this.tabBar.selectedTab;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,6 @@
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
*/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
|
||||
202
core/CHANGELOG.md
Normal file
202
core/CHANGELOG.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.0.13](https://github.com/ionic-team/ionic/compare/v6.0.12...v6.0.13) (2022-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** presentation time emits ionChange once ([#24968](https://github.com/ionic-team/ionic/issues/24968)) ([2909b08](https://github.com/ionic-team/ionic/commit/2909b080b7020299a4554c1459b4b190ff739085)), closes [#24967](https://github.com/ionic-team/ionic/issues/24967)
|
||||
* **popover:** dismissing nested popover via backdrop now works ([#24957](https://github.com/ionic-team/ionic/issues/24957)) ([9e84ef7](https://github.com/ionic-team/ionic/commit/9e84ef7f91d76ca5a1ecaffc7592287267d5368b)), closes [#24954](https://github.com/ionic-team/ionic/issues/24954)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.12](https://github.com/ionic-team/ionic/compare/v6.0.11...v6.0.12) (2022-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** reinit behavior on presentation change ([#24828](https://github.com/ionic-team/ionic/issues/24828)) ([d46e1e8](https://github.com/ionic-team/ionic/commit/d46e1e8506ca5095817b421e9edb37d41451e885))
|
||||
* **toast:** screen readers now announce toasts when presented ([#24937](https://github.com/ionic-team/ionic/issues/24937)) ([8a97f6b](https://github.com/ionic-team/ionic/commit/8a97f6b5c9ca1e77c1790abd1e924955b6b6ea27)), closes [#22333](https://github.com/ionic-team/ionic/issues/22333)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.11](https://github.com/ionic-team/ionic/compare/v6.0.10...v6.0.11) (2022-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** time picker now scrolls to correct value ([#24879](https://github.com/ionic-team/ionic/issues/24879)) ([331ce6d](https://github.com/ionic-team/ionic/commit/331ce6d6769900e2aec9e30d35b52cfd40cbb889)), closes [#24878](https://github.com/ionic-team/ionic/issues/24878)
|
||||
* **ios:** swipe to go back now works in rtl mode ([#24866](https://github.com/ionic-team/ionic/issues/24866)) ([2ac9105](https://github.com/ionic-team/ionic/commit/2ac9105796a0765fabc48592b5b44ac58c568579)), closes [#19488](https://github.com/ionic-team/ionic/issues/19488)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* improve treeshaking functionality ([#24895](https://github.com/ionic-team/ionic/issues/24895)) ([805907a](https://github.com/ionic-team/ionic/commit/805907af4e78179f1acc9cb02263b1ea10d4e3df)), closes [#24280](https://github.com/ionic-team/ionic/issues/24280)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.10](https://github.com/ionic-team/ionic/compare/v6.0.9...v6.0.10) (2022-03-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** confirm method now uses selected date ([#24827](https://github.com/ionic-team/ionic/issues/24827)) ([c35b898](https://github.com/ionic-team/ionic/commit/c35b898f1dc0fb706446b6971982df48fd72fe6d)), closes [#24823](https://github.com/ionic-team/ionic/issues/24823)
|
||||
* **datetime:** persist minutes column on hour change ([#24829](https://github.com/ionic-team/ionic/issues/24829)) ([aacb58a](https://github.com/ionic-team/ionic/commit/aacb58a3224e3cc51c731d0c9aa52f52c9276692)), closes [#24821](https://github.com/ionic-team/ionic/issues/24821)
|
||||
* **item-sliding:** close() will maintain disabled state ([#24847](https://github.com/ionic-team/ionic/issues/24847)) ([ea4a9bb](https://github.com/ionic-team/ionic/commit/ea4a9bb69465f8e97746b36638f0b3a26e45da18)), closes [#24747](https://github.com/ionic-team/ionic/issues/24747)
|
||||
* **modal:** re-enable swipe gestures when modal is dismissed ([#24846](https://github.com/ionic-team/ionic/issues/24846)) ([836c01c](https://github.com/ionic-team/ionic/commit/836c01c73e42caab0101ac4988f0a9b27cf96a5b)), closes [#24817](https://github.com/ionic-team/ionic/issues/24817)
|
||||
* **modal:** sheet modal now allows input focusing when backdrop disabled ([#24840](https://github.com/ionic-team/ionic/issues/24840)) ([e4ec572](https://github.com/ionic-team/ionic/commit/e4ec572043e22bd2626dbcfd204fc22a7335282c)), closes [#24581](https://github.com/ionic-team/ionic/issues/24581)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.9](https://github.com/ionic-team/ionic/compare/v6.0.8...v6.0.9) (2022-02-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** improve datetime sizing in modals ([#24762](https://github.com/ionic-team/ionic/issues/24762)) ([b0ac7de](https://github.com/ionic-team/ionic/commit/b0ac7de168c353ba4899cb74a2b38e25fd0cc0f1)), closes [#23992](https://github.com/ionic-team/ionic/issues/23992)
|
||||
* **datetime:** month and year column order is now locale aware ([#24802](https://github.com/ionic-team/ionic/issues/24802)) ([16647b2](https://github.com/ionic-team/ionic/commit/16647b2c7290389755a4093145788f281c84b7e2)), closes [#24548](https://github.com/ionic-team/ionic/issues/24548)
|
||||
* **datetime:** month picker no longer gives duplicate months on ios 14 and older ([#24792](https://github.com/ionic-team/ionic/issues/24792)) ([b6d7e1c](https://github.com/ionic-team/ionic/commit/b6d7e1c75740a61dcd02c21692e4d4632fb8df5c)), closes [#24663](https://github.com/ionic-team/ionic/issues/24663)
|
||||
* **img:** draggable attribute is now inherited to inner img element ([#24781](https://github.com/ionic-team/ionic/issues/24781)) ([19ac238](https://github.com/ionic-team/ionic/commit/19ac2389eb0843173f51a12de41ac808cd8f0569)), closes [#21325](https://github.com/ionic-team/ionic/issues/21325)
|
||||
* **modal:** backdropBreakpoint allows interactivity behind sheet ([#24798](https://github.com/ionic-team/ionic/issues/24798)) ([fca3f56](https://github.com/ionic-team/ionic/commit/fca3f56ca4568e63fd493debda088263caa86c64)), closes [#24797](https://github.com/ionic-team/ionic/issues/24797)
|
||||
* **popover:** default alignment to 'center' for ios mode ([#24815](https://github.com/ionic-team/ionic/issues/24815)) ([243f673](https://github.com/ionic-team/ionic/commit/243f67362f25699bdb373be6b72cb9c14dc95a32))
|
||||
* **react, vue:** scroll is no longer interrupted on ios ([#24791](https://github.com/ionic-team/ionic/issues/24791)) ([99c91ef](https://github.com/ionic-team/ionic/commit/99c91eff8764c9a1630adedab6a9765dd9754f7d)), closes [#24435](https://github.com/ionic-team/ionic/issues/24435)
|
||||
* **select:** interface components now show correctly ([#24810](https://github.com/ionic-team/ionic/issues/24810)) ([2fc2de5](https://github.com/ionic-team/ionic/commit/2fc2de51771f4a5c3f20c6071284096e5bf31ec8)), closes [#24807](https://github.com/ionic-team/ionic/issues/24807)
|
||||
* **toast:** toast is now correctly excluded from focus trapping ([#24816](https://github.com/ionic-team/ionic/issues/24816)) ([8246112](https://github.com/ionic-team/ionic/commit/8246112ca12f90edb98629ab82e27a792a1fafad)), closes [#24733](https://github.com/ionic-team/ionic/issues/24733)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.8](https://github.com/ionic-team/ionic/compare/v6.0.7...v6.0.8) (2022-02-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **back-button, breadcrumb, item:** flip chevron icons on RTL ([#24705](https://github.com/ionic-team/ionic/issues/24705)) ([a093544](https://github.com/ionic-team/ionic/commit/a093544fdfc438ed03024285b2a35c5f645ea011))
|
||||
* **datetime:** navigate to month within min range ([#24759](https://github.com/ionic-team/ionic/issues/24759)) ([7b3838c](https://github.com/ionic-team/ionic/commit/7b3838cc670de7845bb5937d204e86cdba93b6e6)), closes [#24757](https://github.com/ionic-team/ionic/issues/24757)
|
||||
* **input:** only set native input value if different ([#24758](https://github.com/ionic-team/ionic/issues/24758)) ([fd031aa](https://github.com/ionic-team/ionic/commit/fd031aa1c3f05b7bfa3e0a0ee2a4793e29e22df5)), closes [#24753](https://github.com/ionic-team/ionic/issues/24753)
|
||||
* **router-outlet:** getRouteId() returns the params set in setRouteId(). ([#24656](https://github.com/ionic-team/ionic/issues/24656)) ([be2205e](https://github.com/ionic-team/ionic/commit/be2205e5a2b2f8778bd1f7b4ea5cae0bf96f9ef3)), closes [#24652](https://github.com/ionic-team/ionic/issues/24652)
|
||||
* **router-outlet:** navigating to same route with different params now activates component ([#24760](https://github.com/ionic-team/ionic/issues/24760)) ([abc36ae](https://github.com/ionic-team/ionic/commit/abc36ae80b060a659f7557ad90efe98b78f5ead9)), closes [#24653](https://github.com/ionic-team/ionic/issues/24653)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.7](https://github.com/ionic-team/ionic/compare/v6.0.6...v6.0.7) (2022-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** inline modals now add .ion-page class correctly ([#24751](https://github.com/ionic-team/ionic/issues/24751)) ([ef46eaf](https://github.com/ionic-team/ionic/commit/ef46eafc9476a85ea3369e542f528d01d3cca0a8)), closes [#24750](https://github.com/ionic-team/ionic/issues/24750)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.6](https://github.com/ionic-team/ionic/compare/v6.0.5...v6.0.6) (2022-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **action-sheet:** background includes safe area margin ([#24700](https://github.com/ionic-team/ionic/issues/24700)) ([8c22646](https://github.com/ionic-team/ionic/commit/8c22646d66e2077fc88aaacf350330097733bb9b)), closes [#24699](https://github.com/ionic-team/ionic/issues/24699)
|
||||
* **angular, react, vue:** overlays no longer throw errors when used inside tests ([#24681](https://github.com/ionic-team/ionic/issues/24681)) ([897ae4a](https://github.com/ionic-team/ionic/commit/897ae4a4546ac0dd811125d5513ef23d133a1589)), closes [#24549](https://github.com/ionic-team/ionic/issues/24549) [#24590](https://github.com/ionic-team/ionic/issues/24590)
|
||||
* **datetime:** disable intersection observer during month update ([#24713](https://github.com/ionic-team/ionic/issues/24713)) ([aab4d30](https://github.com/ionic-team/ionic/commit/aab4d306f80851bfd8a02a6361e26d60faeaadb4)), closes [#24712](https://github.com/ionic-team/ionic/issues/24712)
|
||||
* **datetime:** minutes only filtered when max hour matches current hour ([#24710](https://github.com/ionic-team/ionic/issues/24710)) ([231d6df](https://github.com/ionic-team/ionic/commit/231d6df622c1f5dd9ecdff6fed8f61a4bff332df)), closes [#24702](https://github.com/ionic-team/ionic/issues/24702)
|
||||
* **input:** cursor position does not jump to end ([#24736](https://github.com/ionic-team/ionic/issues/24736)) ([4ff9524](https://github.com/ionic-team/ionic/commit/4ff9524e1057aa487069b5982c5f1ecdf51d982b)), closes [#24727](https://github.com/ionic-team/ionic/issues/24727)
|
||||
* **input:** IME composition mode ([#24735](https://github.com/ionic-team/ionic/issues/24735)) ([c6381ce](https://github.com/ionic-team/ionic/commit/c6381ce4f90707774d1c8662bba874f7b306bd1c)), closes [#24669](https://github.com/ionic-team/ionic/issues/24669)
|
||||
* **modal, popover:** quickly opening modal/popover no longer presents duplicates ([#24697](https://github.com/ionic-team/ionic/issues/24697)) ([928c5fb](https://github.com/ionic-team/ionic/commit/928c5fbfcbf3ef1b2c3074464fc20dcf1fe143ae))
|
||||
* **modal:** inline modals inherit ion-page styling ([#24723](https://github.com/ionic-team/ionic/issues/24723)) ([596aad4](https://github.com/ionic-team/ionic/commit/596aad435b5102307da89dd626ca4682b78db452)), closes [#24706](https://github.com/ionic-team/ionic/issues/24706)
|
||||
* **popover:** use alignment with popover options ([#24711](https://github.com/ionic-team/ionic/issues/24711)) ([f5c5c3c](https://github.com/ionic-team/ionic/commit/f5c5c3cffa4f34046b0e9471a9f193db3772180e)), closes [#24709](https://github.com/ionic-team/ionic/issues/24709)
|
||||
* **router:** router push with relative path ([#24719](https://github.com/ionic-team/ionic/issues/24719)) ([d40c0c3](https://github.com/ionic-team/ionic/commit/d40c0c3a0993eaefbe5107e98958c6b0699a62c2)), closes [#24718](https://github.com/ionic-team/ionic/issues/24718)
|
||||
* **select:** value is selected when given array ([#24687](https://github.com/ionic-team/ionic/issues/24687)) ([6ee7d15](https://github.com/ionic-team/ionic/commit/6ee7d159ecfff3382fadb524c5c430172d40c267)), closes [#24742](https://github.com/ionic-team/ionic/issues/24742)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.5](https://github.com/ionic-team/ionic/compare/v6.0.4...v6.0.5) (2022-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** prevent navigating to disabled months ([#24421](https://github.com/ionic-team/ionic/issues/24421)) ([b40fc46](https://github.com/ionic-team/ionic/commit/b40fc4632efcdc01f1062d9bcec826afff5cf4ea)), closes [#24208](https://github.com/ionic-team/ionic/issues/24208) [#24482](https://github.com/ionic-team/ionic/issues/24482)
|
||||
* **input:** min/max compatibility with react-hook-form ([#24657](https://github.com/ionic-team/ionic/issues/24657)) ([1f91883](https://github.com/ionic-team/ionic/commit/1f918835f437a59f7a70fc59d9305647aa9c298d)), closes [#24489](https://github.com/ionic-team/ionic/issues/24489)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **various:** don't use lazy-loaded icon names in components ([#24671](https://github.com/ionic-team/ionic/issues/24671)) ([484de50](https://github.com/ionic-team/ionic/commit/484de5074de212dffb4bf4f73bade7acec103fe8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.4](https://github.com/ionic-team/ionic/compare/v6.0.3...v6.0.4) (2022-01-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **accordion:** items inside of the content now correct display borders ([#24618](https://github.com/ionic-team/ionic/issues/24618)) ([d3311df](https://github.com/ionic-team/ionic/commit/d3311df96765948d0a395e4ba99fb9117b44adcb)), closes [#24613](https://github.com/ionic-team/ionic/issues/24613)
|
||||
* **col:** col no longer errors when running in non-browser environment ([#24603](https://github.com/ionic-team/ionic/issues/24603)) ([af0135c](https://github.com/ionic-team/ionic/commit/af0135ce7dbe737b2df46094fd3dc8a41bdb60ae)), closes [#24446](https://github.com/ionic-team/ionic/issues/24446)
|
||||
* **datetime:** datetime locale with h23 will respect max time range ([#24610](https://github.com/ionic-team/ionic/issues/24610)) ([5925e76](https://github.com/ionic-team/ionic/commit/5925e7608ef04f8877e4dd8a80b2c8bdc1cfd4bd)), closes [#24588](https://github.com/ionic-team/ionic/issues/24588)
|
||||
* **datetime:** timepicker popover will position relative to click target ([#24616](https://github.com/ionic-team/ionic/issues/24616)) ([378c632](https://github.com/ionic-team/ionic/commit/378c63264366964e6ea11e1a2ff8791a40f182d4)), closes [#24531](https://github.com/ionic-team/ionic/issues/24531) [#24415](https://github.com/ionic-team/ionic/issues/24415)
|
||||
* **input:** ion-input accepts any string value ([#24606](https://github.com/ionic-team/ionic/issues/24606)) ([43c5977](https://github.com/ionic-team/ionic/commit/43c5977d48cb12331c1d3107eb73f29b92c5e049)), closes [#19884](https://github.com/ionic-team/ionic/issues/19884)
|
||||
* **item:** label text aligns with input text ([#24620](https://github.com/ionic-team/ionic/issues/24620)) ([94d033c](https://github.com/ionic-team/ionic/commit/94d033c4216ae9978aed6346c1c0ea2aec4d375b)), closes [#24404](https://github.com/ionic-team/ionic/issues/24404)
|
||||
* **item:** match material design character counter ([#24335](https://github.com/ionic-team/ionic/issues/24335)) ([54db1a1](https://github.com/ionic-team/ionic/commit/54db1a1e7c71c78e843370848fc768582768333e)), closes [#24334](https://github.com/ionic-team/ionic/issues/24334)
|
||||
* **menu:** focus trapping with menu and overlays no longer results in errors ([#24611](https://github.com/ionic-team/ionic/issues/24611)) ([632dafc](https://github.com/ionic-team/ionic/commit/632dafcd57de5086deebdc7d586b01710aa1a3ce)), closes [#24361](https://github.com/ionic-team/ionic/issues/24361) [#24481](https://github.com/ionic-team/ionic/issues/24481)
|
||||
* **modal:** native keyboard will dismiss when bottom sheet is dragged ([#24642](https://github.com/ionic-team/ionic/issues/24642)) ([525f01f](https://github.com/ionic-team/ionic/commit/525f01f086ebf95ab62af0162b876a25f50a3d4b)), closes [#23878](https://github.com/ionic-team/ionic/issues/23878)
|
||||
* **range:** setting dir on ion-range to enable rtl mode now supported ([#24597](https://github.com/ionic-team/ionic/issues/24597)) ([5dba4e5](https://github.com/ionic-team/ionic/commit/5dba4e5ce0a07f69a08f2b427e8010b311910f88)), closes [#20201](https://github.com/ionic-team/ionic/issues/20201)
|
||||
* **searchbar:** setting dir on ion-searchbar to enable rtl mode now supported ([#24602](https://github.com/ionic-team/ionic/issues/24602)) ([35e5235](https://github.com/ionic-team/ionic/commit/35e523564561c0f3323efa761c4654016b97ef69))
|
||||
* **segment:** setting dir on ion-segment to enable rtl mode now supported ([#24601](https://github.com/ionic-team/ionic/issues/24601)) ([2940e73](https://github.com/ionic-team/ionic/commit/2940e73a4504247eecb0de6c433104f529530850)), closes [#23978](https://github.com/ionic-team/ionic/issues/23978)
|
||||
* **spinner:** ensure transform doesn't overwrite circular animation ([#24643](https://github.com/ionic-team/ionic/issues/24643)) ([88ce010](https://github.com/ionic-team/ionic/commit/88ce010418eaca38786b51720c696860b417ab6d))
|
||||
* **toast:** allow input focus while toast is visible ([#24572](https://github.com/ionic-team/ionic/issues/24572)) ([29f1140](https://github.com/ionic-team/ionic/commit/29f1140384ae7e1402b09c3760e168cf79833619)), closes [#24571](https://github.com/ionic-team/ionic/issues/24571)
|
||||
* **toggle:** setting dir on ion-toggle to enable rtl mode now supported ([#24600](https://github.com/ionic-team/ionic/issues/24600)) ([353dbc0](https://github.com/ionic-team/ionic/commit/353dbc0537ef3b46b9ba13a365ebc5da269de4c7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** showing calendar grid no longer causes month to switch on ios 15 ([#24554](https://github.com/ionic-team/ionic/issues/24554)) ([3d20959](https://github.com/ionic-team/ionic/commit/3d2095922147ea3763e977412977edd9586fec5d)), closes [#24405](https://github.com/ionic-team/ionic/issues/24405)
|
||||
* **item:** error slot visible in Safari ([#24579](https://github.com/ionic-team/ionic/issues/24579)) ([af01a8b](https://github.com/ionic-team/ionic/commit/af01a8b3073dce784cc042923d712b9492638d32)), closes [#24575](https://github.com/ionic-team/ionic/issues/24575)
|
||||
* **menu:** remove main attribute that was supposed to removed in v5 ([#24565](https://github.com/ionic-team/ionic/issues/24565)) ([7704ac3](https://github.com/ionic-team/ionic/commit/7704ac3a3710396248590daecb945b76825a0539)), closes [#24563](https://github.com/ionic-team/ionic/issues/24563)
|
||||
* **modal:** life cycle events for controller modals ([#24508](https://github.com/ionic-team/ionic/issues/24508)) ([9a15753](https://github.com/ionic-team/ionic/commit/9a15753fd95e32155abdeb490ec57cb72385ad1a)), closes [#24460](https://github.com/ionic-team/ionic/issues/24460)
|
||||
* **overlays:** getTop now returns the top-most presented overlay ([#24547](https://github.com/ionic-team/ionic/issues/24547)) ([f5b4382](https://github.com/ionic-team/ionic/commit/f5b4382fd5728365e4badf39bc1dd0c149b45c2c)), closes [#19111](https://github.com/ionic-team/ionic/issues/19111)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **breadcrumb:** support routerLink on breadcrumb ([#24509](https://github.com/ionic-team/ionic/issues/24509)) ([5bb1414](https://github.com/ionic-team/ionic/commit/5bb1414f7fa04ea07954cb3f68883ee2f162586a)), closes [#24493](https://github.com/ionic-team/ionic/issues/24493)
|
||||
* **css:** inline css source in source maps ([#24514](https://github.com/ionic-team/ionic/issues/24514)) ([987d46c](https://github.com/ionic-team/ionic/commit/987d46cfa6e48a932330f04f2e8eb7054b11baf8)), closes [#24441](https://github.com/ionic-team/ionic/issues/24441)
|
||||
* **datetime:** add top padding to MD calendar month grid ([#24522](https://github.com/ionic-team/ionic/issues/24522)) ([bd82b5d](https://github.com/ionic-team/ionic/commit/bd82b5dc1d06ba22a5410858802d57735fdcf450)), closes [#24408](https://github.com/ionic-team/ionic/issues/24408)
|
||||
* **datetime:** RTL will no longer infinitely scroll ([#24475](https://github.com/ionic-team/ionic/issues/24475)) ([8f00008](https://github.com/ionic-team/ionic/commit/8f000089c2986f292147c7f501f23c8c7d1df457)), closes [#24472](https://github.com/ionic-team/ionic/issues/24472)
|
||||
* **datetime:** time picker format with hourCycle h23 ([#24476](https://github.com/ionic-team/ionic/issues/24476)) ([a3724e6](https://github.com/ionic-team/ionic/commit/a3724e6a5662c5bc1b724d80540530472827506e)), closes [#24474](https://github.com/ionic-team/ionic/issues/24474)
|
||||
* **datetime:** update active day styling when day is selected ([#24454](https://github.com/ionic-team/ionic/issues/24454)) ([4304391](https://github.com/ionic-team/ionic/commit/430439191dba824c11290d7f8622fea10ced6c40)), closes [#24414](https://github.com/ionic-team/ionic/issues/24414) [#24451](https://github.com/ionic-team/ionic/issues/24451)
|
||||
* **datetime:** wheel picker shows correct column order in rtl ([#24546](https://github.com/ionic-team/ionic/issues/24546)) ([c90ce31](https://github.com/ionic-team/ionic/commit/c90ce311a86ccb7c06b1cde91a4659f6682df04d)), closes [#24378](https://github.com/ionic-team/ionic/issues/24378)
|
||||
* **overlays:** define custom element children ([#24439](https://github.com/ionic-team/ionic/issues/24439)) ([4715b83](https://github.com/ionic-team/ionic/commit/4715b83abb30ec5930710d16e5bfe8fc88a940ce)), closes [#24393](https://github.com/ionic-team/ionic/issues/24393)
|
||||
* **popover:** allow arrow configuration with controller approach ([#24512](https://github.com/ionic-team/ionic/issues/24512)) ([b39003a](https://github.com/ionic-team/ionic/commit/b39003a4c67cd7e01d09be012c9e12d99ca1730a)), closes [#24487](https://github.com/ionic-team/ionic/issues/24487)
|
||||
* **radio:** fix radio not showing checked state when not in a group ([#24423](https://github.com/ionic-team/ionic/issues/24423)) ([94a781c](https://github.com/ionic-team/ionic/commit/94a781cb6a3d92c5e6cab1a7603bfe25826a753c))
|
||||
* **react,vue:** backdrop for inline modal/popover overlay ([#24453](https://github.com/ionic-team/ionic/issues/24453)) ([77f8412](https://github.com/ionic-team/ionic/commit/77f8412b746222793cd9d17f12f50d512ab5e886)), closes [#24449](https://github.com/ionic-team/ionic/issues/24449)
|
||||
* **react:** building app for production now works correctly with vite ([#24515](https://github.com/ionic-team/ionic/issues/24515)) ([32fad3d](https://github.com/ionic-team/ionic/commit/32fad3d02cb6b012a772de03eafe3e3a6b1300e0)), closes [#24229](https://github.com/ionic-team/ionic/issues/24229)
|
||||
* **refresher:** import icons to avoid errors in react and vue ([#24525](https://github.com/ionic-team/ionic/issues/24525)) ([388622f](https://github.com/ionic-team/ionic/commit/388622f9734b7b832bca3ede99820a7124faa618)), closes [#24480](https://github.com/ionic-team/ionic/issues/24480)
|
||||
@@ -44,19 +44,55 @@ The `@ionic/core` package can be used in simple HTML, or by vanilla JavaScript w
|
||||
|
||||
In addition to the default, self lazy-loading components built by Stencil, this package also comes with each component exported as a stand-alone custom element within `@ionic/core/components`. Each component extends `HTMLElement`, and does not lazy-load itself. Instead, this package is useful for projects already using a bundler such as Webpack or Rollup. While all components are available to be imported, the custom elements build also ensures bundlers only import what's used, and tree-shakes any unused components.
|
||||
|
||||
Below is an example of importing `ion-toggle`, and initializing Ionic so it's able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
|
||||
Below is an example of importing `ion-badge`, and initializing Ionic so it is able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
|
||||
|
||||
```typescript
|
||||
import { IonBadge } from "@ionic/core/components/ion-badge";
|
||||
import { defineCustomElement } from "@ionic/core/components/ion-badge.js";
|
||||
import { initialize } from "@ionic/core/components";
|
||||
|
||||
// Initializes the Ionic config and `mode` behavior
|
||||
initialize();
|
||||
|
||||
customElements.define("ion-badge", IonBadge);
|
||||
// Defines the `ion-badge` web component
|
||||
defineCustomElement();
|
||||
```
|
||||
|
||||
Notice how `IonBadge` is imported from `@ionic/core/components/ion-badge` rather than just `@ionic/core/components`. Additionally, the `initialize` function is imported from `@ionic/core/components` rather than `@ionic/core`. All of this helps to ensure bundlers do not pull in more code than is needed.
|
||||
Notice how we import from `@ionic/core/components` as opposed to `@ionic/core`. This helps bundlers pull in only the code that is needed.
|
||||
|
||||
The `defineCustomElement` function will automatically define the component as well as any child components that may be required.
|
||||
|
||||
For example, if you wanted to use `ion-modal`, you would do the following:
|
||||
|
||||
```typescript
|
||||
import { defineCustomElement } from "@ionic/core/components/ion-modal.js";
|
||||
import { initialize } from "@ionic/core/components";
|
||||
|
||||
// Initializes the Ionic config and `mode` behavior
|
||||
initialize();
|
||||
|
||||
// Defines the `ion-modal` and child `ion-backdrop` web components.
|
||||
defineCustomElement();
|
||||
```
|
||||
|
||||
The `defineCustomElement` function will define `ion-modal`, but it will also define `ion-backdrop`, which is a component that `ion-modal` uses internally.
|
||||
|
||||
### Using Overlay Controllers
|
||||
|
||||
When using an overlay controller, developers will need to define the overlay component before it can be used. Below is an example of using `modalController`:
|
||||
|
||||
```typescript
|
||||
import { defineCustomElement } from '@ionic/core/components/ion-modal.js';
|
||||
import { initialize, modalController } from '@ionic/core/components';
|
||||
|
||||
initialize();
|
||||
defineCustomElement();
|
||||
|
||||
const showModal = async () => {
|
||||
const modal = await modalController.create({ ... });
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## How to contribute
|
||||
|
||||
|
||||
18
core/api.txt
18
core/api.txt
@@ -3,7 +3,7 @@ ion-accordion,shadow
|
||||
ion-accordion,prop,disabled,boolean,false,false,false
|
||||
ion-accordion,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-accordion,prop,readonly,boolean,false,false,false
|
||||
ion-accordion,prop,toggleIcon,string,'chevron-down',false,false
|
||||
ion-accordion,prop,toggleIcon,string,chevronDown,false,false
|
||||
ion-accordion,prop,toggleIconSlot,"end" | "start",'end',false,false
|
||||
ion-accordion,prop,value,string,`ion-accordion-${accordionIds++}`,false,false
|
||||
ion-accordion,part,content
|
||||
@@ -416,7 +416,7 @@ 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,closeIcon,string,close,false,false
|
||||
ion-fab-button,prop,color,string | undefined,undefined,false,true
|
||||
ion-fab-button,prop,disabled,boolean,false,false,false
|
||||
ion-fab-button,prop,download,string | undefined,undefined,false,false
|
||||
@@ -519,9 +519,9 @@ 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
|
||||
ion-input,prop,inputmode,"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url" | undefined,undefined,false,false
|
||||
ion-input,prop,max,string | undefined,undefined,false,false
|
||||
ion-input,prop,max,number | string | undefined,undefined,false,false
|
||||
ion-input,prop,maxlength,number | undefined,undefined,false,false
|
||||
ion-input,prop,min,string | undefined,undefined,false,false
|
||||
ion-input,prop,min,number | string | undefined,undefined,false,false
|
||||
ion-input,prop,minlength,number | undefined,undefined,false,false
|
||||
ion-input,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-input,prop,multiple,boolean | undefined,undefined,false,false
|
||||
@@ -557,7 +557,7 @@ ion-item,prop,button,boolean,false,false,false
|
||||
ion-item,prop,color,string | undefined,undefined,false,true
|
||||
ion-item,prop,counter,boolean,false,false,false
|
||||
ion-item,prop,detail,boolean | undefined,undefined,false,false
|
||||
ion-item,prop,detailIcon,string,'chevron-forward',false,false
|
||||
ion-item,prop,detailIcon,string,chevronForward,false,false
|
||||
ion-item,prop,disabled,boolean,false,false,false
|
||||
ion-item,prop,download,string | undefined,undefined,false,false
|
||||
ion-item,prop,fill,"outline" | "solid" | undefined,undefined,false,false
|
||||
@@ -877,7 +877,7 @@ ion-picker,css-prop,--min-width
|
||||
ion-picker,css-prop,--width
|
||||
|
||||
ion-popover,shadow
|
||||
ion-popover,prop,alignment,"center" | "end" | "start",'start',false,false
|
||||
ion-popover,prop,alignment,"center" | "end" | "start" | undefined,undefined,false,false
|
||||
ion-popover,prop,animated,boolean,true,false,false
|
||||
ion-popover,prop,arrow,boolean,true,false,false
|
||||
ion-popover,prop,backdropDismiss,boolean,true,false,false
|
||||
@@ -901,7 +901,7 @@ ion-popover,prop,triggerAction,"click" | "context-menu" | "hover",'click',false,
|
||||
ion-popover,method,dismiss,dismiss(data?: any, role?: string | undefined, dismissParentPopover?: boolean) => Promise<boolean>
|
||||
ion-popover,method,onDidDismiss,onDidDismiss<T = any>() => Promise<OverlayEventDetail<T>>
|
||||
ion-popover,method,onWillDismiss,onWillDismiss<T = any>() => Promise<OverlayEventDetail<T>>
|
||||
ion-popover,method,present,present(event?: MouseEvent | TouchEvent | PointerEvent | undefined) => Promise<void>
|
||||
ion-popover,method,present,present(event?: MouseEvent | TouchEvent | PointerEvent | CustomEvent<any> | undefined) => Promise<void>
|
||||
ion-popover,event,didDismiss,OverlayEventDetail<any>,true
|
||||
ion-popover,event,didPresent,void,true
|
||||
ion-popover,event,ionPopoverDidDismiss,OverlayEventDetail<any>,true
|
||||
@@ -1045,7 +1045,7 @@ ion-router,none
|
||||
ion-router,prop,root,string,'/',false,false
|
||||
ion-router,prop,useHash,boolean,true,false,false
|
||||
ion-router,method,back,back() => Promise<void>
|
||||
ion-router,method,push,push(url: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>
|
||||
ion-router,method,push,push(path: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>
|
||||
ion-router,event,ionRouteDidChange,RouterEventDetail,true
|
||||
ion-router,event,ionRouteWillChange,RouterEventDetail,true
|
||||
|
||||
@@ -1070,7 +1070,7 @@ ion-searchbar,scoped
|
||||
ion-searchbar,prop,animated,boolean,false,false,false
|
||||
ion-searchbar,prop,autocomplete,"off" | "on" | "name" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "email" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "url" | "photo",'off',false,false
|
||||
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,cancelButtonIcon,string,config.get('backButtonIcon', arrowBackSharp) 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
|
||||
|
||||
45
core/package-lock.json
generated
45
core/package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "6.0.0",
|
||||
"version": "6.0.13",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/core",
|
||||
"version": "6.0.0",
|
||||
"version": "6.0.12",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@stencil/core": "~2.11.0-0",
|
||||
"@stencil/core": "^2.14.2",
|
||||
"ionicons": "^6.0.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -19,7 +19,7 @@
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/angular-output-target": "^0.4.0",
|
||||
"@stencil/react-output-target": "^0.2.0",
|
||||
"@stencil/react-output-target": "^0.2.1",
|
||||
"@stencil/sass": "1.3.2",
|
||||
"@stencil/vue-output-target": "^0.6.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
@@ -1366,10 +1366,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@stencil/core": {
|
||||
"version": "2.11.0-0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.11.0-0.tgz",
|
||||
"integrity": "sha512-ZJlambyk3s5Xzqd5yfzwvD7adEIHpfv89yOd210XsMhonXOLHK2k7XpFluGYUrv69HDgosu2TjFxQxhyVSS0Vg==",
|
||||
"license": "MIT",
|
||||
"version": "2.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.14.2.tgz",
|
||||
"integrity": "sha512-NMC5Xi8sPFJxaO4rz6CbMHuD6PteE/RJWtjrbkusmpjKRtMXkfZJPIgOrleZ4xO+vXcNyL535Ru7vUADqEsTiQ==",
|
||||
"bin": {
|
||||
"stencil": "bin/stencil"
|
||||
},
|
||||
@@ -1379,9 +1378,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@stencil/react-output-target": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/react-output-target/-/react-output-target-0.2.0.tgz",
|
||||
"integrity": "sha512-xVqDn7XE7os8/Ant30bwHL+PF0+0+GGtHonF9/Acvy2Z58HHk10tGFXwJcMMHxF6zCMWrlHIJ76jqEe2gxZobg==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/react-output-target/-/react-output-target-0.2.1.tgz",
|
||||
"integrity": "sha512-A6ut+ua3s9UPVXHmAco8g6phvRsr8Db1wM6Mws2bdSAawzc1n49afS+FbTMUMcjqKAGShMp5lsM0/QA0jx5SdQ==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@stencil/core": "^2.9.0"
|
||||
@@ -15053,20 +15052,18 @@
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/angular-output-target/-/angular-output-target-0.4.0.tgz",
|
||||
"integrity": "sha512-zauaj0za46IWoPgv2IanDp3tiljwDRDNk4jB7WII6KeL66dkk7ffeqYZ0CgySTU5W2FjnKR6JEKbAnwUxjGIsA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@stencil/core": {
|
||||
"version": "2.11.0-0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.11.0-0.tgz",
|
||||
"integrity": "sha512-ZJlambyk3s5Xzqd5yfzwvD7adEIHpfv89yOd210XsMhonXOLHK2k7XpFluGYUrv69HDgosu2TjFxQxhyVSS0Vg=="
|
||||
"version": "2.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.14.2.tgz",
|
||||
"integrity": "sha512-NMC5Xi8sPFJxaO4rz6CbMHuD6PteE/RJWtjrbkusmpjKRtMXkfZJPIgOrleZ4xO+vXcNyL535Ru7vUADqEsTiQ=="
|
||||
},
|
||||
"@stencil/react-output-target": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/react-output-target/-/react-output-target-0.2.0.tgz",
|
||||
"integrity": "sha512-xVqDn7XE7os8/Ant30bwHL+PF0+0+GGtHonF9/Acvy2Z58HHk10tGFXwJcMMHxF6zCMWrlHIJ76jqEe2gxZobg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/react-output-target/-/react-output-target-0.2.1.tgz",
|
||||
"integrity": "sha512-A6ut+ua3s9UPVXHmAco8g6phvRsr8Db1wM6Mws2bdSAawzc1n49afS+FbTMUMcjqKAGShMp5lsM0/QA0jx5SdQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@stencil/sass": {
|
||||
"version": "1.3.2",
|
||||
@@ -15078,8 +15075,7 @@
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.6.0.tgz",
|
||||
"integrity": "sha512-UxI0RTgz3AfhOePbgK3CNcX7uLVxDrdkpSCJoIyr8EjSrW5HkfraZ/ukIzuPDJDDxtYRRevY7It+qOhfn0B6aA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"@stylelint/postcss-css-in-js": {
|
||||
"version": "0.37.2",
|
||||
@@ -25192,8 +25188,7 @@
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"dev": true
|
||||
},
|
||||
"xdg-basedir": {
|
||||
"version": "4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "6.0.0",
|
||||
"version": "6.0.13",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -31,7 +31,7 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@stencil/core": "~2.11.0-0",
|
||||
"@stencil/core": "^2.14.2",
|
||||
"ionicons": "^6.0.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -41,7 +41,7 @@
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/angular-output-target": "^0.4.0",
|
||||
"@stencil/react-output-target": "^0.2.0",
|
||||
"@stencil/react-output-target": "^0.2.1",
|
||||
"@stencil/sass": "1.3.2",
|
||||
"@stencil/vue-output-target": "^0.6.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
@@ -77,7 +77,7 @@
|
||||
"clean": "node scripts/clean.js",
|
||||
"cdnloader": "node scripts/copy-cdn-loader.js",
|
||||
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
|
||||
"css.sass": "sass src/css:./css",
|
||||
"css.sass": "sass --embed-sources 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\"",
|
||||
|
||||
73
core/src/components.d.ts
vendored
73
core/src/components.d.ts
vendored
@@ -1051,7 +1051,7 @@ export namespace Components {
|
||||
/**
|
||||
* The maximum value, which must not be less than its minimum (min attribute) value.
|
||||
*/
|
||||
"max"?: string;
|
||||
"max"?: string | number;
|
||||
/**
|
||||
* If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter.
|
||||
*/
|
||||
@@ -1059,7 +1059,7 @@ export namespace Components {
|
||||
/**
|
||||
* The minimum value, which must not be greater than its maximum (max attribute) value.
|
||||
*/
|
||||
"min"?: string;
|
||||
"min"?: string | number;
|
||||
/**
|
||||
* If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter.
|
||||
*/
|
||||
@@ -1081,7 +1081,7 @@ export namespace Components {
|
||||
*/
|
||||
"pattern"?: string;
|
||||
/**
|
||||
* Instructional text that shows before the input has a value.
|
||||
* Instructional text that shows before the input has a value. This property applies only when the `type` property is set to `"email"`, `"number"`, `"password"`, `"search"`, `"tel"`, `"text"`, or `"url"`, otherwise it is ignored.
|
||||
*/
|
||||
"placeholder"?: string;
|
||||
/**
|
||||
@@ -1254,11 +1254,11 @@ export namespace Components {
|
||||
}
|
||||
interface IonItemSliding {
|
||||
/**
|
||||
* Close the sliding item. Items can also be closed from the [List](../list).
|
||||
* Close the sliding item. Items can also be closed from the [List](./list).
|
||||
*/
|
||||
"close": () => Promise<void>;
|
||||
/**
|
||||
* Close all of the sliding items in the list. Items can also be closed from the [List](../list).
|
||||
* Close all of the sliding items in the list. Items can also be closed from the [List](./list).
|
||||
*/
|
||||
"closeOpened": () => Promise<boolean>;
|
||||
/**
|
||||
@@ -1616,6 +1616,9 @@ export namespace Components {
|
||||
* @param view The view to get.
|
||||
*/
|
||||
"getPrevious": (view?: ViewController | undefined) => Promise<ViewController | undefined>;
|
||||
/**
|
||||
* Called by <ion-router> to retrieve the current component.
|
||||
*/
|
||||
"getRouteId": () => Promise<RouteID | undefined>;
|
||||
/**
|
||||
* Inserts a component into the navigation stack at the specified index. This is useful to add a component at any point in the navigation stack.
|
||||
@@ -1692,6 +1695,14 @@ export namespace Components {
|
||||
* @param done The transition complete function.
|
||||
*/
|
||||
"setRoot": <T extends NavComponent>(component: T, componentProps?: ComponentProps<T> | null | undefined, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>;
|
||||
/**
|
||||
* Called by the router to update the view.
|
||||
* @param id The component tag.
|
||||
* @param params The component params.
|
||||
* @param direction A direction hint.
|
||||
* @param animation an AnimationBuilder.
|
||||
* @return the status.
|
||||
*/
|
||||
"setRouteId": (id: string, params: ComponentProps | undefined, direction: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<RouteWrite>;
|
||||
/**
|
||||
* If the nav component should allow for swipe-to-go-back.
|
||||
@@ -1823,6 +1834,7 @@ export namespace Components {
|
||||
* If `true`, tapping the picker will reveal a number input keyboard that lets the user type in values for each picker column. This is useful when working with time pickers.
|
||||
*/
|
||||
"numericInput": boolean;
|
||||
"scrollActiveItemIntoView": () => Promise<void>;
|
||||
/**
|
||||
* The selected option in the picker.
|
||||
*/
|
||||
@@ -1836,9 +1848,9 @@ export namespace Components {
|
||||
}
|
||||
interface IonPopover {
|
||||
/**
|
||||
* Describes how to align the popover content with the `reference` point.
|
||||
* Describes how to align the popover content with the `reference` point. Defaults to `'center'` for `ios` mode, and `'start'` for `md` mode.
|
||||
*/
|
||||
"alignment": PositionAlign;
|
||||
"alignment"?: PositionAlign;
|
||||
/**
|
||||
* If `true`, the popover will animate.
|
||||
*/
|
||||
@@ -1918,7 +1930,7 @@ export namespace Components {
|
||||
/**
|
||||
* Present the popover overlay after it has been created. Developers can pass a mouse, touch, or pointer event to position the popover relative to where that event was dispatched.
|
||||
*/
|
||||
"present": (event?: MouseEvent | TouchEvent | PointerEvent | undefined) => Promise<void>;
|
||||
"present": (event?: MouseEvent | TouchEvent | PointerEvent | CustomEvent<any> | undefined) => Promise<void>;
|
||||
/**
|
||||
* When opening a popover from a trigger, we should not be modifying the `event` prop from inside the component. Additionally, when pressing the "Right" arrow key, we need to shift focus to the first descendant in the newly presented popover.
|
||||
*/
|
||||
@@ -2196,11 +2208,11 @@ export namespace Components {
|
||||
"navChanged": (direction: RouterDirection) => Promise<boolean>;
|
||||
"printDebug": () => Promise<void>;
|
||||
/**
|
||||
* Navigate to the specified URL.
|
||||
* @param url The url to navigate to.
|
||||
* Navigate to the specified path.
|
||||
* @param path The path to navigate to.
|
||||
* @param direction The direction of the animation. Defaults to `"forward"`.
|
||||
*/
|
||||
"push": (url: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>;
|
||||
"push": (path: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>;
|
||||
/**
|
||||
* The root path to use when matching URLs. By default, this is set to "/", but you can specify an alternate prefix for all URL paths.
|
||||
*/
|
||||
@@ -2242,7 +2254,7 @@ export namespace Components {
|
||||
*/
|
||||
"animated": boolean;
|
||||
/**
|
||||
* By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions.
|
||||
* This property allows to create custom transition using AnimateBuilder functions.
|
||||
*/
|
||||
"animation"?: AnimationBuilder;
|
||||
"commit": (enteringEl: HTMLElement, leavingEl: HTMLElement | undefined, opts?: RouterOutletOptions | undefined) => Promise<boolean>;
|
||||
@@ -2271,7 +2283,7 @@ export namespace Components {
|
||||
*/
|
||||
"autocorrect": 'on' | 'off';
|
||||
/**
|
||||
* Set the cancel button icon. Only applies to `md` mode. Defaults to `"arrow-back-sharp"`.
|
||||
* Set the cancel button icon. Only applies to `md` mode. Defaults to `arrow-back-sharp`.
|
||||
*/
|
||||
"cancelButtonIcon": string;
|
||||
/**
|
||||
@@ -2279,7 +2291,7 @@ export namespace Components {
|
||||
*/
|
||||
"cancelButtonText": string;
|
||||
/**
|
||||
* Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close-sharp"` for `md`.
|
||||
* Set the clear icon. Defaults to `close-circle` for `ios` and `close-sharp` for `md`.
|
||||
*/
|
||||
"clearIcon"?: string;
|
||||
/**
|
||||
@@ -2315,7 +2327,7 @@ export namespace Components {
|
||||
*/
|
||||
"placeholder": string;
|
||||
/**
|
||||
* The icon to use as the search icon. Defaults to `"search-outline"` in `ios` mode and `"search-sharp"` in `md` mode.
|
||||
* The icon to use as the search icon. Defaults to `search-outline` in `ios` mode and `search-sharp` in `md` mode.
|
||||
*/
|
||||
"searchIcon"?: string;
|
||||
/**
|
||||
@@ -2413,7 +2425,7 @@ export namespace Components {
|
||||
*/
|
||||
"interface": SelectInterface;
|
||||
/**
|
||||
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](../alert), the [ion-action-sheet docs](../action-sheet) and the [ion-popover docs](../popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
|
||||
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](./alert), the [ion-action-sheet docs](./action-sheet) and the [ion-popover docs](./popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
|
||||
*/
|
||||
"interfaceOptions": any;
|
||||
/**
|
||||
@@ -2500,7 +2512,7 @@ export namespace Components {
|
||||
*/
|
||||
"getPreviousIndex": () => Promise<number>;
|
||||
/**
|
||||
* Get the Swiper instance. Use this to access the full Swiper API. See https://idangero.us/swiper/api/ for all API options.
|
||||
* Get the Swiper instance. Use this to access the full Swiper API. See https://swiperjs.com/swiper-api for all API options.
|
||||
*/
|
||||
"getSwiper": () => Promise<any>;
|
||||
/**
|
||||
@@ -2535,7 +2547,7 @@ export namespace Components {
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
/**
|
||||
* Options to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options
|
||||
* Options to pass to the swiper instance. See https://swiperjs.com/swiper-api for valid options
|
||||
*/
|
||||
"options": any;
|
||||
/**
|
||||
@@ -2722,7 +2734,7 @@ export namespace Components {
|
||||
*/
|
||||
"autoGrow": boolean;
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
||||
*/
|
||||
"autocapitalize": string;
|
||||
/**
|
||||
@@ -4752,7 +4764,7 @@ declare namespace LocalJSX {
|
||||
/**
|
||||
* The maximum value, which must not be less than its minimum (min attribute) value.
|
||||
*/
|
||||
"max"?: string;
|
||||
"max"?: string | number;
|
||||
/**
|
||||
* If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter.
|
||||
*/
|
||||
@@ -4760,7 +4772,7 @@ declare namespace LocalJSX {
|
||||
/**
|
||||
* The minimum value, which must not be greater than its maximum (max attribute) value.
|
||||
*/
|
||||
"min"?: string;
|
||||
"min"?: string | number;
|
||||
/**
|
||||
* If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter.
|
||||
*/
|
||||
@@ -4802,7 +4814,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"pattern"?: string;
|
||||
/**
|
||||
* Instructional text that shows before the input has a value.
|
||||
* Instructional text that shows before the input has a value. This property applies only when the `type` property is set to `"email"`, `"number"`, `"password"`, `"search"`, `"tel"`, `"text"`, or `"url"`, otherwise it is ignored.
|
||||
*/
|
||||
"placeholder"?: string;
|
||||
/**
|
||||
@@ -5473,7 +5485,7 @@ declare namespace LocalJSX {
|
||||
}
|
||||
interface IonPopover {
|
||||
/**
|
||||
* Describes how to align the popover content with the `reference` point.
|
||||
* Describes how to align the popover content with the `reference` point. Defaults to `'center'` for `ios` mode, and `'start'` for `md` mode.
|
||||
*/
|
||||
"alignment"?: PositionAlign;
|
||||
/**
|
||||
@@ -5913,7 +5925,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"animated"?: boolean;
|
||||
/**
|
||||
* By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions.
|
||||
* This property allows to create custom transition using AnimateBuilder functions.
|
||||
*/
|
||||
"animation"?: AnimationBuilder;
|
||||
"delegate"?: FrameworkDelegate;
|
||||
@@ -5942,7 +5954,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autocorrect"?: 'on' | 'off';
|
||||
/**
|
||||
* Set the cancel button icon. Only applies to `md` mode. Defaults to `"arrow-back-sharp"`.
|
||||
* Set the cancel button icon. Only applies to `md` mode. Defaults to `arrow-back-sharp`.
|
||||
*/
|
||||
"cancelButtonIcon"?: string;
|
||||
/**
|
||||
@@ -5950,7 +5962,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"cancelButtonText"?: string;
|
||||
/**
|
||||
* Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close-sharp"` for `md`.
|
||||
* Set the clear icon. Defaults to `close-circle` for `ios` and `close-sharp` for `md`.
|
||||
*/
|
||||
"clearIcon"?: string;
|
||||
/**
|
||||
@@ -6010,7 +6022,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"placeholder"?: string;
|
||||
/**
|
||||
* The icon to use as the search icon. Defaults to `"search-outline"` in `ios` mode and `"search-sharp"` in `md` mode.
|
||||
* The icon to use as the search icon. Defaults to `search-outline` in `ios` mode and `search-sharp` in `md` mode.
|
||||
*/
|
||||
"searchIcon"?: string;
|
||||
/**
|
||||
@@ -6116,7 +6128,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"interface"?: SelectInterface;
|
||||
/**
|
||||
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](../alert), the [ion-action-sheet docs](../action-sheet) and the [ion-popover docs](../popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
|
||||
* Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](./alert), the [ion-action-sheet docs](./action-sheet) and the [ion-popover docs](./popover) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface.
|
||||
*/
|
||||
"interfaceOptions"?: any;
|
||||
/**
|
||||
@@ -6278,7 +6290,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"onIonSlidesDidLoad"?: (event: CustomEvent<void>) => void;
|
||||
/**
|
||||
* Options to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options
|
||||
* Options to pass to the swiper instance. See https://swiperjs.com/swiper-api for valid options
|
||||
*/
|
||||
"options"?: any;
|
||||
/**
|
||||
@@ -6347,6 +6359,7 @@ declare namespace LocalJSX {
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
"onIonStyle"?: (event: CustomEvent<StyleEventDetail>) => void;
|
||||
"onIonTabBarChanged"?: (event: CustomEvent<TabBarChangedEventDetail>) => void;
|
||||
/**
|
||||
* The selected tab component
|
||||
@@ -6430,7 +6443,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autoGrow"?: boolean;
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
||||
*/
|
||||
"autocapitalize"?: string;
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, ComponentInterface, Element, Host, Prop, State, h } from '@stencil/core';
|
||||
import { chevronDown } from 'ionicons/icons';
|
||||
|
||||
import { config } from '../../global/config';
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
@@ -71,7 +72,7 @@ export class Accordion implements ComponentInterface {
|
||||
* rotated when the accordion is expanded
|
||||
* or collapsed.
|
||||
*/
|
||||
@Prop() toggleIcon = 'chevron-down';
|
||||
@Prop() toggleIcon = chevronDown;
|
||||
|
||||
/**
|
||||
* The slot inside of `ion-item` to
|
||||
|
||||
@@ -1626,7 +1626,7 @@ export const AccordionExample {
|
||||
| `disabled` | `disabled` | If `true`, the accordion cannot be interacted with. | `boolean` | `false` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `readonly` | `readonly` | If `true`, the accordion cannot be interacted with, but does not alter the opacity. | `boolean` | `false` |
|
||||
| `toggleIcon` | `toggle-icon` | The toggle icon to use. This icon will be rotated when the accordion is expanded or collapsed. | `string` | `'chevron-down'` |
|
||||
| `toggleIcon` | `toggle-icon` | The toggle icon to use. This icon will be rotated when the accordion is expanded or collapsed. | `string` | `chevronDown` |
|
||||
| `toggleIconSlot` | `toggle-icon-slot` | The slot inside of `ion-item` to place the toggle icon. Defaults to `'end'`. | `"end" \| "start"` | `'end'` |
|
||||
| `value` | `value` | The value of the accordion. Defaults to an autogenerated value. | `string` | ``ion-accordion-${accordionIds++}`` |
|
||||
|
||||
|
||||
@@ -9,3 +9,12 @@ test('accordion: axe', async () => {
|
||||
const results = await new AxePuppeteer(page).analyze();
|
||||
expect(results.violations.length).toEqual(0);
|
||||
});
|
||||
|
||||
test('accordion: standalone', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/accordion/test/standalone?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
@@ -85,6 +85,21 @@
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Item In Content</h2>
|
||||
|
||||
<ion-accordion-group value="first">
|
||||
<ion-accordion value="first">
|
||||
<ion-item slot="header">
|
||||
Accordion
|
||||
</ion-item>
|
||||
<div slot="content">
|
||||
<ion-item lines="full">Some Item</ion-item>
|
||||
</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@@ -22,6 +22,13 @@
|
||||
text-align: $action-sheet-ios-text-align;
|
||||
}
|
||||
|
||||
// iOS Action Sheet Wrapper
|
||||
// ---------------------------------------------------
|
||||
|
||||
.action-sheet-wrapper {
|
||||
@include margin(var(--ion-safe-area-top, 0), auto, var(--ion-safe-area-bottom, 0), auto);
|
||||
}
|
||||
|
||||
// iOS Action Sheet Container
|
||||
// ---------------------------------------------------
|
||||
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
--color: #{$action-sheet-md-title-color};
|
||||
}
|
||||
|
||||
|
||||
// Material Design Action Sheet Wrapper
|
||||
// -----------------------------------------
|
||||
|
||||
.action-sheet-wrapper {
|
||||
@include margin(var(--ion-safe-area-top, 0), auto, 0, auto);
|
||||
}
|
||||
|
||||
.action-sheet-title {
|
||||
@include padding($action-sheet-md-title-padding-top, $action-sheet-md-title-padding-end, $action-sheet-md-title-padding-bottom, $action-sheet-md-title-padding-start);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ $action-sheet-md-background-color: $overlay-md-background-c
|
||||
$action-sheet-md-padding-top: 0 !default;
|
||||
|
||||
/// @prop - Padding bottom of the action sheet
|
||||
$action-sheet-md-padding-bottom: 0 !default;
|
||||
$action-sheet-md-padding-bottom: var(--ion-safe-area-bottom) !default;
|
||||
|
||||
|
||||
// Action Sheet Title
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
|
||||
.action-sheet-wrapper {
|
||||
@include position(null, 0, 0, 0);
|
||||
@include margin(var(--ion-safe-area-top, 0), auto, var(--ion-safe-area-bottom, 0), auto);
|
||||
@include transform(translate3d(0, 100%, 0));
|
||||
|
||||
display: block;
|
||||
|
||||
@@ -661,6 +661,10 @@ Type: `Promise<void>`
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Used by
|
||||
|
||||
- [ion-select](../select)
|
||||
|
||||
### Depends on
|
||||
|
||||
- [ion-backdrop](../backdrop)
|
||||
@@ -673,6 +677,7 @@ graph TD;
|
||||
ion-action-sheet --> ion-backdrop
|
||||
ion-action-sheet --> ion-icon
|
||||
ion-action-sheet --> ion-ripple-effect
|
||||
ion-select --> ion-action-sheet
|
||||
style ion-action-sheet fill:#f9f,stroke:#333,stroke-width:4px
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { generateE2EUrl } from '../../../utils/test/utils';
|
||||
import { generateE2EUrl } from '@utils/test';
|
||||
|
||||
export const testActionSheet = async (
|
||||
type: string,
|
||||
|
||||
@@ -1863,6 +1863,10 @@ Type: `Promise<void>`
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Used by
|
||||
|
||||
- [ion-select](../select)
|
||||
|
||||
### Depends on
|
||||
|
||||
- [ion-ripple-effect](../ripple-effect)
|
||||
@@ -1873,6 +1877,7 @@ Type: `Promise<void>`
|
||||
graph TD;
|
||||
ion-alert --> ion-ripple-effect
|
||||
ion-alert --> ion-backdrop
|
||||
ion-select --> ion-alert
|
||||
style ion-alert fill:#f9f,stroke:#333,stroke-width:4px
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { generateE2EUrl } from '../../../utils/test/utils';
|
||||
import { generateE2EUrl } from '@utils/test';
|
||||
|
||||
export const testAlert = async (
|
||||
type: string,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, ComponentInterface, Element, Host, Prop, h } from '@stencil/core';
|
||||
import { arrowBackSharp, chevronBack } from 'ionicons/icons';
|
||||
|
||||
import { config } from '../../global/config';
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { AnimationBuilder, Color } from '../../interface';
|
||||
import { ButtonInterface } from '../../utils/element-interface';
|
||||
import { inheritAttributes } from '../../utils/helpers';
|
||||
import { Attributes, inheritAttributes } from '../../utils/helpers';
|
||||
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -23,7 +24,7 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
shadow: true
|
||||
})
|
||||
export class BackButton implements ComponentInterface, ButtonInterface {
|
||||
private inheritedAttributes: { [k: string]: any } = {};
|
||||
private inheritedAttributes: Attributes = {};
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@@ -83,11 +84,11 @@ export class BackButton implements ComponentInterface, ButtonInterface {
|
||||
|
||||
if (getIonMode(this) === 'ios') {
|
||||
// default ios back button icon
|
||||
return config.get('backButtonIcon', 'chevron-back');
|
||||
return config.get('backButtonIcon', chevronBack);
|
||||
}
|
||||
|
||||
// default md back button icon
|
||||
return config.get('backButtonIcon', 'arrow-back-sharp');
|
||||
return config.get('backButtonIcon', arrowBackSharp);
|
||||
}
|
||||
|
||||
get backButtonText() {
|
||||
@@ -120,7 +121,7 @@ export class BackButton implements ComponentInterface, ButtonInterface {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { color, defaultHref, disabled, type, hasIconOnly, backButtonIcon, backButtonText, inheritedAttributes } = this;
|
||||
const { color, defaultHref, disabled, type, hasIconOnly, backButtonIcon, backButtonText, icon, inheritedAttributes } = this;
|
||||
const showBackButton = defaultHref !== undefined;
|
||||
const mode = getIonMode(this);
|
||||
const ariaLabel = inheritedAttributes['aria-label'] || backButtonText || 'back';
|
||||
@@ -148,7 +149,7 @@ export class BackButton implements ComponentInterface, ButtonInterface {
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<span class="button-inner">
|
||||
{backButtonIcon && <ion-icon part="icon" icon={backButtonIcon} aria-hidden="true" lazy={false}></ion-icon>}
|
||||
{backButtonIcon && <ion-icon part="icon" icon={backButtonIcon} aria-hidden="true" lazy={false} flip-rtl={icon === undefined}></ion-icon>}
|
||||
{backButtonText && <span part="text" aria-hidden="true" class="button-text">{backButtonText}</span>}
|
||||
</span>
|
||||
{mode === 'md' && <ion-ripple-effect type={this.rippleType}></ion-ripple-effect>}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { arrowBackSharp, chevronBack } from 'ionicons/icons';
|
||||
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { BackButton } from "../back-button";
|
||||
import { config } from "../../../global/config";
|
||||
@@ -46,12 +48,12 @@ describe('back button', () => {
|
||||
|
||||
it('default icon for ios mode', async () => {
|
||||
const bb = await newBackButton('ios');
|
||||
expect(bb.backButtonIcon).toBe('chevron-back');
|
||||
expect(bb.backButtonIcon).toBe(chevronBack);
|
||||
});
|
||||
|
||||
it('default icon', async () => {
|
||||
const bb = await newBackButton();
|
||||
expect(bb.backButtonIcon).toBe('arrow-back-sharp');
|
||||
expect(bb.backButtonIcon).toBe(arrowBackSharp);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -8,3 +8,12 @@ test('back-button: basic', async () => {
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
test('back-button: basic-rtl', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/back-button/test/basic?ionic:_testing=true&rtl=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons';
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { AnimationBuilder, BreadcrumbCollapsedClickEventDetail, Color, RouterDirection } from '../../interface';
|
||||
import { inheritAttributes } from '../../utils/helpers';
|
||||
import { Attributes, inheritAttributes } from '../../utils/helpers';
|
||||
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -22,7 +22,7 @@ import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
shadow: true
|
||||
})
|
||||
export class Breadcrumb implements ComponentInterface {
|
||||
private inheritedAttributes: { [k: string]: any } = {};
|
||||
private inheritedAttributes: Attributes = {};
|
||||
private collapsedRef?: HTMLElement;
|
||||
|
||||
/** @internal */
|
||||
@@ -212,7 +212,7 @@ export class Breadcrumb implements ComponentInterface {
|
||||
<span class="breadcrumb-separator" part="separator">
|
||||
<slot name="separator">
|
||||
{ mode === 'ios'
|
||||
? <ion-icon icon={chevronForwardOutline} lazy={false}></ion-icon>
|
||||
? <ion-icon icon={chevronForwardOutline} lazy={false} flip-rtl></ion-icon>
|
||||
: <span>/</span>
|
||||
}
|
||||
</slot>
|
||||
|
||||
@@ -8,3 +8,12 @@ test('breadcrumbs: basic', async () => {
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
test('breadcrumbs: basic-rtl', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/breadcrumbs/test/basic?ionic:_testing=true&rtl=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { AnimationBuilder, Color, RouterDirection } from '../../interface';
|
||||
import { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
|
||||
import { hasShadowDom, inheritAttributes } from '../../utils/helpers';
|
||||
import { Attributes, hasShadowDom, inheritAttributes } from '../../utils/helpers';
|
||||
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -28,7 +28,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
private inItem = false;
|
||||
private inListHeader = false;
|
||||
private inToolbar = false;
|
||||
private inheritedAttributes: { [k: string]: any } = {};
|
||||
private inheritedAttributes: Attributes = {};
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ This attribute lets you specify how wide the button should be. By default, butto
|
||||
|
||||
## Fill
|
||||
|
||||
This attributes determines the background and border color of the button. By default, buttons have a solid background unless the button is inside of a toolbar, in which case it has a transparent background.
|
||||
This attribute determines the background and border color of the button. By default, buttons have a solid background unless the button is inside of a toolbar, in which case it has a transparent background.
|
||||
|
||||
| Value | Details |
|
||||
|----------------|------------------------------------------------------------------------------|
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Component, ComponentInterface, Host, Listen, Prop, forceUpdate, h } fro
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { matchBreakpoint } from '../../utils/media';
|
||||
|
||||
const win = window as any;
|
||||
const SUPPORTS_VARS = !!(win.CSS && win.CSS.supports && win.CSS.supports('--a: 0'));
|
||||
const win = (typeof (window as any) !== 'undefined') ? window as any : undefined;
|
||||
const SUPPORTS_VARS = win && !!(win.CSS && win.CSS.supports && win.CSS.supports('--a: 0'));
|
||||
const BREAKPOINTS = ['', 'xs', 'sm', 'md', 'lg', 'xl'];
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
*/
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
|
||||
margin-top: calc(var(--offset-top) * -1);
|
||||
margin-bottom: calc(var(--offset-bottom) * -1);
|
||||
}
|
||||
@@ -173,10 +173,6 @@
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
left: -100%;
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
|
||||
@@ -185,6 +181,18 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:host(.content-ltr) .transition-effect {
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
left: -100%;
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
:host(.content-rtl) .transition-effect {
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: -100%;
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
.transition-cover {
|
||||
position: absolute;
|
||||
|
||||
@@ -204,10 +212,6 @@
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: 0;
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
|
||||
@@ -216,6 +220,20 @@
|
||||
background-size: 10px 16px;
|
||||
}
|
||||
|
||||
:host(.content-ltr) .transition-shadow {
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: 0;
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
:host(.content-rtl) .transition-shadow {
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
left: 0;
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
|
||||
// Content: Fixed
|
||||
// --------------------------------------------------
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getIonMode } from '../../global/ionic-global';
|
||||
import { Color, ScrollBaseDetail, ScrollDetail } from '../../interface';
|
||||
import { componentOnReady } from '../../utils/helpers';
|
||||
import { isPlatform } from '../../utils/platform';
|
||||
import { isRTL } from '../../utils/rtl';
|
||||
import { createColorClasses, hostContext } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -311,7 +312,8 @@ export class Content implements ComponentInterface {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isMainContent, scrollX, scrollY } = this;
|
||||
const { isMainContent, scrollX, scrollY, el } = this;
|
||||
const rtl = isRTL(el) ? 'rtl' : 'ltr';
|
||||
const mode = getIonMode(this);
|
||||
const forceOverscroll = this.shouldForceOverscroll();
|
||||
const transitionShadow = mode === 'ios';
|
||||
@@ -325,6 +327,7 @@ export class Content implements ComponentInterface {
|
||||
[mode]: true,
|
||||
'content-sizing': hostContext('ion-popover', this.el),
|
||||
'overscroll': forceOverscroll,
|
||||
[`content-${rtl}`]: true
|
||||
})}
|
||||
style={{
|
||||
'--offset-top': `${this.cTop}px`,
|
||||
@@ -339,7 +342,7 @@ export class Content implements ComponentInterface {
|
||||
'scroll-y': scrollY,
|
||||
'overscroll': (scrollX || scrollY) && forceOverscroll
|
||||
}}
|
||||
ref={(el: HTMLElement) => this.scrollEl = el!}
|
||||
ref={(scrollEl: HTMLElement) => this.scrollEl = scrollEl!}
|
||||
onScroll={(this.scrollEvents) ? (ev: UIEvent) => this.onScroll(ev) : undefined}
|
||||
part="scroll"
|
||||
>
|
||||
|
||||
@@ -54,7 +54,12 @@
|
||||
// Calendar / Body
|
||||
// -----------------------------------
|
||||
:host .calendar-body .calendar-month .calendar-month-grid {
|
||||
@include padding(0px, 10px, 0px, 10px);
|
||||
/**
|
||||
* 3px top padding adds enough spacing at
|
||||
* the top of the container for the selected
|
||||
* day box shadow to display without clipping.
|
||||
*/
|
||||
@include padding(3px, 10px, 0px, 10px);
|
||||
|
||||
/**
|
||||
* Calendar on MD will show an empty row
|
||||
|
||||
@@ -48,6 +48,35 @@
|
||||
|
||||
opacity: 1;
|
||||
}
|
||||
/**
|
||||
* Changing the physical order of the
|
||||
* picker columns in the DOM is added
|
||||
* work, so we just use `order` instead.
|
||||
*
|
||||
* The picker automatically configures
|
||||
* the text alignment, so when switching
|
||||
* the order we need to manually switch
|
||||
* the text alignment too.
|
||||
*/
|
||||
:host .datetime-year .order-month-first .month-column {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
:host .datetime-year .order-month-first .year-column {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
:host .datetime-year .order-year-first .month-column {
|
||||
order: 2;
|
||||
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
:host .datetime-year .order-year-first .year-column {
|
||||
order: 1;
|
||||
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
// Calendar
|
||||
// -----------------------------------
|
||||
@@ -79,24 +108,49 @@
|
||||
* unhiding the calendar content.
|
||||
* To workaround this, we set the opacity
|
||||
* of the content to 0 and hide it offscreen.
|
||||
* TODO: This is fixed in Safari 15+, so remove
|
||||
* when Safari 14 support is dropped.
|
||||
*
|
||||
* -webkit-named-image is something only WebKit supports
|
||||
* so we use this to detect general WebKit support.
|
||||
* aspect-ratio is only supported in Safari 15+
|
||||
* so by checking lack of aspect-ratio support, we know
|
||||
* that we are in a pre-Safari 15 browser.
|
||||
*
|
||||
* TODO(FW-554): Remove when iOS 14 support is dropped.
|
||||
*/
|
||||
:host(.show-month-and-year) .calendar-next-prev,
|
||||
:host(.show-month-and-year) .calendar-days-of-week,
|
||||
:host(.show-month-and-year) .calendar-body,
|
||||
:host(.show-month-and-year) .datetime-time {
|
||||
@include position(null, null, null, -99999px);
|
||||
@supports (background: -webkit-named-image(apple-pay-logo-black)) and (not (aspect-ratio: 1/1)) {
|
||||
:host(.show-month-and-year) .calendar-next-prev,
|
||||
:host(.show-month-and-year) .calendar-days-of-week,
|
||||
:host(.show-month-and-year) .calendar-body,
|
||||
:host(.show-month-and-year) .datetime-time {
|
||||
@include position(null, null, null, -99999px);
|
||||
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
|
||||
/**
|
||||
* Use visibility instead of
|
||||
* opacity to ensure element
|
||||
* cannot receive focus
|
||||
*/
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
/**
|
||||
* Use visibility instead of
|
||||
* opacity to ensure element
|
||||
* cannot receive focus
|
||||
*/
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This support check two cases:
|
||||
* 1. A WebKit browser that supports aspect-ratio (Safari 15+)
|
||||
* 2. Any non-WebKit browser.
|
||||
* Note that just overriding this display: none is not
|
||||
* sufficient to resolve the issue mentioned above, which
|
||||
* is why we do another set of @supports checks.
|
||||
*/
|
||||
@supports (not (background: -webkit-named-image(apple-pay-logo-black))) or ((background: -webkit-named-image(apple-pay-logo-black)) and (aspect-ratio: 1/1)) {
|
||||
:host(.show-month-and-year) .calendar-next-prev,
|
||||
:host(.show-month-and-year) .calendar-days-of-week,
|
||||
:host(.show-month-and-year) .calendar-body,
|
||||
:host(.show-month-and-year) .datetime-time {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:host(.datetime-readonly),
|
||||
@@ -216,6 +270,13 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host .calendar-body .calendar-month-disabled {
|
||||
/**
|
||||
* Disables swipe gesture snapping for scroll-snap containers
|
||||
*/
|
||||
scroll-snap-align: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide scrollbars on Chrome and Safari
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getIonMode } from '../../global/ionic-global';
|
||||
import { Color, DatetimeChangeEventDetail, DatetimeParts, Mode, StyleEventDetail } from '../../interface';
|
||||
import { startFocusVisible } from '../../utils/focus-visible';
|
||||
import { getElementRoot, raf, renderHiddenInput } from '../../utils/helpers';
|
||||
import { isRTL } from '../../utils/rtl';
|
||||
import { createColorClasses } from '../../utils/theme';
|
||||
import { PickerColumnItem } from '../picker-column-internal/picker-column-internal-interfaces';
|
||||
|
||||
@@ -31,7 +32,8 @@ import {
|
||||
getMonthAndYear
|
||||
} from './utils/format';
|
||||
import {
|
||||
is24Hour
|
||||
is24Hour,
|
||||
isMonthFirstLocale
|
||||
} from './utils/helpers';
|
||||
import {
|
||||
calculateHourFromAMPM,
|
||||
@@ -55,7 +57,10 @@ import {
|
||||
} from './utils/parse';
|
||||
import {
|
||||
getCalendarDayState,
|
||||
isDayDisabled
|
||||
isDayDisabled,
|
||||
isMonthDisabled,
|
||||
isNextMonthDisabled,
|
||||
isPrevMonthDisabled
|
||||
} from './utils/state';
|
||||
|
||||
/**
|
||||
@@ -89,9 +94,13 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
private destroyCalendarIO?: () => void;
|
||||
private destroyKeyboardMO?: () => void;
|
||||
private destroyOverlayListener?: () => void;
|
||||
|
||||
private minParts?: any;
|
||||
private maxParts?: any;
|
||||
private todayParts = parseDate(getToday());
|
||||
|
||||
private prevPresentation: string | null = null;
|
||||
|
||||
/**
|
||||
* Duplicate reference to `activeParts` that does not trigger a re-render of the component.
|
||||
@@ -119,8 +128,6 @@ export class Datetime implements ComponentInterface {
|
||||
ampm: 'pm'
|
||||
}
|
||||
|
||||
private todayParts = parseDate(getToday())
|
||||
|
||||
@Element() el!: HTMLIonDatetimeElement;
|
||||
|
||||
@State() isPresented = false;
|
||||
@@ -273,6 +280,11 @@ export class Datetime implements ComponentInterface {
|
||||
this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues);
|
||||
}
|
||||
|
||||
@Watch('activeParts')
|
||||
protected activePartsChanged() {
|
||||
this.activePartsClone = this.activeParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* The locale to use for `ion-datetime`. This
|
||||
* impacts month and day name formatting.
|
||||
@@ -414,10 +426,10 @@ export class Datetime implements ComponentInterface {
|
||||
* the date that is currently selected, otherwise
|
||||
* there can be 1 hr difference when dealing w/ DST
|
||||
*/
|
||||
const date = new Date(convertDataToISO(this.workingParts));
|
||||
this.workingParts.tzOffset = date.getTimezoneOffset() * -1;
|
||||
const date = new Date(convertDataToISO(this.activeParts));
|
||||
this.activeParts.tzOffset = date.getTimezoneOffset() * -1;
|
||||
|
||||
this.value = convertDataToISO(this.workingParts);
|
||||
this.value = convertDataToISO(this.activeParts);
|
||||
|
||||
if (closeOverlay) {
|
||||
this.closeParentOverlay();
|
||||
@@ -473,8 +485,18 @@ export class Datetime implements ComponentInterface {
|
||||
this.confirm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stencil sometimes sets calendarBodyRef to null on rerender, even though
|
||||
* the element is present. Query for it manually as a fallback.
|
||||
*
|
||||
* TODO(FW-901) Remove when issue is resolved: https://github.com/ionic-team/stencil/issues/3253
|
||||
*/
|
||||
private getCalendarBodyEl = () => {
|
||||
return this.calendarBodyRef || this.el.shadowRoot?.querySelector('.calendar-body');
|
||||
};
|
||||
|
||||
private initializeKeyboardListeners = () => {
|
||||
const { calendarBodyRef } = this;
|
||||
const calendarBodyRef = this.getCalendarBodyEl();
|
||||
if (!calendarBodyRef) { return; }
|
||||
|
||||
const root = this.el!.shadowRoot!;
|
||||
@@ -520,7 +542,7 @@ export class Datetime implements ComponentInterface {
|
||||
* We must use keydown not keyup as we want
|
||||
* to prevent scrolling when using the arrow keys.
|
||||
*/
|
||||
this.calendarBodyRef!.addEventListener('keydown', (ev: KeyboardEvent) => {
|
||||
calendarBodyRef.addEventListener('keydown', (ev: KeyboardEvent) => {
|
||||
const activeElement = root.activeElement;
|
||||
if (!activeElement || !activeElement.classList.contains('calendar-day')) { return; }
|
||||
|
||||
@@ -647,7 +669,7 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private initializeCalendarIOListeners = () => {
|
||||
const { calendarBodyRef } = this;
|
||||
const calendarBodyRef = this.getCalendarBodyEl();
|
||||
if (!calendarBodyRef) { return; }
|
||||
|
||||
const mode = getIonMode(this);
|
||||
@@ -681,7 +703,7 @@ export class Datetime implements ComponentInterface {
|
||||
* if element is not in viewport. Use scrollLeft instead.
|
||||
*/
|
||||
writeTask(() => {
|
||||
calendarBodyRef.scrollLeft = startMonth.clientWidth;
|
||||
calendarBodyRef.scrollLeft = startMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
|
||||
let endIO: IntersectionObserver | undefined;
|
||||
let startIO: IntersectionObserver | undefined;
|
||||
@@ -708,6 +730,15 @@ export class Datetime implements ComponentInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
const { month, year, day } = refMonthFn(this.workingParts);
|
||||
|
||||
if (isMonthDisabled({ month, year, day: null }, {
|
||||
minParts: { ...this.minParts, day: null },
|
||||
maxParts: { ...this.maxParts, day: null }
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* On iOS, we need to set pointer-events: none
|
||||
* when the user is almost done with the gesture
|
||||
@@ -718,7 +749,8 @@ export class Datetime implements ComponentInterface {
|
||||
*/
|
||||
if (mode === 'ios') {
|
||||
const ratio = ev.intersectionRatio;
|
||||
const shouldDisable = Math.abs(ratio - 0.7) <= 0.1;
|
||||
// `maxTouchPoints` will be 1 in device preview, but > 1 on device
|
||||
const shouldDisable = Math.abs(ratio - 0.7) <= 0.1 && navigator.maxTouchPoints > 1;
|
||||
|
||||
if (shouldDisable) {
|
||||
calendarBodyRef.style.setProperty('pointer-events', 'none');
|
||||
@@ -751,19 +783,29 @@ export class Datetime implements ComponentInterface {
|
||||
* if we did not do this.
|
||||
*/
|
||||
writeTask(() => {
|
||||
const { month, year, day } = refMonthFn(this.workingParts);
|
||||
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
month,
|
||||
day: day!,
|
||||
year
|
||||
// Disconnect all active intersection observers
|
||||
// to avoid a re-render causing a duplicate event.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
}
|
||||
|
||||
raf(() => {
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
month,
|
||||
day: day!,
|
||||
year
|
||||
});
|
||||
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
calendarBodyRef.style.removeProperty('overflow');
|
||||
calendarBodyRef.style.removeProperty('pointer-events');
|
||||
|
||||
endIO?.observe(endMonth);
|
||||
startIO?.observe(startMonth);
|
||||
});
|
||||
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth;
|
||||
calendarBodyRef.style.removeProperty('overflow');
|
||||
calendarBodyRef.style.removeProperty('pointer-events');
|
||||
|
||||
/**
|
||||
* Now that state has been updated
|
||||
* and the correct month is in view,
|
||||
@@ -775,6 +817,18 @@ export class Datetime implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
const threshold = mode === 'ios' &&
|
||||
// tslint:disable-next-line
|
||||
typeof navigator !== 'undefined' &&
|
||||
navigator.maxTouchPoints > 1 ?
|
||||
[0.7, 1] : 1;
|
||||
|
||||
// Intersection observers cannot accurately detect the
|
||||
// intersection with a threshold of 1, when the observed
|
||||
// element width is a sub-pixel value (i.e. 334.05px).
|
||||
// Setting a root margin to 1px solves the issue.
|
||||
const rootMargin = '1px';
|
||||
|
||||
/**
|
||||
* Listen on the first month to
|
||||
* prepend a new month and on the last
|
||||
@@ -793,23 +847,26 @@ export class Datetime implements ComponentInterface {
|
||||
* it applies to active gestures which is not
|
||||
* something WebKit does.
|
||||
*/
|
||||
|
||||
endIO = new IntersectionObserver(ev => ioCallback('end', ev), {
|
||||
threshold: mode === 'ios' ? [0.7, 1] : 1,
|
||||
root: calendarBodyRef
|
||||
threshold,
|
||||
root: calendarBodyRef,
|
||||
rootMargin
|
||||
});
|
||||
endIO.observe(endMonth);
|
||||
|
||||
startIO = new IntersectionObserver(ev => ioCallback('start', ev), {
|
||||
threshold: mode === 'ios' ? [0.7, 1] : 1,
|
||||
root: calendarBodyRef
|
||||
});
|
||||
threshold,
|
||||
root: calendarBodyRef,
|
||||
rootMargin
|
||||
});
|
||||
startIO.observe(startMonth);
|
||||
|
||||
this.destroyCalendarIO = () => {
|
||||
endIO?.disconnect();
|
||||
startIO?.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@@ -828,7 +885,7 @@ export class Datetime implements ComponentInterface {
|
||||
* listener. This is so that we can re-create the listeners
|
||||
* if the datetime has been hidden/presented by a modal or popover.
|
||||
*/
|
||||
private destroyListeners = () => {
|
||||
private destroyInteractionListeners = () => {
|
||||
const { destroyCalendarIO, destroyKeyboardMO } = this;
|
||||
|
||||
if (destroyCalendarIO !== undefined) {
|
||||
@@ -840,6 +897,12 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private initializeListeners() {
|
||||
this.initializeCalendarIOListeners();
|
||||
this.initializeKeyboardListeners();
|
||||
this.initializeOverlayListener();
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
/**
|
||||
* If a scrollable element is hidden using `display: none`,
|
||||
@@ -853,9 +916,7 @@ export class Datetime implements ComponentInterface {
|
||||
const ev = entries[0];
|
||||
if (!ev.isIntersecting) { return; }
|
||||
|
||||
this.initializeCalendarIOListeners();
|
||||
this.initializeKeyboardListeners();
|
||||
this.initializeOverlayListener();
|
||||
this.initializeListeners();
|
||||
|
||||
/**
|
||||
* TODO: Datetime needs a frame to ensure that it
|
||||
@@ -891,7 +952,7 @@ export class Datetime implements ComponentInterface {
|
||||
const ev = entries[0];
|
||||
if (ev.isIntersecting) { return; }
|
||||
|
||||
this.destroyListeners();
|
||||
this.destroyInteractionListeners();
|
||||
|
||||
writeTask(() => {
|
||||
this.el.classList.remove('datetime-ready');
|
||||
@@ -914,6 +975,29 @@ export class Datetime implements ComponentInterface {
|
||||
root.addEventListener('ionBlur', (ev: Event) => ev.stopPropagation());
|
||||
}
|
||||
|
||||
/**
|
||||
* When the presentation is changed, all calendar content is recreated,
|
||||
* so we need to re-init behavior with the new elements.
|
||||
*/
|
||||
componentDidRender() {
|
||||
const { presentation, prevPresentation } = this;
|
||||
|
||||
if (prevPresentation === null) {
|
||||
this.prevPresentation = presentation;
|
||||
return;
|
||||
}
|
||||
|
||||
if (presentation === prevPresentation) { return; }
|
||||
this.prevPresentation = presentation;
|
||||
|
||||
this.destroyInteractionListeners();
|
||||
if (this.destroyOverlayListener !== undefined) {
|
||||
this.destroyOverlayListener();
|
||||
}
|
||||
|
||||
this.initializeListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* When doing subsequent presentations of an inline
|
||||
* overlay, the IO callback will fire again causing
|
||||
@@ -925,9 +1009,15 @@ export class Datetime implements ComponentInterface {
|
||||
const overlay = this.el.closest('ion-popover, ion-modal');
|
||||
if (overlay === null) { return; }
|
||||
|
||||
overlay.addEventListener('willPresent', () => {
|
||||
const overlayListener = () => {
|
||||
this.overlayIsPresenting = true;
|
||||
});
|
||||
};
|
||||
|
||||
overlay.addEventListener('willPresent', overlayListener);
|
||||
|
||||
this.destroyOverlayListener = () => {
|
||||
overlay.removeEventListener('willPresent', overlayListener);
|
||||
};
|
||||
}
|
||||
|
||||
private processValue = (value?: string | null) => {
|
||||
@@ -944,7 +1034,7 @@ export class Datetime implements ComponentInterface {
|
||||
ampm: hour >= 12 ? 'pm' : 'am'
|
||||
}
|
||||
|
||||
this.activePartsClone = this.activeParts = {
|
||||
this.activeParts = {
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
@@ -957,9 +1047,9 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.processValue(this.value);
|
||||
this.processMinParts();
|
||||
this.processMaxParts();
|
||||
this.processValue(this.value);
|
||||
this.parsedHourValues = convertToArrayOfNumbers(this.hourValues);
|
||||
this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues);
|
||||
this.parsedMonthValues = convertToArrayOfNumbers(this.monthValues);
|
||||
@@ -989,21 +1079,23 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private nextMonth = () => {
|
||||
const { calendarBodyRef } = this;
|
||||
const calendarBodyRef = this.getCalendarBodyEl();
|
||||
if (!calendarBodyRef) { return; }
|
||||
|
||||
const nextMonth = calendarBodyRef.querySelector('.calendar-month:last-of-type');
|
||||
if (!nextMonth) { return; }
|
||||
|
||||
const left = (nextMonth as HTMLElement).offsetWidth * 2;
|
||||
|
||||
calendarBodyRef.scrollTo({
|
||||
top: 0,
|
||||
left: (nextMonth as HTMLElement).offsetWidth * 2,
|
||||
left: left * (isRTL(this.el) ? -1 : 1),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
|
||||
private prevMonth = () => {
|
||||
const { calendarBodyRef } = this;
|
||||
const calendarBodyRef = this.getCalendarBodyEl();
|
||||
if (!calendarBodyRef) { return; }
|
||||
|
||||
const prevMonth = calendarBodyRef.querySelector('.calendar-month:first-of-type');
|
||||
@@ -1060,29 +1152,42 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderYearView() {
|
||||
const { presentation, workingParts } = this;
|
||||
const { presentation, workingParts, locale } = this;
|
||||
const calendarYears = getCalendarYears(this.todayParts, this.minParts, this.maxParts, this.parsedYearValues);
|
||||
const showMonth = presentation !== 'year';
|
||||
const showYear = presentation !== 'month';
|
||||
|
||||
const months = getPickerMonths(this.locale, workingParts, this.minParts, this.maxParts, this.parsedMonthValues);
|
||||
const months = getPickerMonths(locale, workingParts, this.minParts, this.maxParts, this.parsedMonthValues);
|
||||
const years = calendarYears.map(year => {
|
||||
return {
|
||||
text: `${year}`,
|
||||
value: year
|
||||
}
|
||||
})
|
||||
const showMonthFirst = isMonthFirstLocale(locale);
|
||||
const columnOrder = showMonthFirst ? 'month-first' : 'year-first';
|
||||
return (
|
||||
<div class="datetime-year">
|
||||
<div class="datetime-year-body">
|
||||
<div class={{
|
||||
'datetime-year-body': true,
|
||||
[`order-${columnOrder}`]: true
|
||||
}}>
|
||||
<ion-picker-internal>
|
||||
{
|
||||
showMonth &&
|
||||
<ion-picker-column-internal
|
||||
class="month-column"
|
||||
color={this.color}
|
||||
items={months}
|
||||
value={workingParts.month}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
// Due to a Safari 14 issue we need to destroy
|
||||
// the intersection observer before we update state
|
||||
// and trigger a re-render.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
}
|
||||
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
month: ev.detail.value
|
||||
@@ -1095,6 +1200,10 @@ export class Datetime implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
// We can re-attach the intersection observer after
|
||||
// the working parts have been updated.
|
||||
this.initializeCalendarIOListeners();
|
||||
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
></ion-picker-column-internal>
|
||||
@@ -1102,10 +1211,18 @@ export class Datetime implements ComponentInterface {
|
||||
{
|
||||
showYear &&
|
||||
<ion-picker-column-internal
|
||||
class="year-column"
|
||||
color={this.color}
|
||||
items={years}
|
||||
value={workingParts.year}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
// Due to a Safari 14 issue we need to destroy
|
||||
// the intersection observer before we update state
|
||||
// and trigger a re-render.
|
||||
if (this.destroyCalendarIO) {
|
||||
this.destroyCalendarIO();
|
||||
}
|
||||
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
year: ev.detail.value
|
||||
@@ -1118,6 +1235,10 @@ export class Datetime implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
// We can re-attach the intersection observer after
|
||||
// the working parts have been updated.
|
||||
this.initializeCalendarIOListeners();
|
||||
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
></ion-picker-column-internal>
|
||||
@@ -1131,6 +1252,10 @@ export class Datetime implements ComponentInterface {
|
||||
private renderCalendarHeader(mode: Mode) {
|
||||
const expandedIcon = mode === 'ios' ? chevronDown : caretUpSharp;
|
||||
const collapsedIcon = mode === 'ios' ? chevronForward : caretDownSharp;
|
||||
|
||||
const prevMonthDisabled = isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
|
||||
const nextMonthDisabled = isNextMonthDisabled(this.workingParts, this.maxParts);
|
||||
|
||||
return (
|
||||
<div class="calendar-header">
|
||||
<div class="calendar-action-buttons">
|
||||
@@ -1144,11 +1269,15 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
<div class="calendar-next-prev">
|
||||
<ion-buttons>
|
||||
<ion-button onClick={() => this.prevMonth()}>
|
||||
<ion-icon slot="icon-only" icon={chevronBack} lazy={false}></ion-icon>
|
||||
<ion-button
|
||||
disabled={prevMonthDisabled}
|
||||
onClick={() => this.prevMonth()}>
|
||||
<ion-icon slot="icon-only" icon={chevronBack} lazy={false} flipRtl></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button onClick={() => this.nextMonth()}>
|
||||
<ion-icon slot="icon-only" icon={chevronForward} lazy={false}></ion-icon>
|
||||
<ion-button
|
||||
disabled={nextMonthDisabled}
|
||||
onClick={() => this.nextMonth()}>
|
||||
<ion-icon slot="icon-only" icon={chevronForward} lazy={false} flipRtl></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</div>
|
||||
@@ -1165,9 +1294,29 @@ export class Datetime implements ComponentInterface {
|
||||
private renderMonth(month: number, year: number) {
|
||||
const yearAllowed = this.parsedYearValues === undefined || this.parsedYearValues.includes(year);
|
||||
const monthAllowed = this.parsedMonthValues === undefined || this.parsedMonthValues.includes(month);
|
||||
const isMonthDisabled = !yearAllowed || !monthAllowed;
|
||||
const isCalMonthDisabled = !yearAllowed || !monthAllowed;
|
||||
const swipeDisabled = isMonthDisabled({
|
||||
month,
|
||||
year,
|
||||
day: null
|
||||
}, {
|
||||
// The day is not used when checking if a month is disabled.
|
||||
// Users should be able to access the min or max month, even if the
|
||||
// min/max date is out of bounds (e.g. min is set to Feb 15, Feb should not be disabled).
|
||||
minParts: { ...this.minParts, day: null },
|
||||
maxParts: { ...this.maxParts, day: null }
|
||||
});
|
||||
// The working month should never have swipe disabled.
|
||||
// Otherwise the CSS scroll snap will not work and the user
|
||||
// can free-scroll the calendar.
|
||||
const isWorkingMonth = this.workingParts.month === month && this.workingParts.year === year;
|
||||
|
||||
return (
|
||||
<div class="calendar-month">
|
||||
<div class={{
|
||||
'calendar-month': true,
|
||||
// Prevents scroll snap swipe gestures for months outside of the min/max bounds
|
||||
'calendar-month-disabled': !isWorkingMonth && swipeDisabled
|
||||
}}>
|
||||
<div class="calendar-month-grid">
|
||||
{getDaysOfMonth(month, year, this.firstDayOfWeek % 7).map((dateObject, index) => {
|
||||
const { day, dayOfWeek } = dateObject;
|
||||
@@ -1182,7 +1331,7 @@ export class Datetime implements ComponentInterface {
|
||||
data-year={year}
|
||||
data-index={index}
|
||||
data-day-of-week={dayOfWeek}
|
||||
disabled={isMonthDisabled || disabled}
|
||||
disabled={isCalMonthDisabled || disabled}
|
||||
class={{
|
||||
'calendar-day-padding': day === null,
|
||||
'calendar-day': true,
|
||||
@@ -1206,7 +1355,7 @@ export class Datetime implements ComponentInterface {
|
||||
month,
|
||||
day,
|
||||
year
|
||||
})
|
||||
});
|
||||
}}
|
||||
>{day}</button>
|
||||
)
|
||||
@@ -1249,7 +1398,7 @@ export class Datetime implements ComponentInterface {
|
||||
minutesItems: PickerColumnItem[],
|
||||
ampmItems: PickerColumnItem[],
|
||||
use24Hour: boolean
|
||||
) {
|
||||
) {
|
||||
const { color, activePartsClone, workingParts } = this;
|
||||
|
||||
return (
|
||||
@@ -1290,7 +1439,7 @@ export class Datetime implements ComponentInterface {
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
></ion-picker-column-internal>
|
||||
{ !use24Hour && <ion-picker-column-internal
|
||||
{!use24Hour && <ion-picker-column-internal
|
||||
color={color}
|
||||
value={activePartsClone.ampm}
|
||||
items={ampmItems}
|
||||
@@ -1311,7 +1460,7 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
></ion-picker-column-internal> }
|
||||
></ion-picker-column-internal>}
|
||||
</ion-picker-internal>
|
||||
)
|
||||
}
|
||||
@@ -1321,7 +1470,7 @@ export class Datetime implements ComponentInterface {
|
||||
minutesItems: PickerColumnItem[],
|
||||
ampmItems: PickerColumnItem[],
|
||||
use24Hour: boolean
|
||||
) {
|
||||
) {
|
||||
return [
|
||||
<div class="time-header">
|
||||
{this.renderTimeLabel()}
|
||||
@@ -1337,9 +1486,13 @@ export class Datetime implements ComponentInterface {
|
||||
const { popoverRef } = this;
|
||||
|
||||
if (popoverRef) {
|
||||
|
||||
this.isTimePopoverOpen = true;
|
||||
popoverRef.present(ev);
|
||||
|
||||
popoverRef.present(new CustomEvent('ionShadowTarget', {
|
||||
detail: {
|
||||
ionShadowTarget: ev.target
|
||||
}
|
||||
}));
|
||||
|
||||
await popoverRef.onWillDismiss();
|
||||
|
||||
@@ -1354,6 +1507,19 @@ export class Datetime implements ComponentInterface {
|
||||
translucent
|
||||
overlayIndex={1}
|
||||
arrow={false}
|
||||
onWillPresent={ev => {
|
||||
/**
|
||||
* Intersection Observers do not consistently fire between Blink and Webkit
|
||||
* when toggling the visibility of the popover and trying to scroll the picker
|
||||
* column to the correct time value.
|
||||
*
|
||||
* This will correctly scroll the element position to the correct time value,
|
||||
* before the popover is fully presented.
|
||||
*/
|
||||
const cols = (ev.target! as HTMLElement).querySelectorAll('ion-picker-column-internal');
|
||||
// TODO (FW-615): Potentially remove this when intersection observers are fixed in picker column
|
||||
cols.forEach(col => col.scrollActiveItemIntoView());
|
||||
}}
|
||||
style={{
|
||||
'--offset-y': '-10px'
|
||||
}}
|
||||
@@ -1411,7 +1577,7 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
return (
|
||||
<div class="datetime-time">
|
||||
{timeOnlyPresentation ? this.renderTimePicker(hoursItems, minutesItems, ampmItems, use24Hour) : this.renderTimeOverlay(hoursItems, minutesItems, ampmItems, use24Hour)}
|
||||
{timeOnlyPresentation ? this.renderTimePicker(hoursItems, minutesItems, ampmItems, use24Hour) : this.renderTimeOverlay(hoursItems, minutesItems, ampmItems, use24Hour)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ion-datetime
|
||||
|
||||
Datetimes present a calendar interface and time wheel, making it easy for users to select dates and times. Datetimes are similar to the native `input` elements of `datetime-local`, however, Ionic Framework's Datetime component makes it easy to display the date and time in the a preferred format, and manage the datetime values.
|
||||
Datetimes present a calendar interface and time wheel, making it easy for users to select dates and times. Datetimes are similar to the native `input` elements of `datetime-local`, however, Ionic Framework's Datetime component makes it easy to display the date and time in the preferred format, and manage the datetime values.
|
||||
|
||||
### Datetime Data
|
||||
|
||||
@@ -21,7 +21,7 @@ for its value. The value is simply a string, rather than using JavaScript's
|
||||
and parse within JSON objects and databases.
|
||||
|
||||
An ISO format can be used as a simple year, or just the hour and minute, or get
|
||||
more detailed down to the millisecond and timezone. Any of the ISO formats below
|
||||
more detailed down to the millisecond and time zone. Any of the ISO formats below
|
||||
can be used, and after a user selects a new value, Ionic Framework will continue to use
|
||||
the same ISO format which datetime value was originally given as.
|
||||
|
||||
@@ -31,8 +31,8 @@ the same ISO format which datetime value was originally given as.
|
||||
| Year and Month | YYYY-MM | 1994-12 |
|
||||
| Complete Date | YYYY-MM-DD | 1994-12-15 |
|
||||
| Date and Time | YYYY-MM-DDTHH:mm | 1994-12-15T13:47 |
|
||||
| UTC Timezone | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20.789Z |
|
||||
| Timezone Offset | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20.789+05:00 |
|
||||
| UTC Timezone | YYYY-MM-DDTHH:mm:ssZ | 1994-12-15T13:47:20Z |
|
||||
| Timezone Offset | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20+05:00 |
|
||||
| Hour and Minute | HH:mm | 13:47 |
|
||||
| Hour, Minute, Second | HH:mm:ss | 13:47:20 |
|
||||
|
||||
@@ -123,11 +123,35 @@ For example, if you wanted to have the first day of the week be Monday, you coul
|
||||
<ion-datetime first-day-of-week="1"></ion-datetime>
|
||||
```
|
||||
|
||||
## Parsing Dates
|
||||
## Time Zones
|
||||
|
||||
When `ionChange` is emitted, we provide an ISO-8601 string in the event payload. From there, it is the developer's responsibility to format it as they see fit. We recommend using a library like [date-fns](https://date-fns.org) to format their dates properly.
|
||||
Ionic's `ion-datetime` follows the [datetime-local](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local) behavior of not manipulating or setting the time zone inside of a datetime control. In other words, a time value of "07:00" will not be adjusted according to different time zones.
|
||||
|
||||
Below is an example of formatting an ISO-8601 string to display the month, date, and year:
|
||||
We recommend using a library such as [date-fns-tz](https://github.com/marnusw/date-fns-tz) to convert a datetime value to the desired time zone.
|
||||
|
||||
Below is an example of formatting an ISO-8601 string to display in the time zone set on a user's device:
|
||||
|
||||
```typescript
|
||||
import { format, utcToZonedTime } from 'date-fns-tz';
|
||||
|
||||
// Get the time zone set on the user's device
|
||||
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
|
||||
// Create a date object from a UTC date string
|
||||
const date = new Date('2014-10-25T10:46:20Z');
|
||||
|
||||
// Use date-fns-tz to convert from UTC to a zoned time
|
||||
const zonedTime = dateFnsTz.utcToZonedTime(date, userTimeZone);
|
||||
|
||||
// Create a formatted string from the zoned time
|
||||
format(zonedTime, 'yyyy-MM-dd HH:mm:ssXXX', { timeZone: userTimeZone });
|
||||
```
|
||||
|
||||
### Parsing Date Values
|
||||
|
||||
The `ionChange` event will emit the date value as an ISO-8601 string in the event payload. It is the developer's responsibility to format it based on their application needs. We recommend using [date-fns](https://date-fns.org) to format the date value.
|
||||
|
||||
Below is an example of formatting an ISO-8601 string to display the month, date and year:
|
||||
|
||||
```typescript
|
||||
import { format, parseISO } from 'date-fns';
|
||||
@@ -135,6 +159,8 @@ import { format, parseISO } from 'date-fns';
|
||||
/**
|
||||
* This is provided in the event
|
||||
* payload from the `ionChange` event.
|
||||
*
|
||||
* The value is an ISO-8601 date string.
|
||||
*/
|
||||
const dateFromIonDatetime = '2021-06-04T14:23:00-04:00';
|
||||
const formattedString = format(parseISO(dateFromIonDatetime), 'MMM d, yyyy');
|
||||
|
||||
@@ -78,4 +78,73 @@ describe('Footer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('datetime: selecting a day', () => {
|
||||
|
||||
it('should update the active day', async () => {
|
||||
const page = await newE2EPage({
|
||||
html: `
|
||||
<ion-datetime show-default-buttons="true" value="2021-12-25T12:40:00.000Z"></ion-datetime>
|
||||
`
|
||||
});
|
||||
|
||||
const activeDay = await page.find('ion-datetime >>> .calendar-day-active');
|
||||
|
||||
expect(activeDay.innerText).toEqual('25');
|
||||
|
||||
const dayBtn = await page.find('ion-datetime >>> .calendar-day[data-day="13"][data-month="12"]');
|
||||
dayBtn.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
const newActiveDay = await page.find('ion-datetime >>> .calendar-day-active');
|
||||
|
||||
expect(newActiveDay.innerText).toEqual('13');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('datetime:rtl: basic', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/basic?ionic:_testing=true&rtl=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
|
||||
describe('datetime: confirm date', () => {
|
||||
|
||||
test('should set the date value based on the selected date', async () => {
|
||||
|
||||
const page = await newE2EPage({
|
||||
html: `
|
||||
<button>Bind datetimeMonthDidChange event</button>
|
||||
<ion-datetime value="2021-12-25T12:40:00.000Z"></ion-datetime>
|
||||
<script type="module">
|
||||
import { InitMonthDidChangeEvent } from '/src/components/datetime/test/utils/month-did-change-event.js';
|
||||
document.querySelector('button').addEventListener('click', function() {
|
||||
InitMonthDidChangeEvent();
|
||||
});
|
||||
</script>
|
||||
`
|
||||
});
|
||||
|
||||
const eventButton = await page.find('button');
|
||||
await eventButton.click();
|
||||
|
||||
const buttons = await page.findAll('ion-datetime >>> .calendar-next-prev ion-button');
|
||||
|
||||
await buttons[1].click();
|
||||
|
||||
await page.waitForEvent('datetimeMonthDidChange');
|
||||
|
||||
await page.$eval('ion-datetime', async (el: any) => {
|
||||
await el.confirm();
|
||||
});
|
||||
|
||||
const value = await (await page.find('ion-datetime')).getProperty('value');
|
||||
|
||||
expect(value).toMatch('2021-12-25T12:40:00');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
generateMonths,
|
||||
getDaysOfWeek,
|
||||
generateTime
|
||||
generateTime,
|
||||
getToday
|
||||
} from '../utils/data';
|
||||
|
||||
describe('generateMonths()', () => {
|
||||
@@ -65,10 +66,10 @@ describe('generateTime()', () => {
|
||||
hour: 2,
|
||||
minute: 40
|
||||
}
|
||||
const { hours, minutes } = generateTime(today, false, min);
|
||||
const { hours, minutes } = generateTime(today, 'h12', min);
|
||||
|
||||
expect(hours.length).toEqual(11);
|
||||
expect(minutes.length).toEqual(20);
|
||||
expect(minutes.length).toEqual(60);
|
||||
})
|
||||
it('should not filter according to min if not on reference day', () => {
|
||||
const today = {
|
||||
@@ -95,7 +96,7 @@ describe('generateTime()', () => {
|
||||
day: 19,
|
||||
month: 5,
|
||||
year: 2021,
|
||||
hour: 5,
|
||||
hour: 7,
|
||||
minute: 43
|
||||
}
|
||||
const max = {
|
||||
@@ -203,9 +204,153 @@ describe('generateTime()', () => {
|
||||
minute: 43
|
||||
}
|
||||
|
||||
const { hours, minutes, use24Hour } = generateTime(today, 'h12', undefined, undefined, [1,2,3], [10,15,20]);
|
||||
const { hours, minutes, use24Hour } = generateTime(today, 'h12', undefined, undefined, [1, 2, 3], [10, 15, 20]);
|
||||
|
||||
expect(hours).toStrictEqual([1, 2, 3]);
|
||||
expect(minutes).toStrictEqual([10, 15, 20]);
|
||||
})
|
||||
|
||||
describe('hourCycle is 23', () => {
|
||||
|
||||
it('should return hours in 24 hour format', () => {
|
||||
const refValue = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50
|
||||
}
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50
|
||||
};
|
||||
|
||||
const { hours } = generateTime(refValue, 'h23', minParts);
|
||||
|
||||
expect(hours).toStrictEqual([19, 20, 21, 22, 23]);
|
||||
});
|
||||
|
||||
describe('current hour is above min hour range', () => {
|
||||
it('should return minutes above the min minute range', () => {
|
||||
const refValue = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 22
|
||||
}
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30
|
||||
};
|
||||
|
||||
const { hours, minutes } = generateTime(refValue, 'h23', minParts);
|
||||
|
||||
expect(hours).toStrictEqual([19, 20, 21, 22, 23]);
|
||||
expect(minutes.length).toEqual(60);
|
||||
});
|
||||
});
|
||||
|
||||
it('should respect the min & max bounds', () => {
|
||||
const refValue = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 30
|
||||
}
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30
|
||||
}
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 40
|
||||
};
|
||||
|
||||
const { hours } = generateTime(refValue, 'h23', minParts, maxParts);
|
||||
|
||||
expect(hours).toStrictEqual([19, 20]);
|
||||
});
|
||||
|
||||
it('should return the filtered minutes when the max bound is set', () => {
|
||||
const refValue = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 0
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2
|
||||
};
|
||||
|
||||
const { minutes } = generateTime(refValue, 'h23', undefined, maxParts);
|
||||
|
||||
expect(minutes).toStrictEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
it('should not filter minutes when the current hour is less than the max hour bound', () => {
|
||||
const refValue = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 12,
|
||||
minute: 0
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
month: undefined,
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2
|
||||
};
|
||||
|
||||
const { minutes } = generateTime(refValue, 'h23', undefined, maxParts);
|
||||
|
||||
expect(minutes.length).toEqual(60);
|
||||
});
|
||||
|
||||
expect(hours).toStrictEqual([1,2,3]);
|
||||
expect(minutes).toStrictEqual([10,15,20]);
|
||||
})
|
||||
})
|
||||
|
||||
describe('getToday', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers('modern');
|
||||
// System time is zero based, 1 = February
|
||||
jest.setSystemTime(new Date(2022, 1, 21));
|
||||
});
|
||||
|
||||
it('should return today', () => {
|
||||
const res = getToday();
|
||||
|
||||
const expected = new Date();
|
||||
expected.setHours(expected.getHours() - (expected.getTimezoneOffset() / 60));
|
||||
|
||||
expect(res).toEqual('2022-02-21T00:00:00.000Z');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -52,3 +52,41 @@ test('display', async () => {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
test('month selection should work after changing presentation', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/display?ionic:_testing=true'
|
||||
});
|
||||
const ionWorkingPartsDidChange = await page.spyOnEvent('ionWorkingPartsDidChange', 'document');
|
||||
let calendarMonthYear;
|
||||
|
||||
await page.select('#presentation', 'date-time');
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.select('#presentation', 'time-date');
|
||||
await page.waitForChanges();
|
||||
|
||||
const nextMonthButton = await page.find('ion-datetime >>> .calendar-next-prev ion-button + ion-button');
|
||||
await nextMonthButton.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await ionWorkingPartsDidChange.next();
|
||||
|
||||
calendarMonthYear = await page.find('ion-datetime >>> .calendar-month-year');
|
||||
|
||||
expect(calendarMonthYear.textContent).toContain('March 2022');
|
||||
|
||||
// ensure it still works if presentation is changed more than once
|
||||
await page.select('#presentation', 'date-time');
|
||||
await page.waitForChanges();
|
||||
|
||||
const prevMonthButton = await page.find('ion-datetime >>> .calendar-next-prev ion-button:first-child');
|
||||
await prevMonthButton.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await ionWorkingPartsDidChange.next();
|
||||
|
||||
calendarMonthYear = await page.find('ion-datetime >>> .calendar-month-year');
|
||||
|
||||
expect(calendarMonthYear.textContent).toContain('February 2022');
|
||||
});
|
||||
|
||||
@@ -1,52 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Datetime - Standalone</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
}
|
||||
ion-datetime {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<label for="presentation">Presentation</label>
|
||||
<select id="presentation" onchange="changePresentation(event)">
|
||||
<option value="date-time" selected>date-time</option>
|
||||
<option value="time-date">time-date</option>
|
||||
<option value="date">date</option>
|
||||
<option value="time">time</option>
|
||||
</select>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Datetime - Standalone</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
ion-datetime {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<label for="presentation">Presentation</label>
|
||||
<select id="presentation" onchange="changePresentation(event)">
|
||||
<option value="date-time" selected>date-time</option>
|
||||
<option value="time-date">time-date</option>
|
||||
<option value="date">date</option>
|
||||
<option value="time">time</option>
|
||||
</select>
|
||||
|
||||
|
||||
<label for="size">Size</label>
|
||||
<select id="size" onchange="changeSize(event)">
|
||||
<option value="fixed" selected>fixed</option>
|
||||
<option value="cover">cover</option>
|
||||
</select>
|
||||
<label for="size">Size</label>
|
||||
<select id="size" onchange="changeSize(event)">
|
||||
<option value="fixed" selected>fixed</option>
|
||||
<option value="cover">cover</option>
|
||||
</select>
|
||||
|
||||
<br /><br />
|
||||
<br /><br />
|
||||
|
||||
<ion-datetime></ion-datetime>
|
||||
<ion-datetime value="2022-02-22"></ion-datetime>
|
||||
|
||||
<script>
|
||||
const datetime = document.querySelector('ion-datetime');
|
||||
<script>
|
||||
const datetime = document.querySelector('ion-datetime');
|
||||
|
||||
const mutationObserver = new MutationObserver(() => {
|
||||
document.dispatchEvent(new CustomEvent('ionWorkingPartsDidChange'));
|
||||
});
|
||||
|
||||
const initCalendarMonthChangeObserver = async () => {
|
||||
if (!datetime.componentOnReady) return;
|
||||
await datetime.componentOnReady();
|
||||
|
||||
// We have to requestAnimationFrame to allow the datetime to render completely.
|
||||
requestAnimationFrame(() => {
|
||||
const calendarBody = datetime.shadowRoot.querySelector('.calendar-body');
|
||||
if (calendarBody) {
|
||||
mutationObserver.observe(calendarBody, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const changePresentation = (ev) => {
|
||||
mutationObserver.disconnect();
|
||||
datetime.presentation = ev.target.value;
|
||||
initCalendarMonthChangeObserver();
|
||||
};
|
||||
|
||||
const changeSize = (ev) => {
|
||||
datetime.size = ev.target.value;
|
||||
};
|
||||
|
||||
initCalendarMonthChangeObserver();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
const changePresentation = (ev) => {
|
||||
datetime.presentation = ev.target.value;
|
||||
}
|
||||
const changeSize = (ev) => {
|
||||
datetime.size = ev.target.value;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -27,6 +27,11 @@ describe('generateDayAriaLabel()', () => {
|
||||
|
||||
expect(generateDayAriaLabel('en-US', false, reference)).toEqual('Monday, May 31');
|
||||
});
|
||||
it('should return Saturday, April 1', () => {
|
||||
const reference = { month: 4, day: 1, year: 2006 };
|
||||
|
||||
expect(generateDayAriaLabel('en-US', false, reference)).toEqual('Saturday, April 1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMonthAndDay()', () => {
|
||||
@@ -37,6 +42,14 @@ describe('getMonthAndDay()', () => {
|
||||
it('should return mar, 11 may', () => {
|
||||
expect(getMonthAndDay('es-ES', { month: 5, day: 11, year: 2021 })).toEqual('mar, 11 may');
|
||||
});
|
||||
|
||||
it('should return Sat, Apr 1', () => {
|
||||
expect(getMonthAndDay('en-US', { month: 4, day: 1, year: 2006 })).toEqual('Sat, Apr 1');
|
||||
});
|
||||
|
||||
it('should return sáb, 1 abr', () => {
|
||||
expect(getMonthAndDay('es-ES', { month: 4, day: 1, year: 2006 })).toEqual('sáb, 1 abr');
|
||||
});
|
||||
})
|
||||
|
||||
describe('getFormattedHour()', () => {
|
||||
@@ -63,7 +76,15 @@ describe('getMonthAndYear()', () => {
|
||||
expect(getMonthAndYear('en-US', { month: 5, day: 11, year: 2021 })).toEqual('May 2021');
|
||||
});
|
||||
|
||||
it('should return mar, 11 may', () => {
|
||||
it('should return mayo de 2021', () => {
|
||||
expect(getMonthAndYear('es-ES', { month: 5, day: 11, year: 2021 })).toEqual('mayo de 2021');
|
||||
});
|
||||
|
||||
it('should return April 2006', () => {
|
||||
expect(getMonthAndYear('en-US', { month: 4, day: 1, year: 2006 })).toEqual('April 2006');
|
||||
});
|
||||
|
||||
it('should return abril de 2006', () => {
|
||||
expect(getMonthAndYear('es-ES', { month: 4, day: 1, year: 2006 })).toEqual('abril de 2006');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
isLeapYear,
|
||||
getNumDaysInMonth,
|
||||
is24Hour
|
||||
is24Hour,
|
||||
isMonthFirstLocale
|
||||
} from '../utils/helpers';
|
||||
|
||||
describe('daysInMonth()', () => {
|
||||
@@ -51,3 +52,18 @@ describe('is24Hour()', () => {
|
||||
expect(is24Hour('en-GB-u-hc-h12')).toBe(false);
|
||||
})
|
||||
})
|
||||
|
||||
describe('isMonthFirstLocale()', () => {
|
||||
it('should return true if the locale shows months first', () => {
|
||||
expect(isMonthFirstLocale('en-US')).toBe(true);
|
||||
expect(isMonthFirstLocale('en-GB')).toBe(true);
|
||||
expect(isMonthFirstLocale('es-ES')).toBe(true);
|
||||
expect(isMonthFirstLocale('ro-RO')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the locale shows years first', () => {
|
||||
expect(isMonthFirstLocale('zh-CN')).toBe(false);
|
||||
expect(isMonthFirstLocale('ja-JP')).toBe(false);
|
||||
expect(isMonthFirstLocale('ko-KR')).toBe(false);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -19,3 +19,53 @@ test('locale', async () => {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
test('it should render month and year with an en-US locale', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/locale?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const screenshotCompares = [];
|
||||
const datetime = await page.find('ion-datetime');
|
||||
|
||||
datetime.setProperty('locale', 'en-US');
|
||||
await page.waitForChanges();
|
||||
|
||||
const button = await page.find('ion-datetime >>> .calendar-month-year ion-item');
|
||||
await button.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
const yearBody = await page.find('ion-datetime >>> .datetime-year-body');
|
||||
expect(yearBody).toHaveClass('order-month-first');
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
for (const screenshotCompare of screenshotCompares) {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
test('it should render year and month with a ja-JP locale', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/locale?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const screenshotCompares = [];
|
||||
const datetime = await page.find('ion-datetime');
|
||||
|
||||
datetime.setProperty('locale', 'ja-JP');
|
||||
await page.waitForChanges();
|
||||
|
||||
const button = await page.find('ion-datetime >>> .calendar-month-year ion-item');
|
||||
await button.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
const yearBody = await page.find('ion-datetime >>> .datetime-year-body');
|
||||
expect(yearBody).toHaveClass('order-year-first');
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
for (const screenshotCompare of screenshotCompares) {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('minmax', async () => {
|
||||
test('datetime: minmax', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/minmax?ionic:_testing=true'
|
||||
});
|
||||
@@ -20,3 +20,44 @@ test('minmax', async () => {
|
||||
expect(screenshotCompare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
test('datetime: minmax months disabled', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/minmax?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const calendarMonths = await page.findAll('ion-datetime#inside >>> .calendar-month');
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(calendarMonths[0]).not.toHaveClass('calendar-month-disabled');
|
||||
expect(calendarMonths[1]).not.toHaveClass('calendar-month-disabled');
|
||||
expect(calendarMonths[2]).toHaveClass('calendar-month-disabled');
|
||||
|
||||
});
|
||||
|
||||
test('datetime: minmax navigation disabled', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/minmax?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const navButtons = await page.findAll('ion-datetime#outside >>> .calendar-next-prev ion-button');
|
||||
|
||||
expect(navButtons[0]).toHaveAttribute('disabled');
|
||||
expect(navButtons[1]).toHaveAttribute('disabled');
|
||||
|
||||
});
|
||||
|
||||
test('datetime: min including day should not disable month', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/datetime/test/minmax?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const calendarMonths = await page.findAll('ion-datetime#min-with-day >>> .calendar-month');
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(calendarMonths[0]).toHaveClass('calendar-month-disabled');
|
||||
expect(calendarMonths[1]).not.toHaveClass('calendar-month-disabled');
|
||||
expect(calendarMonths[2]).not.toHaveClass('calendar-month-disabled');
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user