Compare commits
43 Commits
FW-4506
...
liamdebeas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8e075172b | ||
|
|
60d79103fd | ||
|
|
27b4f86619 | ||
|
|
fe3c3d500a | ||
|
|
1705d064cc | ||
|
|
60303aad23 | ||
|
|
a3cd204f61 | ||
|
|
5c2a73b262 | ||
|
|
34417a5835 | ||
|
|
8be83f9f88 | ||
|
|
2e1261a52c | ||
|
|
3a35fdd2f5 | ||
|
|
bd27846b28 | ||
|
|
196a22eb74 | ||
|
|
01130e12e1 | ||
|
|
b833f0e826 | ||
|
|
7392b1cd4b | ||
|
|
93ed25693c | ||
|
|
f6a740dce5 | ||
|
|
9453132aa8 | ||
|
|
c07312e5ed | ||
|
|
388d19e04f | ||
|
|
adb01e2516 | ||
|
|
4f1b4cdc29 | ||
|
|
1a135ebd76 | ||
|
|
6a2be9fa3c | ||
|
|
9d57758e3e | ||
|
|
f143bd0a11 | ||
|
|
1b6f15dee1 | ||
|
|
9fad566175 | ||
|
|
6dcf9cadb3 | ||
|
|
9bb45b3772 | ||
|
|
78ce39f8c6 | ||
|
|
093c671e3e | ||
|
|
ed80b7f118 | ||
|
|
83f9ac0fac | ||
|
|
a000dd2c0b | ||
|
|
04d32b6d68 | ||
|
|
342511959a | ||
|
|
900267eb36 | ||
|
|
73b8bfde3f | ||
|
|
f0a5d2704c | ||
|
|
fbc9f53d35 |
2
.github/CODEOWNERS
vendored
@@ -11,6 +11,8 @@
|
||||
# In each subsection folders are ordered first by depth, then alphabetically.
|
||||
# This should make it easy to add new rules without breaking existing ones.
|
||||
|
||||
* @ionic-team/framework
|
||||
|
||||
# Frameworks
|
||||
|
||||
## Angular
|
||||
|
||||
@@ -8,7 +8,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
2
.github/workflows/build.yml
vendored
@@ -140,7 +140,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
apps: [ng14, ng15, ng16]
|
||||
apps: [ng14, ng15, ng16, ng17]
|
||||
needs: [build-angular, build-angular-server]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
6
.github/workflows/label.yml
vendored
@@ -11,9 +11,13 @@ on:
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@main
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/labeler@v4
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
|
||||
42
CHANGELOG.md
@@ -3,6 +3,48 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** date inputs render correctly in mobile safari ([#28495](https://github.com/ionic-team/ionic-framework/issues/28495)) ([b833f0e](https://github.com/ionic-team/ionic-framework/commit/b833f0e826ddd261230e2e29b70e2dc884d8cb04)), closes [#28494](https://github.com/ionic-team/ionic-framework/issues/28494)
|
||||
* **datetime:** allow disabling datetime with prefer-wheel ([#28511](https://github.com/ionic-team/ionic-framework/issues/28511)) ([01130e1](https://github.com/ionic-team/ionic-framework/commit/01130e12e1d73bbf558da9d4dffd7122822ff39c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** match MD spec on tablet ([#28501](https://github.com/ionic-team/ionic-framework/issues/28501)) ([6a2be9f](https://github.com/ionic-team/ionic-framework/commit/6a2be9fa3c12a893d98dc139a1575a6e7e3c7c26)), closes [#23977](https://github.com/ionic-team/ionic-framework/issues/23977)
|
||||
* **angular:** ng add @ionic/angular in standalone projects ([#28523](https://github.com/ionic-team/ionic-framework/issues/28523)) ([c07312e](https://github.com/ionic-team/ionic-framework/commit/c07312e5ed931f6f825ccf083c9dead9fa815843)), closes [#28514](https://github.com/ionic-team/ionic-framework/issues/28514)
|
||||
* **angular:** overlays are defined when using standalone controllers ([#28560](https://github.com/ionic-team/ionic-framework/issues/28560)) ([9453132](https://github.com/ionic-team/ionic-framework/commit/9453132aa8952b4adfa1326e61138b329e254f76)), closes [#28385](https://github.com/ionic-team/ionic-framework/issues/28385)
|
||||
* **datetime:** updating value with min scrolls to new value ([#28549](https://github.com/ionic-team/ionic-framework/issues/28549)) ([388d19e](https://github.com/ionic-team/ionic-framework/commit/388d19e04f83f85abd4602adb04cc71ac575764a)), closes [#28548](https://github.com/ionic-team/ionic-framework/issues/28548)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **accordion-group:** correct accordion is open on load ([#28510](https://github.com/ionic-team/ionic-framework/issues/28510)) ([a000dd2](https://github.com/ionic-team/ionic-framework/commit/a000dd2c0b65be8ab5b2ad19f2748fbca13d5085)), closes [#28506](https://github.com/ionic-team/ionic-framework/issues/28506)
|
||||
* **action-sheet:** adjust height for safe area with scrollable options ([#28504](https://github.com/ionic-team/ionic-framework/issues/28504)) ([900267e](https://github.com/ionic-team/ionic-framework/commit/900267eb36c36f2af63435f6b46acca52b3bdab7)), closes [#27777](https://github.com/ionic-team/ionic-framework/issues/27777)
|
||||
* **header:** collapsible large title does not flicker when collapse prop not reflected ([#28472](https://github.com/ionic-team/ionic-framework/issues/28472)) ([8227b0e](https://github.com/ionic-team/ionic-framework/commit/8227b0ee6d5250e122a34a83c644f8a74fbbafd5)), closes [#28466](https://github.com/ionic-team/ionic-framework/issues/28466)
|
||||
* **item-divider:** apply safe area to proper side regardless of direction ([#28420](https://github.com/ionic-team/ionic-framework/issues/28420)) ([4513e0c](https://github.com/ionic-team/ionic-framework/commit/4513e0c6b066d4990800c707e1d97f69c8fcfb0c))
|
||||
* **radio-group:** emit value change on componentDidLoad ([#28488](https://github.com/ionic-team/ionic-framework/issues/28488)) ([73b8bfd](https://github.com/ionic-team/ionic-framework/commit/73b8bfde3f060490958c10f58d0f68de80cb957f)), closes [#28356](https://github.com/ionic-team/ionic-framework/issues/28356)
|
||||
* **searchbar:** cancel icon aligns with back button ([#28478](https://github.com/ionic-team/ionic-framework/issues/28478)) ([c053fd9](https://github.com/ionic-team/ionic-framework/commit/c053fd9c68d9b1add1335db80be962215946a0b1)), closes [#28468](https://github.com/ionic-team/ionic-framework/issues/28468)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.4](https://github.com/ionic-team/ionic-framework/compare/v7.5.3...v7.5.4) (2023-11-08)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,49 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** date inputs render correctly in mobile safari ([#28495](https://github.com/ionic-team/ionic-framework/issues/28495)) ([b833f0e](https://github.com/ionic-team/ionic-framework/commit/b833f0e826ddd261230e2e29b70e2dc884d8cb04)), closes [#28494](https://github.com/ionic-team/ionic-framework/issues/28494)
|
||||
* **datetime:** allow disabling datetime with prefer-wheel ([#28511](https://github.com/ionic-team/ionic-framework/issues/28511)) ([01130e1](https://github.com/ionic-team/ionic-framework/commit/01130e12e1d73bbf558da9d4dffd7122822ff39c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** match MD spec on tablet ([#28501](https://github.com/ionic-team/ionic-framework/issues/28501)) ([6a2be9f](https://github.com/ionic-team/ionic-framework/commit/6a2be9fa3c12a893d98dc139a1575a6e7e3c7c26)), closes [#23977](https://github.com/ionic-team/ionic-framework/issues/23977)
|
||||
* **datetime:** updating value with min scrolls to new value ([#28549](https://github.com/ionic-team/ionic-framework/issues/28549)) ([388d19e](https://github.com/ionic-team/ionic-framework/commit/388d19e04f83f85abd4602adb04cc71ac575764a)), closes [#28548](https://github.com/ionic-team/ionic-framework/issues/28548)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **accordion-group:** correct accordion is open on load ([#28510](https://github.com/ionic-team/ionic-framework/issues/28510)) ([a000dd2](https://github.com/ionic-team/ionic-framework/commit/a000dd2c0b65be8ab5b2ad19f2748fbca13d5085)), closes [#28506](https://github.com/ionic-team/ionic-framework/issues/28506)
|
||||
* **action-sheet:** adjust height for safe area with scrollable options ([#28504](https://github.com/ionic-team/ionic-framework/issues/28504)) ([900267e](https://github.com/ionic-team/ionic-framework/commit/900267eb36c36f2af63435f6b46acca52b3bdab7)), closes [#27777](https://github.com/ionic-team/ionic-framework/issues/27777)
|
||||
* **header:** collapsible large title does not flicker when collapse prop not reflected ([#28472](https://github.com/ionic-team/ionic-framework/issues/28472)) ([8227b0e](https://github.com/ionic-team/ionic-framework/commit/8227b0ee6d5250e122a34a83c644f8a74fbbafd5)), closes [#28466](https://github.com/ionic-team/ionic-framework/issues/28466)
|
||||
* **item-divider:** apply safe area to proper side regardless of direction ([#28420](https://github.com/ionic-team/ionic-framework/issues/28420)) ([4513e0c](https://github.com/ionic-team/ionic-framework/commit/4513e0c6b066d4990800c707e1d97f69c8fcfb0c))
|
||||
* **radio-group:** emit value change on componentDidLoad ([#28488](https://github.com/ionic-team/ionic-framework/issues/28488)) ([73b8bfd](https://github.com/ionic-team/ionic-framework/commit/73b8bfde3f060490958c10f58d0f68de80cb957f)), closes [#28356](https://github.com/ionic-team/ionic-framework/issues/28356)
|
||||
* **searchbar:** cancel icon aligns with back button ([#28478](https://github.com/ionic-team/ionic-framework/issues/28478)) ([c053fd9](https://github.com/ionic-team/ionic-framework/commit/c053fd9c68d9b1add1335db80be962215946a0b1)), closes [#28468](https://github.com/ionic-team/ionic-framework/issues/28468)
|
||||
|
||||
> [!NOTE]
|
||||
> Ionic Vue developers utilizing the `v-ion-change` or `v-ion-input` workaround for https://github.com/ionic-team/ionic-framework/issues/27292 should remove this workaround when updating to Ionic v7.5.5.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.4](https://github.com/ionic-team/ionic-framework/compare/v7.5.3...v7.5.4) (2023-11-08)
|
||||
|
||||
|
||||
|
||||
1622
core/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "7.5.4",
|
||||
"version": "7.5.7",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -31,7 +31,7 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@stencil/core": "^4.7.1",
|
||||
"@stencil/core": "^4.8.0",
|
||||
"ionicons": "^7.2.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -49,7 +49,7 @@
|
||||
"@stencil/angular-output-target": "^0.8.3",
|
||||
"@stencil/react-output-target": "^0.5.3",
|
||||
"@stencil/sass": "^3.0.7",
|
||||
"@stencil/vue-output-target": "^0.8.6",
|
||||
"@stencil/vue-output-target": "^0.8.7",
|
||||
"@types/jest": "^29.5.6",
|
||||
"@types/node": "^14.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||
@@ -64,6 +64,7 @@
|
||||
"jest": "^29.7.0",
|
||||
"jest-cli": "^29.7.0",
|
||||
"prettier": "^2.6.1",
|
||||
"puppeteer": "21.1.1",
|
||||
"rollup": "^2.26.4",
|
||||
"sass": "^1.33.0",
|
||||
"serve": "^14.0.1",
|
||||
|
||||
151
core/scripts/testing/themes/dark.css
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
:root {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66, 140, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80, 200, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47, 223, 117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255, 213, 52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255, 73, 97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244, 245, 248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152, 154, 162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34, 36, 40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0, 0, 0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-toolbar-background: #0d0d0d;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18, 18, 18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
24
core/src/components.d.ts
vendored
@@ -1162,7 +1162,7 @@ export namespace Components {
|
||||
*/
|
||||
"autocorrect": 'on' | 'off';
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus": boolean;
|
||||
/**
|
||||
@@ -1274,7 +1274,7 @@ export namespace Components {
|
||||
*/
|
||||
"required": boolean;
|
||||
/**
|
||||
* Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved.
|
||||
* Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -2043,6 +2043,10 @@ export namespace Components {
|
||||
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* If `true`, the user cannot interact with the picker.
|
||||
*/
|
||||
"disabled": boolean;
|
||||
/**
|
||||
* A list of options to be displayed in the picker
|
||||
*/
|
||||
@@ -2407,7 +2411,6 @@ export namespace Components {
|
||||
interface IonReorder {
|
||||
}
|
||||
interface IonReorderGroup {
|
||||
"activate": 'tap' | 'press';
|
||||
/**
|
||||
* Completes the reorder operation. Must be called by the `ionItemReorder` event. If a list of items is passed, the list will be reordered and returned in the proper order. If no parameters are passed or if `true` is passed in, the reorder will complete and the item will remain in the position it was dragged to. If `false` is passed, the reorder will complete and the item will bounce back to its original position.
|
||||
* @param listOrReorder A list of items to be sorted and returned in the new order or a boolean of whether or not the reorder should reposition the item.
|
||||
@@ -2598,7 +2601,7 @@ export namespace Components {
|
||||
*/
|
||||
"searchIcon"?: string;
|
||||
/**
|
||||
* Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved.
|
||||
* Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -2947,7 +2950,7 @@ export namespace Components {
|
||||
*/
|
||||
"autocapitalize": string;
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus": boolean;
|
||||
/**
|
||||
@@ -3047,7 +3050,7 @@ export namespace Components {
|
||||
*/
|
||||
"rows"?: number;
|
||||
/**
|
||||
* Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`.
|
||||
* Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -5851,7 +5854,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autocorrect"?: 'on' | 'off';
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus"?: boolean;
|
||||
/**
|
||||
@@ -6684,6 +6687,10 @@ declare namespace LocalJSX {
|
||||
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* If `true`, the user cannot interact with the picker.
|
||||
*/
|
||||
"disabled"?: boolean;
|
||||
/**
|
||||
* A list of options to be displayed in the picker
|
||||
*/
|
||||
@@ -7105,7 +7112,6 @@ declare namespace LocalJSX {
|
||||
interface IonReorder {
|
||||
}
|
||||
interface IonReorderGroup {
|
||||
"activate"?: 'tap' | 'press';
|
||||
/**
|
||||
* If `true`, the reorder will be hidden.
|
||||
*/
|
||||
@@ -7693,7 +7699,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autocapitalize"?: string;
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus"?: boolean;
|
||||
/**
|
||||
|
||||
@@ -180,6 +180,16 @@ export class AccordionGroup implements ComponentInterface {
|
||||
if (this.readonly) {
|
||||
this.readonlyChanged();
|
||||
}
|
||||
/**
|
||||
* When binding values in frameworks such as Angular
|
||||
* it is possible for the value to be set after the Web Component
|
||||
* initializes but before the value watcher is set up in Stencil.
|
||||
* As a result, the watcher callback may not be fired.
|
||||
* We work around this by manually calling the watcher
|
||||
* callback when the component has loaded and the watcher
|
||||
* is configured.
|
||||
*/
|
||||
this.valueChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { AccordionGroup } from '../../accordion-group/accordion-group.tsx';
|
||||
import { Item } from '../../item/item.tsx';
|
||||
import { Accordion } from '../accordion.tsx';
|
||||
import { AccordionGroup } from '../../accordion-group/accordion-group';
|
||||
import { Item } from '../../item/item';
|
||||
import { Accordion } from '../accordion';
|
||||
|
||||
it('should open correct accordions when accordion group value is set', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -25,7 +25,7 @@ it('should open correct accordions when accordion group value is set', async ()
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
accordions.forEach((accordion) => {
|
||||
@@ -61,7 +61,7 @@ it('should open correct accordions when accordion value is set', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
accordions.forEach((accordion) => {
|
||||
@@ -97,7 +97,7 @@ it('should open more than one accordion when multiple="true"', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
accordions.forEach((accordion) => {
|
||||
@@ -133,7 +133,7 @@ it('should render with accordion open', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
expect(accordions[0].classList.contains('accordion-collapsed')).toEqual(false);
|
||||
@@ -162,7 +162,7 @@ it('should accept a string when multiple="true"', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
||||
|
||||
expect(accordions[0].classList.contains('accordion-collapsed')).toEqual(false);
|
||||
@@ -183,8 +183,8 @@ it('should set default values if not provided', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
||||
const accordion = accordionGroup.querySelector('ion-accordion');
|
||||
const accordionGroup = page.body.querySelector('ion-accordion-group')!;
|
||||
const accordion = accordionGroup.querySelector('ion-accordion')!;
|
||||
|
||||
/**
|
||||
* ID is determined via an auto incrementing counter
|
||||
|
||||
@@ -128,8 +128,8 @@
|
||||
height: 100%;
|
||||
|
||||
/* Fallback for browsers that do not support dvh */
|
||||
max-height: 100vh;
|
||||
max-height: 100dvh;
|
||||
max-height: calc(100vh - (var(--ion-safe-area-top, 0) + var(--ion-safe-area-bottom, 0)));
|
||||
max-height: calc(100dvh - (var(--ion-safe-area-top, 0) + var(--ion-safe-area-bottom, 0)));
|
||||
}
|
||||
|
||||
.action-sheet-group {
|
||||
|
||||
@@ -337,6 +337,17 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
|
||||
if (this.isOpen === true) {
|
||||
raf(() => this.present());
|
||||
}
|
||||
|
||||
/**
|
||||
* When binding values in frameworks such as Angular
|
||||
* it is possible for the value to be set after the Web Component
|
||||
* initializes but before the value watcher is set up in Stencil.
|
||||
* As a result, the watcher callback may not be fired.
|
||||
* We work around this by manually calling the watcher
|
||||
* callback when the component has loaded and the watcher
|
||||
* is configured.
|
||||
*/
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -100,3 +100,69 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ config, title }) =>
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior needs to be tested in both modes but does not vary
|
||||
* across directions due to the component only applying safe area
|
||||
* to the top and bottom
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('action-sheet: basic'), () => {
|
||||
test.describe('safe area', () => {
|
||||
test('should have padding added by the safe area', async ({ page }, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/27777',
|
||||
});
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
:root {
|
||||
--ion-safe-area-top: 60px;
|
||||
--ion-safe-area-bottom: 40px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<ion-action-sheet></ion-action-sheet>
|
||||
|
||||
<script>
|
||||
const actionSheet = document.querySelector('ion-action-sheet');
|
||||
actionSheet.header = 'Header';
|
||||
actionSheet.subHeader = 'Sub Header';
|
||||
actionSheet.buttons = [
|
||||
'Add Reaction',
|
||||
'Copy Text',
|
||||
'Share Text',
|
||||
'Copy Link to Message',
|
||||
'Remind Me',
|
||||
'Pin File',
|
||||
'Star File',
|
||||
'Mark Unread',
|
||||
'Mark Read',
|
||||
'Edit Title',
|
||||
'Erase Title',
|
||||
'Save Image',
|
||||
'Copy Image',
|
||||
'Erase Image',
|
||||
'Delete File',
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel'
|
||||
},
|
||||
];
|
||||
</script>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
|
||||
const actionSheet = page.locator('ion-action-sheet');
|
||||
|
||||
await actionSheet.evaluate((el: HTMLIonActionSheetElement) => el.present());
|
||||
await ionActionSheetDidPresent.next();
|
||||
|
||||
await expect(actionSheet).toHaveScreenshot(screenshot(`action-sheet-safe-area`));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -7,10 +7,17 @@ describe('action sheet: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [ActionSheet],
|
||||
template: () => <ion-action-sheet htmlAttributes={{ 'data-testid': 'basic-action-sheet' }}></ion-action-sheet>,
|
||||
template: () => (
|
||||
<ion-action-sheet
|
||||
htmlAttributes={{
|
||||
'data-testid': 'basic-action-sheet',
|
||||
}}
|
||||
overlayIndex={1}
|
||||
></ion-action-sheet>
|
||||
),
|
||||
});
|
||||
|
||||
const actionSheet = page.body.querySelector('ion-action-sheet');
|
||||
const actionSheet = page.body.querySelector('ion-action-sheet')!;
|
||||
|
||||
await expect(actionSheet.getAttribute('data-testid')).toBe('basic-action-sheet');
|
||||
});
|
||||
|
||||
@@ -105,6 +105,17 @@
|
||||
&::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-date-and-time-value {
|
||||
/**
|
||||
* The -webkit-date-and-time-value pseudo element is used
|
||||
* to style the date/time input on iOS/Mobile Safari.
|
||||
* To avoid layout shift between an empty state and a selected state,
|
||||
* we set the height `18px` to match the native input height for
|
||||
* date/time inputs on iOS/Mobile Safari.
|
||||
*/
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -52,11 +52,20 @@
|
||||
}
|
||||
|
||||
.alert-message {
|
||||
max-height: $alert-md-content-max-height;
|
||||
|
||||
font-size: $alert-md-message-font-size;
|
||||
}
|
||||
|
||||
/**
|
||||
* MD Alerts on tablets can expand vertically up to
|
||||
* a total maximum height. We only want to set a max-height
|
||||
* on mobile phones.
|
||||
*/
|
||||
@include mobile-viewport() {
|
||||
.alert-message {
|
||||
max-height: $alert-md-content-max-height;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-message:empty {
|
||||
@include padding($alert-md-message-empty-padding-top, $alert-md-message-empty-padding-end, $alert-md-message-empty-padding-bottom, $alert-md-message-empty-padding-start);
|
||||
}
|
||||
@@ -102,14 +111,24 @@
|
||||
.alert-checkbox-group {
|
||||
position: relative;
|
||||
|
||||
max-height: $alert-md-content-max-height;
|
||||
|
||||
border-top: $alert-md-list-border-top;
|
||||
border-bottom: $alert-md-list-border-bottom;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* MD Alerts on tablets can expand vertically up to
|
||||
* a total maximum height. We only want to set a max-height
|
||||
* on mobile phones.
|
||||
*/
|
||||
@include mobile-viewport() {
|
||||
.alert-radio-group,
|
||||
.alert-checkbox-group {
|
||||
max-height: $alert-md-content-max-height;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-tappable {
|
||||
position: relative;
|
||||
|
||||
@@ -282,3 +301,14 @@
|
||||
.alert-button-inner {
|
||||
justify-content: $alert-md-button-group-justify-content;
|
||||
}
|
||||
|
||||
/**
|
||||
* MD alerts should scale up to 560px x 560px
|
||||
* on tablet dimensions.
|
||||
*/
|
||||
@include tablet-viewport() {
|
||||
:host {
|
||||
--max-width: #{$alert-md-max-width-tablet};
|
||||
--max-height: #{$alert-md-max-height-tablet};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,20 @@ $alert-md-font-size: dynamic-font(14px) !default;
|
||||
/// @prop - Max width of the alert
|
||||
$alert-md-max-width: 280px !default;
|
||||
|
||||
/// @prop - Max width of the alert on a tablet
|
||||
/**
|
||||
* Large display requirements for MD Alert:
|
||||
* 1. Maintain a minimum of 48px distance from the leading and
|
||||
* trailing edges of the screen. (48px * 2 = 96px)
|
||||
* 2. The width can increase up to 560px.
|
||||
* 3. The height can increase up to 560px.
|
||||
* Source: https://m2.material.io/components/dialogs#behavior
|
||||
*/
|
||||
$alert-md-max-width-tablet: min(calc(100vw - 96px), 560px) !default;
|
||||
|
||||
/// @prop - Max width of the alert on a tablet
|
||||
$alert-md-max-height-tablet: min(calc(100vh - 96px), 560px) !default;
|
||||
|
||||
/// @prop - Border radius of the alert
|
||||
$alert-md-border-radius: 4px !default;
|
||||
|
||||
|
||||
@@ -84,7 +84,15 @@
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.alert-message {
|
||||
/**
|
||||
* Alert has a maximum height in scenarios
|
||||
* such as the MD alert on tablet devices.
|
||||
* As a result, we need to make sure the inner
|
||||
* containers can scroll otherwise content
|
||||
* may be cut off.
|
||||
*/
|
||||
.alert-message,
|
||||
.alert-input-group {
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -376,6 +376,17 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
if (this.isOpen === true) {
|
||||
raf(() => this.present());
|
||||
}
|
||||
|
||||
/**
|
||||
* When binding values in frameworks such as Angular
|
||||
* it is possible for the value to be set after the Web Component
|
||||
* initializes but before the value watcher is set up in Stencil.
|
||||
* As a result, the watcher callback may not be fired.
|
||||
* We work around this by manually calling the watcher
|
||||
* callback when the component has loaded and the watcher
|
||||
* is configured.
|
||||
*/
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { Alert } from '../alert';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { Alert } from '../alert';
|
||||
|
||||
describe('alert: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -9,7 +10,7 @@ describe('alert: custom html', () => {
|
||||
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.alert-message');
|
||||
const content = page.body.querySelector('.alert-message')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -21,7 +22,7 @@ describe('alert: custom html', () => {
|
||||
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.alert-message');
|
||||
const content = page.body.querySelector('.alert-message')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).not.toBe(null);
|
||||
});
|
||||
@@ -33,7 +34,7 @@ describe('alert: custom html', () => {
|
||||
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.alert-message');
|
||||
const content = page.body.querySelector('.alert-message')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
|
||||
47
core/src/components/alert/test/basic/alert-tablet.e2e.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test, Viewports } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* This behavior does not vary across directions.
|
||||
*/
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('alert: rendering - tablet'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setViewportSize(Viewports.tablet.portrait);
|
||||
await page.goto('/src/components/alert/test/basic', config);
|
||||
});
|
||||
|
||||
test('should expand width and height on larger displays with text', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#longMessage');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await button.click();
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-tablet-text'));
|
||||
});
|
||||
|
||||
test('should expand width and height on larger displays with checkboxes', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#checkbox');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await button.click();
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-tablet-checkboxes'));
|
||||
});
|
||||
|
||||
test('should expand width and height on larger displays with radios', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#radio');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await button.click();
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-tablet-radios'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
@@ -1,7 +1,7 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Breadcrumb } from '../../breadcrumb/breadcrumb.tsx';
|
||||
import { Breadcrumbs } from '../breadcrumbs.tsx';
|
||||
import { Breadcrumb } from '../../breadcrumb/breadcrumb';
|
||||
import { Breadcrumbs } from '../breadcrumbs';
|
||||
|
||||
it('should correctly provide the collapsed breadcrumbs in the event payload', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -18,8 +18,8 @@ it('should correctly provide the collapsed breadcrumbs in the event payload', as
|
||||
});
|
||||
|
||||
const onCollapsedClick = jest.fn((ev) => ev);
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs');
|
||||
const breadcrumb = page.body.querySelectorAll('ion-breadcrumb');
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs')!;
|
||||
const breadcrumb = page.body.querySelectorAll('ion-breadcrumb')!;
|
||||
|
||||
breadcrumbs.addEventListener('ionCollapsedClick', onCollapsedClick);
|
||||
|
||||
@@ -46,8 +46,8 @@ it('should exclude the separator from narrators', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const firstBreadcrumb = page.body.querySelector('ion-breadcrumb:first-of-type');
|
||||
const separator = firstBreadcrumb.shadowRoot.querySelector('[part="separator"]');
|
||||
const firstBreadcrumb = page.body.querySelector('ion-breadcrumb:first-of-type')!;
|
||||
const separator = firstBreadcrumb.shadowRoot!.querySelector('[part="separator"]')!;
|
||||
|
||||
expect(separator.getAttribute('aria-hidden')).toBe('true');
|
||||
});
|
||||
@@ -62,7 +62,7 @@ it('should have color attribute', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs');
|
||||
const breadcrumbs = page.body.querySelector('ion-breadcrumbs')!;
|
||||
|
||||
expect(breadcrumbs.hasAttribute('color')).toBe(true);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Button } from '../../button';
|
||||
|
||||
describe('Button: Hidden Form Button', () => {
|
||||
@@ -15,8 +16,7 @@ describe('Button: Hidden Form Button', () => {
|
||||
return page.body.querySelectorAll('form button');
|
||||
};
|
||||
|
||||
const form = page.body.querySelectorAll('form');
|
||||
const button = page.body.querySelector('ion-button');
|
||||
const button = page.body.querySelector('ion-button')!;
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('ion-checkbox: disabled', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const checkbox = page.body.querySelector('ion-checkbox');
|
||||
const checkbox = page.body.querySelector('ion-checkbox')!;
|
||||
|
||||
expect(checkbox.checked).toBe(false);
|
||||
|
||||
|
||||
@@ -136,7 +136,3 @@
|
||||
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
:host .datetime-view-buttons ion-button {
|
||||
color: $text-color-step-200;
|
||||
}
|
||||
|
||||
@@ -1132,7 +1132,7 @@ export class Datetime implements ComponentInterface {
|
||||
* so we need to re-init behavior with the new elements.
|
||||
*/
|
||||
componentDidRender() {
|
||||
const { presentation, prevPresentation, calendarBodyRef, minParts, preferWheel } = this;
|
||||
const { presentation, prevPresentation, calendarBodyRef, minParts, preferWheel, forceRenderDate } = this;
|
||||
|
||||
/**
|
||||
* TODO(FW-2165)
|
||||
@@ -1150,7 +1150,20 @@ export class Datetime implements ComponentInterface {
|
||||
const hasCalendarGrid = !preferWheel && ['date-time', 'time-date', 'date'].includes(presentation);
|
||||
if (minParts !== undefined && hasCalendarGrid && calendarBodyRef) {
|
||||
const workingMonth = calendarBodyRef.querySelector('.calendar-month:nth-of-type(1)');
|
||||
if (workingMonth) {
|
||||
/**
|
||||
* We need to make sure the datetime is not in the process
|
||||
* of scrolling to a new datetime value if the value
|
||||
* is updated programmatically.
|
||||
* Otherwise, the datetime will appear to not scroll at all because
|
||||
* we are resetting the scroll position to the center of the view.
|
||||
* Prior to the datetime's value being updated programmatically,
|
||||
* the calendarBodyRef is scrolled such that the middle month is centered
|
||||
* in the view. The below code updates the scroll position so the middle
|
||||
* month is also centered in the view. Since the scroll position did not change,
|
||||
* the scroll callback in this file does not fire,
|
||||
* and the resolveForceDateScrolling promise never resolves.
|
||||
*/
|
||||
if (workingMonth && forceRenderDate === undefined) {
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
}
|
||||
}
|
||||
@@ -1525,7 +1538,7 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderCombinedDatePickerColumn() {
|
||||
const { defaultParts, workingParts, locale, minParts, maxParts, todayParts, isDateEnabled } = this;
|
||||
const { defaultParts, disabled, workingParts, locale, minParts, maxParts, todayParts, isDateEnabled } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1604,6 +1617,7 @@ export class Datetime implements ComponentInterface {
|
||||
<ion-picker-column-internal
|
||||
class="date-column"
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
items={items}
|
||||
value={todayString}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
@@ -1715,7 +1729,7 @@ export class Datetime implements ComponentInterface {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { workingParts } = this;
|
||||
const { disabled, workingParts } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1723,6 +1737,7 @@ export class Datetime implements ComponentInterface {
|
||||
<ion-picker-column-internal
|
||||
class="day-column"
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
items={days}
|
||||
value={(workingParts.day !== null ? workingParts.day : this.defaultParts.day) ?? undefined}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
@@ -1759,7 +1774,7 @@ export class Datetime implements ComponentInterface {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { workingParts } = this;
|
||||
const { disabled, workingParts } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1767,6 +1782,7 @@ export class Datetime implements ComponentInterface {
|
||||
<ion-picker-column-internal
|
||||
class="month-column"
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
items={months}
|
||||
value={workingParts.month}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
@@ -1802,7 +1818,7 @@ export class Datetime implements ComponentInterface {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { workingParts } = this;
|
||||
const { disabled, workingParts } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1810,6 +1826,7 @@ export class Datetime implements ComponentInterface {
|
||||
<ion-picker-column-internal
|
||||
class="year-column"
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
items={years}
|
||||
value={workingParts.year}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
@@ -1875,7 +1892,7 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderHourPickerColumn(hoursData: PickerColumnItem[]) {
|
||||
const { workingParts } = this;
|
||||
const { disabled, workingParts } = this;
|
||||
if (hoursData.length === 0) return [];
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
@@ -1883,6 +1900,7 @@ export class Datetime implements ComponentInterface {
|
||||
return (
|
||||
<ion-picker-column-internal
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
value={activePart.hour}
|
||||
items={hoursData}
|
||||
numericInput
|
||||
@@ -1903,7 +1921,7 @@ export class Datetime implements ComponentInterface {
|
||||
);
|
||||
}
|
||||
private renderMinutePickerColumn(minutesData: PickerColumnItem[]) {
|
||||
const { workingParts } = this;
|
||||
const { disabled, workingParts } = this;
|
||||
if (minutesData.length === 0) return [];
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
@@ -1911,6 +1929,7 @@ export class Datetime implements ComponentInterface {
|
||||
return (
|
||||
<ion-picker-column-internal
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
value={activePart.minute}
|
||||
items={minutesData}
|
||||
numericInput
|
||||
@@ -1931,7 +1950,7 @@ export class Datetime implements ComponentInterface {
|
||||
);
|
||||
}
|
||||
private renderDayPeriodPickerColumn(dayPeriodData: PickerColumnItem[]) {
|
||||
const { workingParts } = this;
|
||||
const { disabled, workingParts } = this;
|
||||
if (dayPeriodData.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -1943,6 +1962,7 @@ export class Datetime implements ComponentInterface {
|
||||
<ion-picker-column-internal
|
||||
style={isDayPeriodRTL ? { order: '-1' } : {}}
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
value={activePart.ampm}
|
||||
items={dayPeriodData}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import { isSameDay, isBefore, isAfter } from '../utils/comparison';
|
||||
|
||||
describe('isSameDay()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference = { month: 1, day: 1, year: 2021 };
|
||||
const reference: DatetimeParts = { month: 1, day: 1, year: 2021 };
|
||||
|
||||
expect(isSameDay(reference, { month: 1, day: 1, year: 2021 })).toEqual(true);
|
||||
expect(isSameDay(reference, { month: 2, day: 1, year: 2021 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: 1, day: 2, year: 2021 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: 1, day: 1, year: 2022 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: 0, day: 0, year: 0 })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: null, day: null, year: null })).toEqual(false);
|
||||
expect(isSameDay(reference, { month: null, day: null, year: null } as any)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBefore()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference = { month: 1, day: 1, year: 2021 };
|
||||
const reference: DatetimeParts = { month: 1, day: 1, year: 2021 };
|
||||
|
||||
expect(isBefore(reference, { month: 1, day: 1, year: 2021 })).toEqual(false);
|
||||
expect(isBefore(reference, { month: 2, day: 1, year: 2021 })).toEqual(true);
|
||||
@@ -23,13 +24,13 @@ describe('isBefore()', () => {
|
||||
expect(isBefore(reference, { month: 1, day: 1, year: 2022 })).toEqual(true);
|
||||
expect(isBefore(reference, { month: 1, day: 1, year: 2020 })).toEqual(false);
|
||||
expect(isBefore(reference, { month: 0, day: 0, year: 0 })).toEqual(false);
|
||||
expect(isBefore(reference, { month: null, day: null, year: null })).toEqual(false);
|
||||
expect(isBefore(reference, { month: null, day: null, year: null } as any)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAfter()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference = { month: 2, day: 2, year: 2021 };
|
||||
const reference: DatetimeParts = { month: 2, day: 2, year: 2021 };
|
||||
|
||||
expect(isAfter(reference, { month: 2, day: 2, year: 2021 })).toEqual(false);
|
||||
expect(isAfter(reference, { month: 2, day: 1, year: 2021 })).toEqual(true);
|
||||
@@ -42,6 +43,6 @@ describe('isAfter()', () => {
|
||||
* 2021 > undefined === false
|
||||
* 2021 > null === true
|
||||
*/
|
||||
expect(isAfter(reference, { month: null, day: null, year: null })).toEqual(true);
|
||||
expect(isAfter(reference, { month: null, day: null, year: null } as any)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
generateMonths,
|
||||
getDaysOfWeek,
|
||||
@@ -364,7 +365,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -372,7 +373,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const { hours } = generateTime('en-US', refValue, 'h23', minParts);
|
||||
|
||||
@@ -387,7 +388,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 22,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -395,7 +396,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const { hours, minutes } = generateTime('en-US', refValue, 'h23', minParts);
|
||||
|
||||
@@ -411,7 +412,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 30,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -419,7 +420,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -427,7 +428,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 40,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const { hours } = generateTime('en-US', refValue, 'h23', minParts, maxParts);
|
||||
|
||||
@@ -441,7 +442,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 0,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -449,7 +450,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
|
||||
|
||||
@@ -463,7 +464,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 12,
|
||||
minute: 0,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -471,7 +472,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2,
|
||||
};
|
||||
} as unknown as DatetimeParts;
|
||||
|
||||
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
|
||||
|
||||
@@ -482,7 +483,7 @@ describe('generateTime()', () => {
|
||||
|
||||
describe('getToday', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers('modern');
|
||||
jest.useFakeTimers();
|
||||
// System time is zero based, 1 = February
|
||||
jest.setSystemTime(new Date(2022, 1, 21, 18, 30));
|
||||
});
|
||||
|
||||
39
core/src/components/datetime/test/disabled/datetime.spec.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { h } from '@stencil/core';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Datetime } from '../../../datetime/datetime';
|
||||
import { PickerColumnInternal } from '../../../picker-column-internal/picker-column-internal';
|
||||
import { PickerInternal } from '../../../picker-internal/picker-internal';
|
||||
|
||||
describe('ion-datetime disabled', () => {
|
||||
beforeEach(() => {
|
||||
// IntersectionObserver isn't available in test environment
|
||||
const mockIntersectionObserver = jest.fn();
|
||||
mockIntersectionObserver.mockReturnValue({
|
||||
observe: () => null,
|
||||
unobserve: () => null,
|
||||
disconnect: () => null,
|
||||
});
|
||||
global.IntersectionObserver = mockIntersectionObserver;
|
||||
});
|
||||
|
||||
it('picker should be disabled in prefer wheel mode', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Datetime, PickerColumnInternal, PickerInternal],
|
||||
template: () => (
|
||||
<ion-datetime id="inline-datetime-wheel" disabled prefer-wheel value="2022-04-21T00:00:00"></ion-datetime>
|
||||
),
|
||||
});
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
const datetime = page.body.querySelector('ion-datetime')!;
|
||||
const columns = datetime.shadowRoot!.querySelectorAll('ion-picker-column-internal');
|
||||
|
||||
await expect(columns.length).toEqual(4);
|
||||
|
||||
columns.forEach((column) => {
|
||||
expect(column.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -66,6 +66,11 @@
|
||||
<h2>Inline - No Default Value</h2>
|
||||
<ion-datetime id="inline-datetime-no-value" disabled></ion-datetime>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Inline - Prefer Wheel</h2>
|
||||
<ion-datetime id="inline-datetime-wheel" disabled prefer-wheel value="2022-04-21T00:00:00"></ion-datetime>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
<script>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
generateDayAriaLabel,
|
||||
getMonthAndDay,
|
||||
@@ -109,7 +110,7 @@ describe('getLocalizedDayPeriod', () => {
|
||||
|
||||
describe('getLocalizedTime', () => {
|
||||
it('should localize the time to PM', () => {
|
||||
const datetimeParts = {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -121,7 +122,7 @@ describe('getLocalizedTime', () => {
|
||||
});
|
||||
|
||||
it('should localize the time to AM', () => {
|
||||
const datetimeParts = {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -133,7 +134,7 @@ describe('getLocalizedTime', () => {
|
||||
});
|
||||
|
||||
it('should avoid Chromium bug when using 12 hour time in a 24 hour locale', () => {
|
||||
const datetimeParts = {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -144,12 +145,12 @@ describe('getLocalizedTime', () => {
|
||||
expect(getLocalizedTime('en-GB', datetimeParts, 'h12')).toEqual('12:00 am');
|
||||
});
|
||||
it('should parse time-only values correctly', () => {
|
||||
const datetimeParts = {
|
||||
const datetimeParts: Partial<DatetimeParts> = {
|
||||
hour: 22,
|
||||
minute: 40,
|
||||
};
|
||||
|
||||
expect(getLocalizedTime('en-US', datetimeParts, 'h12')).toEqual('10:40 PM');
|
||||
expect(getLocalizedTime('en-US', datetimeParts, 'h23')).toEqual('22:40');
|
||||
expect(getLocalizedTime('en-US', datetimeParts as DatetimeParts, 'h12')).toEqual('10:40 PM');
|
||||
expect(getLocalizedTime('en-US', datetimeParts as DatetimeParts, 'h23')).toEqual('22:40');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
getPreviousYear,
|
||||
getNextYear,
|
||||
@@ -103,31 +104,31 @@ describe('getInternalHourValue()', () => {
|
||||
|
||||
describe('calculateHourFromAMPM()', () => {
|
||||
it('should correctly convert from AM to PM', () => {
|
||||
expect(calculateHourFromAMPM({ hour: 12, ampm: 'am' }, 'pm')).toEqual(12);
|
||||
expect(calculateHourFromAMPM({ hour: 1, ampm: 'am' }, 'pm')).toEqual(13);
|
||||
expect(calculateHourFromAMPM({ hour: 2, ampm: 'am' }, 'pm')).toEqual(14);
|
||||
expect(calculateHourFromAMPM({ hour: 3, ampm: 'am' }, 'pm')).toEqual(15);
|
||||
expect(calculateHourFromAMPM({ hour: 4, ampm: 'am' }, 'pm')).toEqual(16);
|
||||
expect(calculateHourFromAMPM({ hour: 5, ampm: 'am' }, 'pm')).toEqual(17);
|
||||
expect(calculateHourFromAMPM({ hour: 6, ampm: 'am' }, 'pm')).toEqual(18);
|
||||
expect(calculateHourFromAMPM({ hour: 7, ampm: 'am' }, 'pm')).toEqual(19);
|
||||
expect(calculateHourFromAMPM({ hour: 8, ampm: 'am' }, 'pm')).toEqual(20);
|
||||
expect(calculateHourFromAMPM({ hour: 9, ampm: 'am' }, 'pm')).toEqual(21);
|
||||
expect(calculateHourFromAMPM({ hour: 10, ampm: 'am' }, 'pm')).toEqual(22);
|
||||
expect(calculateHourFromAMPM({ hour: 11, ampm: 'am' }, 'pm')).toEqual(23);
|
||||
expect(calculateHourFromAMPM({ hour: 12, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(12);
|
||||
expect(calculateHourFromAMPM({ hour: 1, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(13);
|
||||
expect(calculateHourFromAMPM({ hour: 2, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(14);
|
||||
expect(calculateHourFromAMPM({ hour: 3, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(15);
|
||||
expect(calculateHourFromAMPM({ hour: 4, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(16);
|
||||
expect(calculateHourFromAMPM({ hour: 5, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(17);
|
||||
expect(calculateHourFromAMPM({ hour: 6, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(18);
|
||||
expect(calculateHourFromAMPM({ hour: 7, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(19);
|
||||
expect(calculateHourFromAMPM({ hour: 8, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(20);
|
||||
expect(calculateHourFromAMPM({ hour: 9, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(21);
|
||||
expect(calculateHourFromAMPM({ hour: 10, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(22);
|
||||
expect(calculateHourFromAMPM({ hour: 11, ampm: 'am' } as DatetimeParts, 'pm')).toEqual(23);
|
||||
|
||||
expect(calculateHourFromAMPM({ hour: 13, ampm: 'pm' }, 'am')).toEqual(1);
|
||||
expect(calculateHourFromAMPM({ hour: 14, ampm: 'pm' }, 'am')).toEqual(2);
|
||||
expect(calculateHourFromAMPM({ hour: 15, ampm: 'pm' }, 'am')).toEqual(3);
|
||||
expect(calculateHourFromAMPM({ hour: 16, ampm: 'pm' }, 'am')).toEqual(4);
|
||||
expect(calculateHourFromAMPM({ hour: 17, ampm: 'pm' }, 'am')).toEqual(5);
|
||||
expect(calculateHourFromAMPM({ hour: 18, ampm: 'pm' }, 'am')).toEqual(6);
|
||||
expect(calculateHourFromAMPM({ hour: 19, ampm: 'pm' }, 'am')).toEqual(7);
|
||||
expect(calculateHourFromAMPM({ hour: 20, ampm: 'pm' }, 'am')).toEqual(8);
|
||||
expect(calculateHourFromAMPM({ hour: 21, ampm: 'pm' }, 'am')).toEqual(9);
|
||||
expect(calculateHourFromAMPM({ hour: 22, ampm: 'pm' }, 'am')).toEqual(10);
|
||||
expect(calculateHourFromAMPM({ hour: 23, ampm: 'pm' }, 'am')).toEqual(11);
|
||||
expect(calculateHourFromAMPM({ hour: 0, ampm: 'pm' }, 'am')).toEqual(12);
|
||||
expect(calculateHourFromAMPM({ hour: 13, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(1);
|
||||
expect(calculateHourFromAMPM({ hour: 14, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(2);
|
||||
expect(calculateHourFromAMPM({ hour: 15, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(3);
|
||||
expect(calculateHourFromAMPM({ hour: 16, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(4);
|
||||
expect(calculateHourFromAMPM({ hour: 17, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(5);
|
||||
expect(calculateHourFromAMPM({ hour: 18, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(6);
|
||||
expect(calculateHourFromAMPM({ hour: 19, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(7);
|
||||
expect(calculateHourFromAMPM({ hour: 20, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(8);
|
||||
expect(calculateHourFromAMPM({ hour: 21, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(9);
|
||||
expect(calculateHourFromAMPM({ hour: 22, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(10);
|
||||
expect(calculateHourFromAMPM({ hour: 23, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(11);
|
||||
expect(calculateHourFromAMPM({ hour: 0, ampm: 'pm' } as DatetimeParts, 'am')).toEqual(12);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ describe('parseDate()', () => {
|
||||
* See https://github.com/ionic-team/ionic-framework/commit/3fb4caf21ffac12f765c4c80bf1850e05d211c6a
|
||||
*/
|
||||
it('should return the correct time zone offset', () => {
|
||||
expect(parseDate('2022-12-15T13:47:30-02:00').tzOffset).toEqual(undefined);
|
||||
// Casting as any since `tzOffset` does not exist on DatetimeParts
|
||||
expect((parseDate('2022-12-15T13:47:30-02:00') as any)?.tzOffset).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should parse an array of dates', () => {
|
||||
@@ -162,8 +163,8 @@ describe('parseMinParts()', () => {
|
||||
minute: 4,
|
||||
hour: 2,
|
||||
};
|
||||
expect(parseMinParts(undefined, today)).toEqual(undefined);
|
||||
expect(parseMinParts(null, today)).toEqual(undefined);
|
||||
expect(parseMinParts(undefined as any, today)).toEqual(undefined);
|
||||
expect(parseMinParts(null as any, today)).toEqual(undefined);
|
||||
expect(parseMinParts('foo', today)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
@@ -225,8 +226,8 @@ describe('parseMaxParts()', () => {
|
||||
minute: 4,
|
||||
hour: 2,
|
||||
};
|
||||
expect(parseMaxParts(undefined, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(null, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(undefined as any, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(null as any, today)).toEqual(undefined);
|
||||
expect(parseMaxParts('foo', today)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,13 +84,13 @@ describe('isPrevMonthDisabled()', () => {
|
||||
// Date month and year is the same as min month and year
|
||||
expect(isPrevMonthDisabled({ month: 1, year: 2021, day: null }, { month: 1, year: 2021, day: null })).toEqual(true);
|
||||
// Date year is the same as min year (month not provided)
|
||||
expect(isPrevMonthDisabled({ month: 1, year: 2021, day: null }, { year: 2021, month: null, day: null })).toEqual(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 1, year: 2021, day: null }, { year: 2021, month: null, day: null } as any)
|
||||
).toEqual(true);
|
||||
// Date year is less than the min year (month not provided)
|
||||
expect(isPrevMonthDisabled({ month: 5, year: 2021, day: null }, { year: 2022, month: null, day: null })).toEqual(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 5, year: 2021, day: null }, { year: 2022, month: null, day: null } as any)
|
||||
).toEqual(true);
|
||||
|
||||
// Date is above the maximum bounds and the previous month does not does not fall within the
|
||||
// min-max range.
|
||||
@@ -118,12 +118,12 @@ describe('isPrevMonthDisabled()', () => {
|
||||
expect(isPrevMonthDisabled({ month: 12, year: 2021, day: null })).toEqual(false);
|
||||
// Date year is the same as min year,
|
||||
// but can navigate to a previous month without reducing the year.
|
||||
expect(isPrevMonthDisabled({ month: 12, year: 2021, day: null }, { year: 2021, month: null, day: null })).toEqual(
|
||||
false
|
||||
);
|
||||
expect(isPrevMonthDisabled({ month: 2, year: 2021, day: null }, { year: 2021, month: null, day: null })).toEqual(
|
||||
false
|
||||
);
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 12, year: 2021, day: null }, { year: 2021, month: null, day: null } as any)
|
||||
).toEqual(false);
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 2, year: 2021, day: null }, { year: 2021, month: null, day: null } as any)
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { InfiniteScrollContent } from '../infinite-scroll-content';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { InfiniteScrollContent } from '../infinite-scroll-content';
|
||||
|
||||
describe('infinite-scroll-content: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -9,7 +10,7 @@ describe('infinite-scroll-content: custom html', () => {
|
||||
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text</button>"></ion-infinite-scroll-content>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.infinite-loading-text');
|
||||
const content = page.body.querySelector('.infinite-loading-text')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -21,7 +22,7 @@ describe('infinite-scroll-content: custom html', () => {
|
||||
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text</button>"></ion-infinite-scroll-content>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.infinite-loading-text');
|
||||
const content = page.body.querySelector('.infinite-loading-text')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).not.toBe(null);
|
||||
});
|
||||
@@ -33,7 +34,7 @@ describe('infinite-scroll-content: custom html', () => {
|
||||
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text2</button>"></ion-infinite-scroll-content>`,
|
||||
});
|
||||
|
||||
const content = page.body.querySelector('.infinite-loading-text');
|
||||
const content = page.body.querySelector('.infinite-loading-text')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
|
||||
@@ -95,7 +95,9 @@ export class Input implements ComponentInterface {
|
||||
@Prop() autocorrect: 'on' | 'off' = 'off';
|
||||
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element.
|
||||
*
|
||||
* This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
@Prop() autofocus = false;
|
||||
|
||||
@@ -424,6 +426,8 @@ export class Input implements ComponentInterface {
|
||||
*
|
||||
* Developers who wish to focus an input when an overlay is presented
|
||||
* should call `setFocus` after `didPresent` has resolved.
|
||||
*
|
||||
* See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
@Method()
|
||||
async setFocus() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Input } from '../input';
|
||||
|
||||
describe('input: rendering', () => {
|
||||
@@ -8,7 +9,7 @@ describe('input: rendering', () => {
|
||||
html: '<ion-input title="my title" tabindex="-1" data-form-type="password"></ion-input>',
|
||||
});
|
||||
|
||||
const nativeEl = page.body.querySelector('ion-input input');
|
||||
const nativeEl = page.body.querySelector('ion-input input')!;
|
||||
expect(nativeEl.getAttribute('title')).toBe('my title');
|
||||
expect(nativeEl.getAttribute('tabindex')).toBe('-1');
|
||||
expect(nativeEl.getAttribute('data-form-type')).toBe('password');
|
||||
@@ -63,9 +64,9 @@ describe('input: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input');
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
|
||||
const labelText = input.querySelector('.label-text-wrapper');
|
||||
const labelText = input.querySelector('.label-text-wrapper')!;
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
@@ -77,9 +78,9 @@ describe('input: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input');
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
|
||||
const labelText = input.querySelector('.label-text-wrapper');
|
||||
const labelText = input.querySelector('.label-text-wrapper')!;
|
||||
|
||||
expect(labelText.textContent).toBe('Label Slot Text');
|
||||
});
|
||||
@@ -91,9 +92,9 @@ describe('input: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input');
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
|
||||
const labelText = input.querySelector('.label-text-wrapper');
|
||||
const labelText = input.querySelector('.label-text-wrapper')!;
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { Input } from '../../input';
|
||||
|
||||
import { Item } from '../../../item/item';
|
||||
import { Input } from '../../input';
|
||||
|
||||
it('should render as modern when label is set asynchronously', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -12,7 +13,7 @@ it('should render as modern when label is set asynchronously', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const input = page.body.querySelector('ion-input');
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
|
||||
// Template should be modern
|
||||
expect(input.classList.contains('legacy-input')).toBe(false);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Radio } from '../../../radio/radio.tsx';
|
||||
import { RadioGroup } from '../../../radio-group/radio-group.tsx';
|
||||
import { Item } from '../../item.tsx';
|
||||
import { List } from '../../../list/list.tsx';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { List } from '../../../list/list';
|
||||
import { RadioGroup } from '../../../radio-group/radio-group';
|
||||
import { Radio } from '../../../radio/radio';
|
||||
import { Item } from '../../item';
|
||||
|
||||
describe('ion-item', () => {
|
||||
it('should not have a role when used without list', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -11,7 +12,7 @@ describe('ion-item', () => {
|
||||
html: `<ion-item>Hello World</ion-item>`,
|
||||
});
|
||||
|
||||
const item = page.body.querySelector('ion-item');
|
||||
const item = page.body.querySelector('ion-item')!;
|
||||
expect(item.getAttribute('role')).toBe(null);
|
||||
});
|
||||
|
||||
@@ -27,7 +28,7 @@ describe('ion-item', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const item = page.body.querySelector('ion-item');
|
||||
const item = page.body.querySelector('ion-item')!;
|
||||
expect(item.getAttribute('role')).toBe('listitem');
|
||||
});
|
||||
|
||||
@@ -45,7 +46,7 @@ describe('ion-item', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const item = page.body.querySelector('ion-item');
|
||||
const item = page.body.querySelector('ion-item')!;
|
||||
expect(item.getAttribute('role')).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -225,6 +225,17 @@ export class Loading implements ComponentInterface, OverlayInterface {
|
||||
if (this.isOpen === true) {
|
||||
raf(() => this.present());
|
||||
}
|
||||
|
||||
/**
|
||||
* When binding values in frameworks such as Angular
|
||||
* it is possible for the value to be set after the Web Component
|
||||
* initializes but before the value watcher is set up in Stencil.
|
||||
* As a result, the watcher callback may not be fired.
|
||||
* We work around this by manually calling the watcher
|
||||
* callback when the component has loaded and the watcher
|
||||
* is configured.
|
||||
*/
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
|
||||
@@ -7,10 +7,10 @@ describe('loading: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Loading],
|
||||
template: () => <ion-loading htmlAttributes={{ 'data-testid': 'basic-loading' }}></ion-loading>,
|
||||
template: () => <ion-loading overlayIndex={1} htmlAttributes={{ 'data-testid': 'basic-loading' }}></ion-loading>,
|
||||
});
|
||||
|
||||
const loading = page.body.querySelector('ion-loading');
|
||||
const loading = page.body.querySelector('ion-loading')!;
|
||||
|
||||
await expect(loading.getAttribute('data-testid')).toBe('basic-loading');
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
// iOS Card Modal
|
||||
// --------------------------------------------------
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
@include mobile-viewport() {
|
||||
@supports (width: max(0px, 1px)) {
|
||||
:host(.modal-card) {
|
||||
--height: calc(100% - max(30px, var(--ion-safe-area-top)) - 10px);
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
@include tablet-viewport() {
|
||||
:host(.modal-card) {
|
||||
--width: calc(100% - 120px);
|
||||
--height: calc(100% - (120px + var(--ion-safe-area-top) + var(--ion-safe-area-bottom)));
|
||||
|
||||
@@ -368,6 +368,17 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
raf(() => this.present());
|
||||
}
|
||||
this.breakpointsChanged(this.breakpoints);
|
||||
|
||||
/**
|
||||
* When binding values in frameworks such as Angular
|
||||
* it is possible for the value to be set after the Web Component
|
||||
* initializes but before the value watcher is set up in Stencil.
|
||||
* As a result, the watcher callback may not be fired.
|
||||
* We work around this by manually calling the watcher
|
||||
* callback when the component has loaded and the watcher
|
||||
* is configured.
|
||||
*/
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,8 @@ describe('modal: a11y', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modalWrapper = modal.shadowRoot.querySelector('.modal-wrapper');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
const modalWrapper = modal.shadowRoot!.querySelector('.modal-wrapper')!;
|
||||
|
||||
await expect(modalWrapper.getAttribute('role')).toBe('alertdialog');
|
||||
});
|
||||
|
||||
@@ -7,10 +7,10 @@ describe('modal: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal htmlAttributes={{ 'data-testid': 'basic-modal' }}></ion-modal>,
|
||||
template: () => <ion-modal htmlAttributes={{ 'data-testid': 'basic-modal' }} overlayIndex={1}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await expect(modal.getAttribute('data-testid')).toBe('basic-modal');
|
||||
});
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { h } from '@stencil/core';
|
||||
import { h, setMode } from '@stencil/core';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { setMode } from '@stencil/core';
|
||||
|
||||
import { Modal } from '../../modal';
|
||||
import { Content } from '../../../content/content';
|
||||
import { Modal } from '../../modal';
|
||||
|
||||
describe('modal: canDismiss', () => {
|
||||
describe('modal: regular modal', () => {
|
||||
it('should dismiss when canDismiss is true', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal animated={false} canDismiss={true}></ion-modal>,
|
||||
template: () => <ion-modal overlayIndex={1} animated={false} canDismiss={true}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -26,10 +25,10 @@ describe('modal: canDismiss', () => {
|
||||
it('should not dismiss when canDismiss is false', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal animated={false} canDismiss={false}></ion-modal>,
|
||||
template: () => <ion-modal overlayIndex={1} animated={false} canDismiss={false}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -44,6 +43,7 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -54,7 +54,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -69,6 +69,7 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -79,7 +80,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -94,19 +95,24 @@ describe('modal: canDismiss', () => {
|
||||
/**
|
||||
* Card modal is only available on iOS
|
||||
*/
|
||||
setMode((elm) => 'ios');
|
||||
setMode(() => 'ios');
|
||||
});
|
||||
it('should dismiss when canDismiss is true', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal presentingElement={document.createElement('div')} animated={false} canDismiss={true}>
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={true}
|
||||
>
|
||||
<ion-content>Test Content</ion-content>
|
||||
</ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -120,13 +126,18 @@ describe('modal: canDismiss', () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal presentingElement={document.createElement('div')} animated={false} canDismiss={false}>
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={false}
|
||||
>
|
||||
<ion-content>Test Content</ion-content>
|
||||
</ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -141,6 +152,7 @@ describe('modal: canDismiss', () => {
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
@@ -154,7 +166,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -169,6 +181,7 @@ describe('modal: canDismiss', () => {
|
||||
components: [Content, Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
presentingElement={document.createElement('div')}
|
||||
animated={false}
|
||||
canDismiss={() => {
|
||||
@@ -182,7 +195,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -197,11 +210,17 @@ describe('modal: canDismiss', () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal breakpoints={[0, 1]} initialBreakpoint={1} animated={false} canDismiss={true}></ion-modal>
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
canDismiss={true}
|
||||
></ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -215,11 +234,17 @@ describe('modal: canDismiss', () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal breakpoints={[0, 1]} initialBreakpoint={1} animated={false} canDismiss={false}></ion-modal>
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
canDismiss={false}
|
||||
></ion-modal>
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -234,6 +259,7 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
@@ -246,7 +272,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -261,6 +287,7 @@ describe('modal: canDismiss', () => {
|
||||
components: [Modal],
|
||||
template: () => (
|
||||
<ion-modal
|
||||
overlayIndex={1}
|
||||
breakpoints={[0, 1]}
|
||||
initialBreakpoint={1}
|
||||
animated={false}
|
||||
@@ -273,7 +300,7 @@ describe('modal: canDismiss', () => {
|
||||
),
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
@@ -288,15 +315,15 @@ describe('modal: canDismiss', () => {
|
||||
const canDismiss = jest.fn();
|
||||
const page = await newSpecPage({
|
||||
components: [Modal],
|
||||
template: () => <ion-modal animated={false} canDismiss={canDismiss}></ion-modal>,
|
||||
template: () => <ion-modal overlayIndex={1} animated={false} canDismiss={canDismiss}></ion-modal>,
|
||||
});
|
||||
|
||||
const modal = page.body.querySelector('ion-modal');
|
||||
const modal = page.body.querySelector('ion-modal')!;
|
||||
|
||||
await modal.present();
|
||||
await page.waitForChanges();
|
||||
|
||||
const returnValue = await modal.dismiss('my data', 'my role');
|
||||
await modal.dismiss('my data', 'my role');
|
||||
|
||||
expect(canDismiss).toHaveBeenCalledWith('my data', 'my role');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Config } from '../../../global/config';
|
||||
import type { ComponentProps } from '../../../interface';
|
||||
import { Nav } from '../nav';
|
||||
import type { NavOptions } from '../nav-interface';
|
||||
@@ -197,7 +196,6 @@ describe('NavController', () => {
|
||||
.insert(-1, null as any, null, null, trnsDone)
|
||||
.then(() => {
|
||||
fail('it should not succeed');
|
||||
done();
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
const hasCompleted = false;
|
||||
@@ -252,7 +250,6 @@ describe('NavController', () => {
|
||||
.pop(null, trnsDone)
|
||||
.then(() => {
|
||||
fail('it should not succeed');
|
||||
done();
|
||||
})
|
||||
.catch((err: any) => {
|
||||
const hasCompleted = false;
|
||||
@@ -819,15 +816,10 @@ describe('NavController', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
trnsDone = jest.fn();
|
||||
const config = new Config();
|
||||
config.reset({ animated: false });
|
||||
const page = await newSpecPage({
|
||||
components: [Nav],
|
||||
html: `<ion-nav></ion-nav>`,
|
||||
autoApplyChanges: true,
|
||||
context: {
|
||||
config,
|
||||
},
|
||||
});
|
||||
nav = page.rootInstance;
|
||||
});
|
||||
@@ -848,7 +840,7 @@ describe('NavController', () => {
|
||||
pause: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
onfinish: undefined,
|
||||
};
|
||||
} as any;
|
||||
|
||||
animation.play = () => {
|
||||
if (animation.onfinish) {
|
||||
|
||||
@@ -71,13 +71,20 @@
|
||||
}
|
||||
|
||||
:host .picker-item-empty,
|
||||
:host .picker-item.picker-item-disabled {
|
||||
scroll-snap-align: none;
|
||||
|
||||
:host .picker-item[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:host .picker-item.picker-item-disabled {
|
||||
:host .picker-item-empty,
|
||||
:host(:not([disabled])) .picker-item[disabled] {
|
||||
scroll-snap-align: none;
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
:host .picker-item[disabled] {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,11 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
|
||||
@Element() el!: HTMLIonPickerColumnInternalElement;
|
||||
|
||||
/**
|
||||
* If `true`, the user cannot interact with the picker.
|
||||
*/
|
||||
@Prop() disabled = false;
|
||||
|
||||
/**
|
||||
* A list of options to be displayed in the picker
|
||||
*/
|
||||
@@ -408,13 +413,15 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
};
|
||||
|
||||
get activeItem() {
|
||||
return getElementRoot(this.el).querySelector(
|
||||
`.picker-item[data-value="${this.value}"]:not([disabled])`
|
||||
) as HTMLElement | null;
|
||||
// If the whole picker column is disabled, the current value should appear active
|
||||
// If the current value item is specifically disabled, it should not appear active
|
||||
const selector = `.picker-item[data-value="${this.value}"]${this.disabled ? '' : ':not([disabled])'}`;
|
||||
|
||||
return getElementRoot(this.el).querySelector(selector) as HTMLElement | null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { items, color, isActive, numericInput } = this;
|
||||
const { items, color, disabled: pickerDisabled, isActive, numericInput } = this;
|
||||
const mode = getIonMode(this);
|
||||
|
||||
/**
|
||||
@@ -423,10 +430,12 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
* the attribute can be moved to datetime.tsx and set on every
|
||||
* instance of ion-picker-column-internal there instead.
|
||||
*/
|
||||
|
||||
return (
|
||||
<Host
|
||||
exportparts={`${PICKER_ITEM_PART}, ${PICKER_ITEM_ACTIVE_PART}`}
|
||||
tabindex={0}
|
||||
disabled={pickerDisabled}
|
||||
tabindex={pickerDisabled ? null : 0}
|
||||
class={createColorClasses(color, {
|
||||
[mode]: true,
|
||||
['picker-column-active']: isActive,
|
||||
@@ -443,6 +452,8 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
|
||||
</div>
|
||||
{items.map((item, index) => {
|
||||
const isItemDisabled = pickerDisabled || item.disabled || false;
|
||||
|
||||
{
|
||||
/*
|
||||
Users should be able to tab
|
||||
@@ -458,14 +469,13 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
tabindex="-1"
|
||||
class={{
|
||||
'picker-item': true,
|
||||
'picker-item-disabled': item.disabled || false,
|
||||
}}
|
||||
data-value={item.value}
|
||||
data-index={index}
|
||||
onClick={(ev: Event) => {
|
||||
this.centerPickerItemInView(ev.target as HTMLElement, true);
|
||||
}}
|
||||
disabled={item.disabled}
|
||||
disabled={isItemDisabled}
|
||||
part={PICKER_ITEM_PART}
|
||||
>
|
||||
{item.text}
|
||||
|
||||
@@ -45,26 +45,39 @@
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Default</h2>
|
||||
<h2>Even items disabled</h2>
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal id="default"></ion-picker-column-internal>
|
||||
<ion-picker-column-internal id="half-disabled"></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Column disabled</h2>
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal id="column-disabled" value="11" disabled></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
<script>
|
||||
const defaultPickerColumn = document.getElementById('default');
|
||||
|
||||
const items = Array(24)
|
||||
const halfDisabledPicker = document.getElementById('half-disabled');
|
||||
const halfDisabledItems = Array(24)
|
||||
.fill()
|
||||
.map((_, i) => ({
|
||||
text: `${i}`,
|
||||
value: i,
|
||||
disabled: i % 2 === 0,
|
||||
}));
|
||||
halfDisabledPicker.items = halfDisabledItems;
|
||||
halfDisabledPicker.value = 12;
|
||||
|
||||
defaultPickerColumn.items = items;
|
||||
defaultPickerColumn.value = 12;
|
||||
const fullDisabledPicker = document.getElementById('column-disabled');
|
||||
const items = Array(24)
|
||||
.fill()
|
||||
.map((_, i) => ({
|
||||
text: `${i}`,
|
||||
value: i,
|
||||
}));
|
||||
fullDisabledPicker.items = items;
|
||||
</script>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
@@ -35,7 +35,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
* This behavior does not vary across modes/directions.
|
||||
*/
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('picker-column-internal: disabled'), () => {
|
||||
test.describe(title('picker-column-internal: disabled items'), () => {
|
||||
test('all picker items should be enabled by default', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
@@ -55,9 +55,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
||||
config
|
||||
);
|
||||
|
||||
const pickerItems = page.locator(
|
||||
'ion-picker-column-internal .picker-item:not(.picker-item-empty, .picker-item-disabled)'
|
||||
);
|
||||
const pickerItems = page.locator('ion-picker-column-internal .picker-item:not(.picker-item-empty, [disabled])');
|
||||
|
||||
expect(await pickerItems.count()).toBe(3);
|
||||
});
|
||||
@@ -80,7 +78,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
||||
config
|
||||
);
|
||||
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item.picker-item-disabled');
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item[disabled]');
|
||||
await expect(disabledItem).not.toBeEnabled();
|
||||
});
|
||||
test('disabled picker item should not be considered active', async ({ page }) => {
|
||||
@@ -130,7 +128,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
||||
await page.waitForChanges();
|
||||
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item[data-value="b"]');
|
||||
await expect(disabledItem).toHaveClass(/picker-item-disabled/);
|
||||
await expect(disabledItem).toBeDisabled();
|
||||
await expect(disabledItem).not.toHaveClass(/picker-item-active/);
|
||||
});
|
||||
test('defaulting the value to a disabled item should not cause that item to be active', async ({ page }) => {
|
||||
@@ -154,8 +152,42 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
||||
);
|
||||
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item[data-value="b"]');
|
||||
await expect(disabledItem).toHaveClass(/picker-item-disabled/);
|
||||
await expect(disabledItem).toBeDisabled();
|
||||
await expect(disabledItem).not.toHaveClass(/picker-item-active/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior does not vary across directions.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('picker-column-internal: disabled column rendering'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/picker-column-internal/test/disabled', config);
|
||||
});
|
||||
|
||||
test('disabled column should not have visual regressions', async ({ page }) => {
|
||||
const disabledColumn = page.locator('#column-disabled');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(disabledColumn).toHaveScreenshot(screenshot('picker-internal-disabled-column'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior does not vary across modes/directions.
|
||||
*/
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('picker-column-internal: disabled column'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/picker-column-internal/test/disabled', config);
|
||||
});
|
||||
|
||||
test('item in disabled column should not be interactive', async ({ page }) => {
|
||||
const secondItem = page.locator('#column-disabled .picker-item:not(.picker-item-empty)').nth(1);
|
||||
await expect(secondItem).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -19,6 +19,7 @@ describe('picker-column', () => {
|
||||
text: 'Java',
|
||||
},
|
||||
],
|
||||
name: 'programmingLanguages',
|
||||
};
|
||||
|
||||
const page = await newSpecPage({
|
||||
@@ -26,8 +27,8 @@ describe('picker-column', () => {
|
||||
template: () => <ion-picker-column col={col}></ion-picker-column>,
|
||||
});
|
||||
|
||||
const firstOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(1)');
|
||||
const secondOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(2)');
|
||||
const firstOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(1)')!;
|
||||
const secondOption = page.body.querySelector('ion-picker-column .picker-opt:nth-child(2)')!;
|
||||
|
||||
expect(firstOption.getAttribute('aria-label')).toBe('C Sharp');
|
||||
expect(secondOption.getAttribute('aria-label')).toBe(null);
|
||||
|
||||
@@ -15,18 +15,19 @@ describe('picker-column: dynamic options', () => {
|
||||
|
||||
const page = await newSpecPage({
|
||||
components: [PickerColumnCmp],
|
||||
template: () => <ion-picker-column col={{ options: defaultOptions }}></ion-picker-column>,
|
||||
template: () => <ion-picker-column col={{ options: defaultOptions, name: 'animals' }}></ion-picker-column>,
|
||||
});
|
||||
|
||||
const pickerCol = page.body.querySelector('ion-picker-column');
|
||||
const pickerCol = page.body.querySelector('ion-picker-column')!;
|
||||
|
||||
pickerCol.col = {
|
||||
options: [...defaultOptions, { text: 'Carrot', value: 'carrot' }],
|
||||
name: 'vegetables',
|
||||
};
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
const pickerOpt = pickerCol.querySelector('.picker-opt:nth(2)');
|
||||
const pickerOpt = pickerCol.querySelector('.picker-opt:nth(2)')!;
|
||||
expect(pickerOpt.getAttribute('style')).toContain('transform');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,14 +5,14 @@ import { PickerColumnCmp } from '../picker-column';
|
||||
|
||||
describe('picker-column', () => {
|
||||
it('should add class to host of component', async () => {
|
||||
const col = { cssClass: 'test-class', options: [] };
|
||||
const col = { cssClass: 'test-class', options: [], name: 'col' };
|
||||
|
||||
const page = await newSpecPage({
|
||||
components: [PickerColumnCmp],
|
||||
template: () => <ion-picker-column col={col}></ion-picker-column>,
|
||||
});
|
||||
|
||||
const pickerCol = page.body.querySelector('ion-picker-column');
|
||||
const pickerCol = page.body.querySelector('ion-picker-column')!;
|
||||
expect(pickerCol.classList.contains('test-class')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -209,6 +209,17 @@ export class Picker implements ComponentInterface, OverlayInterface {
|
||||
if (this.isOpen === true) {
|
||||
raf(() => this.present());
|
||||
}
|
||||
|
||||
/**
|
||||
* When binding values in frameworks such as Angular
|
||||
* it is possible for the value to be set after the Web Component
|
||||
* initializes but before the value watcher is set up in Stencil.
|
||||
* As a result, the watcher callback may not be fired.
|
||||
* We work around this by manually calling the watcher
|
||||
* callback when the component has loaded and the watcher
|
||||
* is configured.
|
||||
*/
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -370,6 +370,17 @@ export class Popover implements ComponentInterface, PopoverInterface {
|
||||
this.dismiss(undefined, undefined, false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When binding values in frameworks such as Angular
|
||||
* it is possible for the value to be set after the Web Component
|
||||
* initializes but before the value watcher is set up in Stencil.
|
||||
* As a result, the watcher callback may not be fired.
|
||||
* We work around this by manually calling the watcher
|
||||
* callback when the component has loaded and the watcher
|
||||
* is configured.
|
||||
*/
|
||||
this.configureTriggerInteraction();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,10 +7,10 @@ describe('popover: htmlAttributes inheritance', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Popover],
|
||||
template: () => <ion-popover htmlAttributes={{ 'data-testid': 'basic-popover' }}></ion-popover>,
|
||||
template: () => <ion-popover overlayIndex={1} htmlAttributes={{ 'data-testid': 'basic-popover' }}></ion-popover>,
|
||||
});
|
||||
|
||||
const popover = page.body.querySelector('ion-popover');
|
||||
const popover = page.body.querySelector('ion-popover')!;
|
||||
|
||||
await expect(popover.getAttribute('data-testid')).toBe('basic-popover');
|
||||
});
|
||||
|
||||
@@ -17,20 +17,20 @@ describe('isTriggerElement', () => {
|
||||
|
||||
describe('getIndexOfItem', () => {
|
||||
it('should return the correct index in an array of ion-items', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
|
||||
expect(getIndexOfItem(array, array[1])).toEqual(1);
|
||||
});
|
||||
|
||||
it('should return -1 when ion-item not found', () => {
|
||||
const el = document.createElement('ion-item');
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item']);
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
|
||||
expect(getIndexOfItem(array, el)).toEqual(-1);
|
||||
});
|
||||
|
||||
it('should return -1 if a non-ion-item is passed in', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'div', 'ion-item']);
|
||||
const array = createArrayOfElements(['ion-item', 'div', 'ion-item']) as HTMLIonItemElement[];
|
||||
|
||||
expect(getIndexOfItem(array, array[1])).toEqual(-1);
|
||||
});
|
||||
@@ -38,24 +38,24 @@ describe('getIndexOfItem', () => {
|
||||
|
||||
describe('getNextItem', () => {
|
||||
it('should get the next item in an array of ion-items', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
expect(getNextItem(array, array[1])).toEqual(array[2]);
|
||||
});
|
||||
|
||||
it('should return undefined if there is no next item', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
expect(getNextItem(array, array[2])).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPrevItem', () => {
|
||||
it('should get the previous item in an array of ion-items', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
expect(getPrevItem(array, array[1])).toEqual(array[0]);
|
||||
});
|
||||
|
||||
it('should return undefined if there is no previous item', () => {
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']);
|
||||
const array = createArrayOfElements(['ion-item', 'ion-item', 'ion-item']) as HTMLIonItemElement[];
|
||||
expect(getPrevItem(array, array[0])).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,7 +52,16 @@ export class RadioGroup implements ComponentInterface {
|
||||
@Event() ionValueChange!: EventEmitter<RadioGroupChangeEventDetail>;
|
||||
|
||||
componentDidLoad() {
|
||||
this.setRadioTabindex(this.value);
|
||||
/**
|
||||
* There's an issue when assigning a value to the radio group
|
||||
* within the Angular primary content (rendering within the
|
||||
* app component template). When the template is isolated to a route,
|
||||
* the value is assigned correctly.
|
||||
* To address this issue, we need to ensure that the watcher is
|
||||
* called after the component has finished loading,
|
||||
* allowing the emit to be dispatched correctly.
|
||||
*/
|
||||
this.valueChanged(this.value);
|
||||
}
|
||||
|
||||
private setRadioTabindex = (value: any | undefined) => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Radio } from '../radio.tsx';
|
||||
import { RadioGroup } from '../../radio-group/radio-group.tsx';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { RadioGroup } from '../../radio-group/radio-group';
|
||||
import { Radio } from '../radio';
|
||||
|
||||
describe('ion-radio', () => {
|
||||
it('should set a default value', async () => {
|
||||
const radio = new Radio();
|
||||
@@ -21,7 +22,7 @@ describe('ion-radio', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const radio = page.root.querySelector('ion-radio');
|
||||
const radio = page.body.querySelector('ion-radio')!;
|
||||
expect(radio.classList.contains('radio-checked')).toBe(false);
|
||||
|
||||
radio.value = 'a';
|
||||
@@ -43,8 +44,8 @@ describe('ion-radio: disabled', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const radio = page.body.querySelector('ion-radio');
|
||||
const radioGroup = page.body.querySelector('ion-radio-group');
|
||||
const radio = page.body.querySelector('ion-radio')!;
|
||||
const radioGroup = page.body.querySelector('ion-radio-group')!;
|
||||
|
||||
expect(radioGroup.value).toBe(undefined);
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { Range } from '../range';
|
||||
import { Item } from '../../item/item';
|
||||
|
||||
let sharedRange;
|
||||
import { Item } from '../../item/item';
|
||||
import { Range } from '../range';
|
||||
|
||||
let sharedRange: Range;
|
||||
|
||||
describe('Range', () => {
|
||||
beforeEach(() => {
|
||||
sharedRange = new Range();
|
||||
@@ -21,7 +23,8 @@ describe('Range', () => {
|
||||
];
|
||||
|
||||
valueTests.forEach((test) => {
|
||||
expect(sharedRange.ensureValueInBounds(test[0])).toBe(test[1]);
|
||||
// Casting as any since we are accessing a private API on the range component
|
||||
expect((sharedRange as any).ensureValueInBounds(test[0])).toBe(test[1]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,7 +61,8 @@ describe('Range', () => {
|
||||
];
|
||||
|
||||
valueTests.forEach((test) => {
|
||||
expect(sharedRange.ensureValueInBounds(test[0])).toEqual(test[1]);
|
||||
// Casting as any since we are accessing a private API on the range component
|
||||
expect((sharedRange as any).ensureValueInBounds(test[0])).toEqual(test[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -73,7 +77,7 @@ describe('range id', () => {
|
||||
</ion-range>`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.getAttribute('id')).toBe('my-custom-range');
|
||||
});
|
||||
});
|
||||
@@ -89,7 +93,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -104,7 +108,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -119,7 +123,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -134,7 +138,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -149,7 +153,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -164,7 +168,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(true);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(true);
|
||||
});
|
||||
@@ -177,7 +181,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -191,7 +195,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -206,7 +210,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
@@ -221,7 +225,7 @@ describe('range: item adjustments', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const range = page.body.querySelector('ion-range');
|
||||
const range = page.body.querySelector('ion-range')!;
|
||||
expect(range.classList.contains('range-item-start-adjustment')).toBe(false);
|
||||
expect(range.classList.contains('range-item-end-adjustment')).toBe(false);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { RefresherContent } from '../refresher-content';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { RefresherContent } from '../refresher-content';
|
||||
|
||||
describe('refresher-content: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -9,11 +10,11 @@ describe('refresher-content: custom html', () => {
|
||||
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-refreshing-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
|
||||
});
|
||||
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text');
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text')!;
|
||||
expect(pullingContent.textContent).toContain('Custom Pulling Text');
|
||||
expect(pullingContent.querySelector('button.custom-pulling-html')).toBe(null);
|
||||
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text')!;
|
||||
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
|
||||
expect(refreshingContent.querySelector('button.custom-refreshing-html')).toBe(null);
|
||||
});
|
||||
@@ -25,11 +26,11 @@ describe('refresher-content: custom html', () => {
|
||||
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-refreshing-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
|
||||
});
|
||||
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text');
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text')!;
|
||||
expect(pullingContent.textContent).toContain('Custom Pulling Text');
|
||||
expect(pullingContent.querySelector('button.custom-pulling-html')).not.toBe(null);
|
||||
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text')!;
|
||||
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
|
||||
expect(refreshingContent.querySelector('button.custom-refreshing-html')).not.toBe(null);
|
||||
});
|
||||
@@ -41,11 +42,11 @@ describe('refresher-content: custom html', () => {
|
||||
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
|
||||
});
|
||||
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text');
|
||||
const pullingContent = page.body.querySelector('.refresher-pulling-text')!;
|
||||
expect(pullingContent.textContent).toContain('Custom Pulling Text');
|
||||
expect(pullingContent.querySelector('button.custom-pulling-html')).toBe(null);
|
||||
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
|
||||
const refreshingContent = page.body.querySelector('.refresher-refreshing-text')!;
|
||||
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
|
||||
expect(refreshingContent.querySelector('button.custom-refreshing-html')).toBe(null);
|
||||
});
|
||||
|
||||
@@ -36,11 +36,6 @@ export class ReorderGroup implements ComponentInterface {
|
||||
private containerTop = 0;
|
||||
private containerBottom = 0;
|
||||
|
||||
private longPressTimeout: any;
|
||||
private pressed = false;
|
||||
// the amount of time in milliseconds that the user must press and hold before the reorder is initiated
|
||||
private longPressDuration = 500;
|
||||
|
||||
@State() state = ReorderGroupState.Idle;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
@@ -56,11 +51,6 @@ export class ReorderGroup implements ComponentInterface {
|
||||
}
|
||||
}
|
||||
|
||||
// default is `tap` to maintain backwards compatibility
|
||||
// `tap` will initiate the reorder immediately
|
||||
// `press` will initiate the reorder after the `longPressDuration`
|
||||
@Prop() activate: 'tap' | 'press' = 'tap'
|
||||
|
||||
/**
|
||||
* Event that needs to be listened to in order to complete the reorder action.
|
||||
* Once the event has been emitted, the `complete()` method then needs
|
||||
@@ -135,78 +125,52 @@ export class ReorderGroup implements ComponentInterface {
|
||||
private onStart(ev: GestureDetail) {
|
||||
ev.event.preventDefault();
|
||||
|
||||
if (this.activate === 'press') {
|
||||
this.longPressTimeout = setTimeout(() => {
|
||||
this.pressed = true;
|
||||
this.clearGestureTimeout();
|
||||
console.log('hits')
|
||||
this.selectingItem(ev);
|
||||
|
||||
}, this.longPressDuration);
|
||||
} else {
|
||||
console.log('nope')
|
||||
this.selectingItem(ev);
|
||||
}
|
||||
}
|
||||
|
||||
private selectingItem(ev: GestureDetail) {
|
||||
const item = (this.selectedItemEl = ev.data);
|
||||
const heights = this.cachedHeights;
|
||||
heights.length = 0;
|
||||
const el = this.el;
|
||||
const children: any = el.children;
|
||||
if (!children || children.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sum = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
sum += child.offsetHeight;
|
||||
heights.push(sum);
|
||||
child.$ionIndex = i;
|
||||
}
|
||||
|
||||
const box = el.getBoundingClientRect();
|
||||
this.containerTop = box.top;
|
||||
this.containerBottom = box.bottom;
|
||||
|
||||
if (this.scrollEl) {
|
||||
const scrollBox = this.scrollEl.getBoundingClientRect();
|
||||
this.scrollElInitial = this.scrollEl.scrollTop;
|
||||
this.scrollElTop = scrollBox.top + AUTO_SCROLL_MARGIN;
|
||||
this.scrollElBottom = scrollBox.bottom - AUTO_SCROLL_MARGIN;
|
||||
} else {
|
||||
this.scrollElInitial = 0;
|
||||
this.scrollElTop = 0;
|
||||
this.scrollElBottom = 0;
|
||||
}
|
||||
|
||||
this.lastToIndex = indexForItem(item);
|
||||
this.selectedItemHeight = item.offsetHeight;
|
||||
this.state = ReorderGroupState.Active;
|
||||
|
||||
item.classList.add(ITEM_REORDER_SELECTED);
|
||||
|
||||
hapticSelectionStart();
|
||||
}
|
||||
|
||||
private clearGestureTimeout = () => {
|
||||
if (this.longPressTimeout) {
|
||||
clearTimeout(this.longPressTimeout);
|
||||
this.longPressTimeout = undefined;
|
||||
const heights = this.cachedHeights;
|
||||
heights.length = 0;
|
||||
const el = this.el;
|
||||
const children: any = el.children;
|
||||
if (!children || children.length === 0) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let sum = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
sum += child.offsetHeight;
|
||||
heights.push(sum);
|
||||
child.$ionIndex = i;
|
||||
}
|
||||
|
||||
const box = el.getBoundingClientRect();
|
||||
this.containerTop = box.top;
|
||||
this.containerBottom = box.bottom;
|
||||
|
||||
if (this.scrollEl) {
|
||||
const scrollBox = this.scrollEl.getBoundingClientRect();
|
||||
this.scrollElInitial = this.scrollEl.scrollTop;
|
||||
this.scrollElTop = scrollBox.top + AUTO_SCROLL_MARGIN;
|
||||
this.scrollElBottom = scrollBox.bottom - AUTO_SCROLL_MARGIN;
|
||||
} else {
|
||||
this.scrollElInitial = 0;
|
||||
this.scrollElTop = 0;
|
||||
this.scrollElBottom = 0;
|
||||
}
|
||||
|
||||
this.lastToIndex = indexForItem(item);
|
||||
this.selectedItemHeight = item.offsetHeight;
|
||||
this.state = ReorderGroupState.Active;
|
||||
|
||||
item.classList.add(ITEM_REORDER_SELECTED);
|
||||
|
||||
hapticSelectionStart();
|
||||
}
|
||||
|
||||
private onMove(ev: GestureDetail) {
|
||||
const selectedItem = this.selectedItemEl;
|
||||
if (!selectedItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.activate === 'press' && !this.pressed) {
|
||||
return;
|
||||
}
|
||||
// Scroll if we reach the scroll margins
|
||||
const scroll = this.autoscroll(ev.currentY);
|
||||
|
||||
@@ -227,8 +191,6 @@ export class ReorderGroup implements ComponentInterface {
|
||||
|
||||
// Update selected item position
|
||||
selectedItem.style.transform = `translateY(${deltaY}px)`;
|
||||
|
||||
this.clearGestureTimeout();
|
||||
}
|
||||
|
||||
private onEnd() {
|
||||
@@ -253,18 +215,10 @@ export class ReorderGroup implements ComponentInterface {
|
||||
}
|
||||
|
||||
hapticSelectionEnd();
|
||||
|
||||
if (this.activate === 'press' && !this.pressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pressed = false;
|
||||
this.clearGestureTimeout();
|
||||
}
|
||||
|
||||
private completeReorder(listOrReorder?: boolean | any[]): any {
|
||||
const selectedItemEl = this.selectedItemEl;
|
||||
console.log('end')
|
||||
if (selectedItemEl && this.state === ReorderGroupState.Complete) {
|
||||
const children = this.el.children as any;
|
||||
const len = children.length;
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Reorder - Basic</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Reorder - Long Press</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<p>Reorder with icons</p>
|
||||
<ion-list>
|
||||
<ion-reorder-group id="reorder-icons" activate="press" disabled="false">
|
||||
<ion-item button>
|
||||
<ion-label> Item 1 (default ion-reorder) </ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-sliding>
|
||||
<ion-item button>
|
||||
<ion-label> Item 2 (default ion-reorder with ion-item-sliding) </ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option>Favorite</ion-item-option>
|
||||
<ion-item-option color="danger">Share</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option>Unread</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</ion-reorder-group>
|
||||
</ion-list>
|
||||
<p>Reorder with wrapper</p>
|
||||
<ion-list>
|
||||
<ion-reorder-group id="reorder-wrappers" disabled="false" activate="press" long-press-duration="5000">
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label> Item 1 </ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item-sliding>
|
||||
<ion-item button>
|
||||
<ion-label> Item 2 (default ion-reorder with ion-item-sliding) </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option>Favorite</ion-item-option>
|
||||
<ion-item-option color="danger">Share</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option>Unread</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</ion-reorder>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const reorderGroup = document.getElementById('reorder-icons');
|
||||
reorderGroup.addEventListener('ionItemReorder', ({ detail }) => {
|
||||
console.log('Dragged from index', detail.from, 'to', detail.to);
|
||||
|
||||
detail.complete();
|
||||
});
|
||||
const reorderGroup2 = document.getElementById('reorder-wrappers');
|
||||
reorderGroup2.addEventListener('ionItemReorder', ({ detail }) => {
|
||||
console.log('Dragged from index', detail.from, 'to', detail.to);
|
||||
|
||||
detail.complete();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -203,17 +203,17 @@ describe('findChainForSegments', () => {
|
||||
describe('mergeParams', () => {
|
||||
it('should merge undefined', () => {
|
||||
expect(mergeParams(undefined, undefined)).toBeUndefined();
|
||||
expect(mergeParams(null, undefined)).toBeUndefined();
|
||||
expect(mergeParams(undefined, null)).toBeUndefined();
|
||||
expect(mergeParams(null, null)).toBeUndefined();
|
||||
expect(mergeParams(null as any, undefined)).toBeUndefined();
|
||||
expect(mergeParams(undefined, null as any)).toBeUndefined();
|
||||
expect(mergeParams(null as any, null as any)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should merge undefined with params', () => {
|
||||
const params = { data: '1' };
|
||||
expect(mergeParams(undefined, params)).toEqual(params);
|
||||
expect(mergeParams(null, params)).toEqual(params);
|
||||
expect(mergeParams(null as any, params)).toEqual(params);
|
||||
expect(mergeParams(params, undefined)).toEqual(params);
|
||||
expect(mergeParams(params, null)).toEqual(params);
|
||||
expect(mergeParams(params, null as any)).toEqual(params);
|
||||
});
|
||||
|
||||
it('should merge params with params', () => {
|
||||
@@ -253,36 +253,44 @@ describe('RouterSegments', () => {
|
||||
|
||||
describe('matchesRedirect', () => {
|
||||
it('should match empty redirect', () => {
|
||||
expect(matchesRedirect([''], { from: [''], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect([''], { from: ['*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect([''], { from: [''], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect([''], { from: ['*'], to: { segments: [''] } })).toBeTruthy();
|
||||
|
||||
expect(matchesRedirect([''], { from: ['hola'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect([''], { from: ['hola', '*'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect([''], { from: ['hola'], to: { segments: [] } })).toBeFalsy();
|
||||
expect(matchesRedirect([''], { from: ['hola', '*'], to: { segments: [''] } })).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should match simple segment redirect', () => {
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', '*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'hola'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['*'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', '*'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'hola'], to: { segments: [''] } })).toBeTruthy();
|
||||
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts', '*'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'adios'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts'], { from: ['workouts', '*'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'hola'], { from: ['workouts', 'adios'], to: { segments: [''] } })).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should match long route', () => {
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', '*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', '*'], to: [''] })).toBeTruthy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to'], to: [''] })).toBeTruthy();
|
||||
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login', '*'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path'], to: [''] })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['*'], to: { segments: [''] } })).toBeTruthy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to', '*'], to: [''] })
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', '*'], to: { segments: [''] } })
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', '*'], to: { segments: [''] } })
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to'], to: { segments: [''] } })
|
||||
).toBeTruthy();
|
||||
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['login', '*'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts'], to: { segments: [''] } })).toBeFalsy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path'], to: { segments: [''] } })
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
matchesRedirect(['workouts', 'path', 'to'], { from: ['workouts', 'path', 'to', '*'], to: { segments: [''] } })
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
@@ -18,19 +18,21 @@ describe('ionic-conference-app', () => {
|
||||
expect(getRouteIDs('/about', routes)).toEqual(['page-tabs', 'page-about']);
|
||||
expect(getRouteIDs('/tutorial', routes)).toEqual(['page-tutorial']);
|
||||
|
||||
expect(getRoutePath([{ id: 'PAGE-TABS' }, { id: 'tab-schedule' }, { id: 'page-schedule' }], routes)).toEqual('/');
|
||||
expect(
|
||||
getRoutePath([{ id: 'PAGE-TABS' }, { id: 'tab-schedule' }, { id: 'page-schedule' }] as RouteID[], routes)
|
||||
).toEqual('/');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }], routes)).toEqual('/speaker');
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }] as RouteID[], routes)).toEqual('/speaker');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }, { id: 'page-speaker-list' }], routes)).toEqual(
|
||||
'/speaker'
|
||||
);
|
||||
expect(
|
||||
getRoutePath([{ id: 'page-tabs' }, { id: 'TAB-SPEAKER' }, { id: 'page-speaker-list' }] as RouteID[], routes)
|
||||
).toEqual('/speaker');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'PAGE-MAP' }], routes)).toEqual('/map');
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'PAGE-MAP' }] as RouteID[], routes)).toEqual('/map');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'page-about' }], routes)).toEqual('/about');
|
||||
expect(getRoutePath([{ id: 'page-tabs' }, { id: 'page-about' }] as RouteID[], routes)).toEqual('/about');
|
||||
|
||||
expect(getRoutePath([{ id: 'page-tutorial' }], routes)).toEqual('/tutorial');
|
||||
expect(getRoutePath([{ id: 'page-tutorial' }] as RouteID[], routes)).toEqual('/tutorial');
|
||||
});
|
||||
|
||||
let win: Window;
|
||||
|
||||
@@ -257,6 +257,8 @@ export class Searchbar implements ComponentInterface {
|
||||
*
|
||||
* Developers who wish to focus an input when an overlay is presented
|
||||
* should call `setFocus` after `didPresent` has resolved.
|
||||
*
|
||||
* See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
@Method()
|
||||
async setFocus() {
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('searchbar: rendering', () => {
|
||||
html: '<ion-searchbar name="search"></ion-searchbar>',
|
||||
});
|
||||
|
||||
const nativeEl = page.body.querySelector('ion-searchbar input');
|
||||
const nativeEl = page.body.querySelector('ion-searchbar input')!;
|
||||
expect(nativeEl.getAttribute('name')).toBe('search');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,14 +9,14 @@ it('should disable segment buttons added to disabled segment async', async () =>
|
||||
html: `<ion-segment disabled="true"></ion-segment>`,
|
||||
});
|
||||
|
||||
const segment = page.body.querySelector('ion-segment');
|
||||
const segment = page.body.querySelector('ion-segment')!;
|
||||
segment.innerHTML = `
|
||||
<ion-segment-button>
|
||||
<ion-label>Segment Button</ion-label>
|
||||
</ion-segment-button>`;
|
||||
await page.waitForChanges();
|
||||
|
||||
const segmentButton = page.body.querySelector('ion-segment-button');
|
||||
const segmentButton = page.body.querySelector('ion-segment-button')!;
|
||||
expect(segmentButton.disabled).toBe(true);
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ it('should set checked state when value is set asynchronously', async () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const segmentButton = page.root.querySelector('ion-segment-button');
|
||||
const segmentButton = page.body.querySelector('ion-segment-button')!;
|
||||
|
||||
expect(segmentButton.classList.contains('segment-button-checked')).toBe(false);
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ describe('ion-select', () => {
|
||||
template: () => <ion-select value="my value" name="my name" disabled={true}></ion-select>,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select');
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
|
||||
const hiddenInput = select.querySelector('input[type="hidden"]');
|
||||
const hiddenInput = select.querySelector<HTMLInputElement>('input[type="hidden"]')!;
|
||||
expect(hiddenInput).not.toBe(null);
|
||||
|
||||
expect(hiddenInput.value).toBe('my value');
|
||||
@@ -28,10 +28,10 @@ describe('ion-select', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select');
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
|
||||
const propEl = select.shadowRoot.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot.querySelector('slot[name="label"]');
|
||||
const propEl = select.shadowRoot!.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot!.querySelector('slot[name="label"]');
|
||||
|
||||
expect(propEl).not.toBe(null);
|
||||
expect(slotEl).toBe(null);
|
||||
@@ -44,10 +44,10 @@ describe('ion-select', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select');
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
|
||||
const propEl = select.shadowRoot.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot.querySelector('slot[name="label"]');
|
||||
const propEl = select.shadowRoot!.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot!.querySelector('slot[name="label"]');
|
||||
|
||||
expect(propEl).toBe(null);
|
||||
expect(slotEl).not.toBe(null);
|
||||
@@ -60,10 +60,10 @@ describe('ion-select', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const select = page.body.querySelector('ion-select');
|
||||
const select = page.body.querySelector('ion-select')!;
|
||||
|
||||
const propEl = select.shadowRoot.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot.querySelector('slot[name="label"]');
|
||||
const propEl = select.shadowRoot!.querySelector('.label-text');
|
||||
const slotEl = select.shadowRoot!.querySelector('slot[name="label"]');
|
||||
|
||||
expect(propEl).not.toBe(null);
|
||||
expect(slotEl).toBe(null);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Textarea } from '../textarea';
|
||||
|
||||
it('should inherit attributes', async () => {
|
||||
@@ -7,7 +8,7 @@ it('should inherit attributes', async () => {
|
||||
html: '<ion-textarea title="my title" tabindex="-1" data-form-type="password"></ion-textarea>',
|
||||
});
|
||||
|
||||
const nativeEl = page.body.querySelector('ion-textarea textarea');
|
||||
const nativeEl = page.body.querySelector('ion-textarea textarea')!;
|
||||
expect(nativeEl.getAttribute('title')).toBe('my title');
|
||||
expect(nativeEl.getAttribute('tabindex')).toBe('-1');
|
||||
expect(nativeEl.getAttribute('data-form-type')).toBe('password');
|
||||
@@ -31,9 +32,9 @@ describe('textarea: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const textarea = page.body.querySelector('ion-textarea');
|
||||
const textarea = page.body.querySelector('ion-textarea')!;
|
||||
|
||||
const labelText = textarea.querySelector('.label-text-wrapper');
|
||||
const labelText = textarea.querySelector('.label-text-wrapper')!;
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
@@ -45,9 +46,9 @@ describe('textarea: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const textarea = page.body.querySelector('ion-textarea');
|
||||
const textarea = page.body.querySelector('ion-textarea')!;
|
||||
|
||||
const labelText = textarea.querySelector('.label-text-wrapper');
|
||||
const labelText = textarea.querySelector('.label-text-wrapper')!;
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Slot');
|
||||
});
|
||||
@@ -59,9 +60,9 @@ describe('textarea: label rendering', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const textarea = page.body.querySelector('ion-textarea');
|
||||
const textarea = page.body.querySelector('ion-textarea')!;
|
||||
|
||||
const labelText = textarea.querySelector('.label-text-wrapper');
|
||||
const labelText = textarea.querySelector('.label-text-wrapper')!;
|
||||
|
||||
expect(labelText.textContent).toBe('Label Prop Text');
|
||||
});
|
||||
|
||||
@@ -93,7 +93,9 @@ export class Textarea implements ComponentInterface {
|
||||
@Prop() autocapitalize = 'none';
|
||||
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element.
|
||||
*
|
||||
* This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
@Prop() autofocus = false;
|
||||
|
||||
@@ -372,6 +374,8 @@ export class Textarea implements ComponentInterface {
|
||||
/**
|
||||
* Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global
|
||||
* `textarea.focus()`.
|
||||
*
|
||||
* See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
@Method()
|
||||
async setFocus() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { h } from '@stencil/core';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
import { Toast } from '../toast';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { toastController } from '../../../utils/overlays';
|
||||
import { Toast } from '../toast';
|
||||
|
||||
describe('toast: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -11,8 +11,8 @@ describe('toast: custom html', () => {
|
||||
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const content = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -24,8 +24,8 @@ describe('toast: custom html', () => {
|
||||
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const content = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).not.toBe(null);
|
||||
});
|
||||
@@ -37,8 +37,8 @@ describe('toast: custom html', () => {
|
||||
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const content = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
expect(content.textContent).toContain('Custom Text');
|
||||
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||
});
|
||||
@@ -56,9 +56,9 @@ describe('toast: a11y smoke test', () => {
|
||||
html: `<ion-toast message="Message" header="Header"></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const header = toast.shadowRoot.querySelector('.toast-header');
|
||||
const message = toast.shadowRoot.querySelector('.toast-message');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
const header = toast.shadowRoot!.querySelector('.toast-header')!;
|
||||
const message = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
|
||||
expect(header.getAttribute('aria-hidden')).toBe('true');
|
||||
expect(message.getAttribute('aria-hidden')).toBe('true');
|
||||
@@ -74,7 +74,7 @@ describe('toast: a11y smoke test', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
|
||||
/**
|
||||
* Wait for present method to resolve
|
||||
@@ -83,8 +83,8 @@ describe('toast: a11y smoke test', () => {
|
||||
await toast.present();
|
||||
await page.waitForChanges();
|
||||
|
||||
const header = toast.shadowRoot.querySelector('.toast-header');
|
||||
const message = toast.shadowRoot.querySelector('.toast-message');
|
||||
const header = toast.shadowRoot!.querySelector('.toast-header')!;
|
||||
const message = toast.shadowRoot!.querySelector('.toast-message')!;
|
||||
|
||||
expect(header.getAttribute('aria-hidden')).toBe(null);
|
||||
expect(message.getAttribute('aria-hidden')).toBe(null);
|
||||
@@ -98,7 +98,7 @@ describe('toast: duration config', () => {
|
||||
html: `<ion-toast></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
|
||||
expect(toast.duration).toBe(0);
|
||||
});
|
||||
@@ -111,7 +111,7 @@ describe('toast: duration config', () => {
|
||||
html: `<ion-toast></ion-toast>`,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
|
||||
expect(toast.duration).toBe(5000);
|
||||
});
|
||||
@@ -121,10 +121,10 @@ describe('toast: htmlAttributes', () => {
|
||||
it('should correctly inherit attributes on host', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Toast],
|
||||
template: () => <ion-toast htmlAttributes={{ 'data-testid': 'basic-toast' }}></ion-toast>,
|
||||
template: () => <ion-toast overlayIndex={1} htmlAttributes={{ 'data-testid': 'basic-toast' }}></ion-toast>,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
|
||||
await expect(toast.getAttribute('data-testid')).toBe('basic-toast');
|
||||
});
|
||||
@@ -134,12 +134,12 @@ describe('toast: button cancel', () => {
|
||||
it('should render the cancel button with part button-cancel', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Toast],
|
||||
template: () => <ion-toast buttons={[{ text: 'Cancel', role: 'cancel' }]}></ion-toast>,
|
||||
template: () => <ion-toast overlayIndex={1} buttons={[{ text: 'Cancel', role: 'cancel' }]}></ion-toast>,
|
||||
});
|
||||
|
||||
const toast = page.body.querySelector('ion-toast');
|
||||
const toast = page.body.querySelector('ion-toast')!;
|
||||
|
||||
const buttonCancel = toast?.shadowRoot?.querySelector('.toast-button-cancel');
|
||||
const buttonCancel = toast.shadowRoot!.querySelector('.toast-button-cancel')!;
|
||||
|
||||
expect(buttonCancel.getAttribute('part')).toBe('button cancel');
|
||||
});
|
||||
|
||||