Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: BREAKING - rename accordion's single mode to eager and add new single mode with differing behavior #6898

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Rename Accordion's \"single\" expand mode to \"eager\" and add new \"single\" mode that does not expand the first item by default and allows the active item to be collapsed.",
"packageName": "@microsoft/fast-foundation",
"email": "[email protected]",
"dependentChangeType": "prerelease"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import type { ValuesOf } from "../utilities/index.js";
* @public
*/
export const AccordionExpandMode = {
/**
* The same as single, but the first {@link @microsoft/fast-foundation#(FASTAccordionItem:class) } is expanded by default.
*/
eager: "eager",

/**
* Designates only a single {@link @microsoft/fast-foundation#(FASTAccordionItem:class) } can be open a time.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ test.describe("Accordion", () => {
await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.single);
});

test("should set an expand mode of `eager` when passed to the `expand-mode` attribute", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="eager">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});

await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.eager);
});

test("should set a default expand mode of `multi` when `expand-mode` attribute is not passed", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
Expand Down Expand Up @@ -150,10 +169,10 @@ test.describe("Accordion", () => {
await expect(secondItem).toHaveBooleanAttribute("expanded");
});

test("should set the expanded items' button to aria-disabled when in single expand mode", async () => {
test("should set the expanded items' button to aria-disabled when in eager expand mode", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion expand-mode="eager">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
Expand Down Expand Up @@ -202,10 +221,10 @@ test.describe("Accordion", () => {
);
});

test("should remove an expanded items' expandbutton aria-disabled attribute when expand mode changes from single to multi", async () => {
test("should remove an expanded items' expandbutton aria-disabled attribute when expand mode changes from eager to multi", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion expand-mode="eager">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
Expand Down Expand Up @@ -238,10 +257,10 @@ test.describe("Accordion", () => {
await expect(firstItem.locator("button")).not.hasAttribute("aria-disabled");
});

test("should set the first item as expanded if no child is expanded by default in single mode", async () => {
test("should set the first item as expanded if no child is expanded by default in eager mode", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion expand-mode="eager">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
Expand Down Expand Up @@ -352,6 +371,52 @@ test.describe("Accordion", () => {
await expect(thirdItem).not.toHaveBooleanAttribute("expanded");
});

test("should allow disabled items to be expanded when in eager mode", async () => {
test.slow();
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="eager">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item expanded disabled>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
<fast-accordion-item expanded>
<span slot="heading">Heading 3</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});

const items = element.locator("fast-accordion-item");

const firstItem = items.nth(0);

const secondItem = items.nth(1);

const thirdItem = items.nth(2);

await expect(firstItem).not.toHaveBooleanAttribute("expanded");

await expect(secondItem).toHaveBooleanAttribute("expanded");

await expect(thirdItem).toHaveBooleanAttribute("expanded");

await secondItem.evaluate(node => {
node.removeAttribute("disabled");
});

await expect(firstItem).not.toHaveBooleanAttribute("expanded");

await expect(secondItem).toHaveBooleanAttribute("expanded");

await expect(thirdItem).not.toHaveBooleanAttribute("expanded");
});

test("should ignore `change` events from components other than accordion items", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
Expand Down
24 changes: 14 additions & 10 deletions packages/web-components/fast-foundation/src/accordion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class FASTAccordion extends FASTElement {
} else if (propertyName === "expanded") {
// we only need to manage single expanded instances
// such as scenarios where a child is programatically expanded
if (source.expanded && this.isSingleExpandMode()) {
if (source.expanded && this.isSingleExpandMode) {
this.setSingleExpandMode(source);
}
}
Expand All @@ -100,12 +100,12 @@ export class FASTAccordion extends FASTElement {
return null;
}

return (
this.accordionItems.find(
(item: Element | FASTAccordionItem) =>
item instanceof FASTAccordionItem && item.expanded
) ?? this.accordionItems[0]
);
return this.accordionItems.find(
(item: Element | FASTAccordionItem) =>
item instanceof FASTAccordionItem && item.expanded
) ?? this.isEagerExpandMode
? this.accordionItems[0]
: null;
}

private setItems = (): void => {
Expand Down Expand Up @@ -141,7 +141,7 @@ export class FASTAccordion extends FASTElement {
item.addEventListener("focus", this.handleItemFocus);
});

if (this.isSingleExpandMode()) {
if (this.isEagerExpandMode || this.isSingleExpandMode) {
const expandedItem = this.findExpandedItem() as FASTAccordionItem;
this.setSingleExpandMode(expandedItem);
}
Expand Down Expand Up @@ -192,7 +192,7 @@ export class FASTAccordion extends FASTElement {
if (item instanceof FASTAccordionItem) {
this.activeid = item.getAttribute("id");

if (!this.isSingleExpandMode()) {
if (!this.isEagerExpandMode) {
item.expanded = !item.expanded;
// setSingleExpandMode sets activeItemIndex on its own
this.activeItemIndex = this.accordionItems.indexOf(item);
Expand All @@ -210,7 +210,11 @@ export class FASTAccordion extends FASTElement {
});
}

private isSingleExpandMode(): boolean {
private get isEagerExpandMode(): boolean {
return this.expandmode === AccordionExpandMode.eager;
}

private get isSingleExpandMode(): boolean {
return this.expandmode === AccordionExpandMode.single;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,32 @@ AccordionWithSingleExpandMode.args = {
<div slot="heading">Accordion Item 1 Heading</div>
Accordion Item 1 Content
</fast-accordion-item>
<fast-accordion-item expanded>
<fast-accordion-item>
<div slot="heading">Accordion Item 2 Heading</div>
<fast-checkbox>A checkbox as content</fast-checkbox>
</fast-accordion-item>
<fast-accordion-item>
<div slot="heading">Accordion Item 3 Heading</div>
Accordion Item 3 Content
</fast-accordion-item>
<fast-accordion-item>
<div slot="heading">Accordion Item 4 Heading</div>
Accordion Item 4 Content
</fast-accordion-item>
`,
};

export const AccordionWithEagerExpandMode: Story<FASTAccordion> = renderComponent(
storyTemplate
).bind({});
AccordionWithEagerExpandMode.args = {
expandmode: "eager",
storyContent: html`
<fast-accordion-item expanded disabled>
<div slot="heading">Accordion Item 1 Heading</div>
Accordion Item 1 Content
</fast-accordion-item>
<fast-accordion-item>
<div slot="heading">Accordion Item 2 Heading</div>
<fast-checkbox>A checkbox as content</fast-checkbox>
</fast-accordion-item>
Expand Down
Loading