chore(): sync with main
17
CHANGELOG.md
@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
# [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
|
**Note:** Version bump only for package ionic-framework
|
||||||
@ -13,6 +24,12 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
|||||||
|
|
||||||
## [6.6.3](https://github.com/ionic-team/ionic-framework/compare/v6.6.2...v6.6.3) (2023-03-22)
|
## [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
|
### 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)
|
* **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)
|
||||||
|
@ -78,8 +78,12 @@ Already have an Ionic app? These guides will help you migrate to the latest vers
|
|||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
The [Ionic Conference App](https://github.com/ionic-team/ionic-conference-app) is a full featured Ionic app.
|
The Ionic Conference App is a full featured Ionic app. It is the perfect starting point for learning and building your own 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
|
### Contributing
|
||||||
|
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
# [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
|
**Note:** Version bump only for package @ionic/angular
|
||||||
|
@ -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.
|
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
|
## 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.
|
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.
|
||||||
|
@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
# [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
|
**Note:** Version bump only for package @ionic/core
|
||||||
|
46
core/package-lock.json
generated
@ -18,7 +18,7 @@
|
|||||||
"@ionic/eslint-config": "^0.3.0",
|
"@ionic/eslint-config": "^0.3.0",
|
||||||
"@ionic/prettier-config": "^2.0.0",
|
"@ionic/prettier-config": "^2.0.0",
|
||||||
"@jest/core": "^27.5.1",
|
"@jest/core": "^27.5.1",
|
||||||
"@playwright/test": "^1.31.1",
|
"@playwright/test": "^1.32.0",
|
||||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||||
"@rollup/plugin-virtual": "^2.0.3",
|
"@rollup/plugin-virtual": "^2.0.3",
|
||||||
"@stencil/angular-output-target": "^0.5.0",
|
"@stencil/angular-output-target": "^0.5.0",
|
||||||
@ -1501,13 +1501,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.31.2",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.2.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.0.tgz",
|
||||||
"integrity": "sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==",
|
"integrity": "sha512-zOdGloaF0jeec7hqoLqM5S3L2rR4WxMJs6lgiAeR70JlH7Ml54ZPoIIf3X7cvnKde3Q9jJ/gaxkFh8fYI9s1rg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"playwright-core": "1.31.2"
|
"playwright-core": "1.32.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@ -8150,14 +8150,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.31.2",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.31.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.0.tgz",
|
||||||
"integrity": "sha512-jpC47n2PKQNtzB7clmBuWh6ftBRS/Bt5EGLigJ9k2QAKcNeYXZkEaDH5gmvb6+AbcE0DO6GnXdbl9ogG6Eh+og==",
|
"integrity": "sha512-zQVzxTGoC/ak2Zu0l3CeBGQ6z5oOka5ecUOk+5QbmAerih6CaVsjY9BjjhiN+UOImd3xLiNeCcmLEWcXlz1Dlg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.31.2"
|
"playwright-core": "1.32.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@ -8167,9 +8167,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.31.2",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
|
||||||
"integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==",
|
"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@ -11398,14 +11398,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@playwright/test": {
|
"@playwright/test": {
|
||||||
"version": "1.31.2",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.2.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.0.tgz",
|
||||||
"integrity": "sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==",
|
"integrity": "sha512-zOdGloaF0jeec7hqoLqM5S3L2rR4WxMJs6lgiAeR70JlH7Ml54ZPoIIf3X7cvnKde3Q9jJ/gaxkFh8fYI9s1rg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"fsevents": "2.3.2",
|
"fsevents": "2.3.2",
|
||||||
"playwright-core": "1.31.2"
|
"playwright-core": "1.32.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@rollup/plugin-node-resolve": {
|
"@rollup/plugin-node-resolve": {
|
||||||
@ -16275,19 +16275,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"playwright": {
|
"playwright": {
|
||||||
"version": "1.31.2",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.31.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.0.tgz",
|
||||||
"integrity": "sha512-jpC47n2PKQNtzB7clmBuWh6ftBRS/Bt5EGLigJ9k2QAKcNeYXZkEaDH5gmvb6+AbcE0DO6GnXdbl9ogG6Eh+og==",
|
"integrity": "sha512-zQVzxTGoC/ak2Zu0l3CeBGQ6z5oOka5ecUOk+5QbmAerih6CaVsjY9BjjhiN+UOImd3xLiNeCcmLEWcXlz1Dlg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"playwright-core": "1.31.2"
|
"playwright-core": "1.32.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"playwright-core": {
|
"playwright-core": {
|
||||||
"version": "1.31.2",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.0.tgz",
|
||||||
"integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==",
|
"integrity": "sha512-Z9Ij17X5Z3bjpp6XKujGBp9Gv4eViESac9aDmwgQFUEJBW0K80T21m/Z+XJQlu4cNsvPygw33b6V1Va6Bda5zQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
"@ionic/eslint-config": "^0.3.0",
|
"@ionic/eslint-config": "^0.3.0",
|
||||||
"@ionic/prettier-config": "^2.0.0",
|
"@ionic/prettier-config": "^2.0.0",
|
||||||
"@jest/core": "^27.5.1",
|
"@jest/core": "^27.5.1",
|
||||||
"@playwright/test": "^1.31.1",
|
"@playwright/test": "^1.32.0",
|
||||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||||
"@rollup/plugin-virtual": "^2.0.3",
|
"@rollup/plugin-virtual": "^2.0.3",
|
||||||
"@stencil/angular-output-target": "^0.5.0",
|
"@stencil/angular-output-target": "^0.5.0",
|
||||||
|
24
core/src/components.d.ts
vendored
@ -272,7 +272,7 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
"leaveAnimation"?: AnimationBuilder;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"message"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
@ -1135,7 +1135,7 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
"loadingSpinner"?: SpinnerTypes | null;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"loadingText"?: string | IonicSafeString;
|
||||||
}
|
}
|
||||||
@ -1554,7 +1554,7 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
"leaveAnimation"?: AnimationBuilder;
|
"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;
|
"message"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
@ -2382,7 +2382,7 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
"pullingIcon"?: SpinnerTypes | string | null;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"pullingText"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
@ -2390,7 +2390,7 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
"refreshingSpinner"?: SpinnerTypes | null;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"refreshingText"?: string | IonicSafeString;
|
||||||
}
|
}
|
||||||
@ -3118,7 +3118,7 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
"leaveAnimation"?: AnimationBuilder;
|
"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;
|
"message"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
@ -4235,7 +4235,7 @@ declare namespace LocalJSX {
|
|||||||
*/
|
*/
|
||||||
"leaveAnimation"?: AnimationBuilder;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"message"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
@ -5169,7 +5169,7 @@ declare namespace LocalJSX {
|
|||||||
*/
|
*/
|
||||||
"loadingSpinner"?: SpinnerTypes | null;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"loadingText"?: string | IonicSafeString;
|
||||||
}
|
}
|
||||||
@ -5584,7 +5584,7 @@ declare namespace LocalJSX {
|
|||||||
*/
|
*/
|
||||||
"leaveAnimation"?: AnimationBuilder;
|
"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;
|
"message"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
@ -6421,7 +6421,7 @@ declare namespace LocalJSX {
|
|||||||
*/
|
*/
|
||||||
"pullingIcon"?: SpinnerTypes | string | null;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"pullingText"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
@ -6429,7 +6429,7 @@ declare namespace LocalJSX {
|
|||||||
*/
|
*/
|
||||||
"refreshingSpinner"?: SpinnerTypes | null;
|
"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 `<Ionic>` 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 `<Ionic>` 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;
|
"refreshingText"?: string | IonicSafeString;
|
||||||
}
|
}
|
||||||
@ -7214,7 +7214,7 @@ declare namespace LocalJSX {
|
|||||||
*/
|
*/
|
||||||
"leaveAnimation"?: AnimationBuilder;
|
"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;
|
"message"?: string | IonicSafeString;
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
||||||
import { Component, Element, Event, Host, Listen, Method, Prop, Watch, forceUpdate, h } 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 { getIonMode } from '../../global/ionic-global';
|
||||||
import type { AnimationBuilder, CssClassMap, OverlayInterface, FrameworkDelegate } from '../../interface';
|
import type { AnimationBuilder, CssClassMap, OverlayInterface, FrameworkDelegate } from '../../interface';
|
||||||
|
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
|
||||||
import type { Gesture } from '../../utils/gesture';
|
import type { Gesture } from '../../utils/gesture';
|
||||||
import { createButtonActiveGesture } from '../../utils/gesture/button-active';
|
import { createButtonActiveGesture } from '../../utils/gesture/button-active';
|
||||||
import {
|
import {
|
||||||
@ -43,7 +45,7 @@ import { mdLeaveAnimation } from './animations/md.leave';
|
|||||||
export class Alert implements ComponentInterface, OverlayInterface {
|
export class Alert implements ComponentInterface, OverlayInterface {
|
||||||
private readonly delegateController = createDelegateController(this);
|
private readonly delegateController = createDelegateController(this);
|
||||||
private readonly triggerController = createTriggerController();
|
private readonly triggerController = createTriggerController();
|
||||||
|
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
|
||||||
private activeId?: string;
|
private activeId?: string;
|
||||||
private inputType?: string;
|
private inputType?: string;
|
||||||
private processedInputs: AlertInput[] = [];
|
private processedInputs: AlertInput[] = [];
|
||||||
@ -105,6 +107,11 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
|||||||
* `<Ionic>`
|
* `<Ionic>`
|
||||||
*
|
*
|
||||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
* 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;
|
@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() {
|
render() {
|
||||||
const { overlayIndex, header, subHeader, message, htmlAttributes } = this;
|
const { overlayIndex, header, subHeader, message, htmlAttributes } = this;
|
||||||
const mode = getIonMode(this);
|
const mode = getIonMode(this);
|
||||||
@ -723,7 +743,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(message)}></div>
|
{this.renderAlertMessage(msgId)}
|
||||||
|
|
||||||
{this.renderAlertInputs()}
|
{this.renderAlertInputs()}
|
||||||
{this.renderAlertButtons()}
|
{this.renderAlertButtons()}
|
||||||
|
40
core/src/components/alert/test/alert.spec.ts
Normal 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 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')).not.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);
|
||||||
|
});
|
||||||
|
});
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 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: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -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()', () => {
|
describe('getPartsFromCalendarDay()', () => {
|
||||||
it('should extract DatetimeParts from a calendar day element', () => {
|
it('should extract DatetimeParts from a calendar day element', () => {
|
||||||
@ -17,7 +17,54 @@ 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,
|
||||||
|
tzOffset: 0,
|
||||||
|
year: 2022,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the correct time zone offset', () => {
|
||||||
|
expect(parseDate('2022-12-15T13:47:30-02:00').tzOffset).toEqual(-120);
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
tzOffset: 0,
|
||||||
|
year: 2022,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ampm: 'pm',
|
||||||
|
day: 23,
|
||||||
|
hour: 20,
|
||||||
|
minute: 19,
|
||||||
|
month: 3,
|
||||||
|
tzOffset: 0,
|
||||||
|
year: 2023,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('clampDate()', () => {
|
describe('clampDate()', () => {
|
||||||
const minParts = {
|
const minParts = {
|
||||||
|
@ -3,6 +3,7 @@ import { Component, Host, Prop, h } from '@stencil/core';
|
|||||||
|
|
||||||
import { config } from '../../global/config';
|
import { config } from '../../global/config';
|
||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
|
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
|
||||||
import type { IonicSafeString } from '../../utils/sanitization';
|
import type { IonicSafeString } from '../../utils/sanitization';
|
||||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||||
import type { SpinnerTypes } from '../spinner/spinner-configs';
|
import type { SpinnerTypes } from '../spinner/spinner-configs';
|
||||||
@ -15,6 +16,8 @@ import type { SpinnerTypes } from '../spinner/spinner-configs';
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
export class InfiniteScrollContent implements ComponentInterface {
|
export class InfiniteScrollContent implements ComponentInterface {
|
||||||
|
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An animated SVG spinner that shows while loading.
|
* An animated SVG spinner that shows while loading.
|
||||||
*/
|
*/
|
||||||
@ -28,6 +31,11 @@ export class InfiniteScrollContent implements ComponentInterface {
|
|||||||
* `<Ionic>`
|
* `<Ionic>`
|
||||||
*
|
*
|
||||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
* 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;
|
@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() {
|
render() {
|
||||||
const mode = getIonMode(this);
|
const mode = getIonMode(this);
|
||||||
return (
|
return (
|
||||||
@ -58,9 +75,7 @@ export class InfiniteScrollContent implements ComponentInterface {
|
|||||||
<ion-spinner name={this.loadingSpinner} />
|
<ion-spinner name={this.loadingSpinner} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.loadingText !== undefined && (
|
{this.loadingText !== undefined && this.renderLoadingText()}
|
||||||
<div class="infinite-loading-text" innerHTML={sanitizeDOMString(this.loadingText)} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Host>
|
</Host>
|
||||||
);
|
);
|
||||||
|
@ -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 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')).not.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);
|
||||||
|
});
|
||||||
|
});
|
@ -4,6 +4,7 @@ import { Watch, Component, Element, Event, Host, Method, Prop, h } from '@stenci
|
|||||||
import { config } from '../../global/config';
|
import { config } from '../../global/config';
|
||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
import type { AnimationBuilder, FrameworkDelegate, OverlayInterface } from '../../interface';
|
import type { AnimationBuilder, FrameworkDelegate, OverlayInterface } from '../../interface';
|
||||||
|
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
|
||||||
import { raf } from '../../utils/helpers';
|
import { raf } from '../../utils/helpers';
|
||||||
import {
|
import {
|
||||||
BACKDROP,
|
BACKDROP,
|
||||||
@ -41,6 +42,7 @@ import { mdLeaveAnimation } from './animations/md.leave';
|
|||||||
export class Loading implements ComponentInterface, OverlayInterface {
|
export class Loading implements ComponentInterface, OverlayInterface {
|
||||||
private readonly delegateController = createDelegateController(this);
|
private readonly delegateController = createDelegateController(this);
|
||||||
private readonly triggerController = createTriggerController();
|
private readonly triggerController = createTriggerController();
|
||||||
|
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
|
||||||
private durationTimeout?: ReturnType<typeof setTimeout>;
|
private durationTimeout?: ReturnType<typeof setTimeout>;
|
||||||
private currentTransition?: Promise<any>;
|
private currentTransition?: Promise<any>;
|
||||||
|
|
||||||
@ -75,6 +77,11 @@ export class Loading implements ComponentInterface, OverlayInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@Prop() message?: string | IonicSafeString;
|
@Prop() message?: string | IonicSafeString;
|
||||||
|
|
||||||
@ -296,6 +303,19 @@ export class Loading implements ComponentInterface, OverlayInterface {
|
|||||||
this.dismiss(undefined, BACKDROP);
|
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() {
|
render() {
|
||||||
const { message, spinner, htmlAttributes, overlayIndex } = this;
|
const { message, spinner, htmlAttributes, overlayIndex } = this;
|
||||||
const mode = getIonMode(this);
|
const mode = getIonMode(this);
|
||||||
@ -335,9 +355,7 @@ export class Loading implements ComponentInterface, OverlayInterface {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{message !== undefined && (
|
{message !== undefined && this.renderLoadingMessage(msgId)}
|
||||||
<div class="loading-content" id={msgId} innerHTML={sanitizeDOMString(message)}></div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div tabindex="0"></div>
|
<div tabindex="0"></div>
|
||||||
|
40
core/src/components/loading/test/loading.spec.ts
Normal 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 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')).not.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);
|
||||||
|
});
|
||||||
|
});
|
@ -162,9 +162,7 @@ test.describe('modal: incorrect usage', () => {
|
|||||||
await ionModalDidPresent.next();
|
await ionModalDidPresent.next();
|
||||||
|
|
||||||
const modal = await page.locator('ion-modal');
|
const modal = await page.locator('ion-modal');
|
||||||
await modal.evaluate((el: HTMLIonModalElement) => {
|
await modal.evaluate((el: HTMLIonModalElement) => el.setCurrentBreakpoint(0.5));
|
||||||
el.setCurrentBreakpoint(0.5);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(warnings.length).toBe(1);
|
expect(warnings.length).toBe(1);
|
||||||
expect(warnings[0]).toBe('[Ionic Warning]: setCurrentBreakpoint is only supported on sheet modals.');
|
expect(warnings[0]).toBe('[Ionic Warning]: setCurrentBreakpoint is only supported on sheet modals.');
|
||||||
|
@ -2,6 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
|||||||
import { Component, Element, Event, Host, Listen, Prop, Watch, h } from '@stencil/core';
|
import { Component, Element, Event, Host, Listen, Prop, Watch, h } from '@stencil/core';
|
||||||
|
|
||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
|
import { renderHiddenInput } from '../../utils/helpers';
|
||||||
|
|
||||||
import type { RadioGroupChangeEventDetail } from './radio-group-interface';
|
import type { RadioGroupChangeEventDetail } from './radio-group-interface';
|
||||||
|
|
||||||
@ -188,9 +189,11 @@ export class RadioGroup implements ComponentInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { label, labelId } = this;
|
const { label, labelId, el, name, value } = this;
|
||||||
const mode = getIonMode(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>;
|
return <Host role="radiogroup" aria-labelledby={label ? labelId : null} onClick={this.onClick} class={mode}></Host>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,3 +31,37 @@ test.describe('radio-group: form', () => {
|
|||||||
await expect(value).toHaveText('');
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -4,6 +4,7 @@ import { arrowDown, caretBackSharp } from 'ionicons/icons';
|
|||||||
|
|
||||||
import { config } from '../../global/config';
|
import { config } from '../../global/config';
|
||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
|
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
|
||||||
import { isPlatform } from '../../utils/platform';
|
import { isPlatform } from '../../utils/platform';
|
||||||
import type { IonicSafeString } from '../../utils/sanitization';
|
import type { IonicSafeString } from '../../utils/sanitization';
|
||||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||||
@ -14,6 +15,8 @@ import { SPINNERS } from '../spinner/spinner-configs';
|
|||||||
tag: 'ion-refresher-content',
|
tag: 'ion-refresher-content',
|
||||||
})
|
})
|
||||||
export class RefresherContent implements ComponentInterface {
|
export class RefresherContent implements ComponentInterface {
|
||||||
|
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
|
||||||
|
|
||||||
@Element() el!: HTMLIonRefresherContentElement;
|
@Element() el!: HTMLIonRefresherContentElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,6 +34,11 @@ export class RefresherContent implements ComponentInterface {
|
|||||||
* `<Ionic>`
|
* `<Ionic>`
|
||||||
*
|
*
|
||||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
* 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;
|
@Prop() pullingText?: string | IonicSafeString;
|
||||||
|
|
||||||
@ -47,6 +55,11 @@ export class RefresherContent implements ComponentInterface {
|
|||||||
* `<Ionic>`
|
* `<Ionic>`
|
||||||
*
|
*
|
||||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
* 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;
|
@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() {
|
render() {
|
||||||
const pullingIcon = this.pullingIcon;
|
const pullingIcon = this.pullingIcon;
|
||||||
const hasSpinner = pullingIcon != null && (SPINNERS[pullingIcon] as any) !== undefined;
|
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>
|
<ion-icon icon={this.pullingIcon} lazy={false} aria-hidden="true"></ion-icon>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.pullingText !== undefined && (
|
{this.pullingText !== undefined && this.renderPullingText()}
|
||||||
<div class="refresher-pulling-text" innerHTML={sanitizeDOMString(this.pullingText)}></div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="refresher-refreshing">
|
<div class="refresher-refreshing">
|
||||||
{this.refreshingSpinner && (
|
{this.refreshingSpinner && (
|
||||||
@ -103,9 +132,7 @@ export class RefresherContent implements ComponentInterface {
|
|||||||
<ion-spinner name={this.refreshingSpinner}></ion-spinner>
|
<ion-spinner name={this.refreshingSpinner}></ion-spinner>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.refreshingText !== undefined && (
|
{this.refreshingText !== undefined && this.renderRefreshingText()}
|
||||||
<div class="refresher-refreshing-text" innerHTML={sanitizeDOMString(this.refreshingText)}></div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Host>
|
</Host>
|
||||||
);
|
);
|
||||||
|
@ -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 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')).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 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);
|
||||||
|
});
|
||||||
|
});
|
@ -19,6 +19,7 @@ test.describe('ripple-effect: basic', () => {
|
|||||||
test.describe('ripple effect with nested ion-button', () => {
|
test.describe('ripple effect with nested ion-button', () => {
|
||||||
test('should add .ion-activated when the block is pressed', async ({ page }) => {
|
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 page.goto('/src/components/ripple-effect/test/basic?ionic:_testing=false');
|
||||||
|
await isIdleCallbackComplete(page);
|
||||||
|
|
||||||
const el = page.locator('#ripple-with-button');
|
const el = page.locator('#ripple-with-button');
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ test.describe('ripple-effect: basic', () => {
|
|||||||
|
|
||||||
const verifyRippleEffect = async (page: E2EPage, selector: string) => {
|
const verifyRippleEffect = async (page: E2EPage, selector: string) => {
|
||||||
await page.goto('/src/components/ripple-effect/test/basic?ionic:_testing=false');
|
await page.goto('/src/components/ripple-effect/test/basic?ionic:_testing=false');
|
||||||
|
await isIdleCallbackComplete(page);
|
||||||
|
|
||||||
const el = page.locator(selector);
|
const el = page.locator(selector);
|
||||||
|
|
||||||
@ -61,3 +63,24 @@ const verifyRippleEffect = async (page: E2EPage, selector: string) => {
|
|||||||
|
|
||||||
await expect(el).toHaveClass(/ion-activated/);
|
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 }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -50,8 +50,9 @@
|
|||||||
|
|
||||||
font-family: $font-family-base;
|
font-family: $font-family-base;
|
||||||
|
|
||||||
cursor: pointer;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
z-index: $z-index-item-input;
|
z-index: $z-index-item-input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +182,7 @@ button {
|
|||||||
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
white-space: nowrap;
|
white-space: inherit;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
48
core/src/components/select/test/wrapping/select.e2e.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
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" aria-label="Should Not Wrap">
|
||||||
|
<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" aria-label="Should 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();
|
||||||
|
|
||||||
|
// TODO(FW-3787) Make label a property of select
|
||||||
|
await page.setContent(`
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Really long label should not wrap</ion-label>
|
||||||
|
<ion-select value="wrap" aria-label="Should 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-label>
|
||||||
|
</ion-item>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const select = page.locator('ion-item');
|
||||||
|
await expect(select).toHaveScreenshot(`select-wrap-with-label-${page.getSnapshotSettings()}.png`);
|
||||||
|
});
|
||||||
|
});
|
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 8.5 KiB |
43
core/src/components/toast/test/toast.spec.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { newSpecPage } from '@stencil/core/testing';
|
||||||
|
import { Toast } from '../toast';
|
||||||
|
import { config } from '../../../global/config';
|
||||||
|
|
||||||
|
describe('alert: custom html', () => {
|
||||||
|
it('should allow for custom html by default', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Toast],
|
||||||
|
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toast = page.body.querySelector('ion-toast');
|
||||||
|
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||||
|
expect(content.textContent).toContain('Custom Text');
|
||||||
|
expect(content.querySelector('button.custom-html')).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow for custom html', async () => {
|
||||||
|
config.reset({ innerHTMLTemplatesEnabled: true });
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Toast],
|
||||||
|
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toast = page.body.querySelector('ion-toast');
|
||||||
|
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||||
|
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: [Toast],
|
||||||
|
html: `<ion-toast message="<button class='custom-html'>Custom Text</button>"></ion-toast>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toast = page.body.querySelector('ion-toast');
|
||||||
|
const content = toast.shadowRoot.querySelector('.toast-message');
|
||||||
|
expect(content.textContent).toContain('Custom Text');
|
||||||
|
expect(content.querySelector('button.custom-html')).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
@ -4,6 +4,7 @@ import { Watch, Component, Element, Event, h, Host, Method, Prop } from '@stenci
|
|||||||
import { config } from '../../global/config';
|
import { config } from '../../global/config';
|
||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
import type { AnimationBuilder, Color, CssClassMap, OverlayInterface, FrameworkDelegate } from '../../interface';
|
import type { AnimationBuilder, Color, CssClassMap, OverlayInterface, FrameworkDelegate } from '../../interface';
|
||||||
|
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
|
||||||
import { printIonWarning } from '../../utils/logging';
|
import { printIonWarning } from '../../utils/logging';
|
||||||
import {
|
import {
|
||||||
createDelegateController,
|
createDelegateController,
|
||||||
@ -49,6 +50,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
|
|||||||
private readonly delegateController = createDelegateController(this);
|
private readonly delegateController = createDelegateController(this);
|
||||||
private readonly triggerController = createTriggerController();
|
private readonly triggerController = createTriggerController();
|
||||||
private currentTransition?: Promise<any>;
|
private currentTransition?: Promise<any>;
|
||||||
|
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
|
||||||
private durationTimeout?: ReturnType<typeof setTimeout>;
|
private durationTimeout?: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
presented = false;
|
presented = false;
|
||||||
@ -111,6 +113,10 @@ export class Toast implements ComponentInterface, OverlayInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@Prop() message?: string | IonicSafeString;
|
@Prop() message?: string | IonicSafeString;
|
||||||
|
|
||||||
@ -401,6 +407,19 @@ export class Toast implements ComponentInterface, OverlayInterface {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderToastMessage() {
|
||||||
|
const { customHTMLEnabled, message } = this;
|
||||||
|
if (customHTMLEnabled) {
|
||||||
|
return <div class="toast-message" part="message" innerHTML={sanitizeDOMString(message)}></div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="toast-message" part="message">
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { layout, el } = this;
|
const { layout, el } = this;
|
||||||
const allButtons = this.getButtons();
|
const allButtons = this.getButtons();
|
||||||
@ -457,9 +476,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
|
|||||||
{this.header}
|
{this.header}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.message !== undefined && (
|
{this.message !== undefined && this.renderToastMessage()}
|
||||||
<div class="toast-message" part="message" innerHTML={sanitizeDOMString(this.message)}></div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.renderButtons(endButtons, 'end')}
|
{this.renderButtons(endButtons, 'end')}
|
||||||
|
@ -189,6 +189,14 @@ export interface IonicConfig {
|
|||||||
*/
|
*/
|
||||||
sanitizerEnabled?: boolean;
|
sanitizerEnabled?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relevant Components: ion-alert, ion-infinite-scroll-content, ion-loading, ion-refresher-content, ion-toast
|
||||||
|
* If `false`, all `innerHTML` usage will be disabled in Ionic, and
|
||||||
|
* custom HTML will not be usable in the relevant components.
|
||||||
|
* `innerHTML` usage is enabled by default.
|
||||||
|
*/
|
||||||
|
innerHTMLTemplatesEnabled?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the default platform detection methods.
|
* Overrides the default platform detection methods.
|
||||||
*/
|
*/
|
||||||
@ -240,3 +248,5 @@ export const getMode = (): Mode => {
|
|||||||
}
|
}
|
||||||
return 'md';
|
return 'md';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ENABLE_HTML_CONTENT_DEFAULT = true;
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [6.7.0](https://github.com/ionic-team/ionic-docs/compare/v6.6.3...v6.7.0) (2023-03-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ionic/docs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [7.0.0-rc.3](https://github.com/ionic-team/ionic-docs/compare/v7.0.0-rc.2...v7.0.0-rc.3) (2023-03-22)
|
# [7.0.0-rc.3](https://github.com/ionic-team/ionic-docs/compare/v7.0.0-rc.2...v7.0.0-rc.3) (2023-03-22)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ionic/docs
|
**Note:** Version bump only for package @ionic/docs
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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-server
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [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)
|
# [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-server
|
**Note:** Version bump only for package @ionic/angular-server
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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/react-router
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [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)
|
# [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/react-router
|
**Note:** Version bump only for package @ionic/react-router
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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/react
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [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)
|
# [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/react
|
**Note:** Version bump only for package @ionic/react
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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/vue-router
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [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)
|
# [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/vue-router
|
**Note:** Version bump only for package @ionic/vue-router
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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/vue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [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)
|
# [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/vue
|
**Note:** Version bump only for package @ionic/vue
|
||||||
|
@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
Ionic Framework supports multiple versions of Vue. As a result, we need to verify that Ionic works correctly with each of these Vue versions.
|
Ionic Framework supports multiple versions of Vue. As a result, we need to verify that Ionic works correctly with each of these Vue versions.
|
||||||
|
|
||||||
|
## Syncing Local Changes
|
||||||
|
|
||||||
|
The Vue test app supports syncing your locally built changes for validation.
|
||||||
|
|
||||||
|
1. Build the `core`, `packages/vue`, and `packages/vue-router` projects using `npm run build`.
|
||||||
|
2. [Build the Vue 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 the build cache in `node_modules/.cache` and restart the dev server/re-build.
|
||||||
|
|
||||||
## Test App Build Structure
|
## Test App Build Structure
|
||||||
|
|
||||||
Unlike other test applications, these test apps are broken up into multiple directories. These directories are then combined to create a single application. This allows us to share common application code, tests, etc so that each app is being tested the same way. Below details the different pieces that help create a single test application.
|
Unlike other test applications, these test apps are broken up into multiple directories. These directories are then combined to create a single application. This allows us to share common application code, tests, etc so that each app is being tested the same way. Below details the different pieces that help create a single test application.
|
||||||
|