Compare commits
28 Commits
ionic-modu
...
ROU-12446
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c11d2b2ab | ||
|
|
5cc8adb1d5 | ||
|
|
a6016f85bd | ||
|
|
177a6ea134 | ||
|
|
1d36021c6f | ||
|
|
89f3b1f956 | ||
|
|
4aaece0bec | ||
|
|
17b8468b04 | ||
|
|
abb950638b | ||
|
|
6c74618a07 | ||
|
|
68b66947d3 | ||
|
|
5b9a0bbc4d | ||
|
|
72826edf9a | ||
|
|
4360e39a58 | ||
|
|
622d62a3f4 | ||
|
|
12ede4b79c | ||
|
|
f83b000530 | ||
|
|
e227fd904e | ||
|
|
3b60a1d68a | ||
|
|
5bb5cc6385 | ||
|
|
8573bf8083 | ||
|
|
1b21e0748a | ||
|
|
34bcf95481 | ||
|
|
18eacab8fb | ||
|
|
2c6fac9060 | ||
|
|
b9fdfab667 | ||
|
|
f7af5d3ca5 | ||
|
|
03fb422bfa |
80
BREAKING.md
@@ -18,7 +18,11 @@ This is a comprehensive list of the breaking changes introduced in the major ver
|
||||
- [Button](#version-9x-button)
|
||||
- [Card](#version-9x-card)
|
||||
- [Chip](#version-9x-chip)
|
||||
- [Datetime](#version-9x-datetime)
|
||||
- [Grid](#version-9x-grid)
|
||||
- [Input Otp](#version-9x-input-otp)
|
||||
- [Radio Group](#version-9x-radio-group)
|
||||
- [Textarea](#version-9x-textarea)
|
||||
|
||||
<h2 id="version-9x-components">Components</h2>
|
||||
|
||||
@@ -28,25 +32,52 @@ This is a comprehensive list of the breaking changes introduced in the major ver
|
||||
|
||||
<h4 id="version-9x-card">Card</h4>
|
||||
|
||||
- The `border-radius` of the `ios` and `md` card now defaults to `14px` and `12px` instead of `8px` and `4px`, respectively, in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"`, or override the `--border-radius` CSS variable to specify a different value.
|
||||
- **ion-card**: The `border-radius` of the `ios` and `md` card now defaults to `14px` and `12px` instead of `8px` and `4px`, respectively, in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"`, or override the `--border-radius` CSS variable to specify a different value.
|
||||
|
||||
- **ion-card-content**: The `ion-card-content` component has been updated to Shadow DOM. With this update, all card-related components now use Shadow DOM for style encapsulation. The default styles for heading elements inside `ion-card-content` have been removed. If you need custom styling for headings, you can add your own CSS targeting these elements. For example:
|
||||
|
||||
```css
|
||||
ion-card-content h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 2px;
|
||||
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
ion-card-content h2 {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
ion-card-content h3,
|
||||
ion-card-content h4,
|
||||
ion-card-content h5,
|
||||
ion-card-content h6 {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
```
|
||||
|
||||
<h4 id="version-9x-chip">Chip</h4>
|
||||
|
||||
- The `border-radius` of the `ios` and `md` chip now defaults to `10px` and `8px`, respectively, instead of `16px` in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"round"`, or override the `--border-radius` CSS variable to specify a different value.
|
||||
|
||||
<h4 id="version-9x-datetime">Datetime</h4>
|
||||
|
||||
- The `ion-buttons` component has been removed from the internal implementation of `ion-datetime` and is no longer required when passing custom buttons to the `slot="buttons"`. When providing custom buttons, use a `div` element instead of `ion-buttons`. While existing code using `ion-buttons` may continue to work visually, future updates to the `ion-buttons` component may cause any styles you rely on to break.
|
||||
|
||||
<h4 id="version-9x-grid">Grid</h4>
|
||||
|
||||
- The properties `pull` and `push` have been deprecated and no longer work. A similar look can be achieved with the newly added property `order`.
|
||||
|
||||
<h4 id="version-9x-radio-group">Radio Group</h4>
|
||||
|
||||
- Converted `ion-radio-group` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).<br/>
|
||||
If you were targeting the internals of `ion-radio-group` in your CSS, you will need to target the `supporting-text`, `helper-text` or `error-text` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.<br/>
|
||||
Additionally, the `radio-group-wrapper` div element has been removed, causing slotted elements to be direct children of the `ion-radio-group`.
|
||||
|
||||
<h5>Example 1: Swap two columns</h5>
|
||||
|
||||
**Version up to 8.x**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -56,7 +87,9 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -68,9 +101,11 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
|
||||
```
|
||||
|
||||
<h5>Example 2: Reorder columns with specific sizes</h5>
|
||||
|
||||
To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `size="3" pull="9"`:
|
||||
|
||||
**Version up to 8.x**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -79,7 +114,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -88,7 +125,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
<h5>Example 3: Push</h5>
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -101,7 +140,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -116,6 +157,7 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
```
|
||||
|
||||
<h5>Example 4: Push and Pull</h5>
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -128,7 +170,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -140,4 +184,24 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
```
|
||||
|
||||
<h4 id="version-9x-input-otp">Input Otp</h4>
|
||||
|
||||
Converted `ion-input-otp` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
|
||||
|
||||
If you were targeting the internals of `ion-input-otp` in your CSS, you will need to target the `group`, `container`, `native`, `separator` or `description` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
|
||||
|
||||
<h4 id="version-9x-radio-group">Radio Group</h4>
|
||||
|
||||
Converted `ion-radio-group` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
|
||||
|
||||
If you were targeting the internals of `ion-radio-group` in your CSS, you will need to target the `supporting-text`, `helper-text` or `error-text` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
|
||||
|
||||
Additionally, the `radio-group-wrapper` div element has been removed, causing slotted elements to be direct children of the `ion-radio-group`.
|
||||
|
||||
<h4 id="version-9x-textarea">Textarea</h4>
|
||||
|
||||
Converted `ion-textarea` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
|
||||
|
||||
If you were targeting the internals of `ion-textarea` in your CSS, you will need to target the `wrapper`, `container`, `label`, `native`, `supporting-text`, `helper-text`, `error-text`, `counter`, or `bottom` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
|
||||
|
||||
25
CHANGELOG.md
@@ -3,6 +3,31 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [8.7.15](https://github.com/ionic-team/ionic-framework/compare/v8.7.14...v8.7.15) (2025-12-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** use Capacitor safe-area CSS variables on older WebViews ([#30865](https://github.com/ionic-team/ionic-framework/issues/30865)) ([8573bf8](https://github.com/ionic-team/ionic-framework/commit/8573bf8083f75eda13c954a56731a6aac8ca5724))
|
||||
* **header:** show iOS condense header when app is in MD mode ([#30690](https://github.com/ionic-team/ionic-framework/issues/30690)) ([f83b000](https://github.com/ionic-team/ionic-framework/commit/f83b0005309400d674e43c497bdffbcb9d2c4d94)), closes [#29929](https://github.com/ionic-team/ionic-framework/issues/29929)
|
||||
* **input-password-toggle:** improve screen reader announcements ([#30885](https://github.com/ionic-team/ionic-framework/issues/30885)) ([12ede4b](https://github.com/ionic-team/ionic-framework/commit/12ede4b79c8d5cffc2b014c7c8a0d2ef1d3bf90d))
|
||||
* **modal:** dismiss top-most overlay when multiple IDs match ([#30883](https://github.com/ionic-team/ionic-framework/issues/30883)) ([3b60a1d](https://github.com/ionic-team/ionic-framework/commit/3b60a1d68a1df1606ffee0bde7db7a206bac404a)), closes [#30030](https://github.com/ionic-team/ionic-framework/issues/30030)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.14](https://github.com/ionic-team/ionic-framework/compare/v8.7.13...v8.7.14) (2025-12-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tabs:** select correct tab when routes have similar prefixes ([#30863](https://github.com/ionic-team/ionic-framework/issues/30863)) ([03fb422](https://github.com/ionic-team/ionic-framework/commit/03fb422bfa775e3e9dd695ea1857fa88d4245ecd)), closes [#30448](https://github.com/ionic-team/ionic-framework/issues/30448)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.13](https://github.com/ionic-team/ionic-framework/compare/v8.7.12...v8.7.13) (2025-12-13)
|
||||
|
||||
**Note:** Version bump only for package ionic-framework
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [8.7.15](https://github.com/ionic-team/ionic-framework/compare/v8.7.14...v8.7.15) (2025-12-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** use Capacitor safe-area CSS variables on older WebViews ([#30865](https://github.com/ionic-team/ionic-framework/issues/30865)) ([8573bf8](https://github.com/ionic-team/ionic-framework/commit/8573bf8083f75eda13c954a56731a6aac8ca5724))
|
||||
* **header:** show iOS condense header when app is in MD mode ([#30690](https://github.com/ionic-team/ionic-framework/issues/30690)) ([f83b000](https://github.com/ionic-team/ionic-framework/commit/f83b0005309400d674e43c497bdffbcb9d2c4d94)), closes [#29929](https://github.com/ionic-team/ionic-framework/issues/29929)
|
||||
* **input-password-toggle:** improve screen reader announcements ([#30885](https://github.com/ionic-team/ionic-framework/issues/30885)) ([12ede4b](https://github.com/ionic-team/ionic-framework/commit/12ede4b79c8d5cffc2b014c7c8a0d2ef1d3bf90d))
|
||||
* **modal:** dismiss top-most overlay when multiple IDs match ([#30883](https://github.com/ionic-team/ionic-framework/issues/30883)) ([3b60a1d](https://github.com/ionic-team/ionic-framework/commit/3b60a1d68a1df1606ffee0bde7db7a206bac404a)), closes [#30030](https://github.com/ionic-team/ionic-framework/issues/30030)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.14](https://github.com/ionic-team/ionic-framework/compare/v8.7.13...v8.7.14) (2025-12-17)
|
||||
|
||||
**Note:** Version bump only for package @ionic/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [8.7.13](https://github.com/ionic-team/ionic-framework/compare/v8.7.12...v8.7.13) (2025-12-13)
|
||||
|
||||
**Note:** Version bump only for package @ionic/core
|
||||
|
||||
24
core/api.txt
@@ -504,7 +504,7 @@ ion-card,css-prop,--color,ios
|
||||
ion-card,css-prop,--color,md
|
||||
ion-card,part,native
|
||||
|
||||
ion-card-content,none
|
||||
ion-card-content,shadow
|
||||
ion-card-content,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-card-content,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
|
||||
@@ -1035,18 +1035,20 @@ ion-input,css-prop,--placeholder-opacity,ionic
|
||||
ion-input,css-prop,--placeholder-opacity,ios
|
||||
ion-input,css-prop,--placeholder-opacity,md
|
||||
|
||||
ion-input-otp,scoped
|
||||
ion-input-otp,shadow
|
||||
ion-input-otp,prop,autocapitalize,string,'off',false,false
|
||||
ion-input-otp,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
|
||||
ion-input-otp,prop,disabled,boolean,false,false,true
|
||||
ion-input-otp,prop,fill,"outline" | "solid" | undefined,'outline',false,false
|
||||
ion-input-otp,prop,inputmode,"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url" | undefined,undefined,false,false
|
||||
ion-input-otp,prop,length,number,4,false,false
|
||||
ion-input-otp,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-input-otp,prop,pattern,string | undefined,undefined,false,false
|
||||
ion-input-otp,prop,readonly,boolean,false,false,true
|
||||
ion-input-otp,prop,separators,number[] | string | undefined,undefined,false,false
|
||||
ion-input-otp,prop,shape,"rectangular" | "round" | "soft",'round',false,false
|
||||
ion-input-otp,prop,size,"large" | "medium" | "small",'medium',false,false
|
||||
ion-input-otp,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
ion-input-otp,prop,type,"number" | "text",'number',false,false
|
||||
ion-input-otp,prop,value,null | number | string | undefined,'',false,false
|
||||
ion-input-otp,method,setFocus,setFocus(index?: number) => Promise<void>
|
||||
@@ -1127,6 +1129,11 @@ ion-input-otp,css-prop,--separator-width,md
|
||||
ion-input-otp,css-prop,--width,ionic
|
||||
ion-input-otp,css-prop,--width,ios
|
||||
ion-input-otp,css-prop,--width,md
|
||||
ion-input-otp,part,container
|
||||
ion-input-otp,part,description
|
||||
ion-input-otp,part,group
|
||||
ion-input-otp,part,native
|
||||
ion-input-otp,part,separator
|
||||
|
||||
ion-input-password-toggle,shadow
|
||||
ion-input-password-toggle,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
|
||||
@@ -2426,7 +2433,7 @@ ion-text,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "second
|
||||
ion-text,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-text,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
|
||||
ion-textarea,scoped
|
||||
ion-textarea,shadow
|
||||
ion-textarea,prop,autoGrow,boolean,false,false,true
|
||||
ion-textarea,prop,autocapitalize,string,'none',false,false
|
||||
ion-textarea,prop,autofocus,boolean,false,false,false
|
||||
@@ -2450,7 +2457,7 @@ ion-textarea,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-textarea,prop,name,string,this.inputId,false,false
|
||||
ion-textarea,prop,placeholder,string | undefined,undefined,false,false
|
||||
ion-textarea,prop,readonly,boolean,false,false,false
|
||||
ion-textarea,prop,required,boolean,false,false,false
|
||||
ion-textarea,prop,required,boolean,false,false,true
|
||||
ion-textarea,prop,rows,number | undefined,undefined,false,false
|
||||
ion-textarea,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
|
||||
ion-textarea,prop,size,"large" | "medium" | "small" | undefined,'medium',false,false
|
||||
@@ -2518,6 +2525,15 @@ ion-textarea,css-prop,--placeholder-font-weight,md
|
||||
ion-textarea,css-prop,--placeholder-opacity,ionic
|
||||
ion-textarea,css-prop,--placeholder-opacity,ios
|
||||
ion-textarea,css-prop,--placeholder-opacity,md
|
||||
ion-textarea,part,bottom
|
||||
ion-textarea,part,container
|
||||
ion-textarea,part,counter
|
||||
ion-textarea,part,error-text
|
||||
ion-textarea,part,helper-text
|
||||
ion-textarea,part,label
|
||||
ion-textarea,part,native
|
||||
ion-textarea,part,supporting-text
|
||||
ion-textarea,part,wrapper
|
||||
|
||||
ion-thumbnail,shadow
|
||||
ion-thumbnail,prop,mode,"ios" | "md",undefined,false,false
|
||||
|
||||
4
core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "8.7.13",
|
||||
"version": "8.7.15",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/core",
|
||||
"version": "8.7.13",
|
||||
"version": "8.7.15",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@phosphor-icons/core": "^2.1.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "8.7.13",
|
||||
"version": "8.7.15",
|
||||
"description": "Base components for Ionic",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
|
||||
20
core/src/components.d.ts
vendored
@@ -600,7 +600,7 @@ export namespace Components {
|
||||
*/
|
||||
"expand"?: 'full' | 'block';
|
||||
/**
|
||||
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except inside of a toolbar, where the default is `"clear"`.
|
||||
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except when inside of a buttons or datetime component, where the default fill is `"clear"`.
|
||||
*/
|
||||
"fill"?: 'clear' | 'outline' | 'solid' | 'default';
|
||||
/**
|
||||
@@ -1779,6 +1779,10 @@ export namespace Components {
|
||||
* @default 4
|
||||
*/
|
||||
"length": number;
|
||||
/**
|
||||
* The mode determines the platform behaviors of the component.
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
/**
|
||||
* A regex pattern string for allowed characters. Defaults based on type. For numbers (`type="number"`): `"[\p{N}]"` For text (`type="text"`): `"[\p{L}\p{N}]"`
|
||||
*/
|
||||
@@ -1807,6 +1811,10 @@ export namespace Components {
|
||||
* @default 'medium'
|
||||
*/
|
||||
"size": 'small' | 'medium' | 'large';
|
||||
/**
|
||||
* The theme determines the visual appearance of the component.
|
||||
*/
|
||||
"theme"?: "ios" | "md" | "ionic";
|
||||
/**
|
||||
* The type of input allowed in the input boxes.
|
||||
* @default 'number'
|
||||
@@ -6555,7 +6563,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"expand"?: 'full' | 'block';
|
||||
/**
|
||||
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except inside of a toolbar, where the default is `"clear"`.
|
||||
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except when inside of a buttons or datetime component, where the default fill is `"clear"`.
|
||||
*/
|
||||
"fill"?: 'clear' | 'outline' | 'solid' | 'default';
|
||||
/**
|
||||
@@ -7762,6 +7770,10 @@ declare namespace LocalJSX {
|
||||
* @default 4
|
||||
*/
|
||||
"length"?: number;
|
||||
/**
|
||||
* The mode determines the platform behaviors of the component.
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
/**
|
||||
* Emitted when the input group loses focus.
|
||||
*/
|
||||
@@ -7805,6 +7817,10 @@ declare namespace LocalJSX {
|
||||
* @default 'medium'
|
||||
*/
|
||||
"size"?: 'small' | 'medium' | 'large';
|
||||
/**
|
||||
* The theme determines the visual appearance of the component.
|
||||
*/
|
||||
"theme"?: "ios" | "md" | "ionic";
|
||||
/**
|
||||
* The type of input allowed in the input boxes.
|
||||
* @default 'number'
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { E2EPage } from '@utils/test/playwright';
|
||||
/**
|
||||
* Safe area tests only check top and bottom edges. RTL checks are not required here.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title, screenshot }) => {
|
||||
configs({ directions: ['ltr'], modes: ['ios', 'md', 'ionic-md'] }).forEach(({ config, title, screenshot }) => {
|
||||
test.describe(title('app: safe-area'), () => {
|
||||
const testOverlay = async (page: E2EPage, trigger: string, event: string, screenshotModifier: string) => {
|
||||
const presentEvent = await page.spyOnEvent(event);
|
||||
@@ -18,20 +18,66 @@ configs({ directions: ['ltr'] }).forEach(({ config, title, screenshot }) => {
|
||||
|
||||
await expect(page).toHaveScreenshot(screenshot(`app-${screenshotModifier}-diff`));
|
||||
};
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/app/test/safe-area`, config);
|
||||
});
|
||||
test('should not have visual regressions with action sheet', async ({ page }) => {
|
||||
await testOverlay(page, '#show-action-sheet', 'ionActionSheetDidPresent', 'action-sheet');
|
||||
|
||||
test.describe(title('Ionic safe area variables'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
const htmlTag = page.locator('html');
|
||||
const hasSafeAreaClass = await htmlTag.evaluate((el) => el.classList.contains('safe-area'));
|
||||
|
||||
expect(hasSafeAreaClass).toBe(true);
|
||||
});
|
||||
|
||||
test('should not have visual regressions with action sheet', async ({ page }) => {
|
||||
await testOverlay(page, '#show-action-sheet', 'ionActionSheetDidPresent', 'action-sheet');
|
||||
});
|
||||
test('should not have visual regressions with menu', async ({ page }) => {
|
||||
await testOverlay(page, '#show-menu', 'ionDidOpen', 'menu');
|
||||
});
|
||||
test('should not have visual regressions with picker', async ({ page }) => {
|
||||
await testOverlay(page, '#show-picker', 'ionPickerDidPresent', 'picker');
|
||||
});
|
||||
test('should not have visual regressions with toast', async ({ page }) => {
|
||||
await testOverlay(page, '#show-toast', 'ionToastDidPresent', 'toast');
|
||||
});
|
||||
});
|
||||
test('should not have visual regressions with menu', async ({ page }) => {
|
||||
await testOverlay(page, '#show-menu', 'ionDidOpen', 'menu');
|
||||
});
|
||||
test('should not have visual regressions with picker', async ({ page }) => {
|
||||
await testOverlay(page, '#show-picker', 'ionPickerDidPresent', 'picker');
|
||||
});
|
||||
test('should not have visual regressions with toast', async ({ page }) => {
|
||||
await testOverlay(page, '#show-toast', 'ionToastDidPresent', 'toast');
|
||||
|
||||
test.describe(title('Capacitor safe area variables'), () => {
|
||||
test('should use safe-area-inset vars when safe-area class is not defined', async ({ page }) => {
|
||||
await page.evaluate(() => {
|
||||
const html = document.documentElement;
|
||||
|
||||
// Remove the safe area class
|
||||
html.classList.remove('safe-area');
|
||||
|
||||
// Set the safe area inset variables
|
||||
html.style.setProperty('--safe-area-inset-top', '10px');
|
||||
html.style.setProperty('--safe-area-inset-bottom', '20px');
|
||||
html.style.setProperty('--safe-area-inset-left', '30px');
|
||||
html.style.setProperty('--safe-area-inset-right', '40px');
|
||||
});
|
||||
|
||||
const top = await page.evaluate(() =>
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-top').trim()
|
||||
);
|
||||
const bottom = await page.evaluate(() =>
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-bottom').trim()
|
||||
);
|
||||
const left = await page.evaluate(() =>
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-left').trim()
|
||||
);
|
||||
const right = await page.evaluate(() =>
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-right').trim()
|
||||
);
|
||||
|
||||
expect(top).toBe('10px');
|
||||
expect(bottom).toBe('20px');
|
||||
expect(left).toBe('30px');
|
||||
expect(right).toBe('40px');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -334,3 +334,12 @@
|
||||
@include globals.position(null, globals.$ion-space-200, globals.$ion-space-200, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Button in Datetime
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.in-datetime) {
|
||||
@include globals.typography(globals.$ion-body-action-md);
|
||||
|
||||
min-height: globals.$ion-space-1200;
|
||||
}
|
||||
|
||||
@@ -293,3 +293,16 @@
|
||||
background: var(--ion-toolbar-color, var(--color));
|
||||
color: #{var(--ion-toolbar-background, var(--background), ion-color(primary, contrast))};
|
||||
}
|
||||
|
||||
// Button in Datetime
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.in-datetime) {
|
||||
--padding-top: 3px;
|
||||
--padding-bottom: 3px;
|
||||
--padding-start: 5px;
|
||||
--padding-end: 5px;
|
||||
|
||||
font-size: dynamic-font-clamp(1, 17px, 1.24);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@@ -243,3 +243,20 @@
|
||||
background: var(--ion-toolbar-background, var(--color));
|
||||
color: #{var(--ion-toolbar-color, var(--background), ion-color(primary, contrast))};
|
||||
}
|
||||
|
||||
// Button in Datetime
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.in-datetime) {
|
||||
--padding-top: 3px;
|
||||
--padding-bottom: 3px;
|
||||
--padding-start: 8px;
|
||||
--padding-end: 8px;
|
||||
--box-shadow: none;
|
||||
}
|
||||
|
||||
:host(.in-datetime.button-clear) {
|
||||
--background-activated: transparent;
|
||||
--background-focused: currentColor;
|
||||
--background-hover: currentColor;
|
||||
}
|
||||
|
||||
@@ -59,3 +59,12 @@
|
||||
:host ::slotted(ion-badge[vertical]:not(:empty)) {
|
||||
@include globals.padding(2px);
|
||||
}
|
||||
|
||||
// Button in Datetime
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.in-datetime) {
|
||||
@include globals.margin(0px, 2px, 0px, 2px);
|
||||
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,10 @@ import type { RouterDirection } from '../router/utils/interface';
|
||||
shadow: true,
|
||||
})
|
||||
export class Button implements ComponentInterface, AnchorInterface, ButtonInterface {
|
||||
private inDatetime = false;
|
||||
private inItem = false;
|
||||
private inListHeader = false;
|
||||
private inToolbar = false;
|
||||
private inButtons = false;
|
||||
private formButtonEl: HTMLButtonElement | null = null;
|
||||
private formEl: HTMLFormElement | null = null;
|
||||
private inheritedAttributes: Attributes = {};
|
||||
@@ -78,7 +79,8 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
/**
|
||||
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"`
|
||||
* for a transparent button with a border, or to `"solid"` for a button with a filled background.
|
||||
* The default fill is `"solid"` except inside of a toolbar, where the default is `"clear"`.
|
||||
* The default fill is `"solid"` except when inside of a buttons or datetime component, where
|
||||
* the default fill is `"clear"`.
|
||||
*/
|
||||
@Prop({ reflect: true, mutable: true }) fill?: 'clear' | 'outline' | 'solid' | 'default';
|
||||
|
||||
@@ -174,6 +176,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
*/
|
||||
@Watch('aria-checked')
|
||||
@Watch('aria-label')
|
||||
@Watch('aria-pressed')
|
||||
onAriaChanged(newValue: string, _oldValue: string, propName: string) {
|
||||
this.inheritedAttributes = {
|
||||
...this.inheritedAttributes,
|
||||
@@ -216,9 +219,10 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.inToolbar = !!this.el.closest('ion-buttons');
|
||||
this.inListHeader = !!this.el.closest('ion-list-header');
|
||||
this.inItem = !!this.el.closest('ion-item') || !!this.el.closest('ion-item-divider');
|
||||
this.inDatetime = hostContext('ion-datetime', this.el);
|
||||
this.inButtons = hostContext('ion-buttons', this.el);
|
||||
this.inListHeader = hostContext('ion-list-header', this.el);
|
||||
this.inItem = hostContext('ion-item', this.el) || hostContext('ion-item-divider', this.el);
|
||||
this.inheritedAttributes = inheritAriaAttributes(this.el);
|
||||
}
|
||||
|
||||
@@ -233,9 +237,11 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
private get rippleType() {
|
||||
const hasClearFill = this.fill === undefined || this.fill === 'clear';
|
||||
|
||||
// If the button is in a toolbar, has a clear fill (which is the default)
|
||||
// and only has an icon we use the unbounded "circular" ripple effect
|
||||
if (hasClearFill && this.hasIconOnly && this.inToolbar) {
|
||||
// Use the unbounded "circular" ripple effect if it:
|
||||
// - Has a clear fill (the default)
|
||||
// - Only has an icon and
|
||||
// - Is inside of buttons (used in a toolbar) or a datetime
|
||||
if (hasClearFill && this.hasIconOnly && (this.inButtons || this.inDatetime)) {
|
||||
return 'unbounded';
|
||||
}
|
||||
|
||||
@@ -400,7 +406,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
};
|
||||
let fill = this.fill;
|
||||
if (fill === undefined) {
|
||||
fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
|
||||
fill = this.inDatetime || this.inButtons || this.inListHeader ? 'clear' : 'solid';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,9 +432,10 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
[`${buttonType}-${shape}`]: true,
|
||||
[`${buttonType}-${fill}`]: true,
|
||||
[`${buttonType}-strong`]: strong,
|
||||
'in-datetime': this.inDatetime,
|
||||
'in-toolbar': hostContext('ion-toolbar', this.el),
|
||||
'in-toolbar-color': hostContext('ion-toolbar[color]', this.el),
|
||||
'in-buttons': hostContext('ion-buttons', this.el),
|
||||
'in-buttons': this.inButtons,
|
||||
'button-has-icon-only': hasIconOnly,
|
||||
'button-has-badge': hasBadge,
|
||||
'button-disabled': disabled,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-card-content {
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Ionic Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.card-content-ionic {
|
||||
:host {
|
||||
@include globals.padding(globals.$ion-space-400);
|
||||
@include globals.typography(globals.$ion-body-md-regular);
|
||||
|
||||
@@ -13,12 +13,8 @@
|
||||
flex-direction: column;
|
||||
|
||||
gap: globals.$ion-space-400;
|
||||
|
||||
img {
|
||||
@include globals.margin(globals.$ion-space-200, 0, globals.$ion-space-200, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-header + .card-content-ionic {
|
||||
padding-top: 0;
|
||||
::slotted(img) {
|
||||
@include globals.margin(globals.$ion-space-200, 0, globals.$ion-space-200, 0);
|
||||
}
|
||||
|
||||
@@ -4,44 +4,16 @@
|
||||
// iOS Card Header
|
||||
// --------------------------------------------------
|
||||
|
||||
.card-content-ios {
|
||||
:host {
|
||||
@include padding($card-ios-padding-top, $card-ios-padding-end, $card-ios-padding-bottom, $card-ios-padding-start);
|
||||
|
||||
font-size: $card-ios-font-size;
|
||||
|
||||
line-height: 1.4;
|
||||
|
||||
h1 {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(24px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(16px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-header + .card-content-ios {
|
||||
padding-top: 0;
|
||||
::slotted(p) {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
}
|
||||
|
||||
@@ -4,47 +4,19 @@
|
||||
// Material Design Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.card-content-md {
|
||||
:host {
|
||||
@include padding($card-md-padding-top, $card-md-padding-end, $card-md-padding-bottom, $card-md-padding-start);
|
||||
|
||||
font-size: $card-md-font-size;
|
||||
|
||||
line-height: $card-md-line-height;
|
||||
|
||||
h1 {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(24px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(16px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-header + .card-content-md {
|
||||
padding-top: 0;
|
||||
::slotted(p) {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
// Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-card-content {
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { getIonTheme } from '../../global/ionic-global';
|
||||
md: 'card-content.md.scss',
|
||||
ionic: 'card-content.ionic.scss',
|
||||
},
|
||||
shadow: true,
|
||||
})
|
||||
export class CardContent implements ComponentInterface {
|
||||
render() {
|
||||
@@ -22,11 +23,10 @@ export class CardContent implements ComponentInterface {
|
||||
<Host
|
||||
class={{
|
||||
[theme]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`card-content-${theme}`]: true,
|
||||
}}
|
||||
></Host>
|
||||
>
|
||||
<slot></slot>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,27 +91,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-color`));
|
||||
});
|
||||
test('headings should have correct size in card', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card>
|
||||
<ion-card-content>
|
||||
<h1>Heading 1</h1>
|
||||
<h2>Heading 2</h2>
|
||||
<h3>Heading 3</h3>
|
||||
<h4>Heading 4</h4>
|
||||
<h5>Heading 5</h5>
|
||||
<h6>Heading 6</h6>
|
||||
<p>Paragraph</p>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-headings`));
|
||||
});
|
||||
test('should render even without header or content elements', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
|
||||
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -60,12 +60,6 @@
|
||||
--border-radius: #{globals.$ion-border-radius-0};
|
||||
}
|
||||
|
||||
// Chip Icon
|
||||
// ---------------------------------------------
|
||||
::slotted(ion-icon) {
|
||||
font-size: globals.$ion-font-size-400;
|
||||
}
|
||||
|
||||
// Size
|
||||
// ---------------------------------------------
|
||||
|
||||
@@ -73,12 +67,20 @@
|
||||
min-height: globals.$ion-scale-600;
|
||||
|
||||
font-size: globals.$ion-font-size-300;
|
||||
|
||||
::slotted(ion-icon) {
|
||||
font-size: globals.$ion-font-size-300;
|
||||
}
|
||||
}
|
||||
|
||||
:host(.chip-large) {
|
||||
min-height: globals.$ion-scale-800;
|
||||
|
||||
font-size: globals.$ion-font-size-350;
|
||||
|
||||
::slotted(ion-icon) {
|
||||
font-size: globals.$ion-font-size-400;
|
||||
}
|
||||
}
|
||||
|
||||
// Subtle Chip
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
@@ -149,7 +149,8 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host .datetime-action-buttons ion-buttons,
|
||||
:host .datetime-action-buttons,
|
||||
::slotted([slot="buttons"]),
|
||||
/**
|
||||
* The confirm and clear buttons are grouped in a
|
||||
* container so that they appear on the end opposite
|
||||
|
||||
@@ -251,15 +251,10 @@
|
||||
);
|
||||
}
|
||||
|
||||
:host .datetime-buttons ion-buttons,
|
||||
.datetime-action-buttons .datetime-action-buttons-container {
|
||||
:host .datetime-buttons .datetime-action-buttons,
|
||||
.datetime-action-buttons .datetime-action-buttons-container,
|
||||
::slotted([slot="buttons"]) {
|
||||
flex-flow: column;
|
||||
align-items: stretch;
|
||||
gap: globals.$ion-space-200;
|
||||
}
|
||||
|
||||
:host .datetime-buttons ion-buttons ion-button {
|
||||
@include globals.typography(globals.$ion-body-action-md);
|
||||
|
||||
min-height: globals.$ion-space-1200;
|
||||
}
|
||||
|
||||
@@ -55,16 +55,38 @@
|
||||
}
|
||||
|
||||
:host .calendar-action-buttons .calendar-month-year-toggle ion-icon,
|
||||
:host .calendar-action-buttons ion-buttons ion-button {
|
||||
:host .calendar-action-buttons ion-button {
|
||||
color: current-color(base);
|
||||
}
|
||||
|
||||
:host .calendar-action-buttons ion-buttons {
|
||||
:host .calendar-action-buttons ion-button {
|
||||
@include margin(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
:host .calendar-next-prev {
|
||||
@include padding($datetime-ios-padding * 0.5, 0, 0, 0);
|
||||
}
|
||||
|
||||
:host .calendar-action-buttons ion-buttons ion-button {
|
||||
@include margin(0, 0, 0, 0);
|
||||
// These styles are copied from buttons.ios.scss
|
||||
.calendar-next-prev ion-button {
|
||||
--padding-top: 0;
|
||||
--padding-bottom: 0;
|
||||
--padding-start: 5px;
|
||||
--padding-end: 5px;
|
||||
|
||||
@include margin-horizontal(2px, 2px);
|
||||
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
// These styles are copied from buttons.ios.scss
|
||||
.calendar-next-prev ion-button ion-icon[slot="icon-only"] {
|
||||
// This value is calculated by dividing the font size the
|
||||
// icon should be in px (28px) by the font size of its
|
||||
// parent button (17px). e.g. 28 / 17 = 1.647
|
||||
font-size: 1.65em;
|
||||
|
||||
line-height: 0.67;
|
||||
}
|
||||
|
||||
// Calendar / Header / Days of Week
|
||||
@@ -297,6 +319,7 @@
|
||||
|
||||
// Footer
|
||||
// -----------------------------------
|
||||
|
||||
:host .datetime-buttons {
|
||||
@include padding(
|
||||
$datetime-ios-padding * 0.5,
|
||||
@@ -308,14 +331,15 @@
|
||||
border-top: $datetime-ios-border-color;
|
||||
}
|
||||
|
||||
:host .datetime-buttons ::slotted(ion-buttons),
|
||||
:host .datetime-buttons ion-buttons {
|
||||
:host .datetime-buttons,
|
||||
::slotted([slot="buttons"]) {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
:host .datetime-action-buttons {
|
||||
:host .datetime-action-buttons,
|
||||
::slotted([slot="buttons"]) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,25 @@
|
||||
--color: #{$text-color-step-350};
|
||||
}
|
||||
|
||||
// These styles are copied from buttons.md.scss
|
||||
.calendar-next-prev ion-button {
|
||||
--padding-top: 12px;
|
||||
--padding-end: 12px;
|
||||
--padding-bottom: 12px;
|
||||
--padding-start: 12px;
|
||||
--border-radius: 50%;
|
||||
|
||||
@include margin(0);
|
||||
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
// These styles are copied from buttons.md.scss
|
||||
.calendar-next-prev ion-button ion-icon[slot="icon-only"] {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.calendar-month-year-toggle {
|
||||
@include padding(12px, 16px, 12px, #{$datetime-md-header-padding});
|
||||
|
||||
@@ -166,6 +185,7 @@
|
||||
|
||||
// Footer
|
||||
// -----------------------------------
|
||||
|
||||
:host .datetime-buttons {
|
||||
@include padding(10px, 10px, 10px, 10px);
|
||||
|
||||
|
||||
@@ -81,6 +81,17 @@
|
||||
// Time / Header
|
||||
// -----------------------------------
|
||||
|
||||
:host .calendar-next-prev {
|
||||
display: flex;
|
||||
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.calendar-next-prev ion-button ion-icon[slot="icon-only"] {
|
||||
@include globals.padding(0);
|
||||
@include globals.margin(0);
|
||||
}
|
||||
|
||||
:host(.datetime-presentation-time) .datetime-time {
|
||||
@include globals.padding(0);
|
||||
}
|
||||
@@ -144,6 +155,6 @@ ion-picker {
|
||||
// Calendar / Footer / Action Buttons
|
||||
// -----------------------------------
|
||||
|
||||
:host .datetime-action-buttons ion-buttons {
|
||||
:host .datetime-action-buttons {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@@ -1585,41 +1585,39 @@ export class Datetime implements ComponentInterface {
|
||||
}}
|
||||
>
|
||||
<slot name="buttons">
|
||||
<ion-buttons>
|
||||
{showDefaultButtons && (
|
||||
{showDefaultButtons && (
|
||||
<ion-button
|
||||
id="cancel-button"
|
||||
color={this.color}
|
||||
onClick={() => this.cancel(true)}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{this.cancelText}
|
||||
</ion-button>
|
||||
)}
|
||||
<div class="datetime-action-buttons-container">
|
||||
{showClearButton && (
|
||||
<ion-button
|
||||
id="cancel-button"
|
||||
id="clear-button"
|
||||
color={this.color}
|
||||
onClick={() => this.cancel(true)}
|
||||
onClick={() => clearButtonClick()}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{this.cancelText}
|
||||
{this.clearText}
|
||||
</ion-button>
|
||||
)}
|
||||
<div class="datetime-action-buttons-container">
|
||||
{showClearButton && (
|
||||
<ion-button
|
||||
id="clear-button"
|
||||
color={this.color}
|
||||
onClick={() => clearButtonClick()}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{this.clearText}
|
||||
</ion-button>
|
||||
)}
|
||||
{showDefaultButtons && (
|
||||
<ion-button
|
||||
id="confirm-button"
|
||||
color={this.color}
|
||||
onClick={() => this.confirm(true)}
|
||||
disabled={isButtonDisabled}
|
||||
fill={confirmFill}
|
||||
>
|
||||
{this.doneText}
|
||||
</ion-button>
|
||||
)}
|
||||
</div>
|
||||
</ion-buttons>
|
||||
{showDefaultButtons && (
|
||||
<ion-button
|
||||
id="confirm-button"
|
||||
fill={confirmFill}
|
||||
color={this.color}
|
||||
onClick={() => this.confirm(true)}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{this.doneText}
|
||||
</ion-button>
|
||||
)}
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2194,28 +2192,26 @@ export class Datetime implements ComponentInterface {
|
||||
</div>
|
||||
|
||||
<div class="calendar-next-prev">
|
||||
<ion-buttons>
|
||||
<ion-button aria-label="Previous month" disabled={prevMonthDisabled} onClick={() => this.prevMonth()}>
|
||||
<ion-icon
|
||||
dir={hostDir}
|
||||
aria-hidden="true"
|
||||
slot="icon-only"
|
||||
icon={datetimePreviousIcon}
|
||||
lazy={false}
|
||||
flipRtl
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button aria-label="Next month" disabled={nextMonthDisabled} onClick={() => this.nextMonth()}>
|
||||
<ion-icon
|
||||
dir={hostDir}
|
||||
aria-hidden="true"
|
||||
slot="icon-only"
|
||||
icon={datetimeNextIcon}
|
||||
lazy={false}
|
||||
flipRtl
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-button aria-label="Previous month" disabled={prevMonthDisabled} onClick={() => this.prevMonth()}>
|
||||
<ion-icon
|
||||
dir={hostDir}
|
||||
aria-hidden="true"
|
||||
slot="icon-only"
|
||||
icon={datetimePreviousIcon}
|
||||
lazy={false}
|
||||
flipRtl
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button aria-label="Next month" disabled={nextMonthDisabled} onClick={() => this.nextMonth()}>
|
||||
<ion-icon
|
||||
dir={hostDir}
|
||||
aria-hidden="true"
|
||||
slot="icon-only"
|
||||
icon={datetimeNextIcon}
|
||||
lazy={false}
|
||||
flipRtl
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calendar-days-of-week" aria-hidden="true">
|
||||
|
||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 54 KiB |
@@ -265,6 +265,7 @@ configs({ modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, screenshot, conf
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-footer-default-buttons`));
|
||||
});
|
||||
|
||||
test('should render clear button', async ({ page }) => {
|
||||
await page.setContent('<ion-datetime value="2022-05-03" show-clear-button="true"></ion-datetime>', config);
|
||||
|
||||
@@ -274,6 +275,7 @@ configs({ modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, screenshot, conf
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-footer-clear-button`));
|
||||
});
|
||||
|
||||
test('should render default and clear buttons', async ({ page }) => {
|
||||
await page.setContent(
|
||||
'<ion-datetime value="2022-05-03" show-default-buttons="true" show-clear-button="true"></ion-datetime>',
|
||||
@@ -292,23 +294,74 @@ configs({ modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, screenshot, conf
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-footer-default-clear-buttons`));
|
||||
});
|
||||
test('should render custom buttons', async ({ page }) => {
|
||||
|
||||
test('should render custom default buttons', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-05-03">
|
||||
<ion-buttons slot="buttons">
|
||||
<ion-button id="custom-button" color="primary">Hello!</ion-button>
|
||||
</ion-buttons>
|
||||
<div slot="buttons">
|
||||
<ion-button id="custom-cancel-button">Cancel</ion-button>
|
||||
<ion-button id="custom-confirm-button">Done</ion-button>
|
||||
</div>
|
||||
</ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const customButton = page.locator('ion-datetime #custom-button');
|
||||
await expect(customButton).toBeVisible();
|
||||
const cancelButton = page.locator('ion-datetime #custom-cancel-button');
|
||||
await expect(cancelButton).toBeVisible();
|
||||
|
||||
const confirmButton = page.locator('ion-datetime #custom-confirm-button');
|
||||
await expect(confirmButton).toBeVisible();
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-footer-custom-buttons`));
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-footer-custom-default-buttons`));
|
||||
});
|
||||
|
||||
test('should render custom clear button', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-05-03">
|
||||
<div slot="buttons">
|
||||
<ion-button color="danger" id="custom-clear-button">Clear</ion-button>
|
||||
</div>
|
||||
</ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const clearButton = page.locator('ion-datetime #custom-clear-button');
|
||||
await expect(clearButton).toBeVisible();
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-footer-custom-clear-button`));
|
||||
});
|
||||
|
||||
test('should render custom default and clear buttons', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2022-05-03">
|
||||
<div slot="buttons">
|
||||
<ion-button id="custom-cancel-button">Cancel</ion-button>
|
||||
<ion-button color="danger" id="custom-clear-button">Clear</ion-button>
|
||||
<ion-button id="custom-confirm-button">Done</ion-button>
|
||||
</div>
|
||||
</ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const cancelButton = page.locator('ion-datetime #custom-cancel-button');
|
||||
await expect(cancelButton).toBeVisible();
|
||||
|
||||
const clearButton = page.locator('ion-datetime #custom-clear-button');
|
||||
await expect(clearButton).toBeVisible();
|
||||
|
||||
const confirmButton = page.locator('ion-datetime #custom-confirm-button');
|
||||
await expect(confirmButton).toBeVisible();
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-footer-custom-default-clear-buttons`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 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: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 22 KiB |