mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 02:31:34 +08:00
fix(accordion): improve functionality with nested accordions (#24302)
This commit is contained in:
@ -199,7 +199,7 @@ export class AccordionGroup implements ComponentInterface {
|
|||||||
*/
|
*/
|
||||||
@Method()
|
@Method()
|
||||||
async getAccordions() {
|
async getAccordions() {
|
||||||
return Array.from(this.el.querySelectorAll('ion-accordion'));
|
return Array.from(this.el.querySelectorAll(':scope > ion-accordion')) as HTMLIonAccordionElement[];
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -58,7 +58,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:host(.accordion-disabled) #header,
|
:host(.accordion-disabled) #header,
|
||||||
:host(.accordion-readonly) #header {
|
:host(.accordion-readonly) #header,
|
||||||
|
:host(.accordion-disabled) #content,
|
||||||
|
:host(.accordion-readonly) #content {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
core/src/components/accordion/test/accordion.e2e.ts
Normal file
55
core/src/components/accordion/test/accordion.e2e.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { newE2EPage } from '@stencil/core/testing';
|
||||||
|
|
||||||
|
test('should properly set readonly on child accordions', async () => {
|
||||||
|
const page = await newE2EPage({
|
||||||
|
html: `
|
||||||
|
<ion-accordion-group animated="false">
|
||||||
|
<ion-accordion>
|
||||||
|
<ion-item slot="header">Label</ion-item>
|
||||||
|
<div slot="content">Content</div>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
const accordion = await page.find('ion-accordion');
|
||||||
|
const value = await accordion.getProperty('readonly');
|
||||||
|
|
||||||
|
expect(value).toBe(false);
|
||||||
|
|
||||||
|
await page.$eval('ion-accordion-group', (el: HTMLIonAccordionGroupElement) => {
|
||||||
|
el.readonly = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForChanges();
|
||||||
|
|
||||||
|
const valueAgain = await accordion.getProperty('readonly');
|
||||||
|
expect(valueAgain).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should properly set disabled on child accordions', async () => {
|
||||||
|
const page = await newE2EPage({
|
||||||
|
html: `
|
||||||
|
<ion-accordion-group animated="false">
|
||||||
|
<ion-accordion>
|
||||||
|
<ion-item slot="header">Label</ion-item>
|
||||||
|
<div slot="content">Content</div>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
const accordion = await page.find('ion-accordion');
|
||||||
|
const value = await accordion.getProperty('disabled');
|
||||||
|
|
||||||
|
expect(value).toBe(false);
|
||||||
|
|
||||||
|
await page.$eval('ion-accordion-group', (el: HTMLIonAccordionGroupElement) => {
|
||||||
|
el.disabled = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForChanges();
|
||||||
|
|
||||||
|
const valueAgain = await accordion.getProperty('disabled');
|
||||||
|
expect(valueAgain).toBe(true);
|
||||||
|
});
|
@ -3,64 +3,6 @@ import { AccordionGroup } from '../../accordion-group/accordion-group.tsx';
|
|||||||
import { Accordion } from '../accordion.tsx';
|
import { Accordion } from '../accordion.tsx';
|
||||||
import { Item } from '../../item/item.tsx';
|
import { Item } from '../../item/item.tsx';
|
||||||
|
|
||||||
it('should properly set readonly on child accordions', async () => {
|
|
||||||
const page = await newSpecPage({
|
|
||||||
components: [Item, Accordion, AccordionGroup],
|
|
||||||
html: `
|
|
||||||
<ion-accordion-group animated="false">
|
|
||||||
<ion-accordion>
|
|
||||||
<ion-item slot="header">Label</ion-item>
|
|
||||||
<div slot="content">Content</div>
|
|
||||||
</ion-accordion>
|
|
||||||
</ion-accordion-group>
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
|
||||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
|
||||||
|
|
||||||
expect(accordions.length).toEqual(1);
|
|
||||||
accordions.forEach(accordion => {
|
|
||||||
expect(accordion.readonly).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
accordionGroup.readonly = true;
|
|
||||||
await page.waitForChanges();
|
|
||||||
|
|
||||||
accordions.forEach(accordion => {
|
|
||||||
expect(accordion.readonly).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should properly set disabled on child accordions', async () => {
|
|
||||||
const page = await newSpecPage({
|
|
||||||
components: [Item, Accordion, AccordionGroup],
|
|
||||||
html: `
|
|
||||||
<ion-accordion-group animated="false">
|
|
||||||
<ion-accordion>
|
|
||||||
<ion-item slot="header">Label</ion-item>
|
|
||||||
<div slot="content">Content</div>
|
|
||||||
</ion-accordion>
|
|
||||||
</ion-accordion-group>
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
const accordionGroup = page.body.querySelector('ion-accordion-group');
|
|
||||||
const accordions = accordionGroup.querySelectorAll('ion-accordion');
|
|
||||||
|
|
||||||
expect(accordions.length).toEqual(1);
|
|
||||||
accordions.forEach(accordion => {
|
|
||||||
expect(accordion.disabled).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
accordionGroup.disabled = true;
|
|
||||||
await page.waitForChanges();
|
|
||||||
|
|
||||||
accordions.forEach(accordion => {
|
|
||||||
expect(accordion.disabled).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should open correct accordions', async () => {
|
it('should open correct accordions', async () => {
|
||||||
const page = await newSpecPage({
|
const page = await newSpecPage({
|
||||||
components: [Item, Accordion, AccordionGroup],
|
components: [Item, Accordion, AccordionGroup],
|
||||||
|
10
core/src/components/accordion/test/nested/e2e.ts
Normal file
10
core/src/components/accordion/test/nested/e2e.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { newE2EPage } from '@stencil/core/testing';
|
||||||
|
|
||||||
|
test('nested: basic', async () => {
|
||||||
|
const page = await newE2EPage({
|
||||||
|
url: '/src/components/accordion/test/nested?ionic:_testing=true'
|
||||||
|
});
|
||||||
|
|
||||||
|
const compare = await page.compareScreenshot();
|
||||||
|
expect(compare).toMatchScreenshot();
|
||||||
|
});
|
145
core/src/components/accordion/test/nested/index.html
Normal file
145
core/src/components/accordion/test/nested/index.html
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Accordion - Nested</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(auto-fill, minmax(250px, 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 - Nested</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content fullscreen="true" color="light">
|
||||||
|
<ion-header collapse="condense">
|
||||||
|
<ion-toolbar color="light">
|
||||||
|
<ion-title size="large">Accordion - Basic</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<div class="grid ion-padding">
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Nested</h2>
|
||||||
|
<ion-accordion-group expand="inset" value="attractions">
|
||||||
|
<ion-accordion value="attractions">
|
||||||
|
<ion-item color="primary" slot="header" button detail="false">
|
||||||
|
<ion-icon slot="start" ios="film-outline" md="film"></ion-icon>
|
||||||
|
<ion-label> Attractions</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-accordion-group slot="content" value="second">
|
||||||
|
<ion-accordion value="first">
|
||||||
|
<ion-item color="primary" slot="header" button detail="false">
|
||||||
|
<ion-label>First Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">First item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
<ion-accordion value="second">
|
||||||
|
<ion-item color="warning" slot="header" button detail="false">
|
||||||
|
<ion-label>Second Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">Second item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
<ion-accordion value="third">
|
||||||
|
<ion-item color="tertiary" slot="header" button detail="false">
|
||||||
|
<ion-label>Third Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">Third item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
</div>
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Nested Disabled</h2>
|
||||||
|
<ion-accordion-group expand="inset" value="attractions">
|
||||||
|
<ion-accordion value="attractions">
|
||||||
|
<ion-item color="primary" slot="header" button detail="false">
|
||||||
|
<ion-icon slot="start" ios="film-outline" md="film"></ion-icon>
|
||||||
|
<ion-label> Attractions</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-accordion-group slot="content" value="second" disabled="true">
|
||||||
|
<ion-accordion value="first">
|
||||||
|
<ion-item color="primary" slot="header" button detail="false">
|
||||||
|
<ion-label>First Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">First item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
<ion-accordion value="second">
|
||||||
|
<ion-item color="warning" slot="header" button detail="false">
|
||||||
|
<ion-label>Second Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">Second item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
<ion-accordion value="third">
|
||||||
|
<ion-item color="tertiary" slot="header" button detail="false">
|
||||||
|
<ion-label>Third Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">Third item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
</div>
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Nested Parent Disabled</h2>
|
||||||
|
<ion-accordion-group expand="inset" value="attractions" disabled="true">
|
||||||
|
<ion-accordion value="attractions">
|
||||||
|
<ion-item color="primary" slot="header" button detail="false">
|
||||||
|
<ion-icon slot="start" ios="film-outline" md="film"></ion-icon>
|
||||||
|
<ion-label> Attractions</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-accordion-group slot="content" value="second">
|
||||||
|
<ion-accordion value="first">
|
||||||
|
<ion-item color="primary" slot="header" button detail="false">
|
||||||
|
<ion-label>First Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">First item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
<ion-accordion value="second">
|
||||||
|
<ion-item color="warning" slot="header" button detail="false">
|
||||||
|
<ion-label>Second Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">Second item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
<ion-accordion value="third">
|
||||||
|
<ion-item color="tertiary" slot="header" button detail="false">
|
||||||
|
<ion-label>Third Item</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div slot="content">Third item content!</div>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
</ion-accordion>
|
||||||
|
</ion-accordion-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -274,19 +274,19 @@ ion-card-header.ion-color .ion-inherit-color {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Accordion Styles
|
// Accordion Styles
|
||||||
ion-accordion-group.accordion-group-expand-inset ion-accordion:first-of-type {
|
ion-accordion-group.accordion-group-expand-inset > ion-accordion:first-of-type {
|
||||||
border-top-left-radius: 8px;
|
border-top-left-radius: 8px;
|
||||||
border-top-right-radius: 8px;
|
border-top-right-radius: 8px;
|
||||||
}
|
}
|
||||||
ion-accordion-group.accordion-group-expand-inset ion-accordion:last-of-type {
|
ion-accordion-group.accordion-group-expand-inset > ion-accordion:last-of-type {
|
||||||
border-bottom-left-radius: 8px;
|
border-bottom-left-radius: 8px;
|
||||||
border-bottom-right-radius: 8px;
|
border-bottom-right-radius: 8px;
|
||||||
}
|
}
|
||||||
ion-accordion-group ion-accordion:last-of-type ion-item {
|
ion-accordion-group > ion-accordion:last-of-type ion-item {
|
||||||
--border-width: 0px;
|
--border-width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-accordion.accordion-animated .ion-accordion-toggle-icon {
|
ion-accordion.accordion-animated > [slot="header"] .ion-accordion-toggle-icon {
|
||||||
transition: 300ms transform cubic-bezier(0.25, 0.8, 0.5, 1);
|
transition: 300ms transform cubic-bezier(0.25, 0.8, 0.5, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,18 +296,24 @@ ion-accordion.accordion-animated .ion-accordion-toggle-icon {
|
|||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
ion-accordion.accordion-expanding .ion-accordion-toggle-icon,
|
* The > [slot="header"] selector ensures that we do
|
||||||
ion-accordion.accordion-expanded .ion-accordion-toggle-icon {
|
* not modify toggle icons for any nested accordions. The state
|
||||||
|
* of one accordion should not affect any accordions inside
|
||||||
|
* of a nested accordion group.
|
||||||
|
*/
|
||||||
|
ion-accordion.accordion-expanding > [slot="header"] .ion-accordion-toggle-icon,
|
||||||
|
ion-accordion.accordion-expanded > [slot="header"] .ion-accordion-toggle-icon {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
ion-accordion-group.accordion-group-expand-inset.md ion-accordion.accordion-previous ion-item[slot="header"] {
|
|
||||||
|
ion-accordion-group.accordion-group-expand-inset.md > ion-accordion.accordion-previous ion-item[slot="header"] {
|
||||||
--border-width: 0px;
|
--border-width: 0px;
|
||||||
--inner-border-width: 0px;
|
--inner-border-width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-accordion-group.accordion-group-expand-inset.md ion-accordion.accordion-expanding:first-of-type,
|
ion-accordion-group.accordion-group-expand-inset.md > ion-accordion.accordion-expanding:first-of-type,
|
||||||
ion-accordion-group.accordion-group-expand-inset.md ion-accordion.accordion-expanded:first-of-type {
|
ion-accordion-group.accordion-group-expand-inset.md > ion-accordion.accordion-expanded:first-of-type {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user