Merge remote-tracking branch 'origin/next' into sp/ROU-4848-sync

This commit is contained in:
Sean Perkins
2024-04-10 16:10:46 -04:00
254 changed files with 3019 additions and 636 deletions

66
.github/CODEOWNERS vendored
View File

@@ -13,73 +13,7 @@
* @ionic-team/framework
# Frameworks
## Angular
/packages/angular/ @sean-perkins @thetaPC
/packages/angular-server @sean-perkins @thetaPC
/packages/angular/test @thetaPC
## React
/packages/react/ @amandaejohnston
/packages/react-router @amandaejohnston
/packages/react/test-app/
/packages/react-router/test-app/
## Vue
/packages/vue/ @liamdebeasi @thetaPC
/packages/vue-router/ @liamdebeasi @thetaPC
/packages/vue/test/ @thetaPC
/packages/vue-router/__tests__ @thetaPC
# Components
/core/src/components/accordion/ @liamdebeasi
/core/src/components/accordion-group/ @liamdebeasi
/core/src/components/checkbox/ @amandaejohnston
/core/src/components/datetime/ @liamdebeasi @amandaejohnston @sean-perkins
/core/src/components/datetime-button/ @liamdebeasi
/core/src/components/item/ @brandyscarney
/core/src/components/menu/ @amandaejohnston
/core/src/components/menu-toggle/ @amandaejohnston
/core/src/components/nav/ @sean-perkins
/core/src/components/nav-link/ @sean-perkins
/core/src/components/picker/ @liamdebeasi
/core/src/components/picker-column/ @liamdebeasi
/core/src/components/radio/ @amandaejohnston
/core/src/components/radio-group/ @amandaejohnston
/core/src/components/refresher/ @liamdebeasi
/core/src/components/refresher-content/ @liamdebeasi
/core/src/components/searchbar/ @brandyscarney
/core/src/components/segment/ @brandyscarney
/core/src/components/segment-button/ @brandyscarney
/core/src/components/skeleton-text/ @brandyscarney
# Utilities
/core/src/utils/animation/ @liamdebeasi
/core/src/utils/content/ @sean-perkins
/core/src/utils/gesture/ @liamdebeasi
/core/src/utils/input-shims/ @liamdebeasi
/core/src/utils/keyboard/ @liamdebeasi
/core/src/utils/logging/ @amandaejohnston
/core/src/utils/sanitization/ @liamdebeasi
/core/src/utils/tap-click/ @liamdebeasi
/core/src/utils/transition/ @liamdebeasi
/core/src/css/ @brandyscarney
/core/src/themes/ @brandyscarney

View File

@@ -25,7 +25,7 @@ Issue number: resolves #
If this introduces a breaking change:
1. Describe the impact and migration path for existing applications below.
2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer for more information.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information.
-->

View File

@@ -13,6 +13,6 @@ jobs:
- name: 'Auto-assign issue'
uses: pozil/auto-assign-issue@edee9537367a8fbc625d27f9e10aa8bad47b8723 # v1.13.0
with:
assignees: liamdebeasi, sean-perkins, brandyscarney, amandaejohnston, mapsandapps, thetaPC
assignees: liamdebeasi, sean-perkins, brandyscarney, amandaejohnston, thetaPC
numOfAssignee: 1
allowSelfAssign: false

View File

@@ -41,8 +41,8 @@ jobs:
uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-docs
output: docs/DocsBuild.zip
paths: docs/core.json docs/core.d.ts
output: packages/docs/DocsBuild.zip
paths: packages/docs/core.json packages/docs/core.d.ts
release-docs:
needs: [release-core]
@@ -53,7 +53,7 @@ jobs:
uses: ./.github/workflows/actions/download-archive
with:
name: ionic-docs
path: ./docs
path: ./packages/docs
filename: DocsBuild.zip
- uses: ./.github/workflows/actions/publish-npm
with:
@@ -61,7 +61,7 @@ jobs:
tag: ${{ inputs.tag }}
version: ${{ inputs.version }}
preid: ${{ inputs.preid }}
working-directory: 'docs'
working-directory: 'packages/docs'
token: ${{ secrets.NPM_TOKEN }}
release-angular:

View File

@@ -3,6 +3,67 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.8.3](https://github.com/ionic-team/ionic-framework/compare/v7.8.2...v7.8.3) (2024-04-03)
### Bug Fixes
* **button:** activated outline button in toolbar no longer blends into background on MD dark mode ([#29216](https://github.com/ionic-team/ionic-framework/issues/29216)) ([ee5da7a](https://github.com/ionic-team/ionic-framework/commit/ee5da7a747c0a0b420c5e371a9fe9ec4938d179e))
* **popover:** viewport can be scrolled if no content present ([#29215](https://github.com/ionic-team/ionic-framework/issues/29215)) ([f08759c](https://github.com/ionic-team/ionic-framework/commit/f08759c2b8256ff66f8d1901bd8e0be4617db262)), closes [#29211](https://github.com/ionic-team/ionic-framework/issues/29211)
# [8.0.0-rc.0](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-beta.4...v8.0.0-rc.0) (2024-03-27)
**Note:** Version bump only for package ionic-framework
# [8.0.0-beta.4](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-beta.3...v8.0.0-beta.4) (2024-03-27)
### Bug Fixes
* **angular:** schematics account for new theme files ([#29185](https://github.com/ionic-team/ionic-framework/issues/29185)) ([b0a10df](https://github.com/ionic-team/ionic-framework/commit/b0a10dfa56a65ee3905cc2c3d48d2221a42f222f))
### Code Refactoring
* **haptics:** remove cordova haptics support ([#29186](https://github.com/ionic-team/ionic-framework/issues/29186)) ([9efeb0a](https://github.com/ionic-team/ionic-framework/commit/9efeb0ad31aadee5f5ae542641d928ecf93fe82a))
* **searchbar:** autocapitalize defaults to off ([#29107](https://github.com/ionic-team/ionic-framework/issues/29107)) ([6e477b7](https://github.com/ionic-team/ionic-framework/commit/6e477b743e41b2b37627c8208901704f599b762a))
### Features
* **button:** add circular shape as round ([#29161](https://github.com/ionic-team/ionic-framework/issues/29161)) ([44529f0](https://github.com/ionic-team/ionic-framework/commit/44529f0a62f1b1ce42750a20f19e829b2789febd))
* **input:** add input-password-toggle component ([#29175](https://github.com/ionic-team/ionic-framework/issues/29175)) ([6c500fd](https://github.com/ionic-team/ionic-framework/commit/6c500fd6b2e2553c11fcddc9d86ac9a29f76e172))
* remove css animations support for ionic animations ([#29123](https://github.com/ionic-team/ionic-framework/issues/29123)) ([892594d](https://github.com/ionic-team/ionic-framework/commit/892594de0665e8fc5c8a737d292812842ea03d64))
### BREAKING CHANGES
* **searchbar:** The `autocapitalize` property on Searchbar now defaults to `'off'`.
* **haptics:** Support for the Cordova Haptics plugin has been removed. Components that integrate with haptics, such as `ion-picker` and `ion-toggle`, will continue to function but will no longer play haptics in Cordova environments. Developers should migrate to Capacitor to continue to have haptics in these components.
## [7.8.2](https://github.com/ionic-team/ionic-framework/compare/v7.8.1...v7.8.2) (2024-03-27)
### Bug Fixes
* **searchbar:** autocapitalize is initialized correctly ([#29197](https://github.com/ionic-team/ionic-framework/issues/29197)) ([8ad66c0](https://github.com/ionic-team/ionic-framework/commit/8ad66c03d777edcab19c97cba696b171acc2d2e8)), closes [#29193](https://github.com/ionic-team/ionic-framework/issues/29193)
# [8.0.0-beta.3](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-beta.2...v8.0.0-beta.3) (2024-03-20)

View File

@@ -20,7 +20,7 @@
<a href="https://github.com/ionic-team/ionic-framework/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Ionic Framework is released under the MIT license." />
</a>
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">
<a href="https://github.com/ionic-team/ionic/blob/main/docs/CONTRIBUTING.md">
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome!" />
</a>
<a href="https://twitter.com/Ionicframework">
@@ -38,7 +38,7 @@
Documentation
</a>
<span> · </span>
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">Contribute</a>
<a href="https://github.com/ionic-team/ionic/blob/main/docs/CONTRIBUTING.md">Contribute</a>
<span> · </span>
<a href="https://blog.ionicframework.com/">Blog</a>
<br />
@@ -88,7 +88,7 @@ The Ionic Conference App is a full featured Ionic app. It is the perfect startin
### Contributing
Thanks for your interest in contributing! Read up on our guidelines for
[contributing](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md)
[contributing](https://github.com/ionic-team/ionic/blob/main/docs/CONTRIBUTING.md)
and then look through our issues with a [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
label.

View File

@@ -3,6 +3,63 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.8.3](https://github.com/ionic-team/ionic-framework/compare/v7.8.2...v7.8.3) (2024-04-03)
### Bug Fixes
* **button:** activated outline button in toolbar no longer blends into background on MD dark mode ([#29216](https://github.com/ionic-team/ionic-framework/issues/29216)) ([ee5da7a](https://github.com/ionic-team/ionic-framework/commit/ee5da7a747c0a0b420c5e371a9fe9ec4938d179e))
* **popover:** viewport can be scrolled if no content present ([#29215](https://github.com/ionic-team/ionic-framework/issues/29215)) ([f08759c](https://github.com/ionic-team/ionic-framework/commit/f08759c2b8256ff66f8d1901bd8e0be4617db262)), closes [#29211](https://github.com/ionic-team/ionic-framework/issues/29211)
# [8.0.0-rc.0](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-beta.4...v8.0.0-rc.0) (2024-03-27)
**Note:** Version bump only for package @ionic/core
# [8.0.0-beta.4](https://github.com/ionic-team/ionic-framework/compare/v8.0.0-beta.3...v8.0.0-beta.4) (2024-03-27)
### Code Refactoring
* **haptics:** remove cordova haptics support ([#29186](https://github.com/ionic-team/ionic-framework/issues/29186)) ([9efeb0a](https://github.com/ionic-team/ionic-framework/commit/9efeb0ad31aadee5f5ae542641d928ecf93fe82a))
* **searchbar:** autocapitalize defaults to off ([#29107](https://github.com/ionic-team/ionic-framework/issues/29107)) ([6e477b7](https://github.com/ionic-team/ionic-framework/commit/6e477b743e41b2b37627c8208901704f599b762a))
### Features
* **button:** add circular shape as round ([#29161](https://github.com/ionic-team/ionic-framework/issues/29161)) ([44529f0](https://github.com/ionic-team/ionic-framework/commit/44529f0a62f1b1ce42750a20f19e829b2789febd))
* **input:** add input-password-toggle component ([#29175](https://github.com/ionic-team/ionic-framework/issues/29175)) ([6c500fd](https://github.com/ionic-team/ionic-framework/commit/6c500fd6b2e2553c11fcddc9d86ac9a29f76e172))
* remove css animations support for ionic animations ([#29123](https://github.com/ionic-team/ionic-framework/issues/29123)) ([892594d](https://github.com/ionic-team/ionic-framework/commit/892594de0665e8fc5c8a737d292812842ea03d64))
### BREAKING CHANGES
* **searchbar:** The `autocapitalize` property on Searchbar now defaults to `'off'`.
* **haptics:** Support for the Cordova Haptics plugin has been removed. Components that integrate with haptics, such as `ion-picker` and `ion-toggle`, will continue to function but will no longer play haptics in Cordova environments. Developers should migrate to Capacitor to continue to have haptics in these components.
## [7.8.2](https://github.com/ionic-team/ionic-framework/compare/v7.8.1...v7.8.2) (2024-03-27)
### Bug Fixes
* **searchbar:** autocapitalize is initialized correctly ([#29197](https://github.com/ionic-team/ionic-framework/issues/29197)) ([8ad66c0](https://github.com/ionic-team/ionic-framework/commit/8ad66c03d777edcab19c97cba696b171acc2d2e8)), closes [#29193](https://github.com/ionic-team/ionic-framework/issues/29193)
# [8.0.0-beta.3](https://github.com/ionic-team/ionic-framework/compare/v7.8.1...v8.0.0-beta.3) (2024-03-20)

View File

@@ -282,6 +282,7 @@ ion-card,prop,mode,"ios" | "md",undefined,false,false
ion-card,prop,rel,string | undefined,undefined,false,false
ion-card,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-card,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-card,prop,shape,"rectangular" | "round" | undefined,undefined,false,true
ion-card,prop,target,string | undefined,undefined,false,false
ion-card,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-card,prop,type,"button" | "reset" | "submit",'button',false,false
@@ -387,6 +388,7 @@ ion-col,css-prop,--ion-grid-columns
ion-content,shadow
ion-content,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-content,prop,fixedSlotPlacement,"after" | "before",'after',false,false
ion-content,prop,forceOverscroll,boolean | undefined,undefined,false,false
ion-content,prop,fullscreen,boolean,false,false,false
ion-content,prop,mode,"ios" | "md",undefined,false,false
@@ -598,6 +600,7 @@ ion-input,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "h
ion-input,prop,autocorrect,"off" | "on",'off',false,false
ion-input,prop,autofocus,boolean,false,false,false
ion-input,prop,clearInput,boolean,false,false,false
ion-input,prop,clearInputIcon,string | undefined,undefined,false,false
ion-input,prop,clearOnEdit,boolean | undefined,undefined,false,false
ion-input,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-input,prop,counter,boolean,false,false,false

40
core/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "8.0.0-beta.3",
"version": "8.0.0-rc.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "8.0.0-beta.3",
"version": "8.0.0-rc.0",
"license": "MIT",
"dependencies": {
"@stencil/core": "^4.12.2",
@@ -612,9 +612,9 @@
"dev": true
},
"node_modules/@capacitor/core": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.7.3.tgz",
"integrity": "sha512-xEuQmP+h0tugl2N+qRcdrUavZydvTnnmtvqu/OtCBb/bKZo2PDRFft7MxuQRE2GxXs6kLy3cvwzhDAHB3a+9mw==",
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.7.4.tgz",
"integrity": "sha512-iZBgvx3o4amzKv5ttA+QHB6i7cxK+/mYpCQd1tnSdipg6ZkvfBhg1HkzhEqHk+I7MNur+QwgYDZho9+ycHRwOw==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
@@ -1832,9 +1832,9 @@
}
},
"node_modules/@stencil/core": {
"version": "4.12.6",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.12.6.tgz",
"integrity": "sha512-15JO2TdaxGVKNdLZb/2TtDa+juj3XGD/V0y/disgdzYYSnajgSh06nwODfdHz9eTUh1Hisz+KIo857I1rCZrfg==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.14.0.tgz",
"integrity": "sha512-+s0u/KsNolXZ7tC2hEMgMA3jaNaqOhZvYKwSzjQbc0Wv+cB481Isxzo7ifgEWRYqsJzNSyqhO6cyu/EJrGGTdg==",
"bin": {
"stencil": "bin/stencil"
},
@@ -1866,9 +1866,9 @@
}
},
"node_modules/@stencil/vue-output-target": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.8.7.tgz",
"integrity": "sha512-hgOzbKKgLdCFrhLpmaw/qQrPSXl6hZ09K+j3p/iWh3esq6sxnwuW1PJKLniwkT4Z/JlDIk6stGPGQYi+WE5I2Q==",
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.8.8.tgz",
"integrity": "sha512-xrpz92lmTuLgwY9CLgl2Q14+zBXfBuXiRS6uXFPfeaFo0pe+do8cZitOOQ8i8tcoCa/tAqgD9B9CD+yQehSIGg==",
"dev": true,
"peerDependencies": {
"@stencil/core": ">=2.0.0 || >=3 || >= 4.0.0-beta.0 || >= 4.0.0"
@@ -10947,9 +10947,9 @@
"dev": true
},
"@capacitor/core": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.7.3.tgz",
"integrity": "sha512-xEuQmP+h0tugl2N+qRcdrUavZydvTnnmtvqu/OtCBb/bKZo2PDRFft7MxuQRE2GxXs6kLy3cvwzhDAHB3a+9mw==",
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.7.4.tgz",
"integrity": "sha512-iZBgvx3o4amzKv5ttA+QHB6i7cxK+/mYpCQd1tnSdipg6ZkvfBhg1HkzhEqHk+I7MNur+QwgYDZho9+ycHRwOw==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
@@ -11826,9 +11826,9 @@
"requires": {}
},
"@stencil/core": {
"version": "4.12.6",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.12.6.tgz",
"integrity": "sha512-15JO2TdaxGVKNdLZb/2TtDa+juj3XGD/V0y/disgdzYYSnajgSh06nwODfdHz9eTUh1Hisz+KIo857I1rCZrfg=="
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.14.0.tgz",
"integrity": "sha512-+s0u/KsNolXZ7tC2hEMgMA3jaNaqOhZvYKwSzjQbc0Wv+cB481Isxzo7ifgEWRYqsJzNSyqhO6cyu/EJrGGTdg=="
},
"@stencil/react-output-target": {
"version": "0.5.3",
@@ -11845,9 +11845,9 @@
"requires": {}
},
"@stencil/vue-output-target": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.8.7.tgz",
"integrity": "sha512-hgOzbKKgLdCFrhLpmaw/qQrPSXl6hZ09K+j3p/iWh3esq6sxnwuW1PJKLniwkT4Z/JlDIk6stGPGQYi+WE5I2Q==",
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.8.8.tgz",
"integrity": "sha512-xrpz92lmTuLgwY9CLgl2Q14+zBXfBuXiRS6uXFPfeaFo0pe+do8cZitOOQ8i8tcoCa/tAqgD9B9CD+yQehSIGg==",
"dev": true,
"requires": {}
},

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "8.0.0-beta.3",
"version": "8.0.0-rc.0",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -79,7 +79,7 @@
"build.tokens": "node ./scripts/tokens.js && npm run lint.sass.fix",
"clean": "node scripts/clean.js",
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
"css.sass": "sass --embed-sources src/css:./css",
"css.sass": "sass --embed-sources --style compressed src/css:./css",
"eslint": "eslint src",
"lint": "npm run lint.ts && npm run lint.sass && npm run prettier -- --write --cache",
"lint.fix": "npm run lint.ts.fix && npm run lint.sass.fix && npm run prettier -- --write --cache",

View File

@@ -1,44 +1,4 @@
## Build
# Core Scripts
### 1. Clone ionic
git@github.com:ionic-team/ionic.git
cd ionic
### 2. Run `npm install`
cd core
npm install
Notice that `@ionic/core` lives in `core`.
### 3. Run `npm start`
Make sure you are inside the `core` directory.
npm start
With the `dev` command, Ionic components will be built with [Stencil](https://stenciljs.com/), changes to source files are watched, a local http server will startup, and http://localhost:3333/ will open in a browser.
### 4. Preview
Navigate to http://localhost:3333/src/components/. Each component has small e2e apps found in the `test` directory, for example: http://localhost:3333/src/components/button/test/basic
As changes are made in an editor to source files, the e2e app will live-reload.
## How to contribute
1. `npm start` allows you to modify the components and have live reloading, just like another ionic app.
2. When everything looks good, run `npm run validate` to verify the tests linter and production build passes.
# Deploy
1. `npm run prepare.deploy`
2. Review/update changelog
3. Commit updates using the package name and version number as the commit message.
4. `npm run deploy`
5. :tada:
This file has been moved to [/docs/core/testing/preview-changes.md](/docs/core/testing/preview-changes.md).

View File

@@ -618,6 +618,10 @@ export namespace Components {
* When using a router, it specifies the transition direction when navigating to another page using `href`.
*/
"routerDirection": RouterDirection;
/**
* Set to `"round"` for a card with more rounded corners, or `"rectangular"` for a card without rounded corners.
*/
"shape"?: 'round' | 'rectangular';
/**
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
*/
@@ -866,6 +870,10 @@ export namespace Components {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* Controls where the fixed content is placed relative to the main content in the DOM. This can be used to control the order in which fixed elements receive keyboard focus. For example, if a FAB in the fixed slot should receive keyboard focus before the main page content, set this property to `'before'`.
*/
"fixedSlotPlacement": 'after' | 'before';
/**
* If `true` and the content does not cause an overflow scroll, the scroll interaction will cause a bounce. If the content exceeds the bounds of ionContent, nothing will change. Note, this does not disable the system bounce on iOS. That is an OS level setting.
*/
@@ -1342,6 +1350,10 @@ export namespace Components {
* If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input.
*/
"clearInput": boolean;
/**
* The icon to use for the clear button. Only applies when `clearInput` is set to `true`.
*/
"clearInputIcon"?: string;
/**
* If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.
*/
@@ -5838,6 +5850,10 @@ declare namespace LocalJSX {
* When using a router, it specifies the transition direction when navigating to another page using `href`.
*/
"routerDirection"?: RouterDirection;
/**
* Set to `"round"` for a card with more rounded corners, or `"rectangular"` for a card without rounded corners.
*/
"shape"?: 'round' | 'rectangular';
/**
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
*/
@@ -6098,6 +6114,10 @@ declare namespace LocalJSX {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* Controls where the fixed content is placed relative to the main content in the DOM. This can be used to control the order in which fixed elements receive keyboard focus. For example, if a FAB in the fixed slot should receive keyboard focus before the main page content, set this property to `'before'`.
*/
"fixedSlotPlacement"?: 'after' | 'before';
/**
* If `true` and the content does not cause an overflow scroll, the scroll interaction will cause a bounce. If the content exceeds the bounds of ionContent, nothing will change. Note, this does not disable the system bounce on iOS. That is an OS level setting.
*/
@@ -6582,6 +6602,10 @@ declare namespace LocalJSX {
* If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input.
*/
"clearInput"?: boolean;
/**
* The icon to use for the clear button. Only applies when `clearInput` is set to `true`.
*/
"clearInputIcon"?: string;
/**
* If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.
*/

View File

@@ -109,7 +109,18 @@
::slotted(ion-icon[slot="start"]),
::slotted(ion-icon[slot="end"]),
::slotted(ion-icon[slot="icon-only"]) {
font-size: 1em;
font-size: px-to-rem(16);
}
:host(.button-xsmall),
:host(.button-small),
:host(.button-large),
:host(.button-xlarge) {
::slotted(ion-icon[slot="start"]),
::slotted(ion-icon[slot="end"]),
::slotted(ion-icon[slot="icon-only"]) {
font-size: 1em;
}
}
/* Button extra small */

View File

@@ -149,7 +149,20 @@
}
::slotted(ion-icon[slot="icon-only"]) {
font-size: 1.125em;
/**
* The values were provided through a native iOS app.
* min font size: 15px, default font size: 18px, max font size: 43px
*
* Since the `ion-button` uses `rem` for the font size, we can't
* just pass the desired icon font size in `em` to the `
* dynamic-font-clamp`. Instead, we need to adjust the base size
* to account for the ion-button` font size.
*
* For example, if the default font size of `ion-button` is 16px
* (derived from rem), then the base size can use the default font
* size of the icon.
*/
font-size: dynamic-font-clamp(0.84, 18px, 2.39, 1em);
}
:host(.button-small.button-has-icon-only) {
@@ -157,11 +170,23 @@
min-width: clamp(23px, 2.16em, 54px);
// TODO(FW-6053): replace em value with the min-height variable.
min-height: clamp(23px, 2.16em, 54px);
}
:host(.button-small) ::slotted(ion-icon[slot="icon-only"]) {
font-size: 1.4em;
/**
* The values were provided through a native iOS app.
* min font size: 12px, default font size: 17px, max font size: 40px
*
* Since the `ion-button` uses `rem` for the font size, we can't
* just pass the desired icon font size in `em` to the `
* dynamic-font-clamp`. Instead, we need to adjust the base size
* to account for the ion-button` font size.
*
* For example, if the default font size of `ion-button` is 13px
* (derived from rem) and the default font size of the icon is
* 17px, then the base size would need to be increased.
*/
font-size: dynamic-font-clamp(0.58, 20.93px, 1.92, 1em);
}
:host(.button-large.button-has-icon-only) {
@@ -172,7 +197,20 @@
}
:host(.button-large) ::slotted(ion-icon[slot="icon-only"]) {
font-size: 1em;
/**
* The values were provided through a native iOS app.
* min font size: 15px, default font size: 18px, max font size: 43px
*
* Since the `ion-button` uses `rem` for the font size, we can't
* just pass the desired icon font size in `em` to the `
* dynamic-font-clamp`. Instead, we need to adjust the base size
* to account for the ion-button` font size.
*
* For example, if the default font size of `ion-button` is 20px
* (derived from rem) and the default font size of the icon is
* 18px, then the base size would need to be decreased.
*/
font-size: dynamic-font-clamp(1.05, 14.4px, 2.99, 1em);
}
// iOS Button Focused
@@ -247,3 +285,12 @@
:host(.button-solid.ion-color.ion-activated) .button-native::after {
background: #{current-color(shade)};
}
// Activated Button in Toolbar
// --------------------------------------------------
:host(.button-outline.ion-activated.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
background: var(--ion-toolbar-color, var(--color));
color: #{var(--ion-toolbar-background, var(--background), ion-color(primary, contrast))};
}

View File

@@ -142,7 +142,21 @@
}
::slotted(ion-icon[slot="icon-only"]) {
font-size: 1.6em;
/**
* The values were provided through MD design,
* large and small are based on the iOS sizes.
* min font size: 15px, default font size: 22.4px, max font size: 43px
*
* Since the `ion-button` uses `rem` for the font size, we can't
* just pass the desired icon font size in `em` to the `
* dynamic-font-clamp`. Instead, we need to adjust the base size
* to account for the ion-button` font size.
*
* For example, if the default font size of `ion-button` is 14px
* (derived from rem) and the default font size of the icon is
* 22.4px, then the base size would need to be increased.
*/
font-size: dynamic-font-clamp(0.59, 25.6px, 1.68, 1em);
}
:host(.button-small.button-has-icon-only) {
@@ -153,7 +167,21 @@
}
:host(.button-small) ::slotted(ion-icon[slot="icon-only"]) {
font-size: 1.23em;
/**
* The values were provided through MD design,
* large and small are based on the iOS sizes.
* min font size: 12px, default font size: 16px, max font size: 40px
*
* Since the `ion-button` uses `rem` for the font size, we can't
* just pass the desired icon font size in `em` to the `
* dynamic-font-clamp`. Instead, we need to adjust the base size
* to account for the ion-button` font size.
*
* For example, if the default font size of `ion-button` is 13px
* (derived from rem) and the default font size of the icon is
* 16px, then the base size would need to be increased.
*/
font-size: dynamic-font-clamp(0.66, 19.7px, 2.05, 1em);
}
:host(.button-large.button-has-icon-only) {
@@ -164,7 +192,21 @@
}
:host(.button-large) ::slotted(ion-icon[slot="icon-only"]) {
font-size: 1.4em;
/**
* The values were provided through MD design,
* large and small are based on the iOS sizes.
* min font size: 15px, default font size: 28px, max font size: 43px
*
* Since the `ion-button` uses `rem` for the font size, we can't
* just pass the desired icon font size in `em` to the `
* dynamic-font-clamp`. Instead, we need to adjust the base size
* to account for the ion-button` font size.
*
* For example, if the default font size of `ion-button` is 20px
* (derived from rem) and the default font size of the icon is
* 28px, then the base size would need to be increased.
*/
font-size: dynamic-font-clamp(0.67, 22.4px, 1.92, 1em);
}
// Material Design Button: Hover
@@ -196,3 +238,12 @@
background: #{current-color(base)};
}
}
// Activated Button in Toolbar
// --------------------------------------------------
:host(.button-outline.ion-activated.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
background: var(--ion-toolbar-background, var(--color));
color: #{var(--ion-toolbar-color, var(--background), ion-color(primary, contrast))};
}

View File

@@ -325,11 +325,3 @@ ion-ripple-effect {
background: #{var(--ion-toolbar-color, var(--background))};
color: #{var(--ion-toolbar-background, var(--color))};
}
// Activated Button in Toolbar
// --------------------------------------------------
:host(.button-outline.ion-activated.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
background: var(--ion-toolbar-color, var(--color));
color: #{var(--ion-toolbar-background, var(--background), ion-color(primary, contrast))};
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 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: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 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: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,28 @@
@use "../../foundations/ionic.vars.scss" as tokens;
@use "../../themes/ionic.mixins" as mixins;
// Ionic Card
// --------------------------------------------------
:host {
--background: #{tokens.$ionic-color-neutral-0};
--border-radius: #{tokens.$ionic-border-radius-rounded-small};
@include mixins.padding(tokens.$ionic-space-base);
@include mixins.border-radius(var(--border-radius));
display: block;
border: #{tokens.$ionic-border-size-small} solid #{tokens.$ionic-color-neutral-50};
background: var(--background);
color: var(--color);
}
:host(.card-round) {
--border-radius: #{tokens.$ionic-border-radius-rounded-large};
}
:host(.card-rectangular) {
--border-radius: #{tokens.$ionic-border-radius-square};
}

View File

@@ -20,7 +20,7 @@ import type { RouterDirection } from '../router/utils/interface';
styleUrls: {
ios: 'card.ios.scss',
md: 'card.md.scss',
ionic: 'card.md.scss',
ionic: 'card.ionic.scss',
},
shadow: true,
})
@@ -82,6 +82,11 @@ export class Card implements ComponentInterface, AnchorInterface, ButtonInterfac
*/
@Prop() routerAnimation: AnimationBuilder | undefined;
/**
* Set to `"round"` for a card with more rounded corners, or `"rectangular"` for a card without rounded corners.
*/
@Prop({ reflect: true }) shape?: 'round' | 'rectangular';
/**
* Specifies where to display the linked URL.
* Only applies when an `href` is provided.
@@ -131,12 +136,16 @@ export class Card implements ComponentInterface, AnchorInterface, ButtonInterfac
}
render() {
const { shape } = this;
const theme = getIonTheme(this);
return (
<Host
class={createColorClasses(this.color, {
[theme]: true,
// TODO(FW-6119): remove theme === 'ionic' when support for other themes is added
[`card-${shape}`]: theme === 'ionic' && shape !== undefined,
'card-disabled': this.disabled,
'ion-activatable': this.isClickable(),
})}

View File

@@ -0,0 +1,40 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* Shape is only available in the Ionic theme.
* TODO(FW-6119): add the `ios` and `md` modes when shape support is added.
*/
configs({ modes: ['ionic-md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('card: shape'), () => {
test.describe('default', () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/card/test/shape`, config);
const container = page.locator('#default');
await expect(container).toHaveScreenshot(screenshot(`card-default`));
});
});
test.describe('round', () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/card/test/shape`, config);
const container = page.locator('#round');
await expect(container).toHaveScreenshot(screenshot(`card-round`));
});
});
test.describe('rectangular', () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/card/test/shape`, config);
const container = page.locator('#rectangular');
await expect(container).toHaveScreenshot(screenshot(`card-rectangular`));
});
});
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Card - Shape</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<!-- Styles only for testing purposes to identify the card shape -->
<style>
:root {
--ion-background-color: #dde2ef;
}
#default,
#round,
#rectangular {
padding: 8px;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Card - Shape</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding ion-text-center" id="content" no-bounce>
<h1>Default</h1>
<div id="default" class="ion-margin-top">
<ion-card>
<ion-card-header>
<ion-card-subtitle>Card Subtitle</ion-card-subtitle>
<ion-card-title>Card Title</ion-card-title>
</ion-card-header>
<ion-card-content>
Keep close to Nature's heart... and break clear away, once in awhile, and climb a mountain or spend a week
in the woods. Wash your spirit clean.
</ion-card-content>
</ion-card>
</div>
<h1>Round</h1>
<div id="round" class="ion-margin-top">
<ion-card shape="round">
<ion-card-header>
<ion-card-subtitle>Card Subtitle</ion-card-subtitle>
<ion-card-title>Card Title</ion-card-title>
</ion-card-header>
<ion-card-content>
Keep close to Nature's heart... and break clear away, once in awhile, and climb a mountain or spend a week
in the woods. Wash your spirit clean.
</ion-card-content>
</ion-card>
</div>
<h1>Rectangular</h1>
<div id="rectangular" class="ion-margin-top">
<ion-card shape="rectangular">
<ion-card-header>
<ion-card-subtitle>Card Subtitle</ion-card-subtitle>
<ion-card-title>Card Title</ion-card-title>
</ion-card-header>
<ion-card-content>
Keep close to Nature's heart... and break clear away, once in awhile, and climb a mountain or spend a week
in the woods. Wash your spirit clean.
</ion-card-content>
</ion-card>
</div>
</ion-content>
</ion-app>
</body>
</html>

View File

@@ -0,0 +1,78 @@
<!-- TODO: FW-6077 This file can be removed once the additional test cases are added -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Card - Theme Ionic</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.ion-margin-top {
margin-top: 32px;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Card - Ionic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding ion-text-center" id="content" no-bounce>
<h4>Preview options</h4>
<p>
<ion-select id="select-shape" justify="space-between" interface="alert" label="Shape" placeholder="">
<ion-select-option value="">default</ion-select-option>
<ion-select-option value="round">Round</ion-select-option>
<ion-select-option value="rectangular">Rectangular</ion-select-option>
</ion-select>
</p>
<div id="screenshot-wrapper" class="ion-margin-top">
<ion-card>
<ion-card-header>
<ion-card-subtitle>Card Subtitle</ion-card-subtitle>
<ion-card-title>Card Title</ion-card-title>
</ion-card-header>
<ion-card-content>
Keep close to Nature's heart... and break clear away, once in awhile, and climb a mountain or spend a week
in the woods. Wash your spirit clean.
</ion-card-content>
</ion-card>
</div>
</ion-content>
</ion-app>
<script>
const listOfCards = Array.from(document.querySelectorAll('ion-card'));
function updateAttr(el, name, value) {
if (value === '' || value === undefined) {
el.removeAttribute(name);
} else {
el.setAttribute(name, value);
}
}
const selectShape = document.getElementById('select-shape');
selectShape.addEventListener('ionChange', (e) => {
for (const el of listOfCards) {
updateAttr(el, 'shape', e.detail.value);
}
});
</script>
</body>
</html>

View File

@@ -78,6 +78,15 @@ export class Content implements ComponentInterface {
*/
@Prop() fullscreen = false;
/**
* Controls where the fixed content is placed relative to the main content
* in the DOM. This can be used to control the order in which fixed elements
* receive keyboard focus.
* For example, if a FAB in the fixed slot should receive keyboard focus before
* the main page content, set this property to `'before'`.
*/
@Prop() fixedSlotPlacement: 'after' | 'before' = 'after';
/**
* If `true` and the content does not cause an overflow scroll, the scroll interaction will cause a bounce.
* If the content exceeds the bounds of ionContent, nothing will change.
@@ -425,7 +434,7 @@ export class Content implements ComponentInterface {
}
render() {
const { isMainContent, scrollX, scrollY, el } = this;
const { fixedSlotPlacement, isMainContent, scrollX, scrollY, el } = this;
const rtl = isRTL(el) ? 'rtl' : 'ltr';
const theme = getIonTheme(this);
const mode = getIonMode(this, theme);
@@ -449,6 +458,9 @@ export class Content implements ComponentInterface {
}}
>
<div ref={(el) => (this.backgroundContentEl = el)} id="background-content" part="background"></div>
{fixedSlotPlacement === 'before' ? <slot name="fixed"></slot> : null}
<div
class={{
'inner-scroll': true,
@@ -470,7 +482,7 @@ export class Content implements ComponentInterface {
</div>
) : null}
<slot name="fixed"></slot>
{fixedSlotPlacement === 'after' ? <slot name="fixed"></slot> : null}
</Host>
);
}

View File

@@ -0,0 +1,31 @@
import { newSpecPage } from '@stencil/core/testing';
import { Content } from '../content';
describe('content: fixed slot placement', () => {
it('should should fixed slot after content', async () => {
const page = await newSpecPage({
components: [Content],
html: '<ion-content></ion-content>',
});
const content = page.body.querySelector('ion-content')!;
const fixedSlot = content.shadowRoot!.querySelector('slot[name="fixed"]')!;
const scrollEl = content.shadowRoot!.querySelector('[part="scroll"]')!;
expect(fixedSlot.nextElementSibling).not.toBe(scrollEl);
});
it('should should fixed slot before content', async () => {
const page = await newSpecPage({
components: [Content],
html: `<ion-content fixed-slot-placement="before"></ion-content>`,
});
const content = page.body.querySelector('ion-content')!;
const fixedSlot = content.shadowRoot!.querySelector('slot[name="fixed"]')!;
const scrollEl = content.shadowRoot!.querySelector('[part="scroll"]')!;
expect(fixedSlot.nextElementSibling).toBe(scrollEl);
});
});

View File

@@ -543,7 +543,7 @@ export class Datetime implements ComponentInterface {
}
if (closeOverlay) {
this.closeParentOverlay();
this.closeParentOverlay(CONFIRM_ROLE);
}
}
@@ -568,7 +568,7 @@ export class Datetime implements ComponentInterface {
this.ionCancel.emit();
if (closeOverlay) {
this.closeParentOverlay();
this.closeParentOverlay(CANCEL_ROLE);
}
}
@@ -618,13 +618,13 @@ export class Datetime implements ComponentInterface {
return Array.isArray(activeParts) ? activeParts[0] : activeParts;
};
private closeParentOverlay = () => {
private closeParentOverlay = (role: string) => {
const popoverOrModal = this.el.closest('ion-modal, ion-popover') as
| HTMLIonModalElement
| HTMLIonPopoverElement
| null;
if (popoverOrModal) {
popoverOrModal.dismiss();
popoverOrModal.dismiss(undefined, role);
}
};
@@ -2647,5 +2647,7 @@ export class Datetime implements ComponentInterface {
}
let datetimeIds = 0;
const CANCEL_ROLE = 'datetime-cancel';
const CONFIRM_ROLE = 'datetime-confirm';
const WHEEL_ITEM_PART = 'wheel-item';
const WHEEL_ITEM_ACTIVE_PART = `active`;

View File

@@ -0,0 +1,41 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ config, title }) => {
test.describe(title('datetime: overlay roles'), () => {
test.beforeEach(async ({ page }) => {
await page.setContent(
`
<ion-modal>
<ion-datetime></ion-datetime>
</ion-modal>
`,
config
);
});
test('should pass role to overlay when calling confirm method', async ({ page }) => {
const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
const modal = page.locator('ion-modal');
const datetime = page.locator('ion-datetime');
await modal.evaluate((el: HTMLIonModalElement) => el.present());
await datetime.evaluate((el: HTMLIonDatetimeElement) => el.confirm(true));
await ionModalDidDismiss.next();
expect(ionModalDidDismiss).toHaveReceivedEventDetail({ data: undefined, role: 'datetime-confirm' });
});
test('should pass role to overlay when calling cancel method', async ({ page }) => {
const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
const modal = page.locator('ion-modal');
const datetime = page.locator('ion-datetime');
await modal.evaluate((el: HTMLIonModalElement) => el.present());
await datetime.evaluate((el: HTMLIonDatetimeElement) => el.cancel(true));
await ionModalDidDismiss.next();
expect(ionModalDidDismiss).toHaveReceivedEventDetail({ data: undefined, role: 'datetime-cancel' });
});
});
});

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Datetime - Overlay Roles</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
ion-modal.ios,
ion-popover.datetime-popover.ios {
--width: 350px;
--height: 420px;
}
ion-modal.md,
ion-popover.datetime-popover.md {
--width: 350px;
--height: 450px;
}
ion-datetime {
width: 350px;
}
</style>
</head>
<body>
<ion-app>
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Datetime - Overlay Roles</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button onclick="presentModal()">Present Modal</ion-button>
</ion-content>
</ion-app>
<script>
const presentModal = async () => {
const modal = await createModal();
await modal.present();
console.log(await modal.onDidDismiss());
};
const createModal = () => {
// create component to open
const element = document.createElement('div');
element.innerHTML = `
<ion-datetime show-default-buttons="true"></ion-datetime>
`;
// present the modal
const modalElement = Object.assign(document.createElement('ion-modal'), {
component: element,
});
const app = document.querySelector('ion-app');
app.appendChild(modalElement);
return modalElement;
};
</script>
</body>
</html>

View File

@@ -95,6 +95,11 @@ export class Input implements ComponentInterface {
*/
@Prop() clearInput = false;
/**
* The icon to use for the clear button. Only applies when `clearInput` is set to `true`.
*/
@Prop() clearInputIcon?: string;
/**
* If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.
*/
@@ -724,7 +729,7 @@ export class Input implements ComponentInterface {
}
render() {
const { disabled, fill, readonly, shape, inputId, el, hasFocus } = this;
const { disabled, fill, readonly, shape, inputId, el, hasFocus, clearInputIcon } = this;
const theme = getIonTheme(this);
const value = this.getValue();
const size = this.getSize();
@@ -732,6 +737,9 @@ export class Input implements ComponentInterface {
const shouldRenderHighlight = theme === 'md' && fill !== 'outline' && !inItem;
const labelPlacement = this.getLabelPlacement();
const defaultClearIcon = theme === 'ios' ? closeCircle : closeSharp;
const clearIconData = clearInputIcon ?? defaultClearIcon;
const hasValue = this.hasValue();
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
@@ -842,7 +850,7 @@ export class Input implements ComponentInterface {
}}
onClick={this.clearTextInput}
>
<ion-icon aria-hidden="true" icon={theme === 'ios' ? closeCircle : closeSharp}></ion-icon>
<ion-icon aria-hidden="true" icon={clearIconData}></ion-icon>
</button>
)}
<slot name="end"></slot>

View File

@@ -99,3 +99,19 @@ describe('input: label rendering', () => {
expect(labelText.textContent).toBe('Label Prop Text');
});
});
// https://github.com/ionic-team/ionic-framework/issues/26974
describe('input: clear icon', () => {
it('should render custom icon', async () => {
const page = await newSpecPage({
components: [Input],
html: `
<ion-input clear-input-icon="foo" clear-input="true"></ion-input>
`,
});
const icon = page.body.querySelector<HTMLIonIconElement>('ion-input ion-icon')!;
expect(icon.getAttribute('icon')).toBe('foo');
});
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -101,8 +101,6 @@
display: flex;
flex-direction: column;
overflow: hidden;
}
// Nested Popovers

View File

@@ -48,6 +48,98 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
* Translucent popovers are only available on iOS
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('popover: scrolling'), async () => {
test.beforeEach(({ skip }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/29211',
});
// We are testing if Ionic sets overflow is set correctly on elements,
// so we do not need to test across browsers
skip.browser('webkit', 'Behavior does not vary across browsers');
skip.browser('firefox', 'Behavior does not vary across browsers');
});
test('should scroll to bottom without IonContent', async ({ page }) => {
await page.setContent(
`
<style>
ion-popover {
--height: 150px;
}
</style>
<ion-popover>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
</ion-popover>
`,
config
);
const popover = page.locator('ion-popover');
const viewport = popover.locator('.popover-viewport');
const p = popover.locator('p');
const lastP = await p.last();
await popover.evaluate((el: HTMLIonPopoverElement) => el.present());
await expect(lastP).not.toBeInViewport();
// hover over viewport and scroll to bottom
await viewport.hover();
await page.mouse.wheel(0, 500);
await expect(lastP).toBeInViewport();
});
test('should scroll to bottom with IonContent', async ({ page }) => {
await page.setContent(
`
<style>
ion-popover {
--height: 150px;
}
</style>
<ion-popover>
<ion-content>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
</ion-content>
</ion-popover>
`,
config
);
const popover = page.locator('ion-popover');
const content = popover.locator('ion-content');
const p = popover.locator('p');
const lastP = await p.last();
await popover.evaluate((el: HTMLIonPopoverElement) => el.present());
await expect(lastP).not.toBeInViewport();
// hover over viewport and scroll to bottom
await content.hover();
await page.mouse.wheel(0, 500);
await expect(lastP).toBeInViewport();
});
});
test.describe(title('popover: translucent variants'), async () => {
let popoverFixture!: PopoverFixture;
test.beforeEach(async ({ page }) => {

View File

@@ -608,7 +608,7 @@ export class Searchbar implements ComponentInterface {
}
render() {
const { cancelButtonText } = this;
const { cancelButtonText, autocapitalize } = this;
const animated = this.animated && config.getBoolean('animated', true);
const theme = getIonTheme(this);
const clearIcon = this.clearIcon || (theme === 'ios' ? closeCircle : closeSharp);
@@ -670,7 +670,7 @@ export class Searchbar implements ComponentInterface {
placeholder={this.placeholder}
type={this.type}
value={this.getValue()}
autoCapitalize={this.autocapitalize}
autoCapitalize={autocapitalize === 'default' ? undefined : autocapitalize}
autoComplete={this.autocomplete}
autoCorrect={this.autocorrect}
spellcheck={this.spellcheck}

View File

@@ -1,14 +1,160 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs().forEach(({ title, screenshot, config }) => {
test.describe(title('toolbar: basic'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/toolbar/test/basic`, config);
configs({ palettes: ['light', 'dark'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('toolbar: basic (LTR only)'), () => {
test('should not have visual regressions with text only', async ({ page }) => {
await page.setContent(
`
<ion-header>
<ion-toolbar>
<ion-title>Toolbar</ion-title>
</ion-toolbar>
</ion-header>
`,
config
);
// capture both header toolbars at once, but don't include all the white space in the ion-content
const header = page.locator('ion-header');
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic`));
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic-text-only`));
});
});
});
configs({ palettes: ['light', 'dark'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('toolbar: basic'), () => {
test('should truncate long title with ellipsis', async ({ page }) => {
await page.setContent(
`
<ion-header>
<ion-toolbar>
<ion-title>This is the title that never ends. It just goes on and on my friend.</ion-title>
</ion-toolbar>
</ion-header>
`,
config
);
const header = page.locator('ion-header');
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic-long-text`));
});
test('should not have visual regressions with icon-only buttons', async ({ page }) => {
await page.setContent(
`
<ion-header>
<ion-toolbar>
<ion-buttons slot="secondary">
<ion-button>
<ion-icon slot="icon-only" name="person-circle"></ion-icon>
</ion-button>
<ion-button>
<ion-icon slot="icon-only" name="search"></ion-icon>
</ion-button>
</ion-buttons>
<ion-buttons slot="primary">
<ion-button color="secondary">
<ion-icon slot="icon-only" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
</ion-button>
</ion-buttons>
<ion-title>Toolbar</ion-title>
</ion-toolbar>
<ion-toolbar>
<ion-buttons slot="secondary">
<ion-button class="ion-activated">
<ion-icon slot="icon-only" name="person-circle"></ion-icon>
</ion-button>
<ion-button class="ion-activated">
<ion-icon slot="icon-only" name="search"></ion-icon>
</ion-button>
</ion-buttons>
<ion-buttons slot="primary">
<ion-button color="secondary" class="ion-activated">
<ion-icon slot="icon-only" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
</ion-button>
</ion-buttons>
<ion-title>Activated Buttons</ion-title>
</ion-toolbar>
</ion-header>
`,
config
);
const header = page.locator('ion-header');
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic-icon-buttons`));
});
test('should not have visual regressions with buttons with icons and text', async ({ page }) => {
await page.setContent(
`
<ion-header>
<ion-toolbar>
<ion-buttons slot="secondary">
<ion-button fill="solid">
<ion-icon slot="start" name="person-circle"></ion-icon>
Solid
</ion-button>
</ion-buttons>
<ion-title>Solid</ion-title>
<ion-buttons slot="primary">
<ion-button fill="solid" color="secondary">
Help
<ion-icon slot="end" name="help-circle"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
<ion-toolbar>
<ion-buttons slot="secondary">
<ion-button fill="solid" class="ion-activated">
<ion-icon slot="start" name="person-circle"></ion-icon>
Solid
</ion-button>
</ion-buttons>
<ion-title>Solid Activated</ion-title>
<ion-buttons slot="primary">
<ion-button fill="solid" color="secondary" class="ion-activated">
Help
<ion-icon slot="end" name="help-circle"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
<ion-toolbar>
<ion-buttons slot="secondary">
<ion-button fill="outline">
<ion-icon slot="start" name="star"></ion-icon>
Star
</ion-button>
</ion-buttons>
<ion-title>Outline</ion-title>
<ion-buttons slot="primary">
<ion-button color="secondary" fill="outline">
Info
<ion-icon slot="end" name="information-circle"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
<ion-toolbar>
<ion-buttons slot="secondary">
<ion-button fill="outline" class="ion-activated">
<ion-icon slot="start" name="star"></ion-icon>
Star
</ion-button>
</ion-buttons>
<ion-title>Outline Activated</ion-title>
<ion-buttons slot="primary">
<ion-button color="secondary" fill="outline" class="ion-activated">
Info
<ion-icon slot="end" name="information-circle"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
`,
config
);
const header = page.locator('ion-header');
await expect(header).toHaveScreenshot(screenshot(`toolbar-basic-text-icon-buttons`));
});
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

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