Skip to content

Commit

Permalink
fix: deprecate direct DOM manipulation in Collapsible, fixes #316
Browse files Browse the repository at this point in the history
  • Loading branch information
Arturo Castillo Delgado authored May 19, 2021
1 parent b2873b7 commit 06e8e2c
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 67 deletions.
30 changes: 29 additions & 1 deletion packages/components/src/components/accordion/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { Element, Component, h, Prop, Host, Listen } from '@stencil/core';
import {
Element,
Component,
h,
Prop,
Host,
Listen,
Watch,
} from '@stencil/core';
import classnames from 'classnames';
@Component({
tag: 'scale-accordion',
Expand All @@ -23,6 +31,9 @@ export class Accordion {
@Prop() dependent: boolean = false;
/** If `true`, scale-collapsibles within the accordion will all be open initially, unless this is dependant */
@Prop() expanded: boolean = false;
/** Heading level for scale-collapsible descendants */
@Prop() headingLevel: number | null = null;

/**
* Handle `dependent`
*/
Expand All @@ -40,6 +51,11 @@ export class Accordion {
});
}

@Watch('headingLevel')
headingLevelChanged(newValue: number | null) {
this.propagatePropsToChildren(newValue);
}

connectedCallback() {
/**
* Handle `expanded`
Expand All @@ -51,10 +67,22 @@ export class Accordion {
}
}

componentDidLoad() {
if (this.headingLevel !== null) {
this.propagatePropsToChildren(this.headingLevel);
}
}

getCollapsibleChildren(): HTMLScaleCollapsibleElement[] {
return Array.from(this.el.querySelectorAll('scale-collapsible'));
}

propagatePropsToChildren(headingLevel: number) {
this.getCollapsibleChildren().forEach((item) => {
item.headingLevel = headingLevel;
});
}

render() {
return (
<Host>
Expand Down
11 changes: 6 additions & 5 deletions packages/components/src/components/accordion/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

## Properties

| Property | Attribute | Description | Type | Default |
| ----------- | ----------- | ------------------------------------------------------------------------------------------------------- | --------- | ----------- |
| `dependent` | `dependent` | If `true`, only one scale-collapsible within the accordion can be open at a time | `boolean` | `false` |
| `expanded` | `expanded` | If `true`, scale-collapsibles within the accordion will all be open initially, unless this is dependant | `boolean` | `false` |
| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` |
| Property | Attribute | Description | Type | Default |
| -------------- | --------------- | ------------------------------------------------------------------------------------------------------- | --------- | ----------- |
| `dependent` | `dependent` | If `true`, only one scale-collapsible within the accordion can be open at a time | `boolean` | `false` |
| `expanded` | `expanded` | If `true`, scale-collapsibles within the accordion will all be open initially, unless this is dependant | `boolean` | `false` |
| `headingLevel` | `heading-level` | Heading level for scale-collapsible descendants | `number` | `null` |
| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` |


## Shadow Parts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ exports[`TextField should match snapshot 1`] = `
<h2 aria-level="2" class="collapsible__heading" part="heading">
<button aria-controls="collapsable-panel-0" aria-expanded="false" class="collapsible__button" id="collapsable-heading-0" part="button">
<scale-icon-navigation-collapse-down class="collapsible__icon" decorative="" part="icon" size="16"></scale-icon-navigation-collapse-down>
<span part="button-text"></span>
<span class="collapsible__button-text" part="button-text">
<slot name="heading"></slot>
</span>
</button>
</h2>
<div aria-labelledby="collapsable-heading-0" class="collapsible__content" hidden="" id="collapsable-panel-0" part="content" role="region">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
letter-spacing: inherit;
}

.collapsible__button > span {
.collapsible__button-text {
margin-left: var(--spacing-left-button-text);
}

Expand Down
59 changes: 26 additions & 33 deletions packages/components/src/components/collapsible/collapsible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import {
h,
Prop,
Host,
Event,
State,
Element,
Event,
EventEmitter,
} from '@stencil/core';
import classNames from 'classnames';
Expand All @@ -33,20 +32,19 @@ let i = 0;
shadow: true,
})
export class Collapsible {
headingElement: HTMLElement;
headingId: string;
panelId: string;
headingElement: HTMLElement;

@Element() el: HTMLElement;
@Element() hostElement: HTMLElement;

/** Set to `true` to expand */
@Prop({ mutable: true, reflect: true }) expanded: boolean;
/** Default aria-level for heading */
@Prop() headingLevel: number = 2;
/** (optional) Injected CSS styles */
@Prop() styles?: string;

/** Default aria-level for heading */
@State() level: number = 2;

/** Emitted so parent <scale-accordion> knows about it */
@Event() scaleExpand: EventEmitter<CollapsibleEventDetail>;

Expand All @@ -60,39 +58,31 @@ export class Collapsible {
this.setHeadingFromLightDOM();
}

handleClick = () => {
this.expanded = !this.expanded;
this.scaleExpand.emit({ expanded: this.expanded });
};

/**
* In this method we:
* - query the first element from the light DOM, it should be a heading (e.g. h2)
* - take its content and place it into our own heading element
* - set aria-level to the level of that provided in the light DOM
* - remove the original heading
* @see https://inclusive-components.design/collapsible-sections/
* @deprecated Safe to remove in 4.0
* @see https://github.com/telekom/scale/pull/319
*/
setHeadingFromLightDOM() {
const lightHeading = this.el.querySelector(':first-child');
const lightHeading: HTMLElement = this.hostElement.querySelector(
':first-child'
);
if (lightHeading == null) {
return;
}
const level = parseInt(lightHeading.tagName.substr(1), 10);

if (!level) {
// tslint:disable-next-line
console.warn(
'The first element inside each <scale-collapsible> should be a heading of an appropriate level.'
);
// Only proceed if the element is not a heading and has no `slot` attribute
const isHeading = lightHeading.tagName.charAt(0).toUpperCase() === 'H';
const hasSlotAttr = lightHeading.hasAttribute('slot');
if (isHeading && !hasSlotAttr) {
this.headingElement.innerHTML = lightHeading.innerHTML;
lightHeading.style.display = 'none';
}
if (level !== this.level) {
this.level = level;
}
this.headingElement.innerHTML = lightHeading.innerHTML;
lightHeading.parentNode.removeChild(lightHeading);
}

handleClick = () => {
this.expanded = !this.expanded;
this.scaleExpand.emit({ expanded: this.expanded });
};

render() {
return (
<Host>
Expand All @@ -103,7 +93,7 @@ export class Collapsible {
part={classNames('base', this.expanded && 'expanded')}
>
<h2
aria-level={this.level}
aria-level={this.headingLevel}
class="collapsible__heading"
part="heading"
>
Expand All @@ -123,8 +113,11 @@ export class Collapsible {
/>
<span
ref={(el) => (this.headingElement = el)}
class="collapsible__button-text"
part="button-text"
/>
>
<slot name="heading"></slot>
</span>
</button>
</h2>
<div
Expand Down
9 changes: 5 additions & 4 deletions packages/components/src/components/collapsible/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

## Properties

| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------ | --------- | ----------- |
| `expanded` | `expanded` | Set to `true` to expand | `boolean` | `undefined` |
| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` |
| Property | Attribute | Description | Type | Default |
| -------------- | --------------- | ------------------------------ | --------- | ----------- |
| `expanded` | `expanded` | Set to `true` to expand | `boolean` | `undefined` |
| `headingLevel` | `heading-level` | Default aria-level for heading | `number` | `2` |
| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` |


## Events
Expand Down
11 changes: 11 additions & 0 deletions packages/components/src/html/theming.html
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ <h2>Heading 2</h2>
</scale-collapsible>
</scale-accordion>

<scale-accordion>
<scale-collapsible>
<span slot="heading">Heading 1 NEW SYNTAX</span>
<p>OK</p>
</scale-collapsible>
<scale-collapsible>
<span slot="heading">Heading 2 NEW SYNTAX</span>
<p>OK, 2</p>
</scale-collapsible>
</scale-accordion>

<a-divider></a-divider>

<a-heading>Breadcrumb</a-heading>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ export const Template = (args, { argTypes }) => ({
:styles="styles"
:dependent="dependent"
:expanded="expanded"
:headingLevel="headingLevel"
>
<scale-collapsible>
<h3>Leo integer malesuada nunc vel risus</h3>
<span slot="heading">Leo integer malesuada nunc vel risus</span>
<p>Freegan kinfolk <scale-link href="/">farm-to-table humblebrag cred</scale-link>, hammock bespoke small batch pabst. 90's tumblr whatever direct trade, organic master cleanse copper mug schlitz palo santo bushwick ethical pop-up chambray portland. Sartorial austin iceland street art, pug asymmetrical marfa mustache mumblecore. Shoreditch raclette knausgaard, swag enamel pin food truck everyday carry 3 wolf moon.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Dolor purus non enim</h3>
<span slot="heading">Dolor purus non enim</span>
<p>Bespoke austin pork belly yuccie pop-up. <scale-link href="/">Before they sold out</scale-link> YOLO kickstarter scenester meggings echo park aesthetic. Thundercats post-ironic wayfarers microdosing etsy hashtag seitan photo booth bitters.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</h3>
<span slot="heading">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</span>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress man bun fashion axe helvetica brunch godard cray viral prism. Street art tattooed bitters, ugh four loko selfies you probably haven't heard of them locavore bushwick. Tattooed 90's kinfolk, banh mi umami banjo palo santo cliche. Cray wolf godard skateboard celiac taxidermy tacos offal.</p>
</scale-collapsible>
</scale-accordion>
Expand All @@ -65,16 +66,16 @@ export const Template = (args, { argTypes }) => ({
```html
<scale-accordion>
<scale-collapsible>
<h3>Leo integer malesuada nunc vel risus</h3>
<p>Freegan kinfolk farm-to-table humblebrag cred, hammock bespoke small batch pabst</p>
<span slot="heading">Leo integer malesuada nunc vel risus</span>
<p>Freegan kinfolk <scale-link href="/">farm-to-table humblebrag cred</scale-link>, hammock bespoke small batch pabst. 90's tumblr whatever direct trade, organic master cleanse copper mug schlitz palo santo bushwick ethical pop-up chambray portland. Sartorial austin iceland street art, pug asymmetrical marfa mustache mumblecore. Shoreditch raclette knausgaard, swag enamel pin food truck everyday carry 3 wolf moon.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Dolor purus non enim</h3>
<p>Bespoke austin pork belly yuccie pop-up</p>
<span slot="heading">Dolor purus non enim</span>
<p>Bespoke austin pork belly yuccie pop-up. <scale-link href="/">Before they sold out</scale-link> YOLO kickstarter scenester meggings echo park aesthetic. Thundercats post-ironic wayfarers microdosing etsy hashtag seitan photo booth bitters.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</h3>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress</p>
<span slot="heading">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</span>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress man bun fashion axe helvetica brunch godard cray viral prism. Street art tattooed bitters, ugh four loko selfies you probably haven't heard of them locavore bushwick. Tattooed 90's kinfolk, banh mi umami banjo palo santo cliche. Cray wolf godard skateboard celiac taxidermy tacos offal.</p>
</scale-collapsible>
</scale-accordion>
```
Expand Down Expand Up @@ -128,16 +129,16 @@ For Shadow Parts, please inspect the element's #shadow.
```html
<scale-accordion dependent>
<scale-collapsible>
<h3>Leo integer malesuada nunc vel risus</h3>
<p>Freegan kinfolk farm-to-table humblebrag cred, hammock bespoke small batch pabst</p>
<span slot="heading">Leo integer malesuada nunc vel risus</span>
<p>Freegan kinfolk <scale-link href="/">farm-to-table humblebrag cred</scale-link>, hammock bespoke small batch pabst. 90's tumblr whatever direct trade, organic master cleanse copper mug schlitz palo santo bushwick ethical pop-up chambray portland. Sartorial austin iceland street art, pug asymmetrical marfa mustache mumblecore. Shoreditch raclette knausgaard, swag enamel pin food truck everyday carry 3 wolf moon.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Dolor purus non enim</h3>
<p>Bespoke austin pork belly yuccie pop-up</p>
<span slot="heading">Dolor purus non enim</span>
<p>Bespoke austin pork belly yuccie pop-up. <scale-link href="/">Before they sold out</scale-link> YOLO kickstarter scenester meggings echo park aesthetic. Thundercats post-ironic wayfarers microdosing etsy hashtag seitan photo booth bitters.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</h3>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress</p>
<span slot="heading">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</span>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress man bun fashion axe helvetica brunch godard cray viral prism. Street art tattooed bitters, ugh four loko selfies you probably haven't heard of them locavore bushwick. Tattooed 90's kinfolk, banh mi umami banjo palo santo cliche. Cray wolf godard skateboard celiac taxidermy tacos offal.</p>
</scale-collapsible>
</scale-accordion>
```
Expand All @@ -159,16 +160,50 @@ For Shadow Parts, please inspect the element's #shadow.
```html
<scale-accordion expanded>
<scale-collapsible>
<h3>Leo integer malesuada nunc vel risus</h3>
<p>Freegan kinfolk farm-to-table humblebrag cred, hammock bespoke small batch pabst</p>
<span slot="heading">Leo integer malesuada nunc vel risus</span>
<p>Freegan kinfolk <scale-link href="/">farm-to-table humblebrag cred</scale-link>, hammock bespoke small batch pabst. 90's tumblr whatever direct trade, organic master cleanse copper mug schlitz palo santo bushwick ethical pop-up chambray portland. Sartorial austin iceland street art, pug asymmetrical marfa mustache mumblecore. Shoreditch raclette knausgaard, swag enamel pin food truck everyday carry 3 wolf moon.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Dolor purus non enim</h3>
<p>Bespoke austin pork belly yuccie pop-up</p>
<span slot="heading">Dolor purus non enim</span>
<p>Bespoke austin pork belly yuccie pop-up. <scale-link href="/">Before they sold out</scale-link> YOLO kickstarter scenester meggings echo park aesthetic. Thundercats post-ironic wayfarers microdosing etsy hashtag seitan photo booth bitters.</p>
</scale-collapsible>
<scale-collapsible>
<h3>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</h3>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress…</p>
<span slot="heading">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</span>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress man bun fashion axe helvetica brunch godard cray viral prism. Street art tattooed bitters, ugh four loko selfies you probably haven't heard of them locavore bushwick. Tattooed 90's kinfolk, banh mi umami banjo palo santo cliche. Cray wolf godard skateboard celiac taxidermy tacos offal.</p>
</scale-collapsible>
</scale-accordion>
```

## Heading Level

The default level for the heading (h1, h2, h3…) inside the toggle button is `2`. So setting `heading-level` in `<scale-accordion>` will affect all `<scale-collapsible>` children automatically.

Adding `aria-level=""` to the heading slot on each item is not needed, but results in better accessibility in case JavaScript fails to load or execute.

<Canvas withSource="none">
<Story
name="Heading Level"
args={{
headingLevel: 3,
}}
>
{Template.bind({})}
</Story>
</Canvas>

```html
<scale-accordion heading-level="3">
<scale-collapsible>
<span slot="heading" aria-level="3">Leo integer malesuada nunc vel risus</span>
<p>Freegan kinfolk <scale-link href="/">farm-to-table humblebrag cred</scale-link>, hammock bespoke small batch pabst. 90's tumblr whatever direct trade, organic master cleanse copper mug schlitz palo santo bushwick ethical pop-up chambray portland. Sartorial austin iceland street art, pug asymmetrical marfa mustache mumblecore. Shoreditch raclette knausgaard, swag enamel pin food truck everyday carry 3 wolf moon.</p>
</scale-collapsible>
<scale-collapsible>
<span slot="heading" aria-level="3">Dolor purus non enim</span>
<p>Bespoke austin pork belly yuccie pop-up. <scale-link href="/">Before they sold out</scale-link> YOLO kickstarter scenester meggings echo park aesthetic. Thundercats post-ironic wayfarers microdosing etsy hashtag seitan photo booth bitters.</p>
</scale-collapsible>
<scale-collapsible>
<span slot="heading" aria-level="3">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam</span>
<p>Biodiesel chia af hoodie tumeric bespoke letterpress man bun fashion axe helvetica brunch godard cray viral prism. Street art tattooed bitters, ugh four loko selfies you probably haven't heard of them locavore bushwick. Tattooed 90's kinfolk, banh mi umami banjo palo santo cliche. Cray wolf godard skateboard celiac taxidermy tacos offal.</p>
</scale-collapsible>
</scale-accordion>
```
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<scale-accordion :styles="styles" :dependent="dependent" :expanded="expanded">
<scale-accordion :styles="styles" :dependent="dependent" :expanded="expanded" :heading-level="headingLevel">
<slot></slot>
</scale-accordion>
</template>
Expand All @@ -10,6 +10,7 @@ export default {
styles: { type: String },
dependent: { type: Boolean, default: false },
expanded: { type: Boolean, default: false },
headingLevel: { type: Number },
},
};
</script>

0 comments on commit 06e8e2c

Please sign in to comment.