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

refactor(sbb-accordion): lit migration #2069

Merged
merged 9 commits into from
Oct 17, 2023
Merged
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
1 change: 1 addition & 0 deletions src/components/sbb-accordion/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './sbb-accordion';
298 changes: 146 additions & 152 deletions src/components/sbb-accordion/sbb-accordion.e2e.ts

Large diffs are not rendered by default.

92 changes: 62 additions & 30 deletions src/components/sbb-accordion/sbb-accordion.spec.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,72 @@
import { SbbAccordion } from './sbb-accordion';
import { newSpecPage } from '@stencil/core/testing';
import { expect, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js';
import './sbb-accordion';
import '../sbb-expansion-panel';
dauriamarco marked this conversation as resolved.
Show resolved Hide resolved
import '../sbb-expansion-panel-header';
import '../sbb-expansion-panel-content';

describe('sbb-accordion', () => {
it('renders', async () => {
const { root } = await newSpecPage({
components: [SbbAccordion],
html: `
<sbb-accordion>
<sbb-expansion-panel>
<sbb-expansion-panel-header>Header 1</sbb-expansion-panel-header>
<sbb-expansion-panel-content>Content 1</sbb-expansion-panel-content>
</sbb-expansion-panel>
<sbb-expansion-panel>
<sbb-expansion-panel-header>Header 2</sbb-expansion-panel-header>
<sbb-expansion-panel-content>Content 2</sbb-expansion-panel-content>
</sbb-expansion-panel>
</sbb-accordion>
`,
});
const root = await fixture(html`
<sbb-accordion>
<sbb-expansion-panel>
<sbb-expansion-panel-header>Header 1</sbb-expansion-panel-header>
<sbb-expansion-panel-content>Content 1</sbb-expansion-panel-content>
</sbb-expansion-panel>
<sbb-expansion-panel>
<sbb-expansion-panel-header>Header 2</sbb-expansion-panel-header>
<sbb-expansion-panel-content>Content 2</sbb-expansion-panel-content>
</sbb-expansion-panel>
</sbb-accordion>
`);

expect(root).toEqualHtml(`
expect(root).dom.to.be.equal(
`
<sbb-accordion>
<mock:shadow-root>
<div class="sbb-accordion">
<slot></slot>
</div>
</mock:shadow-root>
<sbb-expansion-panel>
<sbb-expansion-panel-header>Header 1</sbb-expansion-panel-header>
<sbb-expansion-panel-content>Content 1</sbb-expansion-panel-content>
<sbb-expansion-panel data-accordion data-accordion-first>
<sbb-expansion-panel-header
aria-controls="sbb-expansion-panel-content-1"
aria-expanded="false"
dir="ltr"
id="sbb-expansion-panel-header-1"
role="button"
slot="header"
tabindex="0"
>Header 1</sbb-expansion-panel-header>
<sbb-expansion-panel-content
aria-hidden="true"
aria-labelledby="sbb-expansion-panel-header-1"
id="sbb-expansion-panel-content-1"
role="region"
slot="content"
>Content 1</sbb-expansion-panel-content>
</sbb-expansion-panel>
<sbb-expansion-panel>
<sbb-expansion-panel-header>Header 2</sbb-expansion-panel-header>
<sbb-expansion-panel-content>Content 2</sbb-expansion-panel-content>
<sbb-expansion-panel data-accordion data-accordion-last>
<sbb-expansion-panel-header
aria-controls="sbb-expansion-panel-content-2"
aria-expanded="false"
dir="ltr"
id="sbb-expansion-panel-header-2"
role="button"
slot="header"
tabindex="0">Header 2</sbb-expansion-panel-header>
<sbb-expansion-panel-content
aria-hidden="true"
aria-labelledby="sbb-expansion-panel-header-2"
id="sbb-expansion-panel-content-2"
role="region"
slot="content"
>Content 2</sbb-expansion-panel-content>
</sbb-expansion-panel>
</sbb-accordion>
`);
`,
);
expect(root).shadowDom.to.be.equal(
`
<div class="sbb-accordion">
<slot></slot>
</div>
`,
);
});
});
11 changes: 8 additions & 3 deletions src/components/sbb-accordion/sbb-accordion.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/** @jsx h */
import { h, JSX } from 'jsx-dom';
import readme from './readme.md?raw';
import type { Meta, StoryObj, ArgTypes, Args } from '@storybook/html';
import type { Meta, StoryObj, ArgTypes, Args } from '@storybook/web-components';
import { InputType, StoryContext } from '@storybook/types';
import sbbExpansionPanelEvents from '../sbb-expansion-panel/sbb-expansion-panel.events';
import { events as sbbExpansionPanelEvents } from '../sbb-expansion-panel/sbb-expansion-panel';
import { withActions } from '@storybook/addon-actions/decorator';
import { Decorator } from '@storybook/html';
import { Decorator } from '@storybook/web-components';
import './sbb-accordion';
import '../sbb-expansion-panel';
import '../sbb-expansion-panel-header';
import '../sbb-expansion-panel-content';
import '../sbb-icon';

const numberOfPanels: InputType = {
control: {
Expand Down
85 changes: 59 additions & 26 deletions src/components/sbb-accordion/sbb-accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
import { Component, ComponentInterface, Element, h, JSX, Listen, Prop, Watch } from '@stencil/core';
import { InterfaceTitleAttributes } from '../sbb-title/sbb-title.custom';
import { toggleDatasetEntry } from '../../global/dom';
import { CSSResult, html, LitElement, TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { ConnectedAbortController } from '../../global/eventing';
import { SbbExpansionPanel } from '../sbb-expansion-panel';
import { TitleLevel } from '../sbb-title';
import Style from './sbb-accordion.scss?lit&inline';

/**
* @slot unnamed - Use this to add one or more sbb-expansion-panel.
*/
@Component({
shadow: true,
styleUrl: 'sbb-accordion.scss',
tag: 'sbb-accordion',
})
export class SbbAccordion implements ComponentInterface {
@customElement('sbb-accordion')
export class SbbAccordion extends LitElement {
public static override styles: CSSResult = Style;

/** The heading level for the sbb-expansion-panel-headers within the component. */
@Prop() public titleLevel?: InterfaceTitleAttributes['level'];
@property({ attribute: 'title-level' })
public get titleLevel(): TitleLevel | null {
return this._titleLevel;
}
public set titleLevel(value: TitleLevel | null) {
const oldValue = this._titleLevel;
this._titleLevel = value;
this._setTitleLevelOnChildren();
this.requestUpdate('titleLevel', oldValue);
}
private _titleLevel: TitleLevel | null = null;

/** Whether the animation should be disabled. */
@Prop({ reflect: true }) public disableAnimation = false;
@property({ attribute: 'disable-animation', reflect: true, type: Boolean })
public disableAnimation = false;

/** Whether more than one sbb-expansion-panel can be open at the same time. */
@Prop() public multi = false;
@property({ type: Boolean })
public get multi(): boolean {
return this._multi;
}
public set multi(value: boolean) {
const oldValue = this._multi;
this._multi = value;
this._resetExpansionPanels(this._multi, oldValue);
this.requestUpdate('multi', oldValue);
}
private _multi: boolean = false;

private _abort = new ConnectedAbortController(this);

@Listen('will-open')
public closePanels(e): void {
private _closePanels(e): void {
if (e.target?.tagName !== 'SBB-EXPANSION-PANEL' || this.multi) {
return;
}
Expand All @@ -31,8 +55,7 @@ export class SbbAccordion implements ComponentInterface {
.forEach((panel) => (panel.expanded = false));
}

@Watch('multi')
public resetExpansionPanels(newValue: boolean, oldValue: boolean): void {
private _resetExpansionPanels(newValue: boolean, oldValue: boolean): void {
// If it's changing from "multi = true" to "multi = false", open the first panel and close all the others.
const expansionPanels = this._expansionPanels;
if (expansionPanels.length > 1 && oldValue && !newValue) {
Expand All @@ -43,15 +66,12 @@ export class SbbAccordion implements ComponentInterface {
}
}

@Watch('titleLevel')
public setTitleLevelOnChildren(): void {
private _setTitleLevelOnChildren(): void {
this._expansionPanels.forEach((panel) => (panel.titleLevel = this.titleLevel));
}

@Element() private _element!: HTMLElement;

private get _expansionPanels(): HTMLSbbExpansionPanelElement[] {
return Array.from(this._element.querySelectorAll('sbb-expansion-panel'));
private get _expansionPanels(): SbbExpansionPanel[] {
return Array.from(this.querySelectorAll('sbb-expansion-panel'));
}

private _setChildrenParameters(): void {
Expand All @@ -60,7 +80,7 @@ export class SbbAccordion implements ComponentInterface {
return;
}

expansionPanels.forEach((panel: HTMLSbbExpansionPanelElement) => {
expansionPanels.forEach((panel: SbbExpansionPanel) => {
panel.titleLevel = this.titleLevel;

toggleDatasetEntry(panel, 'accordionFirst', false);
Expand All @@ -76,11 +96,24 @@ export class SbbAccordion implements ComponentInterface {
toggleDatasetEntry(expansionPanels[expansionPanels.length - 1], 'accordionLast', true);
}

public render(): JSX.Element {
return (
public override connectedCallback(): void {
super.connectedCallback();
const signal = this._abort.signal;
this.addEventListener('will-open', (e) => this._closePanels(e), { signal });
}

protected override render(): TemplateResult {
return html`
<div class="sbb-accordion">
<slot onSlotchange={() => this._setChildrenParameters()}></slot>
<slot @slotchange=${() => this._setChildrenParameters()}></slot>
</div>
);
`;
}
}

declare global {
interface HTMLElementTagNameMap {
// eslint-disable-next-line @typescript-eslint/naming-convention
'sbb-accordion': SbbAccordion;
}
}
1 change: 1 addition & 0 deletions src/components/sbb-expansion-panel-content/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './sbb-expansion-panel-content';
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { E2EElement, E2EPage, newE2EPage } from '@stencil/core/testing';
import { assert, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js';
import { SbbExpansionPanelContent } from './sbb-expansion-panel-content';

describe('sbb-expansion-panel-content', () => {
let element: E2EElement, page: E2EPage;
let element: SbbExpansionPanelContent;

it('renders', async () => {
page = await newE2EPage();
await page.setContent('<sbb-expansion-panel-content>Content</sbb-expansion-panel-content>');

element = await page.find('sbb-expansion-panel-content');
expect(element).toHaveClass('hydrated');
element = await fixture(
html`<sbb-expansion-panel-content>Content</sbb-expansion-panel-content>`,
);
assert.instanceOf(element, SbbExpansionPanelContent);
});
});
Original file line number Diff line number Diff line change
@@ -1,40 +1,47 @@
import { SbbExpansionPanelContent } from './sbb-expansion-panel-content';
import { newSpecPage } from '@stencil/core/testing';
import { expect, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js';
import './sbb-expansion-panel-content';

describe('sbb-expansion-panel-content', () => {
it('renders', async () => {
const { root } = await newSpecPage({
components: [SbbExpansionPanelContent],
html: '<sbb-expansion-panel-content>Content</sbb-expansion-panel-content>',
});
const root = await fixture(
html`<sbb-expansion-panel-content>Content</sbb-expansion-panel-content>`,
);

expect(root).toEqualHtml(`
<sbb-expansion-panel-content slot="content" role="region">
<mock:shadow-root>
<div class="sbb-expansion-panel-content">
<slot></slot>
</div>
</mock:shadow-root>
Content
</sbb-expansion-panel-content>
`);
expect(root).dom.to.be.equal(
`
<sbb-expansion-panel-content slot="content" role="region">
Content
</sbb-expansion-panel-content>
`,
);
expect(root).shadowDom.to.be.equal(
`
<div class="sbb-expansion-panel-content">
<slot></slot>
</div>
`,
);
});

it('renders expanded', async () => {
const { root } = await newSpecPage({
components: [SbbExpansionPanelContent],
html: '<sbb-expansion-panel-content expanded>Content</sbb-expansion-panel-content>',
});
const root = await fixture(
html`<sbb-expansion-panel-content expanded>Content</sbb-expansion-panel-content>`,
);

expect(root).toEqualHtml(`
<sbb-expansion-panel-content slot="content" role="region" expanded>
<mock:shadow-root>
<div class="sbb-expansion-panel-content">
<slot></slot>
</div>
</mock:shadow-root>
Content
</sbb-expansion-panel-content>
`);
expect(root).dom.to.be.equal(
`
<sbb-expansion-panel-content slot="content" role="region" expanded>
Content
</sbb-expansion-panel-content>
`,
);
expect(root).shadowDom.to.be.equal(
`
<div class="sbb-expansion-panel-content">
<slot></slot>
</div>
`,
);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/** @jsx h */
import { h, JSX } from 'jsx-dom';
import readme from './readme.md?raw';
import type { Meta, StoryObj } from '@storybook/html';
import type { Meta, StoryObj } from '@storybook/web-components';
import '../sbb-card';

const Template = (): JSX.Element => (
<sbb-card color="milk">
Expand Down
Loading
Loading