feat(accordion-group): add the shape property and styles for the ionic theme (#29971)
- Adds support for the `shape` property in accordion group. - Adds styles for the `"soft"`, `"round"` and `"rectangular"` shapes in the `ionic` theme - Defaults the shape to `"round"` for the ionic theme - Adds an e2e test for `shape` with screenshots of all shapes - Renames the `accordion.e2e.ts` files in other tests to `accordion-group.e2e.ts` which also renames the screenshot folder
@ -18,6 +18,7 @@ ion-accordion-group,prop,expand,"compact" | "inset",'compact',false,false
|
||||
ion-accordion-group,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-accordion-group,prop,multiple,boolean | undefined,undefined,false,false
|
||||
ion-accordion-group,prop,readonly,boolean,false,false,false
|
||||
ion-accordion-group,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
|
||||
ion-accordion-group,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
ion-accordion-group,prop,value,null | string | string[] | undefined,undefined,false,false
|
||||
ion-accordion-group,event,ionChange,AccordionGroupChangeEventDetail<any>,true
|
||||
|
8
core/src/components.d.ts
vendored
@ -136,6 +136,10 @@ export namespace Components {
|
||||
* This method is used to ensure that the value of ion-accordion-group is being set in a valid way. This method should only be called in response to a user generated action.
|
||||
*/
|
||||
"requestAccordionToggle": (accordionValue: string | undefined, accordionExpand: boolean) => Promise<void>;
|
||||
/**
|
||||
* Set to `"soft"` for an accordion group with slightly rounded corners, `"round"` for an accordion group with fully rounded corners, or `"rectangular"` for an accordion group without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. Only applies when `expand` is set to `"inset"`.
|
||||
*/
|
||||
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||
/**
|
||||
* The theme determines the visual appearance of the component.
|
||||
*/
|
||||
@ -5392,6 +5396,10 @@ declare namespace LocalJSX {
|
||||
* If `true`, the accordion group cannot be interacted with, but does not alter the opacity.
|
||||
*/
|
||||
"readonly"?: boolean;
|
||||
/**
|
||||
* Set to `"soft"` for an accordion group with slightly rounded corners, `"round"` for an accordion group with fully rounded corners, or `"rectangular"` for an accordion group without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. Only applies when `expand` is set to `"inset"`.
|
||||
*/
|
||||
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||
/**
|
||||
* The theme determines the visual appearance of the component.
|
||||
*/
|
||||
|
@ -12,9 +12,20 @@
|
||||
|
||||
// Inset Accordion Group
|
||||
// --------------------------------------------------
|
||||
|
||||
// Shape and padding only apply if the group is inset
|
||||
|
||||
:host(.accordion-group-expand-inset) {
|
||||
@include globals.border-radius(globals.$ion-border-radius-400);
|
||||
@include globals.padding(globals.$ion-space-100);
|
||||
}
|
||||
|
||||
:host(.accordion-group-expand-inset.accordion-group-shape-round) {
|
||||
@include globals.border-radius(globals.$ion-border-radius-400);
|
||||
}
|
||||
|
||||
:host(.accordion-group-expand-inset.accordion-group-shape-soft) {
|
||||
@include globals.border-radius(globals.$ion-border-radius-200);
|
||||
}
|
||||
|
||||
:host(.accordion-group-expand-inset.accordion-group-shape-rectangular) {
|
||||
@include globals.border-radius(globals.$ion-border-radius-0);
|
||||
}
|
||||
|
@ -60,6 +60,16 @@ export class AccordionGroup implements ComponentInterface {
|
||||
*/
|
||||
@Prop() expand: 'compact' | 'inset' = 'compact';
|
||||
|
||||
/**
|
||||
* Set to `"soft"` for an accordion group with slightly rounded corners,
|
||||
* `"round"` for an accordion group with fully rounded corners, or
|
||||
* `"rectangular"` for an accordion group without rounded corners.
|
||||
*
|
||||
* Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
|
||||
* Only applies when `expand` is set to `"inset"`.
|
||||
*/
|
||||
@Prop() shape?: 'soft' | 'round' | 'rectangular';
|
||||
|
||||
/**
|
||||
* Emitted when the value property has changed as a result of a user action such as a click.
|
||||
*
|
||||
@ -278,9 +288,26 @@ export class AccordionGroup implements ComponentInterface {
|
||||
return Array.from(this.el.querySelectorAll(':scope > ion-accordion')) as HTMLIonAccordionElement[];
|
||||
}
|
||||
|
||||
private getShape(): string | undefined {
|
||||
const theme = getIonTheme(this);
|
||||
const { shape } = this;
|
||||
|
||||
// TODO(ROU-11328): Remove theme check when shapes are defined for all themes.
|
||||
if (theme !== 'ionic') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (shape === undefined) {
|
||||
return 'round';
|
||||
}
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { disabled, readonly, expand } = this;
|
||||
const theme = getIonTheme(this);
|
||||
const shape = this.getShape();
|
||||
|
||||
return (
|
||||
<Host
|
||||
@ -289,6 +316,7 @@ export class AccordionGroup implements ComponentInterface {
|
||||
'accordion-group-disabled': disabled,
|
||||
'accordion-group-readonly': readonly,
|
||||
[`accordion-group-expand-${expand}`]: true,
|
||||
[`accordion-group-shape-${shape}`]: shape !== undefined,
|
||||
}}
|
||||
role="presentation"
|
||||
>
|
||||
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
@ -0,0 +1,47 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('accordion-group: shape'), () => {
|
||||
['round', 'soft', 'rectangular'].forEach((shape) => {
|
||||
test(`${shape} - should not have visual regressions`, async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
/* Background styles to show the border radius */
|
||||
:root {
|
||||
background: #222;
|
||||
}
|
||||
</style>
|
||||
|
||||
<ion-accordion-group value="first" expand="inset" shape="${shape}">
|
||||
<ion-accordion value="first">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="second">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="third">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const accordionGroup = page.locator('ion-accordion-group');
|
||||
|
||||
await expect(accordionGroup).toHaveScreenshot(screenshot(`accordion-group-shape-${shape}`));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 8.7 KiB |
135
core/src/components/accordion-group/test/shape/index.html
Normal file
@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Accordion Group - Shape</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #6f7378;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Accordion Group - Shape</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content fullscreen="true" color="light">
|
||||
<div class="grid ion-padding">
|
||||
<div class="grid-item">
|
||||
<h2>Default</h2>
|
||||
<ion-accordion-group expand="inset" value="first">
|
||||
<ion-accordion value="first">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="second">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="third">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Round</h2>
|
||||
<ion-accordion-group expand="inset" value="first" shape="round">
|
||||
<ion-accordion value="first">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="second">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="third">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Soft</h2>
|
||||
<ion-accordion-group expand="inset" value="first" shape="soft">
|
||||
<ion-accordion value="first">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="second">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="third">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Rectangular</h2>
|
||||
<ion-accordion-group expand="inset" value="first" shape="rectangular">
|
||||
<ion-accordion value="first">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="second">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="third">
|
||||
<ion-item slot="header">
|
||||
<ion-label>Accordion title</ion-label>
|
||||
</ion-item>
|
||||
<div slot="content">This is the body of the accordion.</div>
|
||||
</ion-accordion>
|
||||
</ion-accordion-group>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
@ -30,14 +30,14 @@ export declare interface IonAccordion extends Components.IonAccordion {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'theme', 'value']
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'shape', 'theme', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-accordion-group',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'theme', 'value'],
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'shape', 'theme', 'value'],
|
||||
})
|
||||
export class IonAccordionGroup {
|
||||
protected el: HTMLElement;
|
||||
|
@ -103,14 +103,14 @@ export declare interface IonAccordion extends Components.IonAccordion {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonAccordionGroup,
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'theme', 'value']
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'shape', 'theme', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-accordion-group',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'theme', 'value'],
|
||||
inputs: ['animated', 'disabled', 'expand', 'mode', 'multiple', 'readonly', 'shape', 'theme', 'value'],
|
||||
standalone: true
|
||||
})
|
||||
export class IonAccordionGroup {
|
||||
|
@ -97,6 +97,7 @@ export const IonAccordionGroup = /*@__PURE__*/ defineContainer<JSX.IonAccordionG
|
||||
'disabled',
|
||||
'readonly',
|
||||
'expand',
|
||||
'shape',
|
||||
'ionChange',
|
||||
'ionValueChange'
|
||||
],
|
||||
|