Compare commits

...

33 Commits

Author SHA1 Message Date
ionitron
ae173317ec v7.0.0-rc.4 2023-03-27 16:15:39 +00:00
Liam DeBeasi
b7e46038e0 fix(many): innerHTML is disabled by default (#27029)
BREAKING CHANGE:

The `innerHTMLTemplatesEnabled` Ionic Config now defaults to `false`. Developers can set this option to `true` if they would like to continue to use custom HTML features in `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, and `ion-toast`.
2023-03-27 12:07:00 -04:00
Liam DeBeasi
b148384a13 chore(): sync with main
chore(): sync with main
2023-03-24 17:38:59 -04:00
Liam DeBeasi
e9208eae53 chore(): update textarea test 2023-03-24 16:22:30 -04:00
ionitron
8b1a41646e chore(): add updated snapshots 2023-03-24 19:59:09 +00:00
Liam DeBeasi
317f4eefec test(select): update wrapping tests for v7 2023-03-24 19:37:27 +00:00
Liam DeBeasi
4349f1c2c8 test(datetime): update timezone test for v7 2023-03-24 19:13:12 +00:00
ionitron
83537bc5cf chore(): add updated snapshots 2023-03-24 18:59:37 +00:00
Liam DeBeasi
eb10a2e5f6 chore(): sync with main 2023-03-24 14:37:39 -04:00
Liam DeBeasi
3b99c31bab fix(radio-group): radios participate in form submission (#27018)
resolves #27016
2023-03-24 12:43:30 -04:00
Shawn Taylor
739f5706db test(datetime): add tests for parseDate (#27015) 2023-03-24 11:57:55 -04:00
Brandy Carney
19c1e25399 fix(select): inherit white-space in select-text to allow text wrapping (#26973)
fixes #19949

Co-authored-by: Shreyas <telishreyas10@gmail.com>
2023-03-24 10:53:25 -04:00
Liam DeBeasi
e23fd9ecee fix(fab, tab-button): rtl alignment in safari and firefox (#26986)
resolves #22739
2023-03-23 17:17:09 -04:00
Liam DeBeasi
3e0a905e81 fix(segment): change event fires when clicking (#27010)
resolves #27002
2023-03-23 16:18:43 -04:00
Brandy Carney
6e04562b3b chore(readme): update examples of conference app to include react conference app (#27013) 2023-03-23 14:46:57 -04:00
dependabot[bot]
53af005122 chore(deps-dev): bump @playwright/test from 1.31.2 to 1.32.0 in /core (#27003) 2023-03-23 12:44:59 -04:00
Liam DeBeasi
342d448767 merge feature-6.7
Feature 6.7
2023-03-23 11:30:41 -04:00
Liam DeBeasi
7fef2909e3 merge release-6.7.0
Release 6.7.0
2023-03-23 11:07:32 -04:00
ionitron
5f3ed2b3e8 chore(): update package lock files 2023-03-23 14:43:10 +00:00
ionitron
f2ea05d9a2 v6.7.0 2023-03-23 14:42:57 +00:00
Liam DeBeasi
1348205358 chore(): sync with main
chore(): sync with main
2023-03-23 10:33:58 -04:00
Liam DeBeasi
68113b8741 Merge remote-tracking branch 'origin/main' into sync-feature-67-23-2 2023-03-23 09:59:16 -04:00
Liam DeBeasi
d59a181879 chore(ci): update release scripts (#27008) 2023-03-23 09:59:09 -04:00
Liam DeBeasi
841cbe337d chore(): sync with main
chore(): sync with main
2023-03-23 09:35:25 -04:00
Liam DeBeasi
cac3ad4c84 Merge remote-tracking branch 'origin/main' into sync-67-23 2023-03-23 09:17:19 -04:00
Liam DeBeasi
a0e88ea65f chore(ci): remove unused workflows (#27000) 2023-03-22 16:32:11 -04:00
Sean Perkins
26a7e86338 test(ripple-effect): reduce flakiness (#26985) 2023-03-22 15:37:00 -04:00
Liam DeBeasi
3b0af7c55d feat(config): add option to disable custom html functionality (#26956) 2023-03-22 13:59:59 -04:00
Liam DeBeasi
af16d40930 test(angular, vue): add note on how to sync changes (#26958) 2023-03-22 13:59:40 -04:00
Liam DeBeasi
6c639ff3a5 chore(): sync with main
chore(): sync with main
2023-03-22 12:35:27 -04:00
Liam DeBeasi
79ba8816f4 merge release-7.0.0-rc.3
Release 7.0.0 rc.3
2023-03-22 11:50:57 -04:00
Liam DeBeasi
ec10847706 chore(): clean up changelogs 2023-03-22 11:06:17 -04:00
ionitron
388a63f672 chore(): update package lock files 2023-03-22 14:59:58 +00:00
192 changed files with 997 additions and 169 deletions

View File

@@ -35,6 +35,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Textarea](#version-7x-textarea)
- [Toggle](#version-7x-toggle)
- [Virtual Scroll](#version-7x-virtual-scroll)
- [Config](#version-7x-config)
- [Types](#version-7x-types)
- [Overlay Attribute Interfaces](#version-7x-overlay-attribute-interfaces)
- [JavaScript Frameworks](#version-7x-javascript-frameworks)
@@ -297,6 +298,10 @@ Developers using the component will need to migrate to a virtual scroll solution
Any references to the virtual scroll types from `@ionic/core` have been removed. Please remove or replace these types: `Cell`, `VirtualNode`, `CellType`, `NodeChange`, `HeaderFn`, `ItemHeightFn`, `FooterHeightFn`, `ItemRenderFn` and `DomRenderFn`.
<h2 id="version-7x-config">Config</h2>
- `innerHTMLTemplatesEnabled` defaults to `false`. Developers who wish to use the `innerHTML` functionality inside of `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, and `ion-toast` must set this config to `true` and properly sanitize their content.
<h2 id="version-7x-types">Types</h2>
<h4 id="version-7x-overlay-attribute-interfaces">Overlay Attribute Interfaces</h4>

View File

@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [7.0.0-rc.4](https://github.com/ionic-team/ionic-framework/compare/v6.7.0...v7.0.0-rc.4) (2023-03-27)
### Bug Fixes
* **fab, tab-button:** rtl alignment in safari and firefox ([#26986](https://github.com/ionic-team/ionic-framework/issues/26986)) ([e23fd9e](https://github.com/ionic-team/ionic-framework/commit/e23fd9eceed289284eb047261c0d6bdab6ac5e8e)), closes [#22739](https://github.com/ionic-team/ionic-framework/issues/22739)
* **many:** innerHTML is disabled by default ([#27029](https://github.com/ionic-team/ionic-framework/issues/27029)) ([b7e4603](https://github.com/ionic-team/ionic-framework/commit/b7e46038e0eee611bb9f5d83772804e83b19a63d))
* **radio-group:** radios participate in form submission ([#27018](https://github.com/ionic-team/ionic-framework/issues/27018)) ([3b99c31](https://github.com/ionic-team/ionic-framework/commit/3b99c31bab41bf7fcec340ac7159d3e8fce126c1)), closes [#27016](https://github.com/ionic-team/ionic-framework/issues/27016)
* **segment:** change event fires when clicking ([#27010](https://github.com/ionic-team/ionic-framework/issues/27010)) ([3e0a905](https://github.com/ionic-team/ionic-framework/commit/3e0a905e81b2308450dcd2dc20489b7988f0e647)), closes [#27002](https://github.com/ionic-team/ionic-framework/issues/27002)
* **select:** inherit white-space in select-text to allow text wrapping ([#26973](https://github.com/ionic-team/ionic-framework/issues/26973)) ([19c1e25](https://github.com/ionic-team/ionic-framework/commit/19c1e25399ca18c8e6a8dd39c0131979c0bb01e9)), closes [#19949](https://github.com/ionic-team/ionic-framework/issues/19949)
### BREAKING CHANGES
* **many:** The `innerHTMLTemplatesEnabled` Ionic Config now defaults to `false`. Developers can set this option to `true` if they would like to continue to use custom HTML features in `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, and `ion-toast`.
# [7.0.0-rc.3](https://github.com/ionic-team/ionic-framework/compare/v6.6.3...v7.0.0-rc.3) (2023-03-22)
@@ -283,9 +301,33 @@ Developers can add the following CSS to their global stylesheet if they need the
# [6.7.0](https://github.com/ionic-team/ionic-framework/compare/v6.6.3...v6.7.0) (2023-03-23)
### Features
* **config:** add option to disable custom html functionality ([#26956](https://github.com/ionic-team/ionic-framework/issues/26956)) ([3b0af7c](https://github.com/ionic-team/ionic-framework/commit/3b0af7c55d4fa039be33d6605414761494d5af8f))
# [7.0.0-rc.3](https://github.com/ionic-team/ionic-framework/compare/v7.0.0-rc.2...v7.0.0-rc.3) (2023-03-22)
**Note:** Version bump only for package ionic-framework
## [6.6.3](https://github.com/ionic-team/ionic-framework/compare/v6.6.2...v6.6.3) (2023-03-22)
**Note:** Version bump only for package ionic-framework
### Bug Fixes
* **menu:** main content is not scrollable while swiping ([#26976](https://github.com/ionic-team/ionic-framework/issues/26976)) ([88bd8a4](https://github.com/ionic-team/ionic-framework/commit/88bd8a47c5e844d1d3a2b3b13621826faf776afb)), closes [#21193](https://github.com/ionic-team/ionic-framework/issues/21193)

View File

@@ -42,7 +42,7 @@
<span> · </span>
<a href="https://blog.ionicframework.com/">Blog</a>
<br />
Community:
Community:
<a href="https://ionic.link/discord">Discord</a>
<span> · </span>
<a href="https://forum.ionicframework.com/">Forums</a>
@@ -78,8 +78,12 @@ Already have an Ionic app? These guides will help you migrate to the latest vers
### Examples
The [Ionic Conference App](https://github.com/ionic-team/ionic-conference-app) is a full featured Ionic app.
It is the perfect starting point for learning and building your own app.
The Ionic Conference App is a full featured Ionic app. It is the perfect starting point for learning and building your own app.
- [Angular Ionic Conference App](https://github.com/ionic-team/ionic-conference-app)
- [React Ionic Conference App](https://github.com/ionic-team/ionic-react-conference-app)
<!-- TODO(FW-3811): add this when the vue conference app is updated -->
<!-- - [Vue Ionic Conference App](https://github.com/ionic-team/ionic-vue-conference-app) -->
### Contributing

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [7.0.0-rc.4](https://github.com/ionic-team/ionic/compare/v6.7.0...v7.0.0-rc.4) (2023-03-27)
# [7.0.0-rc.3](https://github.com/ionic-team/ionic/compare/v6.6.3...v7.0.0-rc.3) (2023-03-22)
@@ -147,6 +151,22 @@ Angular:
# [6.7.0](https://github.com/ionic-team/ionic/compare/v6.6.3...v6.7.0) (2023-03-23)
**Note:** Version bump only for package @ionic/angular
# [7.0.0-rc.3](https://github.com/ionic-team/ionic/compare/v7.0.0-rc.2...v7.0.0-rc.3) (2023-03-22)
**Note:** Version bump only for package @ionic/angular
## [6.6.3](https://github.com/ionic-team/ionic/compare/v6.6.2...v6.6.3) (2023-03-22)
**Note:** Version bump only for package @ionic/angular

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "7.0.0-rc.3",
"version": "7.0.0-rc.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "7.0.0-rc.3",
"version": "7.0.0-rc.4",
"license": "MIT",
"dependencies": {
"@ionic/core": "^7.0.0-rc.3",
"@ionic/core": "^7.0.0-rc.4",
"ionicons": "^7.0.0",
"jsonc-parser": "^3.0.0",
"tslib": "^2.3.0"
@@ -1227,9 +1227,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "7.0.0-rc.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.0-rc.2.tgz",
"integrity": "sha512-zkBRi2iF+dVd80Y1dFjqcVDfSAsKwyfk+S7S+iTDPqjBPNoA8N9i85evP6Sl1+w6pgyrD+3Rxn3CjmIMsZv7iw==",
"version": "7.0.0-rc.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.0-rc.3.tgz",
"integrity": "sha512-gRwEk0UwARAnpRUhUpr7GT+0YzxV1da88pybfE6Zl+zNNgjH4hQYfwQhOZL2kmW3wAMOFtbG2xnu6G1kT6A2fA==",
"dependencies": {
"@stencil/core": "^3.1.0",
"ionicons": "^7.0.0",
@@ -8104,9 +8104,9 @@
"dev": true
},
"@ionic/core": {
"version": "7.0.0-rc.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.0-rc.2.tgz",
"integrity": "sha512-zkBRi2iF+dVd80Y1dFjqcVDfSAsKwyfk+S7S+iTDPqjBPNoA8N9i85evP6Sl1+w6pgyrD+3Rxn3CjmIMsZv7iw==",
"version": "7.0.0-rc.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.0.0-rc.3.tgz",
"integrity": "sha512-gRwEk0UwARAnpRUhUpr7GT+0YzxV1da88pybfE6Zl+zNNgjH4hQYfwQhOZL2kmW3wAMOFtbG2xnu6G1kT6A2fA==",
"requires": {
"@stencil/core": "^3.1.0",
"ionicons": "^7.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "7.0.0-rc.3",
"version": "7.0.0-rc.4",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@@ -47,7 +47,7 @@
}
},
"dependencies": {
"@ionic/core": "^7.0.0-rc.3",
"@ionic/core": "^7.0.0-rc.4",
"ionicons": "^7.0.0",
"jsonc-parser": "^3.0.0",
"tslib": "^2.3.0"

View File

@@ -2,6 +2,18 @@
Ionic Framework supports multiple versions of Angular. As a result, we need to verify that Ionic works correctly with each of these Angular versions.
## Syncing Local Changes
The Angular test app supports syncing your locally built changes for validation.
1. Build the `core` and `packages/angular` projects using `npm run build`.
2. [Build the Angular test app](#test-app-build-structure).
3. Navigate to the built test app.
4. Install dependencies using `npm install`.
5. Sync your local changes using `npm run sync`.
From here you can either build the application or start a local dev server. When re-syncing changes, you will need to [wipe or disable the application cache](#application-cache).
## Application Cache
Angular CLI creates a cache of several files on disk by default in the `.angular` directory. This decreases the time taken to build the test application. However, the cache makes it difficult to quickly sync and check local changes of Ionic. As a result, the `.angular` cache is disabled by default in the test app projects.

View File

@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [7.0.0-rc.4](https://github.com/ionic-team/ionic/compare/v6.7.0...v7.0.0-rc.4) (2023-03-27)
### Bug Fixes
* **fab, tab-button:** rtl alignment in safari and firefox ([#26986](https://github.com/ionic-team/ionic/issues/26986)) ([e23fd9e](https://github.com/ionic-team/ionic/commit/e23fd9eceed289284eb047261c0d6bdab6ac5e8e)), closes [#22739](https://github.com/ionic-team/ionic/issues/22739)
* **many:** innerHTML is disabled by default ([#27029](https://github.com/ionic-team/ionic/issues/27029)) ([b7e4603](https://github.com/ionic-team/ionic/commit/b7e46038e0eee611bb9f5d83772804e83b19a63d))
* **radio-group:** radios participate in form submission ([#27018](https://github.com/ionic-team/ionic/issues/27018)) ([3b99c31](https://github.com/ionic-team/ionic/commit/3b99c31bab41bf7fcec340ac7159d3e8fce126c1)), closes [#27016](https://github.com/ionic-team/ionic/issues/27016)
* **segment:** change event fires when clicking ([#27010](https://github.com/ionic-team/ionic/issues/27010)) ([3e0a905](https://github.com/ionic-team/ionic/commit/3e0a905e81b2308450dcd2dc20489b7988f0e647)), closes [#27002](https://github.com/ionic-team/ionic/issues/27002)
* **select:** inherit white-space in select-text to allow text wrapping ([#26973](https://github.com/ionic-team/ionic/issues/26973)) ([19c1e25](https://github.com/ionic-team/ionic/commit/19c1e25399ca18c8e6a8dd39c0131979c0bb01e9)), closes [#19949](https://github.com/ionic-team/ionic/issues/19949)
### BREAKING CHANGES
* **many:** The `innerHTMLTemplatesEnabled` Ionic Config now defaults to `false`. Developers can set this option to `true` if they would like to continue to use custom HTML features in `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, and `ion-toast`.
# [7.0.0-rc.3](https://github.com/ionic-team/ionic/compare/v6.6.3...v7.0.0-rc.3) (2023-03-22)
@@ -273,6 +291,25 @@ Developers can add the following CSS to their global stylesheet if they need the
# [6.7.0](https://github.com/ionic-team/ionic/compare/v6.6.3...v6.7.0) (2023-03-23)
### Features
* **config:** add option to disable custom html functionality ([#26956](https://github.com/ionic-team/ionic/issues/26956)) ([3b0af7c](https://github.com/ionic-team/ionic/commit/3b0af7c55d4fa039be33d6605414761494d5af8f))
# [7.0.0-rc.3](https://github.com/ionic-team/ionic/compare/v7.0.0-rc.2...v7.0.0-rc.3) (2023-03-22)
**Note:** Version bump only for package @ionic/core
## [6.6.3](https://github.com/ionic-team/ionic/compare/v6.6.2...v6.6.3) (2023-03-22)
### Bug Fixes

50
core/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "7.0.0-rc.3",
"version": "7.0.0-rc.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "7.0.0-rc.3",
"version": "7.0.0-rc.4",
"license": "MIT",
"dependencies": {
"@stencil/core": "^3.1.0",
@@ -18,7 +18,7 @@
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@jest/core": "^27.5.1",
"@playwright/test": "^1.31.1",
"@playwright/test": "^1.32.0",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.5.0",
@@ -1501,13 +1501,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.31.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.2.tgz",
"integrity": "sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==",
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.0.tgz",
"integrity": "sha512-zOdGloaF0jeec7hqoLqM5S3L2rR4WxMJs6lgiAeR70JlH7Ml54ZPoIIf3X7cvnKde3Q9jJ/gaxkFh8fYI9s1rg==",
"dev": true,
"dependencies": {
"@types/node": "*",
"playwright-core": "1.31.2"
"playwright-core": "1.32.0"
},
"bin": {
"playwright": "cli.js"
@@ -8150,14 +8150,14 @@
}
},
"node_modules/playwright": {
"version": "1.31.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.31.2.tgz",
"integrity": "sha512-jpC47n2PKQNtzB7clmBuWh6ftBRS/Bt5EGLigJ9k2QAKcNeYXZkEaDH5gmvb6+AbcE0DO6GnXdbl9ogG6Eh+og==",
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.0.tgz",
"integrity": "sha512-zQVzxTGoC/ak2Zu0l3CeBGQ6z5oOka5ecUOk+5QbmAerih6CaVsjY9BjjhiN+UOImd3xLiNeCcmLEWcXlz1Dlg==",
"dev": true,
"hasInstallScript": true,
"peer": true,
"dependencies": {
"playwright-core": "1.31.2"
"playwright-core": "1.32.0"
},
"bin": {
"playwright": "cli.js"
@@ -8167,9 +8167,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.31.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz",
"integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==",
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
"dev": true,
"bin": {
"playwright": "cli.js"
@@ -11398,14 +11398,14 @@
}
},
"@playwright/test": {
"version": "1.31.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.2.tgz",
"integrity": "sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==",
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.0.tgz",
"integrity": "sha512-zOdGloaF0jeec7hqoLqM5S3L2rR4WxMJs6lgiAeR70JlH7Ml54ZPoIIf3X7cvnKde3Q9jJ/gaxkFh8fYI9s1rg==",
"dev": true,
"requires": {
"@types/node": "*",
"fsevents": "2.3.2",
"playwright-core": "1.31.2"
"playwright-core": "1.32.0"
}
},
"@rollup/plugin-node-resolve": {
@@ -16275,19 +16275,19 @@
}
},
"playwright": {
"version": "1.31.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.31.2.tgz",
"integrity": "sha512-jpC47n2PKQNtzB7clmBuWh6ftBRS/Bt5EGLigJ9k2QAKcNeYXZkEaDH5gmvb6+AbcE0DO6GnXdbl9ogG6Eh+og==",
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.0.tgz",
"integrity": "sha512-zQVzxTGoC/ak2Zu0l3CeBGQ6z5oOka5ecUOk+5QbmAerih6CaVsjY9BjjhiN+UOImd3xLiNeCcmLEWcXlz1Dlg==",
"dev": true,
"peer": true,
"requires": {
"playwright-core": "1.31.2"
"playwright-core": "1.32.0"
}
},
"playwright-core": {
"version": "1.31.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz",
"integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==",
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
"dev": true
},
"postcss": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "7.0.0-rc.3",
"version": "7.0.0-rc.4",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -40,7 +40,7 @@
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@jest/core": "^27.5.1",
"@playwright/test": "^1.31.1",
"@playwright/test": "^1.32.0",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.5.0",

View File

@@ -272,7 +272,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
@@ -1135,7 +1135,7 @@ export namespace Components {
*/
"loadingSpinner"?: SpinnerTypes | null;
/**
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"loadingText"?: string | IonicSafeString;
}
@@ -1554,7 +1554,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Optional text content to display in the loading indicator.
* Optional text content to display in the loading indicator. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
@@ -2382,7 +2382,7 @@ export namespace Components {
*/
"pullingIcon"?: SpinnerTypes | string | null;
/**
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"pullingText"?: string | IonicSafeString;
/**
@@ -2390,7 +2390,7 @@ export namespace Components {
*/
"refreshingSpinner"?: SpinnerTypes | null;
/**
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"refreshingText"?: string | IonicSafeString;
}
@@ -3118,7 +3118,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Message to be shown in the toast.
* Message to be shown in the toast. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
@@ -4235,7 +4235,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
@@ -5169,7 +5169,7 @@ declare namespace LocalJSX {
*/
"loadingSpinner"?: SpinnerTypes | null;
/**
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"loadingText"?: string | IonicSafeString;
}
@@ -5584,7 +5584,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Optional text content to display in the loading indicator.
* Optional text content to display in the loading indicator. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
@@ -6421,7 +6421,7 @@ declare namespace LocalJSX {
*/
"pullingIcon"?: SpinnerTypes | string | null;
/**
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"pullingText"?: string | IonicSafeString;
/**
@@ -6429,7 +6429,7 @@ declare namespace LocalJSX {
*/
"refreshingSpinner"?: SpinnerTypes | null;
/**
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"refreshingText"?: string | IonicSafeString;
}
@@ -7214,7 +7214,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Message to be shown in the toast.
* Message to be shown in the toast. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**

View File

@@ -1,8 +1,10 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Listen, Method, Prop, Watch, forceUpdate, h } from '@stencil/core';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type { AnimationBuilder, CssClassMap, OverlayInterface, FrameworkDelegate } from '../../interface';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import type { Gesture } from '../../utils/gesture';
import { createButtonActiveGesture } from '../../utils/gesture/button-active';
import {
@@ -43,7 +45,7 @@ import { mdLeaveAnimation } from './animations/md.leave';
export class Alert implements ComponentInterface, OverlayInterface {
private readonly delegateController = createDelegateController(this);
private readonly triggerController = createTriggerController();
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
private activeId?: string;
private inputType?: string;
private processedInputs: AlertInput[] = [];
@@ -105,6 +107,11 @@ export class Alert implements ComponentInterface, OverlayInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() message?: string | IonicSafeString;
@@ -671,6 +678,19 @@ export class Alert implements ComponentInterface, OverlayInterface {
);
}
private renderAlertMessage(msgId: string) {
const { customHTMLEnabled, message } = this;
if (customHTMLEnabled) {
return <div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(message)}></div>;
}
return (
<div id={msgId} class="alert-message">
{message}
</div>
);
}
render() {
const { overlayIndex, header, subHeader, message, htmlAttributes } = this;
const mode = getIonMode(this);
@@ -723,7 +743,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
)}
</div>
<div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(message)}></div>
{this.renderAlertMessage(msgId)}
{this.renderAlertInputs()}
{this.renderAlertButtons()}

View File

@@ -0,0 +1,40 @@
import { newSpecPage } from '@stencil/core/testing';
import { Alert } from '../alert';
import { config } from '../../../global/config';
describe('alert: custom html', () => {
it('should not allow for custom html by default', async () => {
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});
const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});
const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});
it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});
const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
});

View File

@@ -58,6 +58,12 @@
</ion-app>
<script>
window.Ionic = {
config: {
innerHTMLTemplatesEnabled: true,
},
};
async function openAlert(opts) {
const alert = await alertController.create(opts);
await alert.present();

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,4 +1,4 @@
import { clampDate, getPartsFromCalendarDay, parseAmPm, parseMinParts, parseMaxParts } from '../utils/parse';
import { clampDate, getPartsFromCalendarDay, parseAmPm, parseDate, parseMinParts, parseMaxParts } from '../utils/parse';
describe('getPartsFromCalendarDay()', () => {
it('should extract DatetimeParts from a calendar day element', () => {
@@ -17,7 +17,55 @@ describe('getPartsFromCalendarDay()', () => {
});
});
// TODO FW-2794: parseDate()
describe('parseDate()', () => {
it('should return undefined when passed undefined', () => {
expect(parseDate(undefined)).toStrictEqual(undefined);
});
it('should return undefined when passed null', () => {
expect(parseDate(null)).toStrictEqual(undefined);
});
it('should return the correct date object when passed a date', () => {
expect(parseDate('2022-12-15T13:47')).toEqual({
ampm: 'pm',
day: 15,
hour: 13,
minute: 47,
month: 12,
year: 2022,
});
});
/**
* Note: As Ionic v7 datetime no longer parses time zone information/
* See https://github.com/ionic-team/ionic-framework/commit/3fb4caf21ffac12f765c4c80bf1850e05d211c6a
*/
it('should return the correct time zone offset', () => {
expect(parseDate('2022-12-15T13:47:30-02:00').tzOffset).toEqual(undefined);
});
it('should parse an array of dates', () => {
expect(parseDate(['2022-12-15T13:47', '2023-03-23T20:19:33.517Z'])).toEqual([
{
ampm: 'pm',
day: 15,
hour: 13,
minute: 47,
month: 12,
year: 2022,
},
{
ampm: 'pm',
day: 23,
hour: 20,
minute: 19,
month: 3,
year: 2023,
},
]);
});
});
describe('clampDate()', () => {
const minParts = {

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -3,6 +3,7 @@ import { Component, Host, Prop, h } from '@stencil/core';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import type { IonicSafeString } from '../../utils/sanitization';
import { sanitizeDOMString } from '../../utils/sanitization';
import type { SpinnerTypes } from '../spinner/spinner-configs';
@@ -15,6 +16,8 @@ import type { SpinnerTypes } from '../spinner/spinner-configs';
},
})
export class InfiniteScrollContent implements ComponentInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
/**
* An animated SVG spinner that shows while loading.
*/
@@ -28,6 +31,11 @@ export class InfiniteScrollContent implements ComponentInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() loadingText?: string | IonicSafeString;
@@ -41,6 +49,15 @@ export class InfiniteScrollContent implements ComponentInterface {
}
}
private renderLoadingText() {
const { customHTMLEnabled, loadingText } = this;
if (customHTMLEnabled) {
return <div class="infinite-loading-text" innerHTML={sanitizeDOMString(loadingText)}></div>;
}
return <div class="infinite-loading-text">{this.loadingText}</div>;
}
render() {
const mode = getIonMode(this);
return (
@@ -58,9 +75,7 @@ export class InfiniteScrollContent implements ComponentInterface {
<ion-spinner name={this.loadingSpinner} />
</div>
)}
{this.loadingText !== undefined && (
<div class="infinite-loading-text" innerHTML={sanitizeDOMString(this.loadingText)} />
)}
{this.loadingText !== undefined && this.renderLoadingText()}
</div>
</Host>
);

View File

@@ -0,0 +1,40 @@
import { newSpecPage } from '@stencil/core/testing';
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 () => {
const page = await newSpecPage({
components: [InfiniteScrollContent],
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');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [InfiniteScrollContent],
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');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});
it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [InfiniteScrollContent],
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');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 234 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -4,6 +4,7 @@ import { Watch, Component, Element, Event, Host, Method, Prop, h } from '@stenci
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type { AnimationBuilder, FrameworkDelegate, OverlayInterface } from '../../interface';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import { raf } from '../../utils/helpers';
import {
BACKDROP,
@@ -41,6 +42,7 @@ import { mdLeaveAnimation } from './animations/md.leave';
export class Loading implements ComponentInterface, OverlayInterface {
private readonly delegateController = createDelegateController(this);
private readonly triggerController = createTriggerController();
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
private durationTimeout?: ReturnType<typeof setTimeout>;
private currentTransition?: Promise<any>;
@@ -75,6 +77,11 @@ export class Loading implements ComponentInterface, OverlayInterface {
/**
* Optional text content to display in the loading indicator.
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() message?: string | IonicSafeString;
@@ -296,6 +303,19 @@ export class Loading implements ComponentInterface, OverlayInterface {
this.dismiss(undefined, BACKDROP);
};
private renderLoadingMessage(msgId: string) {
const { customHTMLEnabled, message } = this;
if (customHTMLEnabled) {
return <div class="loading-content" id={msgId} innerHTML={sanitizeDOMString(message)}></div>;
}
return (
<div class="loading-content" id={msgId}>
{message}
</div>
);
}
render() {
const { message, spinner, htmlAttributes, overlayIndex } = this;
const mode = getIonMode(this);
@@ -335,9 +355,7 @@ export class Loading implements ComponentInterface, OverlayInterface {
</div>
)}
{message !== undefined && (
<div class="loading-content" id={msgId} innerHTML={sanitizeDOMString(message)}></div>
)}
{message !== undefined && this.renderLoadingMessage(msgId)}
</div>
<div tabindex="0"></div>

View File

@@ -100,6 +100,12 @@
</ion-content>
</ion-app>
<script>
window.Ionic = {
config: {
innerHTMLTemplatesEnabled: true,
},
};
async function openLoading(opts) {
const loading = await loadingController.create(opts);
await loading.present();

View File

@@ -0,0 +1,40 @@
import { newSpecPage } from '@stencil/core/testing';
import { Loading } from '../loading';
import { config } from '../../../global/config';
describe('alert: custom html', () => {
it('should not allow for custom html by default', async () => {
const page = await newSpecPage({
components: [Loading],
html: `<ion-loading message="<button class='custom-html'>Custom Text</button>"></ion-loading>`,
});
const content = page.body.querySelector('.loading-content');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [Loading],
html: `<ion-loading message="<button class='custom-html'>Custom Text</button>"></ion-loading>`,
});
const content = page.body.querySelector('.loading-content');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});
it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [Loading],
html: `<ion-loading message="<button class='custom-html'>Custom Text</button>"></ion-loading>`,
});
const content = page.body.querySelector('.loading-content');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
});

View File

@@ -162,9 +162,7 @@ test.describe('modal: incorrect usage', () => {
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
await modal.evaluate((el: HTMLIonModalElement) => {
el.setCurrentBreakpoint(0.5);
});
await modal.evaluate((el: HTMLIonModalElement) => el.setCurrentBreakpoint(0.5));
expect(warnings.length).toBe(1);
expect(warnings[0]).toBe('[Ionic Warning]: setCurrentBreakpoint is only supported on sheet modals.');

View File

@@ -2,6 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Listen, Prop, Watch, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { renderHiddenInput } from '../../utils/helpers';
import type { RadioGroupChangeEventDetail } from './radio-group-interface';
@@ -188,9 +189,11 @@ export class RadioGroup implements ComponentInterface {
}
render() {
const { label, labelId } = this;
const { label, labelId, el, name, value } = this;
const mode = getIonMode(this);
renderHiddenInput(true, el, name, value, false);
return <Host role="radiogroup" aria-labelledby={label ? labelId : null} onClick={this.onClick} class={mode}></Host>;
}
}

View File

@@ -31,3 +31,37 @@ test.describe('radio-group: form', () => {
await expect(value).toHaveText('');
});
});
test.describe('radio-group: form submission', () => {
test('should submit radio data in a form', async ({ page, skip }) => {
skip.rtl();
skip.mode('md');
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/27016',
});
await page.setContent(`
<form>
<ion-radio-group value="a" name="my-group">
<ion-radio value="a"></ion-radio>
<ion-radio value="b"></ion-radio>
<ion-radio value="c"></ion-radio>
</ion-radio-group>
</form>
`);
const radioGroupData = await page.evaluate(() => {
const form = document.querySelector('form');
if (!form) {
return;
}
const formData = new FormData(form);
return formData.get('my-group');
});
await expect(radioGroupData).toBe('a');
});
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -4,6 +4,7 @@ import { arrowDown, caretBackSharp } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import { isPlatform } from '../../utils/platform';
import type { IonicSafeString } from '../../utils/sanitization';
import { sanitizeDOMString } from '../../utils/sanitization';
@@ -14,6 +15,8 @@ import { SPINNERS } from '../spinner/spinner-configs';
tag: 'ion-refresher-content',
})
export class RefresherContent implements ComponentInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
@Element() el!: HTMLIonRefresherContentElement;
/**
@@ -31,6 +34,11 @@ export class RefresherContent implements ComponentInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() pullingText?: string | IonicSafeString;
@@ -47,6 +55,11 @@ export class RefresherContent implements ComponentInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() refreshingText?: string | IonicSafeString;
@@ -68,6 +81,24 @@ export class RefresherContent implements ComponentInterface {
}
}
private renderPullingText() {
const { customHTMLEnabled, pullingText } = this;
if (customHTMLEnabled) {
return <div class="refresher-pulling-text" innerHTML={sanitizeDOMString(pullingText)}></div>;
}
return <div class="refresher-pulling-text">{pullingText}</div>;
}
private renderRefreshingText() {
const { customHTMLEnabled, refreshingText } = this;
if (customHTMLEnabled) {
return <div class="refresher-refreshing-text" innerHTML={sanitizeDOMString(refreshingText)}></div>;
}
return <div class="refresher-refreshing-text">{refreshingText}</div>;
}
render() {
const pullingIcon = this.pullingIcon;
const hasSpinner = pullingIcon != null && (SPINNERS[pullingIcon] as any) !== undefined;
@@ -93,9 +124,7 @@ export class RefresherContent implements ComponentInterface {
<ion-icon icon={this.pullingIcon} lazy={false} aria-hidden="true"></ion-icon>
</div>
)}
{this.pullingText !== undefined && (
<div class="refresher-pulling-text" innerHTML={sanitizeDOMString(this.pullingText)}></div>
)}
{this.pullingText !== undefined && this.renderPullingText()}
</div>
<div class="refresher-refreshing">
{this.refreshingSpinner && (
@@ -103,9 +132,7 @@ export class RefresherContent implements ComponentInterface {
<ion-spinner name={this.refreshingSpinner}></ion-spinner>
</div>
)}
{this.refreshingText !== undefined && (
<div class="refresher-refreshing-text" innerHTML={sanitizeDOMString(this.refreshingText)}></div>
)}
{this.refreshingText !== undefined && this.renderRefreshingText()}
</div>
</Host>
);

View File

@@ -0,0 +1,52 @@
import { newSpecPage } from '@stencil/core/testing';
import { RefresherContent } from '../refresher-content';
import { config } from '../../../global/config';
describe('refresher-content: custom html', () => {
it('should not allow for custom html by default', async () => {
const page = await newSpecPage({
components: [RefresherContent],
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-refreshing-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
});
const pullingContent = page.body.querySelector('.refresher-pulling-text');
expect(pullingContent.textContent).toContain('Custom Pulling Text');
expect(pullingContent.querySelector('button.custom-pulling-html')).toBe(null);
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
expect(refreshingContent.querySelector('button.custom-refreshing-html')).toBe(null);
});
it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [RefresherContent],
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-refreshing-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
});
const pullingContent = page.body.querySelector('.refresher-pulling-text');
expect(pullingContent.textContent).toContain('Custom Pulling Text');
expect(pullingContent.querySelector('button.custom-pulling-html')).not.toBe(null);
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
expect(refreshingContent.querySelector('button.custom-refreshing-html')).not.toBe(null);
});
it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [RefresherContent],
html: `<ion-refresher-content pulling-text="<button class='custom-pulling-html'>Custom Pulling Text</button>" refreshing-text="<button class='custom-html'>Custom Refreshing Text</button>"></ion-refresher-content>`,
});
const pullingContent = page.body.querySelector('.refresher-pulling-text');
expect(pullingContent.textContent).toContain('Custom Pulling Text');
expect(pullingContent.querySelector('button.custom-pulling-html')).toBe(null);
const refreshingContent = page.body.querySelector('.refresher-refreshing-text');
expect(refreshingContent.textContent).toContain('Custom Refreshing Text');
expect(refreshingContent.querySelector('button.custom-refreshing-html')).toBe(null);
});
});

View File

@@ -19,6 +19,7 @@ test.describe('ripple-effect: basic', () => {
test.describe('ripple effect with nested ion-button', () => {
test('should add .ion-activated when the block is pressed', async ({ page }) => {
await page.goto('/src/components/ripple-effect/test/basic?ionic:_testing=false');
await isIdleCallbackComplete(page);
const el = page.locator('#ripple-with-button');
@@ -45,6 +46,7 @@ test.describe('ripple-effect: basic', () => {
const verifyRippleEffect = async (page: E2EPage, selector: string) => {
await page.goto('/src/components/ripple-effect/test/basic?ionic:_testing=false');
await isIdleCallbackComplete(page);
const el = page.locator(selector);
@@ -61,3 +63,24 @@ const verifyRippleEffect = async (page: E2EPage, selector: string) => {
await expect(el).toHaveClass(/ion-activated/);
};
/**
* This function is used to wait for the idle callback to be called.
* It mirrors the custom implementation in app.tsx for either
* using requestIdleCallback on supported browsers or a setTimeout
* of 32ms (~2 frames) on unsupported browsers (Safari).
*/
const isIdleCallbackComplete = async (page: E2EPage) => {
await page.waitForFunction(
() => {
return new Promise((resolve) => {
if ('requestIdleCallback' in window) {
window.requestIdleCallback(resolve);
} else {
setTimeout(resolve, 32);
}
});
},
{ timeout: 5000 }
);
};

View File

@@ -24,7 +24,6 @@ import type { SegmentChangeEventDetail } from './segment-interface';
})
export class Segment implements ComponentInterface {
private gesture?: Gesture;
private checked?: HTMLIonSegmentButtonElement;
// Value before the segment is dragged
private valueBeforeGesture?: string;
@@ -227,6 +226,10 @@ export class Segment implements ComponentInterface {
return Array.from(this.el.querySelectorAll('ion-segment-button'));
}
private get checked() {
return this.getButtons().find((button) => button.value === this.value);
}
/**
* The gesture blocks the segment button ripple. This
* function adds the ripple based on the checked segment
@@ -341,9 +344,6 @@ export class Segment implements ComponentInterface {
const index = buttons.findIndex((button) => button.value === this.value);
const next = index + 1;
// Keep track of the currently checked button
this.checked = buttons.find((button) => button.value === this.value);
for (const button of buttons) {
button.classList.remove('segment-button-after-checked');
}
@@ -474,8 +474,6 @@ export class Segment implements ComponentInterface {
this.setCheckedClasses();
}
}
this.checked = current;
};
private getSegmentButton = (selector: 'first' | 'last' | 'next' | 'previous'): HTMLIonSegmentButtonElement | null => {
@@ -533,7 +531,7 @@ export class Segment implements ComponentInterface {
if (keyDownSelectsButton) {
const previous = this.checked;
this.checkButton(this.checked || current, current);
this.checkButton(previous || current, current);
if (current !== previous) {
this.emitValueChange();
}

View File

@@ -175,4 +175,30 @@ test.describe('segment: events: ionChange', () => {
expect(ionChangeSpy).toHaveReceivedEventTimes(0);
expect(await segment.evaluate((el: HTMLIonSegmentElement) => el.value)).toBe('2');
});
test('should emit when clicking after changing value programmatically', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/27002',
});
await page.setContent(`
<ion-segment value="1" swipe-gesture="false">
<ion-segment-button value="1">One</ion-segment-button>
<ion-segment-button value="2">Two</ion-segment-button>
<ion-segment-button value="3">Three</ion-segment-button>
</ion-segment>
`);
const segment = page.locator('ion-segment');
const segmentButtons = segment.locator('ion-segment-button');
const ionChangeSpy = await page.spyOnEvent('ionChange');
await segment.evaluate((el: HTMLIonSegmentElement) => (el.value = '2'));
await segmentButtons.nth(0).click();
await ionChangeSpy.next();
expect(ionChangeSpy).toHaveReceivedEventDetail({ value: '1' });
});
});

View File

@@ -50,8 +50,9 @@
font-family: $font-family-base;
cursor: pointer;
white-space: nowrap;
cursor: pointer;
z-index: $z-index-item-input;
}
@@ -181,7 +182,7 @@ button {
text-overflow: ellipsis;
white-space: nowrap;
white-space: inherit;
overflow: hidden;
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,46 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('select: wrapping', () => {
test('should not wrap text by default', async ({ page, skip }) => {
skip.rtl();
await page.setContent(`
<ion-select value="nowrap">
<ion-select-option value="nowrap">Should not wrap when no label exists and no class is added to make the text wrap</ion-select-option>
</ion-select>
`);
const select = page.locator('ion-select');
await expect(select).toHaveScreenshot(`select-nowrap-${page.getSnapshotSettings()}.png`);
});
test('should wrap text with class', async ({ page, skip }) => {
skip.rtl();
await page.setContent(`
<ion-select value="wrap" class="ion-text-wrap">
<ion-select-option value="wrap">Should wrap when no label exists and really long text exists to make it wrap the text</ion-select-option>
</ion-select>
`);
const select = page.locator('ion-select');
await expect(select).toHaveScreenshot(`select-wrap-${page.getSnapshotSettings()}.png`);
});
test('should not wrap label while wrapping text with class', async ({ page, skip }) => {
skip.rtl();
await page.setContent(`
<ion-item>
<ion-label>Really long label should not wrap</ion-label>
<ion-select value="wrap" class="ion-text-wrap">
<ion-select-option value="wrap">Should wrap value only when label exists and really long text exists to make it wrap the text</ion-select-option>
</ion-select>
</ion-item>
`);
const select = page.locator('ion-item');
await expect(select).toHaveScreenshot(`select-wrap-with-label-${page.getSnapshotSettings()}.png`);
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

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