swisspost / design-system

The Swiss Post Design System pattern library for a consistent and accessible user experience across the web platform.
https://design-system.post.ch
Apache License 2.0
106 stars 13 forks source link

feat(components): #155 collapsible component #683

Closed alizedebray closed 1 year ago

changeset-bot[bot] commented 1 year ago

🦋 Changeset detected

Latest commit: 11b63b3986e486bb855c86359f687c91810c237e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages | Name | Type | | -------------------------------------- | ----- | | @swisspost/design-system-components | Minor | | @swisspost/design-system-documentation | Patch |

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

gfellerph commented 1 year ago

Had an idea about how to manage the slot issue. It's not ideal, but leaves the definition of the header level and layout entirely in the hands of the project while also retaining accessibility standards:

<post-collapsible>
  <h2 slot="header">
    <post-collapsible-button>
      <post-icon name="island" />
      Some heading content here
    </post-collapsible-button>
  </h2>
  <div>I'm the body of the collapsible in the default slot</div>
</post-collapsible>

Another approach would include less flexibility but more stability:

<post-collapsible>
  <post-collapsible-header level="2" slot="header">
    <post-icon name="island" />
    Some header text
  </post-collapsible-header>
  <div>Some collapsible content</div>
</post-collapsible>

<!-- which renders as -->
<post-collapsible>
  <post-collapsible-header>
    <h2>
      <button aria-expanded="...">
        <post-icon ... />
        Some header text
      </button>
    </h2>
  </post-collapsible-header>
  <div class="collapsible-content-container">
    <div>Some collapsible content</div>
  </div>
</post-collapsible>
oliverschuerch commented 1 year ago

My solution to manage the slot issue is - of course - inspired by the way how it's done in vuejs.

import { Component, Host, Element, Prop, h } from '@stencil/core';

@Component({
  tag: 'post-collapse',
  // styling the header, header-button and the body can be overwritten by the user with the css :part() selectors
  styleUrl: 'collapse.scss',
  shadow: true
})
export class PostButton {
  @Element() host: HTMLElement;

  // use some guid generator to get a strong and valid guid (do not use one, that only uses Math.random function)
  collapseId: guid = '2f9fc627-39f7-4eed-905f-987365523c05';
  // define headerTag default as 'h2' but give the possibility to change it to 'h1', 'h3', 'h4', 'h5' or 'h6'
  @Prop() headerTag: string = 'h2';

  @Prop({ mutable: true }) collapsed = false;

  // all the code alize allready defined to manage the collapse to work...

  render () {
    // this gives us the possibility to add the header only if a header-slot is defined
    let headerDom = [];

    if (this.host.querySelector('[slot="header"]')) {
      // We can add custom attributes to header by creating propertis like 'headerClass' or 'headerButtonRole', if there is really a need for that.
      // Otherwise I would prefer to leave the elements as clean as possible, because they need to be like so, because of semantic reasons!
      headerDom.push(
        <this.headerTag
          id={`${this.collapseId}--header`}
          part="header"
        >
          <button
            aria-expanded={`${!this.collapsed}`}
            aria-controls={`${this.collapseId}--body`}
            part="header-button"
          >
            <slot name="header">Provide some fallback content (if possible/necessary)</slot>
          </button>
        </this.headerTag>
      );
    }

    return (
      <Host class="post-collapse">
        { headerDom }

        <div
          id={`${this.collapseId}--body`}
          aria-labelledby={`${this.collapseId}--header`}
          part="body"
        >
          {/* default slot, every thing you define directly inside of the post-collapse component without an explicit slot wrapper goes here */}
          <slot>Provide some fallback content (if possible/necessary)</slot>
        </div>
      </Host>
    );
  }
}
gfellerph commented 1 year ago

Discussion summary:

alizedebray commented 1 year ago

The collapsible component now matches the last discussion's outcomes.

Props are currently not managed with constants as this introduce side effects in the auto-generated documentation (see the "Default" column in the readme, also appeared in storybook "Docs" tab)

swisspost-bot commented 1 year ago

Preview environment ready: https://preview-683--swisspost-design-system-styles.netlify.app Preview environment ready: https://preview-683--swisspost-design-system-next.netlify.app

sonarcloud[bot] commented 1 year ago

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
2.0% 2.0% Duplication