Compare commits
9 Commits
sp/angular
...
FW-4398
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
735d276bf9 | ||
|
|
71f7d536b7 | ||
|
|
da02130b58 | ||
|
|
8e80779b01 | ||
|
|
21eecf06de | ||
|
|
55be77bf38 | ||
|
|
75cc691497 | ||
|
|
e282058e23 | ||
|
|
99f08a9578 |
2
.github/CODEOWNERS
vendored
@@ -11,8 +11,6 @@
|
||||
# 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
|
||||
|
||||
15
.github/labeler.yml
vendored
@@ -6,17 +6,16 @@
|
||||
# https://github.com/actions/labeler
|
||||
|
||||
'package: core':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['core/**/*']
|
||||
- core/**/*
|
||||
|
||||
'package: angular':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['packages/angular/**/*', 'packages/angular-*/**/*']
|
||||
- packages/angular/**/*
|
||||
- packages/angular-*/**/*
|
||||
|
||||
'package: react':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['packages/react/**/*', 'packages/react-*/**/*']
|
||||
- packages/react/**/*
|
||||
- packages/react-*/**/*
|
||||
|
||||
'package: vue':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['packages/vue/**/*', 'packages/vue-*/**/*']
|
||||
- packages/vue/**/*
|
||||
- packages/vue-*/**/*
|
||||
|
||||
@@ -8,7 +8,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 16
|
||||
- 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, ng17]
|
||||
apps: [ng14, ng15, ng16]
|
||||
needs: [build-angular, build-angular-server]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
2
.github/workflows/label.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@main
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
|
||||
118
CHANGELOG.md
@@ -3,124 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [7.6.0](https://github.com/ionic-team/ionic-framework/compare/v7.5.8...v7.6.0) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular,vue:** range form value updates while dragging knob ([#28422](https://github.com/ionic-team/ionic-framework/issues/28422)) ([0854a11](https://github.com/ionic-team/ionic-framework/commit/0854a11a25759d0201eae66c96a62fe138d486f8)), closes [#28256](https://github.com/ionic-team/ionic-framework/issues/28256)
|
||||
* **animation:** add stronger types to Animation interface ([#28334](https://github.com/ionic-team/ionic-framework/issues/28334)) ([4a088d5](https://github.com/ionic-team/ionic-framework/commit/4a088d5d612ab0387064d388b37d46cdf15cf1ff))
|
||||
* **animation:** progressEnd coercion is reset before onFinish ([#28394](https://github.com/ionic-team/ionic-framework/issues/28394)) ([eae8162](https://github.com/ionic-team/ionic-framework/commit/eae8162d0dc2e0bd7a9d56a3662a8e5f5d142b72)), closes [#28393](https://github.com/ionic-team/ionic-framework/issues/28393)
|
||||
* **infinite-scroll:** remaining in threshold after ionInfinite can trigger event again on scroll ([#28569](https://github.com/ionic-team/ionic-framework/issues/28569)) ([8c235fd](https://github.com/ionic-team/ionic-framework/commit/8c235fd30c50f317de1f37f69068507aa0979068)), closes [#18071](https://github.com/ionic-team/ionic-framework/issues/18071)
|
||||
* **item:** allow item to grow when it is used in a flex container ([#28594](https://github.com/ionic-team/ionic-framework/issues/28594)) ([1c1b567](https://github.com/ionic-team/ionic-framework/commit/1c1b567279dee44da70bb9b90c129946c9043987))
|
||||
* **item:** wrap elements and label contents when the font size increases or the elements do not fit ([#28146](https://github.com/ionic-team/ionic-framework/issues/28146)) ([6438e3e](https://github.com/ionic-team/ionic-framework/commit/6438e3e919c665569b731a2d74fe1547b4f3c1cc))
|
||||
* **select:** do not collapse to width: 0 when placed in flex container ([#28631](https://github.com/ionic-team/ionic-framework/issues/28631)) ([e71e7a0](https://github.com/ionic-team/ionic-framework/commit/e71e7a069000db8738abc304758de64286817442))
|
||||
* **toast:** add swipeGesture to ToastOptions ([#28518](https://github.com/ionic-team/ionic-framework/issues/28518)) ([4ad6df6](https://github.com/ionic-team/ionic-framework/commit/4ad6df67f01cebce30d4da46c7541c4b14c5d4a4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **checkbox:** add shadow part for label ([#28604](https://github.com/ionic-team/ionic-framework/issues/28604)) ([f9f5654](https://github.com/ionic-team/ionic-framework/commit/f9f5654ab0e920bf97089fbabfb9eedbcf6fe8ae)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **input, textarea, select:** add start and end slots ([#28583](https://github.com/ionic-team/ionic-framework/issues/28583)) ([357b8b2](https://github.com/ionic-team/ionic-framework/commit/357b8b2beb29b95d53ef043af349067be1d32658)), closes [#26297](https://github.com/ionic-team/ionic-framework/issues/26297)
|
||||
* **radio-group:** add compareWith property ([#28452](https://github.com/ionic-team/ionic-framework/issues/28452)) ([0ae327f](https://github.com/ionic-team/ionic-framework/commit/0ae327f0e09cd97d705f2d3051c215034381e226))
|
||||
* **radio:** add shadow part for label ([#28607](https://github.com/ionic-team/ionic-framework/issues/28607)) ([b757970](https://github.com/ionic-team/ionic-framework/commit/b757970d23e87c59aa883ecb1bfa9b66bcae8de2)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **range:** expose label wrapper as shadow part ([#28601](https://github.com/ionic-team/ionic-framework/issues/28601)) ([52ed2bf](https://github.com/ionic-team/ionic-framework/commit/52ed2bf63777c764f57bb4c3a5d4a127bff46c50))
|
||||
* **toast:** add swipe to dismiss functionality ([#28442](https://github.com/ionic-team/ionic-framework/issues/28442)) ([30c21aa](https://github.com/ionic-team/ionic-framework/commit/30c21aab3ed40d73c28e7d60d0952d8891b0a9d3)), closes [#21769](https://github.com/ionic-team/ionic-framework/issues/21769)
|
||||
* **toggle:** expose label wrapper as shadow part ([#28585](https://github.com/ionic-team/ionic-framework/issues/28585)) ([a34188f](https://github.com/ionic-team/ionic-framework/commit/a34188f7dbec4a16e4f2043ed3dc096e337725a7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.8](https://github.com/ionic-team/ionic-framework/compare/v7.5.7...v7.5.8) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** add missing menu controller methods ([#28618](https://github.com/ionic-team/ionic-framework/issues/28618)) ([7871b56](https://github.com/ionic-team/ionic-framework/commit/7871b56eccfe63326b6dd4b56ade3b3afd444fce)), closes [#20053](https://github.com/ionic-team/ionic-framework/issues/20053)
|
||||
* **overlays:** trigger is configured on load ([#28526](https://github.com/ionic-team/ionic-framework/issues/28526)) ([a3cd204](https://github.com/ionic-team/ionic-framework/commit/a3cd204f616606ccffc35082655e55fdfb19fe28)), closes [#28524](https://github.com/ionic-team/ionic-framework/issues/28524)
|
||||
* **react:** router creates new view instances of parameterized routes ([#28616](https://github.com/ionic-team/ionic-framework/issues/28616)) ([1705d06](https://github.com/ionic-team/ionic-framework/commit/1705d064cc041e99f432a27207f3aab7fa62c778)), closes [#26524](https://github.com/ionic-team/ionic-framework/issues/26524)
|
||||
* **vue:** nav component accepts kebab-case component properties ([#28615](https://github.com/ionic-team/ionic-framework/issues/28615)) ([60303aa](https://github.com/ionic-team/ionic-framework/commit/60303aad23f823488afc8f8824e9c72e3ab86acc)), closes [#28611](https://github.com/ionic-team/ionic-framework/issues/28611)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **inputs:** remove invalid legacy warnings in input, textarea, and select ([#28484](https://github.com/ionic-team/ionic-framework/issues/28484)) ([c765dcb](https://github.com/ionic-team/ionic-framework/commit/c765dcbac4148762768d8c2bea9103e7d38c510b))
|
||||
* **item:** apply safe area to proper side regardless of direction ([#28403](https://github.com/ionic-team/ionic-framework/issues/28403)) ([ed040b0](https://github.com/ionic-team/ionic-framework/commit/ed040b09e9cbd4246864e690542132defc6a6578))
|
||||
* **list:** remove border from last item with item-sliding ([#28439](https://github.com/ionic-team/ionic-framework/issues/28439)) ([cafafcc](https://github.com/ionic-team/ionic-framework/commit/cafafcc9d166ef536dcb73edd522c8f2a0fb95b6)), closes [#28435](https://github.com/ionic-team/ionic-framework/issues/28435)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.3](https://github.com/ionic-team/ionic-framework/compare/v7.5.2...v7.5.3) (2023-11-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** long words wrap to next line ([#28408](https://github.com/ionic-team/ionic-framework/issues/28408)) ([34257d6](https://github.com/ionic-team/ionic-framework/commit/34257d681e9034b0a001aa45e17222f3aab5ed76)), closes [#28406](https://github.com/ionic-team/ionic-framework/issues/28406)
|
||||
* **angular:** inputs on standalone form controls are reactive ([#28434](https://github.com/ionic-team/ionic-framework/issues/28434)) ([3b6e631](https://github.com/ionic-team/ionic-framework/commit/3b6e6318bf94125b3f8305b4d072a5945ceb3730)), closes [#28431](https://github.com/ionic-team/ionic-framework/issues/28431)
|
||||
* **angular:** NavController works with nested outlets ([#28421](https://github.com/ionic-team/ionic-framework/issues/28421)) ([90acad1](https://github.com/ionic-team/ionic-framework/commit/90acad1837b117542830ec0083389a35c5b4ee76)), closes [#28417](https://github.com/ionic-team/ionic-framework/issues/28417)
|
||||
* **angular:** run platform subscriptions inside zone ([#28404](https://github.com/ionic-team/ionic-framework/issues/28404)) ([a4b303e](https://github.com/ionic-team/ionic-framework/commit/a4b303e1338a35756a9cf7f67508d24d2f8537a2)), closes [#19539](https://github.com/ionic-team/ionic-framework/issues/19539)
|
||||
* **angular:** standalone form components do not error when multiple are used ([#28423](https://github.com/ionic-team/ionic-framework/issues/28423)) ([89698b3](https://github.com/ionic-team/ionic-framework/commit/89698b338fb05cde427c98720c238d2365abdaa7)), closes [#28418](https://github.com/ionic-team/ionic-framework/issues/28418)
|
||||
* **datetime:** allow calendar navigation in readonly mode; disallow keyboard navigation when disabled ([#28336](https://github.com/ionic-team/ionic-framework/issues/28336)) ([f6a6877](https://github.com/ionic-team/ionic-framework/commit/f6a6877044a6d912a92aab00c3c78897da09415d)), closes [#28121](https://github.com/ionic-team/ionic-framework/issues/28121)
|
||||
* **input, textarea, select:** use consistent sizes ([#28390](https://github.com/ionic-team/ionic-framework/issues/28390)) ([b31ecbb](https://github.com/ionic-team/ionic-framework/commit/b31ecbbfe8deb87604686d752e92e672dd9b277a)), closes [#28388](https://github.com/ionic-team/ionic-framework/issues/28388)
|
||||
* **list-header:** apply safe area to proper side regardless of direction ([#28371](https://github.com/ionic-team/ionic-framework/issues/28371)) ([f99d530](https://github.com/ionic-team/ionic-framework/commit/f99d5305fb4b1607b42e34a0b7653d8e1b5bf23f))
|
||||
* **segment:** avoid scrolling webkit bug ([#28376](https://github.com/ionic-team/ionic-framework/issues/28376)) ([8e2f818](https://github.com/ionic-team/ionic-framework/commit/8e2f81867175e9980e6d072b0a4414baae571223)), closes [#28373](https://github.com/ionic-team/ionic-framework/issues/28373)
|
||||
* **tab-bar:** apply safe area to proper side regardless of direction ([#28372](https://github.com/ionic-team/ionic-framework/issues/28372)) ([d47b7e7](https://github.com/ionic-team/ionic-framework/commit/d47b7e750310ceb2f2c7ecfda8343923ff8d564a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.2](https://github.com/ionic-team/ionic-framework/compare/v7.5.1...v7.5.2) (2023-10-25)
|
||||
|
||||
|
||||
|
||||
@@ -3,119 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [7.6.0](https://github.com/ionic-team/ionic-framework/compare/v7.5.8...v7.6.0) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular,vue:** range form value updates while dragging knob ([#28422](https://github.com/ionic-team/ionic-framework/issues/28422)) ([0854a11](https://github.com/ionic-team/ionic-framework/commit/0854a11a25759d0201eae66c96a62fe138d486f8)), closes [#28256](https://github.com/ionic-team/ionic-framework/issues/28256)
|
||||
* **animation:** add stronger types to Animation interface ([#28334](https://github.com/ionic-team/ionic-framework/issues/28334)) ([4a088d5](https://github.com/ionic-team/ionic-framework/commit/4a088d5d612ab0387064d388b37d46cdf15cf1ff))
|
||||
* **animation:** progressEnd coercion is reset before onFinish ([#28394](https://github.com/ionic-team/ionic-framework/issues/28394)) ([eae8162](https://github.com/ionic-team/ionic-framework/commit/eae8162d0dc2e0bd7a9d56a3662a8e5f5d142b72)), closes [#28393](https://github.com/ionic-team/ionic-framework/issues/28393)
|
||||
* **infinite-scroll:** remaining in threshold after ionInfinite can trigger event again on scroll ([#28569](https://github.com/ionic-team/ionic-framework/issues/28569)) ([8c235fd](https://github.com/ionic-team/ionic-framework/commit/8c235fd30c50f317de1f37f69068507aa0979068)), closes [#18071](https://github.com/ionic-team/ionic-framework/issues/18071)
|
||||
* **item:** allow item to grow when it is used in a flex container ([#28594](https://github.com/ionic-team/ionic-framework/issues/28594)) ([1c1b567](https://github.com/ionic-team/ionic-framework/commit/1c1b567279dee44da70bb9b90c129946c9043987))
|
||||
* **item:** wrap elements and label contents when the font size increases or the elements do not fit ([#28146](https://github.com/ionic-team/ionic-framework/issues/28146)) ([6438e3e](https://github.com/ionic-team/ionic-framework/commit/6438e3e919c665569b731a2d74fe1547b4f3c1cc))
|
||||
* **select:** do not collapse to width: 0 when placed in flex container ([#28631](https://github.com/ionic-team/ionic-framework/issues/28631)) ([e71e7a0](https://github.com/ionic-team/ionic-framework/commit/e71e7a069000db8738abc304758de64286817442))
|
||||
* **toast:** add swipeGesture to ToastOptions ([#28518](https://github.com/ionic-team/ionic-framework/issues/28518)) ([4ad6df6](https://github.com/ionic-team/ionic-framework/commit/4ad6df67f01cebce30d4da46c7541c4b14c5d4a4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **checkbox:** add shadow part for label ([#28604](https://github.com/ionic-team/ionic-framework/issues/28604)) ([f9f5654](https://github.com/ionic-team/ionic-framework/commit/f9f5654ab0e920bf97089fbabfb9eedbcf6fe8ae)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **input, textarea, select:** add start and end slots ([#28583](https://github.com/ionic-team/ionic-framework/issues/28583)) ([357b8b2](https://github.com/ionic-team/ionic-framework/commit/357b8b2beb29b95d53ef043af349067be1d32658)), closes [#26297](https://github.com/ionic-team/ionic-framework/issues/26297)
|
||||
* **radio-group:** add compareWith property ([#28452](https://github.com/ionic-team/ionic-framework/issues/28452)) ([0ae327f](https://github.com/ionic-team/ionic-framework/commit/0ae327f0e09cd97d705f2d3051c215034381e226))
|
||||
* **radio:** add shadow part for label ([#28607](https://github.com/ionic-team/ionic-framework/issues/28607)) ([b757970](https://github.com/ionic-team/ionic-framework/commit/b757970d23e87c59aa883ecb1bfa9b66bcae8de2)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **range:** expose label wrapper as shadow part ([#28601](https://github.com/ionic-team/ionic-framework/issues/28601)) ([52ed2bf](https://github.com/ionic-team/ionic-framework/commit/52ed2bf63777c764f57bb4c3a5d4a127bff46c50))
|
||||
* **toast:** add swipe to dismiss functionality ([#28442](https://github.com/ionic-team/ionic-framework/issues/28442)) ([30c21aa](https://github.com/ionic-team/ionic-framework/commit/30c21aab3ed40d73c28e7d60d0952d8891b0a9d3)), closes [#21769](https://github.com/ionic-team/ionic-framework/issues/21769)
|
||||
* **toggle:** expose label wrapper as shadow part ([#28585](https://github.com/ionic-team/ionic-framework/issues/28585)) ([a34188f](https://github.com/ionic-team/ionic-framework/commit/a34188f7dbec4a16e4f2043ed3dc096e337725a7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.8](https://github.com/ionic-team/ionic-framework/compare/v7.5.7...v7.5.8) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **overlays:** trigger is configured on load ([#28526](https://github.com/ionic-team/ionic-framework/issues/28526)) ([a3cd204](https://github.com/ionic-team/ionic-framework/commit/a3cd204f616606ccffc35082655e55fdfb19fe28)), closes [#28524](https://github.com/ionic-team/ionic-framework/issues/28524)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **inputs:** remove invalid legacy warnings in input, textarea, and select ([#28484](https://github.com/ionic-team/ionic-framework/issues/28484)) ([c765dcb](https://github.com/ionic-team/ionic-framework/commit/c765dcbac4148762768d8c2bea9103e7d38c510b))
|
||||
* **item:** apply safe area to proper side regardless of direction ([#28403](https://github.com/ionic-team/ionic-framework/issues/28403)) ([ed040b0](https://github.com/ionic-team/ionic-framework/commit/ed040b09e9cbd4246864e690542132defc6a6578))
|
||||
* **list:** remove border from last item with item-sliding ([#28439](https://github.com/ionic-team/ionic-framework/issues/28439)) ([cafafcc](https://github.com/ionic-team/ionic-framework/commit/cafafcc9d166ef536dcb73edd522c8f2a0fb95b6)), closes [#28435](https://github.com/ionic-team/ionic-framework/issues/28435)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.3](https://github.com/ionic-team/ionic-framework/compare/v7.5.2...v7.5.3) (2023-11-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** long words wrap to next line ([#28408](https://github.com/ionic-team/ionic-framework/issues/28408)) ([34257d6](https://github.com/ionic-team/ionic-framework/commit/34257d681e9034b0a001aa45e17222f3aab5ed76)), closes [#28406](https://github.com/ionic-team/ionic-framework/issues/28406)
|
||||
* **angular:** standalone form components do not error when multiple are used ([#28423](https://github.com/ionic-team/ionic-framework/issues/28423)) ([89698b3](https://github.com/ionic-team/ionic-framework/commit/89698b338fb05cde427c98720c238d2365abdaa7)), closes [#28418](https://github.com/ionic-team/ionic-framework/issues/28418)
|
||||
* **datetime:** allow calendar navigation in readonly mode; disallow keyboard navigation when disabled ([#28336](https://github.com/ionic-team/ionic-framework/issues/28336)) ([f6a6877](https://github.com/ionic-team/ionic-framework/commit/f6a6877044a6d912a92aab00c3c78897da09415d)), closes [#28121](https://github.com/ionic-team/ionic-framework/issues/28121)
|
||||
* **input, textarea, select:** use consistent sizes ([#28390](https://github.com/ionic-team/ionic-framework/issues/28390)) ([b31ecbb](https://github.com/ionic-team/ionic-framework/commit/b31ecbbfe8deb87604686d752e92e672dd9b277a)), closes [#28388](https://github.com/ionic-team/ionic-framework/issues/28388)
|
||||
* **list-header:** apply safe area to proper side regardless of direction ([#28371](https://github.com/ionic-team/ionic-framework/issues/28371)) ([f99d530](https://github.com/ionic-team/ionic-framework/commit/f99d5305fb4b1607b42e34a0b7653d8e1b5bf23f))
|
||||
* **segment:** avoid scrolling webkit bug ([#28376](https://github.com/ionic-team/ionic-framework/issues/28376)) ([8e2f818](https://github.com/ionic-team/ionic-framework/commit/8e2f81867175e9980e6d072b0a4414baae571223)), closes [#28373](https://github.com/ionic-team/ionic-framework/issues/28373)
|
||||
* **tab-bar:** apply safe area to proper side regardless of direction ([#28372](https://github.com/ionic-team/ionic-framework/issues/28372)) ([d47b7e7](https://github.com/ionic-team/ionic-framework/commit/d47b7e750310ceb2f2c7ecfda8343923ff8d564a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.2](https://github.com/ionic-team/ionic-framework/compare/v7.5.1...v7.5.2) (2023-10-25)
|
||||
|
||||
|
||||
|
||||
@@ -315,7 +315,6 @@ ion-checkbox,css-prop,--checkmark-width
|
||||
ion-checkbox,css-prop,--size
|
||||
ion-checkbox,css-prop,--transition
|
||||
ion-checkbox,part,container
|
||||
ion-checkbox,part,label
|
||||
ion-checkbox,part,mark
|
||||
|
||||
ion-chip,shadow
|
||||
@@ -1030,12 +1029,10 @@ ion-radio,css-prop,--color
|
||||
ion-radio,css-prop,--color-checked
|
||||
ion-radio,css-prop,--inner-border-radius
|
||||
ion-radio,part,container
|
||||
ion-radio,part,label
|
||||
ion-radio,part,mark
|
||||
|
||||
ion-radio-group,none
|
||||
ion-radio-group,prop,allowEmptySelection,boolean,false,false,false
|
||||
ion-radio-group,prop,compareWith,((currentValue: any, compareValue: any) => boolean) | null | string | undefined,undefined,false,false
|
||||
ion-radio-group,prop,name,string,this.inputId,false,false
|
||||
ion-radio-group,prop,value,any,undefined,false,false
|
||||
ion-radio-group,event,ionChange,RadioGroupChangeEventDetail<any>,true
|
||||
@@ -1079,7 +1076,6 @@ ion-range,css-prop,--pin-color
|
||||
ion-range,part,bar
|
||||
ion-range,part,bar-active
|
||||
ion-range,part,knob
|
||||
ion-range,part,label
|
||||
ion-range,part,pin
|
||||
ion-range,part,tick
|
||||
ion-range,part,tick-active
|
||||
@@ -1450,7 +1446,6 @@ ion-toast,prop,message,IonicSafeString | string | undefined,undefined,false,fals
|
||||
ion-toast,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-toast,prop,position,"bottom" | "middle" | "top",'bottom',false,false
|
||||
ion-toast,prop,positionAnchor,HTMLElement | string | undefined,undefined,false,false
|
||||
ion-toast,prop,swipeGesture,"vertical" | undefined,undefined,false,false
|
||||
ion-toast,prop,translucent,boolean,false,false,false
|
||||
ion-toast,prop,trigger,string | undefined,undefined,false,false
|
||||
ion-toast,method,dismiss,dismiss(data?: any, role?: string) => Promise<boolean>
|
||||
@@ -1517,7 +1512,6 @@ ion-toggle,css-prop,--handle-width
|
||||
ion-toggle,css-prop,--track-background
|
||||
ion-toggle,css-prop,--track-background-checked
|
||||
ion-toggle,part,handle
|
||||
ion-toggle,part,label
|
||||
ion-toggle,part,track
|
||||
|
||||
ion-toolbar,shadow
|
||||
|
||||
5283
core/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "7.6.0",
|
||||
"version": "7.5.2",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -31,7 +31,7 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@stencil/core": "^4.8.1",
|
||||
"@stencil/core": "^4.6.0",
|
||||
"ionicons": "^7.2.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -43,14 +43,15 @@
|
||||
"@capacitor/status-bar": "^5.0.6",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
"@jest/core": "^27.5.1",
|
||||
"@playwright/test": "^1.39.0",
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@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.7",
|
||||
"@types/jest": "^29.5.6",
|
||||
"@stencil/vue-output-target": "^0.8.6",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^14.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||
"@typescript-eslint/parser": "^6.7.2",
|
||||
@@ -61,10 +62,9 @@
|
||||
"eslint-plugin-custom-rules": "file:custom-rules",
|
||||
"execa": "^5.0.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-cli": "^29.7.0",
|
||||
"jest": "^27.5.1",
|
||||
"jest-cli": "^27.5.1",
|
||||
"prettier": "^2.6.1",
|
||||
"puppeteer": "21.1.1",
|
||||
"rollup": "^2.26.4",
|
||||
"sass": "^1.33.0",
|
||||
"serve": "^14.0.1",
|
||||
@@ -86,11 +86,13 @@
|
||||
"lint.sass.fix": "npm run lint.sass -- --fix",
|
||||
"lint.ts": "npm run eslint",
|
||||
"lint.ts.fix": "npm run eslint -- --fix",
|
||||
"prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next",
|
||||
"prerender.e2e": "node scripts/testing/prerender.js",
|
||||
"prettier": "prettier \"./src/**/*.{html,ts,tsx,js,jsx}\"",
|
||||
"start": "npm run build.css && stencil build --dev --watch --serve",
|
||||
"test": "npm run test.spec && npm run test.e2e",
|
||||
"test.spec": "stencil test --spec --max-workers=2",
|
||||
"test.spec.debug": "npx --node-arg=\"--inspect-brk\" stencil test --spec",
|
||||
"test.e2e": "npx playwright test",
|
||||
"test.e2e.update-snapshots": "npm run test.e2e -- --update-snapshots",
|
||||
"test.watch": "jest --watch --no-cache",
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
expect.extend({
|
||||
toHaveShadowPart(received, part) {
|
||||
if (typeof part !== 'string') {
|
||||
throw new Error('expected toHaveShadowPart to be called with a string of the CSS shadow part name');
|
||||
}
|
||||
|
||||
if (received.shadowRoot === null) {
|
||||
throw new Error('expected toHaveShadowPart to be called on an element with a shadow root');
|
||||
}
|
||||
|
||||
const shadowPart = received.shadowRoot.querySelector(`[part="${part}"]`);
|
||||
const pass = shadowPart !== null;
|
||||
|
||||
const message = `expected ${received.tagName.toLowerCase()} to have shadow part "${part}"`;
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () => message,
|
||||
};
|
||||
},
|
||||
});
|
||||
73
core/src/components.d.ts
vendored
@@ -27,7 +27,7 @@ import { PickerButton, PickerColumn } from "./components/picker/picker-interface
|
||||
import { PickerColumnItem } from "./components/picker-column-internal/picker-column-internal-interfaces";
|
||||
import { PickerInternalChangeEventDetail } from "./components/picker-internal/picker-internal-interfaces";
|
||||
import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface";
|
||||
import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
|
||||
import { RadioGroupChangeEventDetail } from "./components/radio-group/radio-group-interface";
|
||||
import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
|
||||
import { RefresherEventDetail } from "./components/refresher/refresher-interface";
|
||||
import { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
@@ -39,7 +39,7 @@ import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
|
||||
import { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
|
||||
import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
|
||||
import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
|
||||
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
|
||||
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
|
||||
import { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
|
||||
export { AccordionGroupChangeEventDetail } from "./components/accordion-group/accordion-group-interface";
|
||||
export { AnimationBuilder, AutocompleteTypes, Color, ComponentProps, ComponentRef, FrameworkDelegate, StyleEventDetail, TextFieldTypes } from "./interface";
|
||||
@@ -63,7 +63,7 @@ export { PickerButton, PickerColumn } from "./components/picker/picker-interface
|
||||
export { PickerColumnItem } from "./components/picker-column-internal/picker-column-internal-interfaces";
|
||||
export { PickerInternalChangeEventDetail } from "./components/picker-internal/picker-internal-interfaces";
|
||||
export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface";
|
||||
export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
|
||||
export { RadioGroupChangeEventDetail } from "./components/radio-group/radio-group-interface";
|
||||
export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
|
||||
export { RefresherEventDetail } from "./components/refresher/refresher-interface";
|
||||
export { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
@@ -75,7 +75,7 @@ export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
|
||||
export { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
|
||||
export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
|
||||
export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
|
||||
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
|
||||
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
|
||||
export { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
|
||||
export namespace Components {
|
||||
interface IonAccordion {
|
||||
@@ -915,7 +915,7 @@ export namespace Components {
|
||||
*/
|
||||
"presentation": DatetimePresentation;
|
||||
/**
|
||||
* If `true`, the datetime appears normal but the selected date cannot be changed.
|
||||
* If `true`, the datetime appears normal but is not interactive.
|
||||
*/
|
||||
"readonly": boolean;
|
||||
/**
|
||||
@@ -1162,7 +1162,7 @@ export namespace Components {
|
||||
*/
|
||||
"autocorrect": 'on' | 'off';
|
||||
/**
|
||||
* 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.
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
*/
|
||||
"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. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
* 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.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -2043,10 +2043,6 @@ 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
|
||||
*/
|
||||
@@ -2269,10 +2265,6 @@ export namespace Components {
|
||||
* If `true`, the radios can be deselected.
|
||||
*/
|
||||
"allowEmptySelection": boolean;
|
||||
/**
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
*/
|
||||
"compareWith"?: string | RadioGroupCompareFn | null;
|
||||
/**
|
||||
* The name of the control, which is submitted with the form data.
|
||||
*/
|
||||
@@ -2605,7 +2597,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. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
* 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.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -2692,7 +2684,7 @@ export namespace Components {
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-select. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
* A property name or function used to compare object values
|
||||
*/
|
||||
"compareWith"?: string | SelectCompareFn | null;
|
||||
/**
|
||||
@@ -2954,7 +2946,7 @@ export namespace Components {
|
||||
*/
|
||||
"autocapitalize": string;
|
||||
/**
|
||||
* 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.
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
*/
|
||||
"autofocus": boolean;
|
||||
/**
|
||||
@@ -3054,7 +3046,7 @@ export namespace Components {
|
||||
*/
|
||||
"rows"?: number;
|
||||
/**
|
||||
* 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.
|
||||
* Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -3176,10 +3168,6 @@ export namespace Components {
|
||||
* Present the toast overlay after it has been created.
|
||||
*/
|
||||
"present": () => Promise<void>;
|
||||
/**
|
||||
* If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
|
||||
*/
|
||||
"swipeGesture"?: ToastSwipeGestureDirection;
|
||||
/**
|
||||
* If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
|
||||
*/
|
||||
@@ -3398,10 +3386,6 @@ export interface IonSelectCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLIonSelectElement;
|
||||
}
|
||||
export interface IonSkeletonTextCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLIonSkeletonTextElement;
|
||||
}
|
||||
export interface IonSplitPaneCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLIonSplitPaneElement;
|
||||
@@ -4416,18 +4400,7 @@ declare global {
|
||||
prototype: HTMLIonSelectPopoverElement;
|
||||
new (): HTMLIonSelectPopoverElement;
|
||||
};
|
||||
interface HTMLIonSkeletonTextElementEventMap {
|
||||
"ionStyle": StyleEventDetail;
|
||||
}
|
||||
interface HTMLIonSkeletonTextElement extends Components.IonSkeletonText, HTMLStencilElement {
|
||||
addEventListener<K extends keyof HTMLIonSkeletonTextElementEventMap>(type: K, listener: (this: HTMLIonSkeletonTextElement, ev: IonSkeletonTextCustomEvent<HTMLIonSkeletonTextElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
||||
removeEventListener<K extends keyof HTMLIonSkeletonTextElementEventMap>(type: K, listener: (this: HTMLIonSkeletonTextElement, ev: IonSkeletonTextCustomEvent<HTMLIonSkeletonTextElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
||||
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
||||
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
||||
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
||||
}
|
||||
var HTMLIonSkeletonTextElement: {
|
||||
prototype: HTMLIonSkeletonTextElement;
|
||||
@@ -5622,7 +5595,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"presentation"?: DatetimePresentation;
|
||||
/**
|
||||
* If `true`, the datetime appears normal but the selected date cannot be changed.
|
||||
* If `true`, the datetime appears normal but is not interactive.
|
||||
*/
|
||||
"readonly"?: boolean;
|
||||
/**
|
||||
@@ -5877,7 +5850,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autocorrect"?: 'on' | 'off';
|
||||
/**
|
||||
* 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.
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
*/
|
||||
"autofocus"?: boolean;
|
||||
/**
|
||||
@@ -6710,10 +6683,6 @@ 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
|
||||
*/
|
||||
@@ -6957,10 +6926,6 @@ declare namespace LocalJSX {
|
||||
* If `true`, the radios can be deselected.
|
||||
*/
|
||||
"allowEmptySelection"?: boolean;
|
||||
/**
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
*/
|
||||
"compareWith"?: string | RadioGroupCompareFn | null;
|
||||
/**
|
||||
* The name of the control, which is submitted with the form data.
|
||||
*/
|
||||
@@ -7443,7 +7408,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-select. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
* A property name or function used to compare object values
|
||||
*/
|
||||
"compareWith"?: string | SelectCompareFn | null;
|
||||
/**
|
||||
@@ -7580,10 +7545,6 @@ declare namespace LocalJSX {
|
||||
* If `true`, the skeleton text will animate.
|
||||
*/
|
||||
"animated"?: boolean;
|
||||
/**
|
||||
* Emitted when the styles change.
|
||||
*/
|
||||
"onIonStyle"?: (event: IonSkeletonTextCustomEvent<StyleEventDetail>) => void;
|
||||
}
|
||||
interface IonSpinner {
|
||||
/**
|
||||
@@ -7730,7 +7691,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autocapitalize"?: string;
|
||||
/**
|
||||
* 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.
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
*/
|
||||
"autofocus"?: boolean;
|
||||
/**
|
||||
@@ -7982,10 +7943,6 @@ declare namespace LocalJSX {
|
||||
* The element to anchor the toast's position to. Can be set as a direct reference or the ID of the element. With `position="bottom"`, the toast will sit above the chosen element. With `position="top"`, the toast will sit below the chosen element. With `position="middle"`, the value of `positionAnchor` is ignored.
|
||||
*/
|
||||
"positionAnchor"?: HTMLElement | string;
|
||||
/**
|
||||
* If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
|
||||
*/
|
||||
"swipeGesture"?: ToastSwipeGestureDirection;
|
||||
/**
|
||||
* If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
|
||||
*/
|
||||
|
||||
@@ -180,16 +180,6 @@ 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';
|
||||
import { Item } from '../../item/item';
|
||||
import { Accordion } from '../accordion';
|
||||
import { AccordionGroup } from '../../accordion-group/accordion-group.tsx';
|
||||
import { Item } from '../../item/item.tsx';
|
||||
import { Accordion } from '../accordion.tsx';
|
||||
|
||||
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: 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)));
|
||||
max-height: 100vh;
|
||||
max-height: 100dvh;
|
||||
}
|
||||
|
||||
.action-sheet-group {
|
||||
|
||||
@@ -337,17 +337,6 @@ 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,69 +100,3 @@ 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`));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -7,17 +7,10 @@ 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',
|
||||
}}
|
||||
overlayIndex={1}
|
||||
></ion-action-sheet>
|
||||
),
|
||||
template: () => <ion-action-sheet htmlAttributes={{ 'data-testid': 'basic-action-sheet' }}></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,17 +105,6 @@
|
||||
&::-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,18 +52,9 @@
|
||||
}
|
||||
|
||||
.alert-message {
|
||||
font-size: $alert-md-message-font-size;
|
||||
}
|
||||
max-height: $alert-md-content-max-height;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
font-size: $alert-md-message-font-size;
|
||||
}
|
||||
|
||||
.alert-message:empty {
|
||||
@@ -111,24 +102,14 @@
|
||||
.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;
|
||||
|
||||
@@ -301,14 +282,3 @@
|
||||
.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,20 +10,6 @@ $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,15 +84,7 @@
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
.alert-message {
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -376,17 +376,6 @@ 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,7 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { Alert } from '../alert';
|
||||
import { config } from '../../../global/config';
|
||||
|
||||
describe('alert: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -10,7 +9,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);
|
||||
});
|
||||
@@ -22,7 +21,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);
|
||||
});
|
||||
@@ -34,7 +33,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);
|
||||
});
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
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'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before 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: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
@@ -1,7 +1,7 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Breadcrumb } from '../../breadcrumb/breadcrumb';
|
||||
import { Breadcrumbs } from '../breadcrumbs';
|
||||
import { Breadcrumb } from '../../breadcrumb/breadcrumb.tsx';
|
||||
import { Breadcrumbs } from '../breadcrumbs.tsx';
|
||||
|
||||
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,5 +1,4 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Button } from '../../button';
|
||||
|
||||
describe('Button: Hidden Form Button', () => {
|
||||
@@ -16,7 +15,8 @@ describe('Button: Hidden Form Button', () => {
|
||||
return page.body.querySelectorAll('form button');
|
||||
};
|
||||
|
||||
const button = page.body.querySelector('ion-button')!;
|
||||
const form = page.body.querySelectorAll('form');
|
||||
const button = page.body.querySelector('ion-button');
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
@@ -90,6 +90,13 @@
|
||||
}
|
||||
|
||||
.label-text-wrapper {
|
||||
/**
|
||||
* This ensures that double tapping this text
|
||||
* clicks the <label> and focuses the checkbox
|
||||
* when a screen reader is enabled.
|
||||
*/
|
||||
pointer-events: none;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
|
||||
white-space: nowrap;
|
||||
@@ -166,6 +173,7 @@ input {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
// Justify Content
|
||||
// ---------------------------------------------
|
||||
|
||||
@@ -192,6 +200,7 @@ input {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
// Label Placement - Start
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@@ -212,6 +221,7 @@ input {
|
||||
@include margin(null, $form-control-label-margin, null, 0);
|
||||
}
|
||||
|
||||
|
||||
// Label Placement - End
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@@ -232,6 +242,7 @@ input {
|
||||
@include margin(null, 0, null, $form-control-label-margin);
|
||||
}
|
||||
|
||||
|
||||
// Label Placement - Fixed
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@@ -306,6 +317,7 @@ input {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
// Disabled Checkbox
|
||||
// ---------------------------------------------
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import type { CheckboxChangeEventDetail } from './checkbox-interface';
|
||||
* @slot - The label text to associate with the checkbox. Use the "labelPlacement" property to control where the label is placed relative to the checkbox.
|
||||
*
|
||||
* @part container - The container for the checkbox mark.
|
||||
* @part label - The label text describing the checkbox.
|
||||
* @part mark - The checkmark used to indicate the checked state.
|
||||
*/
|
||||
@Component({
|
||||
@@ -165,8 +164,6 @@ export class Checkbox implements ComponentInterface {
|
||||
private emitStyle() {
|
||||
const style: StyleEventDetail = {
|
||||
'interactive-disabled': this.disabled,
|
||||
// TODO(FW-3100): remove this
|
||||
legacy: !!this.legacy,
|
||||
};
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
@@ -285,7 +282,6 @@ export class Checkbox implements ComponentInterface {
|
||||
'label-text-wrapper': true,
|
||||
'label-text-wrapper-hidden': el.textContent === '',
|
||||
}}
|
||||
part="label"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
@@ -2,23 +2,6 @@ import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Checkbox } from '../checkbox';
|
||||
|
||||
describe('ion-checkbox: shadow parts', () => {
|
||||
it('should render the checkbox with shadow parts', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Checkbox],
|
||||
html: `
|
||||
<ion-checkbox>Checkbox</ion-checkbox>
|
||||
`,
|
||||
});
|
||||
|
||||
const checkbox = page.body.querySelector('ion-checkbox')!;
|
||||
|
||||
expect(checkbox).toHaveShadowPart('container');
|
||||
expect(checkbox).toHaveShadowPart('label');
|
||||
expect(checkbox).toHaveShadowPart('mark');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ion-checkbox: disabled', () => {
|
||||
it('clicking disabled checkbox should not toggle checked state', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -28,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,3 +136,7 @@
|
||||
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
:host .datetime-view-buttons ion-button {
|
||||
color: $text-color-step-200;
|
||||
}
|
||||
|
||||
@@ -185,37 +185,13 @@ ion-picker-column-internal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host(.datetime-readonly),
|
||||
:host(.datetime-disabled) {
|
||||
pointer-events: none;
|
||||
|
||||
.calendar-days-of-week,
|
||||
.datetime-time {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
:host(.datetime-readonly) {
|
||||
pointer-events: none;
|
||||
|
||||
/**
|
||||
* Allow user to navigate months
|
||||
* while in readonly mode
|
||||
*/
|
||||
.calendar-action-buttons,
|
||||
.calendar-body,
|
||||
.datetime-year {
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabled buttons should have full opacity
|
||||
* in readonly mode
|
||||
*/
|
||||
|
||||
.calendar-day[disabled]:not(.calendar-day-constrained),
|
||||
.datetime-action-buttons ion-button[disabled] {
|
||||
opacity: 1;
|
||||
}
|
||||
:host(.datetime-disabled) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,9 +243,6 @@ ion-picker-column-internal {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// TODO(FW-3547): the styles targeting ion-item
|
||||
// can be removed if we refactor datetime to
|
||||
// not use an ion-item
|
||||
:host .calendar-action-buttons ion-item,
|
||||
:host .calendar-action-buttons ion-button {
|
||||
--background: translucent;
|
||||
@@ -279,11 +252,6 @@ ion-picker-column-internal {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
// Width is set to auto because it is set
|
||||
// to min-content elsewhere and we want to
|
||||
// prevent wrapping the label in datetime
|
||||
width: auto;
|
||||
}
|
||||
|
||||
:host .calendar-action-buttons ion-item ion-icon {
|
||||
|
||||
@@ -172,7 +172,7 @@ export class Datetime implements ComponentInterface {
|
||||
@Prop() disabled = false;
|
||||
|
||||
/**
|
||||
* If `true`, the datetime appears normal but the selected date cannot be changed.
|
||||
* If `true`, the datetime appears normal but is not interactive.
|
||||
*/
|
||||
@Prop() readonly = false;
|
||||
|
||||
@@ -599,14 +599,6 @@ export class Datetime implements ComponentInterface {
|
||||
};
|
||||
|
||||
private setActiveParts = (parts: DatetimeParts, removeDate = false) => {
|
||||
/** if the datetime component is in readonly mode,
|
||||
* allow browsing of the calendar without changing
|
||||
* the set value
|
||||
*/
|
||||
if (this.readonly) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { multiple, minParts, maxParts, activeParts } = this;
|
||||
|
||||
/**
|
||||
@@ -1132,7 +1124,7 @@ export class Datetime implements ComponentInterface {
|
||||
* so we need to re-init behavior with the new elements.
|
||||
*/
|
||||
componentDidRender() {
|
||||
const { presentation, prevPresentation, calendarBodyRef, minParts, preferWheel, forceRenderDate } = this;
|
||||
const { presentation, prevPresentation, calendarBodyRef, minParts, preferWheel } = this;
|
||||
|
||||
/**
|
||||
* TODO(FW-2165)
|
||||
@@ -1150,20 +1142,7 @@ 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)');
|
||||
/**
|
||||
* 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) {
|
||||
if (workingMonth) {
|
||||
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
||||
}
|
||||
}
|
||||
@@ -1435,13 +1414,7 @@ export class Datetime implements ComponentInterface {
|
||||
*/
|
||||
|
||||
private renderFooter() {
|
||||
const { disabled, readonly, showDefaultButtons, showClearButton } = this;
|
||||
/**
|
||||
* The cancel, clear, and confirm buttons
|
||||
* should not be interactive if the datetime
|
||||
* is disabled or readonly.
|
||||
*/
|
||||
const isButtonDisabled = disabled || readonly;
|
||||
const { showDefaultButtons, showClearButton } = this;
|
||||
const hasSlottedButtons = this.el.querySelector('[slot="buttons"]') !== null;
|
||||
if (!hasSlottedButtons && !showDefaultButtons && !showClearButton) {
|
||||
return;
|
||||
@@ -1471,33 +1444,18 @@ export class Datetime implements ComponentInterface {
|
||||
<slot name="buttons">
|
||||
<ion-buttons>
|
||||
{showDefaultButtons && (
|
||||
<ion-button
|
||||
id="cancel-button"
|
||||
color={this.color}
|
||||
onClick={() => this.cancel(true)}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
<ion-button id="cancel-button" color={this.color} onClick={() => this.cancel(true)}>
|
||||
{this.cancelText}
|
||||
</ion-button>
|
||||
)}
|
||||
<div class="datetime-action-buttons-container">
|
||||
{showClearButton && (
|
||||
<ion-button
|
||||
id="clear-button"
|
||||
color={this.color}
|
||||
onClick={() => clearButtonClick()}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
<ion-button id="clear-button" color={this.color} onClick={() => clearButtonClick()}>
|
||||
{this.clearText}
|
||||
</ion-button>
|
||||
)}
|
||||
{showDefaultButtons && (
|
||||
<ion-button
|
||||
id="confirm-button"
|
||||
color={this.color}
|
||||
onClick={() => this.confirm(true)}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
<ion-button id="confirm-button" color={this.color} onClick={() => this.confirm(true)}>
|
||||
{this.doneText}
|
||||
</ion-button>
|
||||
)}
|
||||
@@ -1538,7 +1496,7 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderCombinedDatePickerColumn() {
|
||||
const { defaultParts, disabled, workingParts, locale, minParts, maxParts, todayParts, isDateEnabled } = this;
|
||||
const { defaultParts, workingParts, locale, minParts, maxParts, todayParts, isDateEnabled } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1617,7 +1575,6 @@ export class Datetime implements ComponentInterface {
|
||||
<ion-picker-column-internal
|
||||
class="date-column"
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
items={items}
|
||||
value={todayString}
|
||||
onIonChange={(ev: CustomEvent) => {
|
||||
@@ -1729,7 +1686,7 @@ export class Datetime implements ComponentInterface {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { disabled, workingParts } = this;
|
||||
const { workingParts } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1737,7 +1694,6 @@ 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) => {
|
||||
@@ -1774,7 +1730,7 @@ export class Datetime implements ComponentInterface {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { disabled, workingParts } = this;
|
||||
const { workingParts } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1782,7 +1738,6 @@ 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) => {
|
||||
@@ -1818,7 +1773,7 @@ export class Datetime implements ComponentInterface {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { disabled, workingParts } = this;
|
||||
const { workingParts } = this;
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -1826,7 +1781,6 @@ 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) => {
|
||||
@@ -1892,7 +1846,7 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderHourPickerColumn(hoursData: PickerColumnItem[]) {
|
||||
const { disabled, workingParts } = this;
|
||||
const { workingParts } = this;
|
||||
if (hoursData.length === 0) return [];
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
@@ -1900,7 +1854,6 @@ export class Datetime implements ComponentInterface {
|
||||
return (
|
||||
<ion-picker-column-internal
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
value={activePart.hour}
|
||||
items={hoursData}
|
||||
numericInput
|
||||
@@ -1921,7 +1874,7 @@ export class Datetime implements ComponentInterface {
|
||||
);
|
||||
}
|
||||
private renderMinutePickerColumn(minutesData: PickerColumnItem[]) {
|
||||
const { disabled, workingParts } = this;
|
||||
const { workingParts } = this;
|
||||
if (minutesData.length === 0) return [];
|
||||
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
@@ -1929,7 +1882,6 @@ export class Datetime implements ComponentInterface {
|
||||
return (
|
||||
<ion-picker-column-internal
|
||||
color={this.color}
|
||||
disabled={disabled}
|
||||
value={activePart.minute}
|
||||
items={minutesData}
|
||||
numericInput
|
||||
@@ -1950,7 +1902,7 @@ export class Datetime implements ComponentInterface {
|
||||
);
|
||||
}
|
||||
private renderDayPeriodPickerColumn(dayPeriodData: PickerColumnItem[]) {
|
||||
const { disabled, workingParts } = this;
|
||||
const { workingParts } = this;
|
||||
if (dayPeriodData.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -1962,7 +1914,6 @@ 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) => {
|
||||
@@ -2006,12 +1957,11 @@ export class Datetime implements ComponentInterface {
|
||||
*/
|
||||
|
||||
private renderCalendarHeader(mode: Mode) {
|
||||
const { disabled } = this;
|
||||
const expandedIcon = mode === 'ios' ? chevronDown : caretUpSharp;
|
||||
const collapsedIcon = mode === 'ios' ? chevronForward : caretDownSharp;
|
||||
|
||||
const prevMonthDisabled = disabled || isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
|
||||
const nextMonthDisabled = disabled || isNextMonthDisabled(this.workingParts, this.maxParts);
|
||||
const prevMonthDisabled = isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
|
||||
const nextMonthDisabled = isNextMonthDisabled(this.workingParts, this.maxParts);
|
||||
|
||||
// don't use the inheritAttributes util because it removes dir from the host, and we still need that
|
||||
const hostDir = this.el.getAttribute('dir') || undefined;
|
||||
@@ -2027,7 +1977,6 @@ export class Datetime implements ComponentInterface {
|
||||
aria-label="Show year picker"
|
||||
detail={false}
|
||||
lines="none"
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
this.toggleMonthAndYearView();
|
||||
/**
|
||||
@@ -2094,28 +2043,23 @@ export class Datetime implements ComponentInterface {
|
||||
);
|
||||
}
|
||||
private renderMonth(month: number, year: number) {
|
||||
const { disabled, readonly } = this;
|
||||
|
||||
const yearAllowed = this.parsedYearValues === undefined || this.parsedYearValues.includes(year);
|
||||
const monthAllowed = this.parsedMonthValues === undefined || this.parsedMonthValues.includes(month);
|
||||
const isCalMonthDisabled = !yearAllowed || !monthAllowed;
|
||||
const isDatetimeDisabled = disabled || readonly;
|
||||
const swipeDisabled =
|
||||
disabled ||
|
||||
isMonthDisabled(
|
||||
{
|
||||
month,
|
||||
year,
|
||||
day: null,
|
||||
},
|
||||
{
|
||||
// The day is not used when checking if a month is disabled.
|
||||
// Users should be able to access the min or max month, even if the
|
||||
// min/max date is out of bounds (e.g. min is set to Feb 15, Feb should not be disabled).
|
||||
minParts: { ...this.minParts, day: null },
|
||||
maxParts: { ...this.maxParts, day: null },
|
||||
}
|
||||
);
|
||||
const swipeDisabled = isMonthDisabled(
|
||||
{
|
||||
month,
|
||||
year,
|
||||
day: null,
|
||||
},
|
||||
{
|
||||
// The day is not used when checking if a month is disabled.
|
||||
// Users should be able to access the min or max month, even if the
|
||||
// min/max date is out of bounds (e.g. min is set to Feb 15, Feb should not be disabled).
|
||||
minParts: { ...this.minParts, day: null },
|
||||
maxParts: { ...this.maxParts, day: null },
|
||||
}
|
||||
);
|
||||
// The working month should never have swipe disabled.
|
||||
// Otherwise the CSS scroll snap will not work and the user
|
||||
// can free-scroll the calendar.
|
||||
@@ -2139,14 +2083,7 @@ export class Datetime implements ComponentInterface {
|
||||
const { el, highlightedDates, isDateEnabled, multiple } = this;
|
||||
const referenceParts = { month, day, year };
|
||||
const isCalendarPadding = day === null;
|
||||
const {
|
||||
isActive,
|
||||
isToday,
|
||||
ariaLabel,
|
||||
ariaSelected,
|
||||
disabled: isDayDisabled,
|
||||
text,
|
||||
} = getCalendarDayState(
|
||||
const { isActive, isToday, ariaLabel, ariaSelected, disabled, text } = getCalendarDayState(
|
||||
this.locale,
|
||||
referenceParts,
|
||||
this.activeParts,
|
||||
@@ -2157,8 +2094,7 @@ export class Datetime implements ComponentInterface {
|
||||
);
|
||||
|
||||
const dateIsoString = convertDataToISO(referenceParts);
|
||||
|
||||
let isCalDayDisabled = isCalMonthDisabled || isDayDisabled;
|
||||
let isCalDayDisabled = isCalMonthDisabled || disabled;
|
||||
|
||||
if (!isCalDayDisabled && isDateEnabled !== undefined) {
|
||||
try {
|
||||
@@ -2177,15 +2113,6 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some days are constrained through max & min or allowed dates
|
||||
* and also disabled because the component is readonly or disabled.
|
||||
* These need to be displayed differently.
|
||||
*/
|
||||
const isCalDayConstrained = isCalDayDisabled && isDatetimeDisabled;
|
||||
|
||||
const isButtonDisabled = isCalDayDisabled || isDatetimeDisabled;
|
||||
|
||||
let dateStyle: DatetimeHighlightStyle | undefined = undefined;
|
||||
|
||||
/**
|
||||
@@ -2231,12 +2158,11 @@ export class Datetime implements ComponentInterface {
|
||||
data-year={year}
|
||||
data-index={index}
|
||||
data-day-of-week={dayOfWeek}
|
||||
disabled={isButtonDisabled}
|
||||
disabled={isCalDayDisabled}
|
||||
class={{
|
||||
'calendar-day-padding': isCalendarPadding,
|
||||
'calendar-day': true,
|
||||
'calendar-day-active': isActive,
|
||||
'calendar-day-constrained': isCalDayConstrained,
|
||||
'calendar-day-today': isToday,
|
||||
}}
|
||||
part={dateParts}
|
||||
@@ -2311,7 +2237,7 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderTimeOverlay() {
|
||||
const { disabled, hourCycle, isTimePopoverOpen, locale } = this;
|
||||
const { hourCycle, isTimePopoverOpen, locale } = this;
|
||||
const computedHourCycle = getHourCycle(locale, hourCycle);
|
||||
const activePart = this.getActivePartsWithFallback();
|
||||
|
||||
@@ -2325,7 +2251,6 @@ export class Datetime implements ComponentInterface {
|
||||
part={`time-button${isTimePopoverOpen ? ' active' : ''}`}
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
disabled={disabled}
|
||||
onClick={async (ev) => {
|
||||
const { popoverRef } = this;
|
||||
|
||||
|
||||
@@ -30,102 +30,3 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior does not differ across
|
||||
* modes/directions.
|
||||
*/
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('datetime: a11y'), () => {
|
||||
test('datetime should be keyboard navigable', async ({ page, browserName }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-22T16:30:00"></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
const monthYearButton = page.locator('.calendar-month-year ion-item');
|
||||
const prevButton = page.locator('.calendar-next-prev ion-button:nth-child(1)');
|
||||
const nextButton = page.locator('.calendar-next-prev ion-button:nth-child(2)');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(monthYearButton).toBeFocused();
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(prevButton).toBeFocused();
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(nextButton).toBeFocused();
|
||||
|
||||
// check value before & after selecting via keyboard
|
||||
const initialValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
|
||||
expect(initialValue).toBe('2022-02-22T16:30:00');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
const newValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
|
||||
expect(newValue).not.toBe('2022-02-22T16:30:00');
|
||||
});
|
||||
|
||||
test('buttons should be keyboard navigable', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
|
||||
<ion-datetime value="2022-02-22T16:30:00" show-default-buttons="true" show-clear-button="true"></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const clearButton = page.locator('#clear-button button');
|
||||
const selectedDay = page.locator('.calendar-day-active');
|
||||
|
||||
await expect(selectedDay).toHaveText('22');
|
||||
|
||||
await clearButton.focus();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(clearButton).toBeFocused();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(selectedDay).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('should navigate through months via right arrow key', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
|
||||
<ion-datetime value="2022-02-28"></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
const calendarBody = page.locator('.calendar-body');
|
||||
await expect(calendarMonthYear).toHaveText('February 2022');
|
||||
|
||||
await calendarBody.focus();
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.keyboard.press('ArrowRight');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(calendarMonthYear).toHaveText('March 2022');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@@ -1,22 +1,21 @@
|
||||
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: DatetimeParts = { month: 1, day: 1, year: 2021 };
|
||||
const reference = { 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 } as any)).toEqual(false);
|
||||
expect(isSameDay(reference, { month: null, day: null, year: null })).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBefore()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference: DatetimeParts = { month: 1, day: 1, year: 2021 };
|
||||
const reference = { 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);
|
||||
@@ -24,13 +23,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 } as any)).toEqual(false);
|
||||
expect(isBefore(reference, { month: null, day: null, year: null })).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAfter()', () => {
|
||||
it('should return correct results for month, day, and year', () => {
|
||||
const reference: DatetimeParts = { month: 2, day: 2, year: 2021 };
|
||||
const reference = { 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);
|
||||
@@ -43,6 +42,6 @@ describe('isAfter()', () => {
|
||||
* 2021 > undefined === false
|
||||
* 2021 > null === true
|
||||
*/
|
||||
expect(isAfter(reference, { month: null, day: null, year: null } as any)).toEqual(true);
|
||||
expect(isAfter(reference, { month: null, day: null, year: null })).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
generateMonths,
|
||||
getDaysOfWeek,
|
||||
@@ -365,7 +364,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -373,7 +372,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 50,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { hours } = generateTime('en-US', refValue, 'h23', minParts);
|
||||
|
||||
@@ -388,7 +387,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 22,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -396,7 +395,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { hours, minutes } = generateTime('en-US', refValue, 'h23', minParts);
|
||||
|
||||
@@ -412,7 +411,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 30,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const minParts = {
|
||||
day: undefined,
|
||||
@@ -420,7 +419,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 19,
|
||||
minute: 30,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -428,7 +427,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 20,
|
||||
minute: 40,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { hours } = generateTime('en-US', refValue, 'h23', minParts, maxParts);
|
||||
|
||||
@@ -442,7 +441,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 0,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -450,7 +449,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
|
||||
|
||||
@@ -464,7 +463,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 12,
|
||||
minute: 0,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
day: undefined,
|
||||
@@ -472,7 +471,7 @@ describe('generateTime()', () => {
|
||||
year: undefined,
|
||||
hour: 13,
|
||||
minute: 2,
|
||||
} as unknown as DatetimeParts;
|
||||
};
|
||||
|
||||
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
|
||||
|
||||
@@ -483,7 +482,7 @@ describe('generateTime()', () => {
|
||||
|
||||
describe('getToday', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.useFakeTimers('modern');
|
||||
// System time is zero based, 1 = February
|
||||
jest.setSystemTime(new Date(2022, 1, 21, 18, 30));
|
||||
});
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* This behavior does not differ across
|
||||
* modes/directions.
|
||||
*/
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config, screenshot }) => {
|
||||
test.describe(title('datetime: disabled'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-05T00:00:00" min="2022-01-01T00:00:00" max="2022-02-20T23:59:59" day-values="5,6,10,11,15,16,20" show-default-buttons disabled></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-disabled`));
|
||||
});
|
||||
|
||||
test('date should be disabled', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-28" disabled></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const febFirstButton = page.locator(`.calendar-day[data-day='1'][data-month='2']`);
|
||||
|
||||
await expect(febFirstButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test('month-year button should be disabled', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-28" disabled></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(calendarMonthYear.locator('button')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('next and prev buttons should be disabled', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-28" disabled></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const prevMonthButton = page.locator('ion-datetime .calendar-next-prev ion-button:first-of-type button');
|
||||
const nextMonthButton = page.locator('ion-datetime .calendar-next-prev ion-button:last-of-type button');
|
||||
|
||||
await expect(prevMonthButton).toBeDisabled();
|
||||
await expect(nextMonthButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test('clear button should be disabled', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
|
||||
<ion-datetime value="2022-02-22T16:30:00" show-default-buttons="true" show-clear-button="true" disabled></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const clearButton = page.locator('#clear-button button');
|
||||
|
||||
await expect(clearButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test('should not navigate through months via right arrow key', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-28" disabled></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
const calendarBody = page.locator('.calendar-body');
|
||||
await expect(calendarMonthYear).toHaveText('February 2022');
|
||||
|
||||
await calendarBody.focus();
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.keyboard.press('ArrowRight');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(calendarMonthYear).toHaveText('February 2022');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,39 +0,0 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,82 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Datetime - Disabled</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(250px, 1fr));
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #6f7378;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-datetime {
|
||||
width: 350px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Datetime - Disabled</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Inline - Default Value</h2>
|
||||
<ion-datetime id="inline-datetime-value" disabled value="2022-04-21T00:00:00"></ion-datetime>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Inline</h2>
|
||||
<ion-datetime
|
||||
id="inline-datetime"
|
||||
presentation="date"
|
||||
disabled
|
||||
show-default-buttons="true"
|
||||
show-clear-button="true"
|
||||
multiple="true"
|
||||
></ion-datetime>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<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>
|
||||
const firstDatetime = document.querySelector('#inline-datetime');
|
||||
firstDatetime.value = ['2023-08-03', '2023-08-13', '2023-08-29'];
|
||||
</script>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
generateDayAriaLabel,
|
||||
getMonthAndDay,
|
||||
@@ -110,7 +109,7 @@ describe('getLocalizedDayPeriod', () => {
|
||||
|
||||
describe('getLocalizedTime', () => {
|
||||
it('should localize the time to PM', () => {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
const datetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -122,7 +121,7 @@ describe('getLocalizedTime', () => {
|
||||
});
|
||||
|
||||
it('should localize the time to AM', () => {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
const datetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -134,7 +133,7 @@ describe('getLocalizedTime', () => {
|
||||
});
|
||||
|
||||
it('should avoid Chromium bug when using 12 hour time in a 24 hour locale', () => {
|
||||
const datetimeParts: DatetimeParts = {
|
||||
const datetimeParts = {
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2022,
|
||||
@@ -145,12 +144,12 @@ describe('getLocalizedTime', () => {
|
||||
expect(getLocalizedTime('en-GB', datetimeParts, 'h12')).toEqual('12:00 am');
|
||||
});
|
||||
it('should parse time-only values correctly', () => {
|
||||
const datetimeParts: Partial<DatetimeParts> = {
|
||||
const datetimeParts = {
|
||||
hour: 22,
|
||||
minute: 40,
|
||||
};
|
||||
|
||||
expect(getLocalizedTime('en-US', datetimeParts as DatetimeParts, 'h12')).toEqual('10:40 PM');
|
||||
expect(getLocalizedTime('en-US', datetimeParts as DatetimeParts, 'h23')).toEqual('22:40');
|
||||
expect(getLocalizedTime('en-US', datetimeParts, 'h12')).toEqual('10:40 PM');
|
||||
expect(getLocalizedTime('en-US', datetimeParts, 'h23')).toEqual('22:40');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
import {
|
||||
getPreviousYear,
|
||||
getNextYear,
|
||||
@@ -104,31 +103,31 @@ describe('getInternalHourValue()', () => {
|
||||
|
||||
describe('calculateHourFromAMPM()', () => {
|
||||
it('should correctly convert from AM to PM', () => {
|
||||
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: 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: 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);
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -42,8 +42,7 @@ describe('parseDate()', () => {
|
||||
* See https://github.com/ionic-team/ionic-framework/commit/3fb4caf21ffac12f765c4c80bf1850e05d211c6a
|
||||
*/
|
||||
it('should return the correct time zone offset', () => {
|
||||
// Casting as any since `tzOffset` does not exist on DatetimeParts
|
||||
expect((parseDate('2022-12-15T13:47:30-02:00') as any)?.tzOffset).toEqual(undefined);
|
||||
expect(parseDate('2022-12-15T13:47:30-02:00').tzOffset).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should parse an array of dates', () => {
|
||||
@@ -163,8 +162,8 @@ describe('parseMinParts()', () => {
|
||||
minute: 4,
|
||||
hour: 2,
|
||||
};
|
||||
expect(parseMinParts(undefined as any, today)).toEqual(undefined);
|
||||
expect(parseMinParts(null as any, today)).toEqual(undefined);
|
||||
expect(parseMinParts(undefined, today)).toEqual(undefined);
|
||||
expect(parseMinParts(null, today)).toEqual(undefined);
|
||||
expect(parseMinParts('foo', today)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
@@ -226,8 +225,8 @@ describe('parseMaxParts()', () => {
|
||||
minute: 4,
|
||||
hour: 2,
|
||||
};
|
||||
expect(parseMaxParts(undefined as any, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(null as any, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(undefined, today)).toEqual(undefined);
|
||||
expect(parseMaxParts(null, today)).toEqual(undefined);
|
||||
expect(parseMaxParts('foo', today)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* This behavior does not differ across
|
||||
* modes/directions.
|
||||
*/
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config, screenshot }) => {
|
||||
test.describe(title('datetime: readonly'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-05T00:00:00" min="2022-01-01T00:00:00" max="2022-02-20T23:59:59" day-values="5,6,10,11,15,16,20" show-default-buttons readonly></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-readonly`));
|
||||
});
|
||||
|
||||
test('date should be disabled', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-28" readonly></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const febFirstButton = page.locator(`.calendar-day[data-day='1'][data-month='2']`);
|
||||
|
||||
await expect(febFirstButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test('should navigate months via month-year button', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(calendarMonthYear).toHaveText('February 2022');
|
||||
|
||||
await calendarMonthYear.click();
|
||||
await page.waitForChanges();
|
||||
await page.locator('.month-column .picker-item[data-value="3"]').click();
|
||||
await page.waitForChanges();
|
||||
await expect(calendarMonthYear).toHaveText('March 2022');
|
||||
|
||||
await expect(ionChange).not.toHaveReceivedEvent();
|
||||
});
|
||||
|
||||
test('should open picker using keyboard navigation', async ({ page, browserName }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
const monthYearButton = page.locator('.calendar-month-year ion-item');
|
||||
await expect(calendarMonthYear).toHaveText('February 2022');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(monthYearButton).toBeFocused();
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForChanges();
|
||||
|
||||
const marchPickerItem = page.locator('.month-column .picker-item[data-value="3"]');
|
||||
await expect(marchPickerItem).toBeVisible();
|
||||
});
|
||||
|
||||
test('should view next month via next button', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
|
||||
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
await expect(calendarMonthYear).toHaveText('February 2022');
|
||||
|
||||
const nextMonthButton = page.locator('ion-datetime .calendar-next-prev ion-button + ion-button');
|
||||
await nextMonthButton.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(calendarMonthYear).toHaveText('March 2022');
|
||||
await expect(ionChange).not.toHaveReceivedEvent();
|
||||
});
|
||||
|
||||
test('should not change value when the month is changed via keyboard navigation', async ({ page, browserName }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
const monthYearButton = page.locator('.calendar-month-year ion-item');
|
||||
const prevButton = page.locator('.calendar-next-prev ion-button:nth-child(1)');
|
||||
const nextButton = page.locator('.calendar-next-prev ion-button:nth-child(2)');
|
||||
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(monthYearButton).toBeFocused();
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(prevButton).toBeFocused();
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(nextButton).toBeFocused();
|
||||
|
||||
// check value before & after selecting via keyboard
|
||||
const initialValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
|
||||
expect(initialValue).toBe('2022-02-22T16:30:00');
|
||||
await expect(calendarMonthYear).toHaveText('February 2022');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await page.waitForChanges();
|
||||
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(calendarMonthYear).toHaveText('January 2022');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForChanges();
|
||||
|
||||
const newValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
|
||||
// should not have changed
|
||||
expect(newValue).toBe('2022-02-22T16:30:00');
|
||||
});
|
||||
|
||||
test('clear button should be disabled', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
|
||||
<ion-datetime value="2022-02-22T16:30:00" show-default-buttons="true" show-clear-button="true" readonly></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const clearButton = page.locator('#clear-button button');
|
||||
|
||||
await expect(clearButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 19 KiB |
@@ -1,83 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Datetime - Readonly</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(250px, 1fr));
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #6f7378;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-datetime {
|
||||
width: 350px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Datetime - Readonly</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Inline</h2>
|
||||
<ion-datetime
|
||||
id="inline-datetime"
|
||||
presentation="date"
|
||||
readonly
|
||||
show-default-buttons="true"
|
||||
show-clear-button="true"
|
||||
multiple="true"
|
||||
></ion-datetime>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Inline - No Default Value</h2>
|
||||
<ion-datetime id="inline-datetime-no-value" readonly></ion-datetime>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
<script>
|
||||
const firstDatetime = document.querySelector('#inline-datetime');
|
||||
firstDatetime.value = ['2023-08-03', '2023-08-13', '2023-08-29'];
|
||||
|
||||
firstDatetime.isDateEnabled = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
const utcDay = date.getUTCDay();
|
||||
|
||||
/**
|
||||
* Date will be enabled if it is not
|
||||
* Sunday or Saturday
|
||||
*/
|
||||
return utcDay !== 0 && utcDay !== 6;
|
||||
};
|
||||
</script>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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 } as any)
|
||||
).toEqual(true);
|
||||
expect(isPrevMonthDisabled({ month: 1, year: 2021, day: null }, { year: 2021, month: null, day: null })).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 } as any)
|
||||
).toEqual(true);
|
||||
expect(isPrevMonthDisabled({ month: 5, year: 2021, day: null }, { year: 2022, month: null, day: null })).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 } as any)
|
||||
).toEqual(false);
|
||||
expect(
|
||||
isPrevMonthDisabled({ month: 2, year: 2021, day: null }, { year: 2021, month: null, day: null } as any)
|
||||
).toEqual(false);
|
||||
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
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -130,10 +130,7 @@
|
||||
* 2. This will only apply when that content has a collapse header (ion-header[collapse="condense"])
|
||||
*
|
||||
* We use opacity: 0 to avoid a layout shift.
|
||||
* We target both the attribute and the class in the event that the attribute
|
||||
* is not reflected on the host in some frameworks.
|
||||
*/
|
||||
ion-header:not(.header-collapse-main):has(~ ion-content ion-header[collapse="condense"],
|
||||
~ ion-content ion-header.header-collapse-condense) {
|
||||
ion-header:not(.header-collapse-main):has(~ ion-content ion-header[collapse="condense"]) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { config } from '../../../global/config';
|
||||
import { InfiniteScrollContent } from '../infinite-scroll-content';
|
||||
import { config } from '../../../global/config';
|
||||
|
||||
describe('infinite-scroll-content: custom html', () => {
|
||||
it('should not allow for custom html by default', async () => {
|
||||
@@ -10,7 +9,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);
|
||||
});
|
||||
@@ -22,7 +21,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);
|
||||
});
|
||||
@@ -34,7 +33,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);
|
||||
});
|
||||
|
||||
@@ -12,14 +12,6 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
private thrPx = 0;
|
||||
private thrPc = 0;
|
||||
private scrollEl?: HTMLElement;
|
||||
|
||||
/**
|
||||
* didFire exists so that ionInfinite
|
||||
* does not fire multiple times if
|
||||
* users continue to scroll after
|
||||
* scrolling into the infinite
|
||||
* scroll threshold.
|
||||
*/
|
||||
private didFire = false;
|
||||
private isBusy = false;
|
||||
|
||||
@@ -135,6 +127,8 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
this.ionInfinite.emit();
|
||||
return 3;
|
||||
}
|
||||
} else {
|
||||
this.didFire = false;
|
||||
}
|
||||
|
||||
return 4;
|
||||
@@ -196,13 +190,10 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
writeTask(() => {
|
||||
scrollEl.scrollTop = newScrollTop;
|
||||
this.isBusy = false;
|
||||
this.didFire = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.didFire = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Infinite Scroll - Small DOM Update</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>
|
||||
<style>
|
||||
#list .item {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid gray;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-content class="ion-padding" id="content">
|
||||
<div id="list"></div>
|
||||
|
||||
<ion-infinite-scroll threshold="100px" id="infinite-scroll">
|
||||
<ion-infinite-scroll-content loading-spinner="crescent" loading-text="Loading more data...">
|
||||
</ion-infinite-scroll-content>
|
||||
</ion-infinite-scroll>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const list = document.getElementById('list');
|
||||
const infiniteScroll = document.getElementById('infinite-scroll');
|
||||
|
||||
infiniteScroll.addEventListener('ionInfinite', () => {
|
||||
setTimeout(() => {
|
||||
appendItems();
|
||||
|
||||
infiniteScroll.complete();
|
||||
|
||||
// Custom event consumed in the e2e tests
|
||||
window.dispatchEvent(new CustomEvent('ionInfiniteComplete'));
|
||||
}, 500);
|
||||
});
|
||||
|
||||
function appendItems(count = 3) {
|
||||
for (var i = 0; i < count; i++) {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('item');
|
||||
el.textContent = `${1 + i}`;
|
||||
list.appendChild(el);
|
||||
}
|
||||
}
|
||||
appendItems(30);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,34 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('infinite-scroll: appending small amounts to dom'), () => {
|
||||
test('should load more after remaining in threshold', async ({ page }) => {
|
||||
await page.goto('/src/components/infinite-scroll/test/small-dom-update', config);
|
||||
|
||||
const ionInfiniteComplete = await page.spyOnEvent('ionInfiniteComplete');
|
||||
const content = page.locator('ion-content');
|
||||
const items = page.locator('#list .item');
|
||||
expect(await items.count()).toBe(30);
|
||||
|
||||
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
|
||||
await ionInfiniteComplete.next();
|
||||
|
||||
/**
|
||||
* Even after appending we'll still be within
|
||||
* the infinite scroll's threshold
|
||||
*/
|
||||
expect(await items.count()).toBe(33);
|
||||
|
||||
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
|
||||
await ionInfiniteComplete.next();
|
||||
|
||||
/**
|
||||
* Scrolling down again without leaving
|
||||
* the threshold should still trigger
|
||||
* infinite scroll again.
|
||||
*/
|
||||
expect(await items.count()).toBe(36);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -66,15 +66,20 @@
|
||||
|
||||
:host(.input-fill-outline.input-label-placement-stacked) .label-text-wrapper,
|
||||
:host(.input-fill-outline.input-label-placement-floating) .label-text-wrapper {
|
||||
@include transform(translateY(-50%), scale(1));
|
||||
@include transform-origin(start, top);
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
||||
/**
|
||||
* Label text should not extend
|
||||
* beyond the bounds of the input.
|
||||
*/
|
||||
max-width: calc(100% - var(--padding-start) - var(--padding-end));
|
||||
|
||||
transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1), transform 150ms cubic-bezier(0.4, 0, 0.2, 1),
|
||||
top 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,9 +94,12 @@
|
||||
/**
|
||||
* This makes the label sit above the input.
|
||||
*/
|
||||
:host(.label-floating.input-fill-outline) .label-text-wrapper {
|
||||
:host(.has-focus.input-fill-outline.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.has-value.input-fill-outline.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.input-fill-outline.input-label-placement-stacked) .label-text-wrapper {
|
||||
@include transform(translateY(-32%), scale(#{$form-control-label-stacked-scale}));
|
||||
@include margin(0);
|
||||
top: 0;
|
||||
|
||||
/**
|
||||
* Label text should not extend
|
||||
@@ -214,6 +222,8 @@
|
||||
* the floating/stacked label. We simulate this "cut out"
|
||||
* by removing the top border from the notch fragment.
|
||||
*/
|
||||
:host(.label-floating.input-fill-outline) .input-outline-notch {
|
||||
:host(.has-focus.input-fill-outline.input-label-placement-floating) .input-outline-notch,
|
||||
:host(.has-value.input-fill-outline.input-label-placement-floating) .input-outline-notch,
|
||||
:host(.input-fill-outline.input-label-placement-stacked) .input-outline-notch {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,9 @@
|
||||
// Input Label
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
:host(.label-floating.input-fill-solid.input-label-placement-floating) .label-text-wrapper {
|
||||
:host(.input-fill-solid.input-label-placement-stacked) .label-text-wrapper,
|
||||
:host(.has-focus.input-fill-solid.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.has-value.input-fill-solid.input-label-placement-floating) .label-text-wrapper {
|
||||
/**
|
||||
* Label text should not extend
|
||||
* beyond the bounds of the input.
|
||||
|
||||
@@ -322,9 +322,6 @@
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
// ensure start/end slot content is vertically centered
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -644,7 +641,9 @@
|
||||
/**
|
||||
* This makes the label sit above the input.
|
||||
*/
|
||||
:host(.label-floating) .label-text-wrapper {
|
||||
:host(.input-label-placement-stacked) .label-text-wrapper,
|
||||
:host(.has-focus.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.has-value.input-label-placement-floating) .label-text-wrapper {
|
||||
@include transform(translateY(50%), scale(#{$form-control-label-stacked-scale}));
|
||||
|
||||
/**
|
||||
@@ -653,14 +652,3 @@
|
||||
*/
|
||||
max-width: calc(100% / #{$form-control-label-stacked-scale});
|
||||
}
|
||||
|
||||
// Start/End Slots
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
::slotted([slot="start"]) {
|
||||
margin-inline-end: $form-control-label-margin;
|
||||
}
|
||||
|
||||
::slotted([slot="end"]) {
|
||||
margin-inline-start: $form-control-label-margin;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ import { getCounterText } from './input.utils';
|
||||
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
||||
*
|
||||
* @slot label - The label text to associate with the input. Use the `labelPlacement` property to control where the label is placed relative to the input. Use this if you need to render a label with custom HTML. (EXPERIMENTAL)
|
||||
* @slot start - Content to display at the leading edge of the input. (EXPERIMENTAL)
|
||||
* @slot end - Content to display at the trailing edge of the input. (EXPERIMENTAL)
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-input',
|
||||
@@ -97,9 +95,7 @@ export class Input implements ComponentInterface {
|
||||
@Prop() autocorrect: 'on' | 'off' = 'off';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
*/
|
||||
@Prop() autofocus = false;
|
||||
|
||||
@@ -373,7 +369,7 @@ export class Input implements ComponentInterface {
|
||||
const { el } = this;
|
||||
|
||||
this.legacyFormController = createLegacyFormController(el);
|
||||
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
|
||||
this.slotMutationController = createSlotMutationController(el, 'label', () => forceUpdate(this));
|
||||
this.notchController = createNotchController(
|
||||
el,
|
||||
() => this.notchSpacerEl,
|
||||
@@ -428,8 +424,6 @@ 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() {
|
||||
@@ -498,8 +492,6 @@ export class Input implements ComponentInterface {
|
||||
'has-value': this.hasValue(),
|
||||
'has-focus': this.hasFocus,
|
||||
'interactive-disabled': this.disabled,
|
||||
// TODO(FW-2764): remove this
|
||||
legacy: !!this.legacy,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -703,42 +695,18 @@ export class Input implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderInput() {
|
||||
const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus } = this;
|
||||
const { disabled, fill, readonly, shape, inputId, labelPlacement } = this;
|
||||
const mode = getIonMode(this);
|
||||
const value = this.getValue();
|
||||
const inItem = hostContext('ion-item', this.el);
|
||||
const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
|
||||
|
||||
const hasValue = this.hasValue();
|
||||
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
|
||||
|
||||
/**
|
||||
* If the label is stacked, it should always sit above the input.
|
||||
* For floating labels, the label should move above the input if
|
||||
* the input has a value, is focused, or has anything in either
|
||||
* the start or end slot.
|
||||
*
|
||||
* If there is content in the start slot, the label would overlap
|
||||
* it if not forced to float. This is also applied to the end slot
|
||||
* because with the default or solid fills, the input is not
|
||||
* vertically centered in the container, but the label is. This
|
||||
* causes the slots and label to appear vertically offset from each
|
||||
* other when the label isn't floating above the input. This doesn't
|
||||
* apply to the outline fill, but this was not accounted for to keep
|
||||
* things consistent.
|
||||
*
|
||||
* TODO(FW-5592): Remove hasStartEndSlots condition
|
||||
*/
|
||||
const labelShouldFloat =
|
||||
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
||||
|
||||
return (
|
||||
<Host
|
||||
class={createColorClasses(this.color, {
|
||||
[mode]: true,
|
||||
'has-value': hasValue,
|
||||
'has-focus': hasFocus,
|
||||
'label-floating': labelShouldFloat,
|
||||
'has-value': this.hasValue(),
|
||||
'has-focus': this.hasFocus,
|
||||
[`input-fill-${fill}`]: fill !== undefined,
|
||||
[`input-shape-${shape}`]: shape !== undefined,
|
||||
[`input-label-placement-${labelPlacement}`]: true,
|
||||
@@ -747,16 +715,9 @@ export class Input implements ComponentInterface {
|
||||
'input-disabled': disabled,
|
||||
})}
|
||||
>
|
||||
{/**
|
||||
* htmlFor is needed so that clicking the label always focuses
|
||||
* the input. Otherwise, if the start slot has something
|
||||
* interactable, clicking the label would focus that instead
|
||||
* since it comes before the input in the DOM.
|
||||
*/}
|
||||
<label class="input-wrapper" htmlFor={inputId}>
|
||||
<label class="input-wrapper">
|
||||
{this.renderLabelContainer()}
|
||||
<div class="native-wrapper">
|
||||
<slot name="start"></slot>
|
||||
<input
|
||||
class="native-input"
|
||||
ref={(input) => (this.nativeInput = input)}
|
||||
@@ -811,7 +772,6 @@ export class Input implements ComponentInterface {
|
||||
<ion-icon aria-hidden="true" icon={mode === 'ios' ? closeCircle : closeSharp}></ion-icon>
|
||||
</button>
|
||||
)}
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
{shouldRenderHighlight && <div class="input-highlight"></div>}
|
||||
</label>
|
||||
|
||||
@@ -21,13 +21,7 @@
|
||||
<ion-input label="Email" label-placement="stacked" value="hi@ionic.io"></ion-input><br />
|
||||
<ion-input label="Email" label-placement="floating"></ion-input> <br />
|
||||
<ion-input label="Email" label-placement="floating" fill="outline" value="hi@ionic.io"></ion-input> <br />
|
||||
<ion-input label="Email" label-placement="floating" fill="solid" value="hi@ionic.io"></ion-input><br />
|
||||
<ion-input label="Email" fill="solid" value="hi@ionic.io">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button slot="end" aria-label="button">
|
||||
<ion-icon slot="icon-only" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
<ion-input label="Email" label-placement="floating" fill="solid" value="hi@ionic.io"></ion-input>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Input } from '../input';
|
||||
|
||||
describe('input: rendering', () => {
|
||||
@@ -9,7 +8,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');
|
||||
@@ -64,9 +63,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');
|
||||
});
|
||||
@@ -78,9 +77,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');
|
||||
});
|
||||
@@ -92,9 +91,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,7 +1,6 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Item } from '../../../item/item';
|
||||
import { Input } from '../../input';
|
||||
import { Item } from '../../../item/item';
|
||||
|
||||
it('should render as modern when label is set asynchronously', async () => {
|
||||
const page = await newSpecPage({
|
||||
@@ -13,7 +12,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);
|
||||
|
||||
@@ -231,4 +231,28 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-label-layering`));
|
||||
});
|
||||
});
|
||||
|
||||
test.describe(title('input: floating label custom font size'), () => {
|
||||
test('large label should be centered when field is not focused', async ({ page }, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/27194',
|
||||
});
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input
|
||||
style="font-size: 30px"
|
||||
fill="outline"
|
||||
label="Large floating label"
|
||||
label-placement="floating"
|
||||
></ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
|
||||
await expect(input).toHaveScreenshot(screenshot('input-large-floating-label'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
@@ -29,7 +29,7 @@
|
||||
<h2>Inactive</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<h2>Focused</h2>
|
||||
<ion-item class="item-has-focus">
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<h2>Activated</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<h2>Hover</h2>
|
||||
<ion-item class="item-hovered">
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input legacy="true"></ion-input>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<h2>Disabled</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input legacy="true" disabled></ion-input>
|
||||
<ion-input disabled></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<h2>Toggle Placeholder</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input legacy="true" id="floatingToggle" type="password"></ion-input>
|
||||
<ion-input id="floatingToggle" type="password"></ion-input>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
slot="end"
|
||||
@@ -89,7 +89,7 @@
|
||||
<h2>Inactive</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input legacy="true"></ion-input>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<h2>Focused</h2>
|
||||
<ion-item class="item-has-focus">
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<h2>Activated</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
<h2>Hover</h2>
|
||||
<ion-item class="item-hovered">
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input legacy="true"></ion-input>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<h2>Disabled</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input legacy="true" disabled></ion-input>
|
||||
<ion-input disabled></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
<h2>Toggle Placeholder</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input legacy="true" id="stackedToggle" type="password"></ion-input>
|
||||
<ion-input id="stackedToggle" type="password"></ion-input>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
slot="end"
|
||||
|
||||
@@ -49,125 +49,53 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content" class="ion-padding">
|
||||
<h1>Label Slot</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Start Label</h2>
|
||||
<h2>No Fill / Start</h2>
|
||||
<ion-input label-placement="start" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Solid / Start Label</h2>
|
||||
<h2>Solid / Start</h2>
|
||||
<ion-input label-placement="start" fill="solid" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Start Label</h2>
|
||||
<h2>Outline / Start</h2>
|
||||
<ion-input label-placement="start" fill="outline" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Floating Label</h2>
|
||||
<h2>No Fill / Floating</h2>
|
||||
<ion-input label-placement="floating" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Solid / Floating Label</h2>
|
||||
<h2>Solid / Floating</h2>
|
||||
<ion-input label-placement="floating" fill="solid" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Floating Label</h2>
|
||||
<h2>Outline / Floating</h2>
|
||||
<ion-input label-placement="floating" fill="outline" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Start/End Slots</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Start Label</h2>
|
||||
<ion-input label-placement="start" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Solid / Start Label</h2>
|
||||
<ion-input label-placement="start" fill="solid" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Start Label</h2>
|
||||
<ion-input label-placement="start" fill="outline" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Floating Label</h2>
|
||||
<ion-input label-placement="floating" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Solid / Floating Label</h2>
|
||||
<ion-input label-placement="floating" fill="solid" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Floating Label</h2>
|
||||
<ion-input label-placement="floating" fill="outline" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Async Slot Content</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Async Label</h2>
|
||||
<h2>Outline / Floating / Async</h2>
|
||||
<ion-input id="solid-async" label-placement="floating" fill="outline" value="hi@ionic.io"></ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Async Decorations</h2>
|
||||
<ion-input id="async-decorations" label-placement="floating" fill="outline" label="Email"></ion-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-button onclick="addSlot()">Add Slotted Content</ion-button>
|
||||
@@ -178,65 +106,29 @@
|
||||
|
||||
<script>
|
||||
const solidAsync = document.querySelector('#solid-async');
|
||||
const asyncDecos = document.querySelector('#async-decorations');
|
||||
|
||||
const getSlottedLabel = () => {
|
||||
const getSlottedContent = () => {
|
||||
return solidAsync.querySelector('[slot="label"]');
|
||||
};
|
||||
|
||||
const getSlottedStartDeco = () => {
|
||||
return asyncDecos.querySelector('[slot="start"]');
|
||||
};
|
||||
|
||||
const getSlottedEndDeco = () => {
|
||||
return asyncDecos.querySelector('[slot="end"]');
|
||||
};
|
||||
|
||||
const addSlot = () => {
|
||||
if (getSlottedLabel() === null) {
|
||||
if (getSlottedContent() === null) {
|
||||
const labelEl = document.createElement('div');
|
||||
labelEl.slot = 'label';
|
||||
labelEl.innerHTML = 'Email <span class="required">*</span>';
|
||||
|
||||
solidAsync.appendChild(labelEl);
|
||||
}
|
||||
|
||||
if (getSlottedStartDeco() === null) {
|
||||
const startEl = document.createElement('div');
|
||||
startEl.slot = 'start';
|
||||
startEl.innerHTML = 'Start';
|
||||
|
||||
asyncDecos.insertAdjacentElement('afterbegin', startEl);
|
||||
}
|
||||
|
||||
if (getSlottedEndDeco() === null) {
|
||||
const endEl = document.createElement('div');
|
||||
endEl.slot = 'end';
|
||||
endEl.innerHTML = 'End';
|
||||
|
||||
asyncDecos.insertAdjacentElement('beforeend', endEl);
|
||||
}
|
||||
};
|
||||
|
||||
const removeSlot = () => {
|
||||
const slottedLabel = getSlottedLabel();
|
||||
if (slottedLabel !== null) {
|
||||
slottedLabel.remove();
|
||||
}
|
||||
|
||||
const slottedStartDeco = getSlottedStartDeco();
|
||||
if (slottedStartDeco !== null) {
|
||||
slottedStartDeco.remove();
|
||||
}
|
||||
|
||||
const slottedEndDeco = getSlottedEndDeco();
|
||||
if (slottedEndDeco !== null) {
|
||||
slottedEndDeco.remove();
|
||||
if (getSlottedContent() !== null) {
|
||||
solidAsync.querySelector('[slot="label"]').remove();
|
||||
}
|
||||
};
|
||||
|
||||
const updateSlot = () => {
|
||||
const slottedContent = getSlottedLabel();
|
||||
const slottedContent = getSlottedContent();
|
||||
|
||||
if (slottedContent !== null) {
|
||||
slottedContent.textContent = 'This is my really really really long text';
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('input: start and end slots (visual checks)'), () => {
|
||||
test('should not have visual regressions with a start-positioned label', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="start" fill="solid" value="100" label="Weight" clear-input="true">
|
||||
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
|
||||
<ion-label slot="end">lbs</ion-label>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-slots-label-start`));
|
||||
});
|
||||
|
||||
test('should not have visual regressions with a floating label', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="floating" fill="solid" value="100" label="Weight" clear-input="true">
|
||||
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
|
||||
<ion-label slot="end">lbs</ion-label>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-slots-label-floating`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('input: start and end slots (functionality checks)'), () => {
|
||||
test('should raise floating label when there is content in the start slot', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="floating" fill="solid" label="Weight">
|
||||
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveClass(/label-floating/);
|
||||
});
|
||||
|
||||
test('should raise floating label when there is content in the end slot', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="floating" fill="solid" label="Weight">
|
||||
<ion-icon slot="end" name="barbell" aria-hidden="true"></ion-icon>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveClass(/label-floating/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |