Skip to content

Commit

Permalink
feat(ld-heading): set aria-label on b headings implicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
borisdiakur committed May 26, 2021
1 parent 2237b02 commit c045ca3
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 50 deletions.
44 changes: 28 additions & 16 deletions src/liquid/components/ld-heading/ld-heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,23 @@ export class LdHeading {
| 'xh6'

/**
* **This prop is required if you're using a b1 to b6 or xb1 to xb3 visual level**:
* Since b1 to b6 headings are uppercase headings, screen readers need to be served a
* (non-uppercase) aria-label (otherwise they will read out the heading letter by letter).
* If you're using a b1 to b6 or xb1 to xb3 visual level heading, an aria-label will be
* set automatically on the heading element. The component will use the inner HTML for the
* label implicitly. If you want to set an aria-label explicitly (such as when you have
* inner HTML that should not be part of the label), you can use this property.
*/
@Prop({ mutable: true })
ariaLabel: string | undefined
@Prop()
ariaLabel: string

private validateLevel(newValue: number | string) {
if (![1, 2, 3, 4, 5, 6].includes(parseInt(newValue + '', 10))) {
throw new TypeError(`ld-heading level prop invalid; got ${newValue}`)
private validateLevel() {
if (![1, 2, 3, 4, 5, 6].includes(parseInt(this.level + '', 10))) {
throw new TypeError(`ld-heading level prop invalid; got ${this.level}`)
}
}

private validateVisualLevel(newValue: undefined | string) {
private validateVisualLevel() {
if (
![
undefined,
Expand All @@ -84,26 +87,36 @@ export class LdHeading {
'xh4',
'xh5',
'xh6',
].includes(newValue)
].includes(this.visualLevel)
) {
throw new TypeError(
`ld-heading visualLevel prop invalid; got ${newValue}`
`ld-heading visualLevel prop invalid; got ${this.visualLevel}`
)
}
}

const isBHeading = this.visualLevel?.indexOf('b') === 0
if (isBHeading && !this.ariaLabel) {
throw new TypeError(
'ld-heading with visualLevel prop b* requires an ariaLabel prop'
private applyAriaLabel() {
const isBHeading =
this.visualLevel?.indexOf('b') === 0 ||
this.visualLevel?.indexOf('xb') === 0
if (isBHeading) {
const heading = this.el.querySelector('.ld-heading')
heading.setAttribute(
'aria-label',
this.ariaLabel || heading.innerHTML.trim()
)
}
}

componentWillLoad() {
applyPropAliases.apply(this)

this.validateLevel(this.level)
this.validateVisualLevel(this.visualLevel)
this.validateLevel()
this.validateVisualLevel()
}

componentDidRender() {
this.applyAriaLabel()
}

render() {
Expand All @@ -113,7 +126,6 @@ export class LdHeading {
return (
<HTag
class={cl}
aria-label={this.ariaLabel}
{...cloneAttributes<HeadingHTMLAttributes<HTMLHeadingElement>>(this.el)}
>
<slot></slot>
Expand Down
30 changes: 15 additions & 15 deletions src/liquid/components/ld-heading/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,27 +72,27 @@ Headings are used as an introduction into a topic and for visual differentiation
### With deviating visual level `b*`

{% example "html", true %}
<ld-heading level="1" visual-level="b1" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="1" visual-level="b1">
Almost before we knew it, we had left the ground.
</ld-heading>

<ld-heading level="2" visual-level="b2" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="2" visual-level="b2">
Almost before we knew it, we had left the ground.
</ld-heading>

<ld-heading level="3" visual-level="b3" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="3" visual-level="b3">
Almost before we knew it, we had left the ground.
</ld-heading>

<ld-heading level="4" visual-level="b4" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="4" visual-level="b4">
Almost before we knew it, we had left the ground.
</ld-heading>

<ld-heading level="5" visual-level="b5" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="5" visual-level="b5">
Almost before we knew it, we had left the ground.
</ld-heading>

<ld-heading level="6" visual-level="b6" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="6" visual-level="b6">
Almost before we knew it, we had left the ground.
</ld-heading>

Expand Down Expand Up @@ -156,15 +156,15 @@ Almost before we knew it, we had left the ground.
### With deviating visual level `xb*`

{% example "html", true %}
<ld-heading level="1" visual-level="xb1" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="1" visual-level="xb1">
Almost before we knew it, we had left the ground.
</ld-heading>

<ld-heading level="2" visual-level="xb2" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="2" visual-level="xb2">
Almost before we knew it, we had left the ground.
</ld-heading>

<ld-heading level="3" visual-level="xb3" aria-label="Almost before we knew it, we had left the ground.">
<ld-heading level="3" visual-level="xb3">
Almost before we knew it, we had left the ground.
</ld-heading>

Expand All @@ -189,18 +189,18 @@ Almost before we knew it, we had left the ground.

## Properties

| Property | Attribute | Description | Type | Default |
| -------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `ariaLabel` | `aria-label` | **This prop is required if you're using a b1 to b6 or xb1 to xb3 visual level**: Since b1 to b6 headings are uppercase headings, screen readers need to be served a (non-uppercase) aria-label (otherwise they will read out the heading letter by letter). | `string` | `undefined` |
| `level` _(required)_ | `level` | The heading level. | `"1" \| "2" \| "3" \| "4" \| "5" \| "6" \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `undefined` |
| `visualLevel` | `visual-level` | The heading style. Overrides the style inferred from the heading level. | `"h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6" \| "b1" \| "b2" \| "b3" \| "b4" \| "b5" \| "b6" \| "xb1" \| "xb2" \| "xb3" \| "xh1" \| "xh2" \| "xh3" \| "xh4" \| "xh5" \| "xh6"` | `undefined` |
| Property | Attribute | Description | Type | Default |
| -------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `ariaLabel` | `aria-label` | Since b1 to b6 headings are uppercase headings, screen readers need to be served a (non-uppercase) aria-label (otherwise they will read out the heading letter by letter). If you're using a b1 to b6 or xb1 to xb3 visual level heading, an aria-label will be set automatically on the heading element. The component will use the inner HTML for the label implicitly. If you want to set an aria-label explicitly (such as when you have inner HTML that should not be part of the label), you can use this property. | `string` | `undefined` |
| `level` _(required)_ | `level` | The heading level. | `"1" \| "2" \| "3" \| "4" \| "5" \| "6" \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `undefined` |
| `visualLevel` | `visual-level` | The heading style. Overrides the style inferred from the heading level. | `"h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6" \| "b1" \| "b2" \| "b3" \| "b4" \| "b5" \| "b6" \| "xb1" \| "xb2" \| "xb3" \| "xh1" \| "xh2" \| "xh3" \| "xh4" \| "xh5" \| "xh6"` | `undefined` |


## Dependencies

### Used by

- docs-nav
- docs-nav

### Graph
```mermaid
Expand Down
34 changes: 15 additions & 19 deletions src/liquid/components/ld-heading/test/ld-heading.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('ld-heading', () => {
)
}
})
it('with visual level', async () => {
it('renders with visual level', async () => {
const page = await newSpecPage({
components: [LdHeading],
html: `<ld-heading level="1" visual-level="h3">Text</ld-heading>`,
Expand All @@ -50,31 +50,27 @@ describe('ld-heading', () => {
</ld-heading>
`)
})
it('with visual b level and aria-label', async () => {
it('renders with visual b level and explicit aria-label', async () => {
const page = await newSpecPage({
components: [LdHeading],
html: `<ld-heading level="1" visual-level="b3" aria-label="Text">Text</ld-heading>`,
html: `<ld-heading level="1" visual-level="b3" aria-label="Yolo">Text</ld-heading>`,
})
expect(page.root).toEqualHtml(`
<ld-heading aria-label="Text" level="1" visual-level="b3">
<h1 aria-label="Text" class="ld-heading ld-heading--b3">Text</h1>
<ld-heading aria-label="Yolo" level="1" visual-level="b3">
<h1 aria-label="Yolo" class="ld-heading ld-heading--b3">Text</h1>
</ld-heading>
`)
})
it('throws with visual b level but without aria-label', async () => {
try {
await newSpecPage({
components: [LdHeading],
html: `<ld-heading level="1" visual-level="b3">Text</ld-heading>`,
})
expect(true).toBe(false)
} catch (err) {
expect(err).toStrictEqual(
TypeError(
'ld-heading with visualLevel prop b* requires an ariaLabel prop'
)
)
}
it('renders with visual b level and implicit aria-label', async () => {
const page = await newSpecPage({
components: [LdHeading],
html: `<ld-heading level="1" visual-level="b3">Text</ld-heading>`,
})
expect(page.root).toEqualHtml(`
<ld-heading level="1" visual-level="b3">
<h1 aria-label="Text" class="ld-heading ld-heading--b3">Text</h1>
</ld-heading>
`)
})
it('throws with invalid visual level prop', async () => {
try {
Expand Down

0 comments on commit c045ca3

Please sign in to comment.