Skip to content

Commit

Permalink
chore(accordion): remove foundation (VIV-2010) (#1996)
Browse files Browse the repository at this point in the history
* chore: wip

* chore: remove start-end mixin

* chore: updates accordion

* chore: format

* chore: eslint errors

* chore: removes unneeded ignore comments

* chore: removes unneeded ignore comments

* chore: removed start-end mixin

* chore: updates test

* chore: fixes single expand code coverage

* chore: adds empty accordion test

* chore: update tests

* fix: allows different items to be set as expanded

* Add test for keypress on slotted content

* Refactor activeItemIndex handling

* Add test for empty setItems()

* Add test for bubbled change events

---------

Co-authored-by: Richard Helm <[email protected]>
  • Loading branch information
TaylorJ76 and RichardHelm authored Nov 18, 2024
1 parent ed94617 commit c29b83c
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const header = (context: ElementDefinitionContext, hTag: string) => {
id="${(x) => x.id}"
aria-expanded="${(x) => x.expanded}"
aria-controls="${(x) => x.id}-panel"
@click="${(x, c) => x.clickHandler(c.event as MouseEvent)}"
@click="${(x) => x.clickHandler()}"
${ref('expandbutton')}
>
Expand Down
64 changes: 58 additions & 6 deletions libs/components/src/lib/accordion-item/accordion-item.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { attr } from '@microsoft/fast-element';
import {
applyMixins,
AccordionItem as FASTAccordionItem,
} from '@microsoft/fast-foundation';
import { attr, nullableNumberConverter } from '@microsoft/fast-element';
import { applyMixins, FoundationElement } from '@microsoft/fast-foundation';
import { AffixIconWithTrailing } from '../../shared/patterns/affix';
import type { Size } from '../enums.js';

Expand All @@ -20,7 +17,44 @@ export type AccordionItemSize = Extract<Size, Size.Condensed | Size.Normal>;
* @slot icon - Add an icon to the component.
* @event {CustomEvent<undefined>} change - Fires a custom 'change' event when the button is invoked
*/
export class AccordionItem extends FASTAccordionItem {
export class AccordionItem extends FoundationElement {
/**
* Configures the {@link https://www.w3.org/TR/wai-aria-1.1/#aria-level | level} of the
* heading element.
*
* @defaultValue 2
* @public
* @remarks
* HTML attribute: heading-level
*/
@attr({
attribute: 'heading-level',
mode: 'fromView',
converter: nullableNumberConverter,
})
headinglevel: 1 | 2 | 3 | 4 | 5 | 6 = 2;

/**
* Expands or collapses the item.
*
* @public
* @remarks
* HTML attribute: expanded
*/
@attr({ mode: 'boolean' })
expanded = false;

/**
* The item ID
*
* @public
* @remarks
* HTML Attribute: id
*/
@attr
// @ts-expect-error Type is incorrectly non-optional
id: string;

/**
*
*
Expand Down Expand Up @@ -54,6 +88,24 @@ export class AccordionItem extends FASTAccordionItem {
* HTML Attribute: size
*/
@attr size?: AccordionItemSize;

/**
* @internal
*/
// @ts-expect-error Type is incorrectly non-optional
expandbutton: HTMLElement;

/**
* @internal
*/
clickHandler = () => {
this.expanded = !this.expanded;
this.change();
};

private change = (): void => {
this.$emit('change');
};
}

export interface AccordionItem extends AffixIconWithTrailing {}
Expand Down
74 changes: 70 additions & 4 deletions libs/components/src/lib/accordion/accordion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const COMPONENT_HTML = `
</${COMPONENT_TAG}>
`;

const EMPTY_COMPONENT_HTML = `<${COMPONENT_TAG} id="tested"></${COMPONENT_TAG}>`;

describe('vwc-accordion', () => {
function triggerAccordionUpdate() {
const newItem = document.createElement(
Expand Down Expand Up @@ -52,6 +54,21 @@ describe('vwc-accordion', () => {
});
});

describe('empty', () => {
it('should not set the accordionIds property', async () => {
element = (await fixture(EMPTY_COMPONENT_HTML)) as Accordion;
await elementUpdated(element);
expect(element.accordionIds).toBe(undefined);
});

it('should handle all accordion items being removed without error', async () => {
expect(() => {
accordionItem1.remove();
accordionItem2.remove();
}).not.toThrow();
});
});

describe('expandmode', () => {
it('should allow only one accordion item expanded when set to "single"', async () => {
element.expandmode = 'single';
Expand Down Expand Up @@ -83,7 +100,21 @@ describe('vwc-accordion', () => {
expect(accordionItem2.expanded).toBeTruthy();
});

it('should always open the first accordion-item::DOCUMENTED BUG SHOULD FAIL ONCE FIXED IN FAST! ', async function () {
it('should open the first accordion-item if none of the others are set to expanded', async function () {
element = (await fixture(
`<${COMPONENT_TAG} expand-mode="single">
<vwc-accordion-item heading="accordion item" id="item1"><p>content</p></vwc-accordion-item>
<vwc-accordion-item heading="accordion item" id="item2"><p>content</p></vwc-accordion-item>
</${COMPONENT_TAG}>`
)) as Accordion;
await elementUpdated(element);

expect(element.expandmode).toBe('single');
expect((element.children[0] as AccordionItem).expanded).toBeTruthy();
expect((element.children[1] as AccordionItem).expanded).toBeFalsy();
});

it('should open the accordion-item with expanded set', async function () {
element = (await fixture(
`<${COMPONENT_TAG} expand-mode="single">
<vwc-accordion-item heading="accordion item" id="item1"><p>content</p></vwc-accordion-item>
Expand All @@ -92,6 +123,20 @@ describe('vwc-accordion', () => {
)) as Accordion;
await elementUpdated(element);

expect(element.expandmode).toBe('single');
expect((element.children[0] as AccordionItem).expanded).toBeFalsy();
expect((element.children[1] as AccordionItem).expanded).toBeTruthy();
});

it('should open the first accordion-item with expanded set', async function () {
element = (await fixture(
`<${COMPONENT_TAG} expand-mode="single">
<vwc-accordion-item heading="accordion item" id="item1" expanded><p>content</p></vwc-accordion-item>
<vwc-accordion-item heading="accordion item" id="item2" expanded><p>content</p></vwc-accordion-item>
</${COMPONENT_TAG}>`
)) as Accordion;
await elementUpdated(element);

expect(element.expandmode).toBe('single');
expect((element.children[0] as AccordionItem).expanded).toBeTruthy();
expect((element.children[1] as AccordionItem).expanded).toBeFalsy();
Expand Down Expand Up @@ -178,6 +223,18 @@ describe('vwc-accordion', () => {
);
expect(accordionItem2.contains(document.activeElement)).toBeTruthy();
});

it('should ignore key presses on accordion item slotted content', async () => {
const button = document.createElement('button');
accordionItem1.appendChild(button);
button.focus();

button.dispatchEvent(
new KeyboardEvent('keydown', { key: 'End', bubbles: true })
);

expect(document.activeElement).toBe(button);
});
});

describe('accordion-item focus', () => {
Expand All @@ -199,6 +256,15 @@ describe('vwc-accordion', () => {
});
});

it('should ignore change events bubbled up from slotted accordion item content', () => {
const input = document.createElement('button');
accordionItem2.appendChild(input);

input.dispatchEvent(new Event('change', { bubbles: true }));

expect(element.activeid).toBe('item1');
});

describe('a11y', () => {
it('should pass HTML a11y test', async () => {
expect(await axe(element)).toHaveNoViolations();
Expand All @@ -207,11 +273,11 @@ describe('vwc-accordion', () => {
it('should set aria-disabled on active item in single mode', async function () {
element = (await fixture(`
<${COMPONENT_TAG} id="tested">
<vwc-accordion-item heading="accordion item 1" expanded id="item1"><p>content</p></vwc-accordion-item>
<vwc-accordion-item heading="accordion item 2" id="item2"><p>content</p></vwc-accordion-item>
<vwc-accordion-item heading="accordion item 1" id="item1"><p>content</p></vwc-accordion-item>
<vwc-accordion-item heading="accordion item 2" expanded id="item2"><p>content</p></vwc-accordion-item>
</${COMPONENT_TAG}>`)) as Accordion;
await elementUpdated(element);
accordionItem1 = element.querySelector('#item1') as AccordionItem;
accordionItem1 = element.querySelector('#item2') as AccordionItem;

expect(accordionItem1.hasAttribute('aria-disabled')).toBe(true);
});
Expand Down
Loading

0 comments on commit c29b83c

Please sign in to comment.