diff --git a/screenshot/builds/master.json b/screenshot/builds/master.json index 70533c0865..569f697e3a 100644 --- a/screenshot/builds/master.json +++ b/screenshot/builds/master.json @@ -381,6 +381,58 @@ "isLandscape": false, "isMobile": false }, + { + "id": "eff4f911", + "image": "a37d4364febc6a1c279bb675ea0eb056.png", + "userAgent": "default", + "desc": "ld-breadcrumbs renders as CSS Component", + "testPath": "./src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.e2e.ts", + "width": 600, + "height": 600, + "deviceScaleFactor": 1, + "hasTouch": false, + "isLandscape": false, + "isMobile": false + }, + { + "id": "f91c2718", + "image": "a37d4364febc6a1c279bb675ea0eb056.png", + "userAgent": "default", + "desc": "ld-breadcrumbs renders as Web Component", + "testPath": "./src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.e2e.ts", + "width": 600, + "height": 600, + "deviceScaleFactor": 1, + "hasTouch": false, + "isLandscape": false, + "isMobile": false + }, + { + "id": "94e3f635", + "image": "053c6df29d132a360ae66ba7abd42795.png", + "userAgent": "default", + "desc": "ld-breadcrumbs renders with icons as CSS component", + "testPath": "./src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.e2e.ts", + "width": 600, + "height": 600, + "deviceScaleFactor": 1, + "hasTouch": false, + "isLandscape": false, + "isMobile": false + }, + { + "id": "cdf7649b", + "image": "053c6df29d132a360ae66ba7abd42795.png", + "userAgent": "default", + "desc": "ld-breadcrumbs renders with icons as Web Component", + "testPath": "./src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.e2e.ts", + "width": 600, + "height": 600, + "deviceScaleFactor": 1, + "hasTouch": false, + "isLandscape": false, + "isMobile": false + }, { "id": "c5c7582a", "image": "e0193ce006e37fabf36900daaadd0712.png", @@ -7377,7 +7429,7 @@ }, { "id": "11c3a598", - "image": "f63a4918e6f270256c006b1df8b99d6c.png", + "image": "796d9b032fbdbcef50000743d0889bc8.png", "userAgent": "default", "desc": "ld-link css component with chevron end", "testPath": "./src/liquid/components/ld-link/test/ld-link.e2e.ts", @@ -7390,7 +7442,7 @@ }, { "id": "ebcd4fea", - "image": "f9c3e4ef4d16627a54a6ec0640ca4c50.png", + "image": "2507876c594192cb4fb29e004ae599df.png", "userAgent": "default", "desc": "ld-link css component with chevron start", "testPath": "./src/liquid/components/ld-link/test/ld-link.e2e.ts", @@ -7429,7 +7481,7 @@ }, { "id": "a51ed960", - "image": "025b40f6e717937da79d23075f94454a.png", + "image": "6f0fe7041f6573b8a71a8622b40e7c22.png", "userAgent": "default", "desc": "ld-link web component with chevron end", "testPath": "./src/liquid/components/ld-link/test/ld-link.e2e.ts", @@ -7442,7 +7494,7 @@ }, { "id": "235718a5", - "image": "db0056903ab347b75e15880aeed38984.png", + "image": "e180cf4b6a412aedb2a005aa9df45864.png", "userAgent": "default", "desc": "ld-link web component with chevron start", "testPath": "./src/liquid/components/ld-link/test/ld-link.e2e.ts", diff --git a/screenshot/images/025b40f6e717937da79d23075f94454a.png b/screenshot/images/025b40f6e717937da79d23075f94454a.png deleted file mode 100644 index 5327a0f1f1..0000000000 Binary files a/screenshot/images/025b40f6e717937da79d23075f94454a.png and /dev/null differ diff --git a/screenshot/images/053c6df29d132a360ae66ba7abd42795.png b/screenshot/images/053c6df29d132a360ae66ba7abd42795.png new file mode 100644 index 0000000000..0a9d07ecb8 Binary files /dev/null and b/screenshot/images/053c6df29d132a360ae66ba7abd42795.png differ diff --git a/screenshot/images/2507876c594192cb4fb29e004ae599df.png b/screenshot/images/2507876c594192cb4fb29e004ae599df.png new file mode 100644 index 0000000000..ac1b9a1277 Binary files /dev/null and b/screenshot/images/2507876c594192cb4fb29e004ae599df.png differ diff --git a/screenshot/images/6f0fe7041f6573b8a71a8622b40e7c22.png b/screenshot/images/6f0fe7041f6573b8a71a8622b40e7c22.png new file mode 100644 index 0000000000..b772b4f0aa Binary files /dev/null and b/screenshot/images/6f0fe7041f6573b8a71a8622b40e7c22.png differ diff --git a/screenshot/images/796d9b032fbdbcef50000743d0889bc8.png b/screenshot/images/796d9b032fbdbcef50000743d0889bc8.png new file mode 100644 index 0000000000..66a29fbddc Binary files /dev/null and b/screenshot/images/796d9b032fbdbcef50000743d0889bc8.png differ diff --git a/screenshot/images/a37d4364febc6a1c279bb675ea0eb056.png b/screenshot/images/a37d4364febc6a1c279bb675ea0eb056.png new file mode 100644 index 0000000000..1bac149e15 Binary files /dev/null and b/screenshot/images/a37d4364febc6a1c279bb675ea0eb056.png differ diff --git a/screenshot/images/db0056903ab347b75e15880aeed38984.png b/screenshot/images/db0056903ab347b75e15880aeed38984.png deleted file mode 100644 index 1bc1ebd3bb..0000000000 Binary files a/screenshot/images/db0056903ab347b75e15880aeed38984.png and /dev/null differ diff --git a/screenshot/images/e180cf4b6a412aedb2a005aa9df45864.png b/screenshot/images/e180cf4b6a412aedb2a005aa9df45864.png new file mode 100644 index 0000000000..0510e22830 Binary files /dev/null and b/screenshot/images/e180cf4b6a412aedb2a005aa9df45864.png differ diff --git a/screenshot/images/f63a4918e6f270256c006b1df8b99d6c.png b/screenshot/images/f63a4918e6f270256c006b1df8b99d6c.png deleted file mode 100644 index a86cbcdc52..0000000000 Binary files a/screenshot/images/f63a4918e6f270256c006b1df8b99d6c.png and /dev/null differ diff --git a/screenshot/images/f9c3e4ef4d16627a54a6ec0640ca4c50.png b/screenshot/images/f9c3e4ef4d16627a54a6ec0640ca4c50.png deleted file mode 100644 index ecfd307704..0000000000 Binary files a/screenshot/images/f9c3e4ef4d16627a54a6ec0640ca4c50.png and /dev/null differ diff --git a/src/docs/components/docs-breadcrumbs/assets/chevron-dark.svg b/src/docs/components/docs-breadcrumbs/assets/chevron-dark.svg deleted file mode 100644 index 15c1c0de09..0000000000 --- a/src/docs/components/docs-breadcrumbs/assets/chevron-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/docs/components/docs-breadcrumbs/assets/chevron-light.svg b/src/docs/components/docs-breadcrumbs/assets/chevron-light.svg deleted file mode 100644 index db9943731c..0000000000 --- a/src/docs/components/docs-breadcrumbs/assets/chevron-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/docs/components/docs-breadcrumbs/docs-breadcrumbs.css b/src/docs/components/docs-breadcrumbs/docs-breadcrumbs.css deleted file mode 100644 index cce6ca085f..0000000000 --- a/src/docs/components/docs-breadcrumbs/docs-breadcrumbs.css +++ /dev/null @@ -1,57 +0,0 @@ -@define-mixin docs-breadcrumbs-ui-light { - .docs-breadcrumbs__list li::after { - background-image: url('dist/build/assets/chevron-dark.svg'); - } -} - -@define-mixin docs-breadcrumbs-ui-dark { - .docs-breadcrumbs__list li::after { - background-image: url('dist/build/assets/chevron-light.svg'); - } -} - -@mixin docs-breadcrumbs-ui-light; - -@media (prefers-color-scheme: dark) { - @mixin docs-breadcrumbs-ui-dark; -} -.docs-ui-dark { - @mixin docs-breadcrumbs-ui-dark; -} -.docs-ui-light { - @mixin docs-breadcrumbs-ui-light; -} - -docs-breadcrumbs, -.docs-breadcrumbs { - min-height: 1.75rem; - width: 100%; -} - -.docs-breadcrumbs__list { - display: flex; - flex-wrap: wrap; - list-style: none; - font: var(--ld-typo-body-s); - - li:not(:last-of-type) { - margin-right: var(--ld-sp-8); - - &::after { - content: ''; - background-repeat: no-repeat; - width: 0.8rem; - height: 1.375rem; - margin-left: var(--ld-sp-12); - display: inline-flex; - align-items: center; - justify-content: center; - } - } - - a:not([href]), - a[href='.'] { - cursor: default; - pointer-events: none; - } -} diff --git a/src/docs/components/docs-breadcrumbs/docs-breadcrumbs.tsx b/src/docs/components/docs-breadcrumbs/docs-breadcrumbs.tsx deleted file mode 100644 index 9eb8fadbed..0000000000 --- a/src/docs/components/docs-breadcrumbs/docs-breadcrumbs.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, h, Host } from '@stencil/core' - -/** @internal **/ -@Component({ - tag: 'docs-breadcrumbs', - styleUrl: 'docs-breadcrumbs.css', - shadow: false, - assetsDirs: ['assets'], -}) -export class DocsBreadcrumbs { - render() { - return ( - - - - ) - } -} diff --git a/src/docs/components/docs-main/docs-main.css b/src/docs/components/docs-main/docs-main.css index 4c3ef5fe79..58851fd3f7 100644 --- a/src/docs/components/docs-main/docs-main.css +++ b/src/docs/components/docs-main/docs-main.css @@ -5,6 +5,12 @@ } main { + > header { + ld-breadcrumbs { + filter: none; + } + } + > p, > ol, > ul, @@ -77,6 +83,12 @@ } main { + > header { + ld-breadcrumbs { + filter: invert(1) hue-rotate(180deg); + } + } + > p, > ol, > ul, @@ -179,9 +191,14 @@ display: flex; justify-content: flex-end; flex-wrap: wrap; + align-items: center; + } - > * + *:not(.docs-breadcrumbs) { - margin-top: var(--ld-sp-16); + > header { + ld-breadcrumbs { + flex-grow: 1; + margin-inline: 0 var(--ld-sp-24); + margin-block: var(--ld-sp-16); } } diff --git a/src/docs/includes/layout.njk b/src/docs/includes/layout.njk index 61c83798a8..b8f572aea8 100644 --- a/src/docs/includes/layout.njk +++ b/src/docs/includes/layout.njk @@ -112,18 +112,14 @@ title: Introduction
{% if 'Introduction' not in title and not '404' in title and not 'legal' in page.url %} - + {% set navPages = collections.all | eleventyNavigationBreadcrumb(title) %} {% macro renderBreadcrumb(entry) %} -
  • - {{ entry.title }} -
  • + {{ entry.title }} {% endmacro %} {% for entry in navPages %}{{ renderBreadcrumb(entry) }}{% endfor %} -
  • - {{ title }} -
  • -
    + {{ title }} + {% endif %}
    diff --git a/src/liquid/components/ld-breadcrumbs/ld-breadcrumbs.css b/src/liquid/components/ld-breadcrumbs/ld-breadcrumbs.css new file mode 100644 index 0000000000..61ef751565 --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/ld-breadcrumbs.css @@ -0,0 +1,15 @@ +:host, +.ld-breadcrumbs { + --ld-crumb-icon-gap: 0.5em; + --ld-crumb-gap: 0.6em; + + line-height: 1.5; +} + +.ld-breadcrumbs > ol { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 0; +} diff --git a/src/liquid/components/ld-breadcrumbs/ld-breadcrumbs.tsx b/src/liquid/components/ld-breadcrumbs/ld-breadcrumbs.tsx new file mode 100644 index 0000000000..6637d869fd --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/ld-breadcrumbs.tsx @@ -0,0 +1,52 @@ +import '../../components' // type definitions for type checks and intelliSense +import { Component, h, Element } from '@stencil/core' + +/** + * @part list - Breadcrumbs list + * @virtualProp ref - reference to component + * @virtualProp {string | number} key - for tracking the node's identity when working with lists + */ + +@Component({ + tag: 'ld-breadcrumbs', + styleUrl: 'ld-breadcrumbs.css', + shadow: true, +}) +export class LdBreadcrumbs { + @Element() el: HTMLElement + + private observer: MutationObserver + + private updateCurrent = () => { + const crumbs = this.el.querySelectorAll('ld-crumb') + crumbs.forEach((crumb) => { + crumb.current = undefined + }) + crumbs[crumbs.length - 1].current = true + } + + componentDidLoad() { + this.observer = new MutationObserver(this.updateCurrent) + this.observer.observe(this.el, { + subtree: true, + childList: true, + attributes: false, + }) + + this.updateCurrent() + } + + disconnectedCallback() { + if (this.observer) this.observer.disconnect() + } + + render() { + return ( + + ) + } +} diff --git a/src/liquid/components/ld-breadcrumbs/ld-crumb/ld-crumb.css b/src/liquid/components/ld-breadcrumbs/ld-crumb/ld-crumb.css new file mode 100644 index 0000000000..2347fe05d2 --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/ld-crumb/ld-crumb.css @@ -0,0 +1,32 @@ +.ld-breadcrumbs .ld-link, +.ld-crumb__link::part(anchor) { + --ld-link-gap: var(--ld-crumb-icon-gap); + --ld-link-chevron-gap: var(--ld-crumb-gap); + display: inline-flex; +} + +.ld-breadcrumbs li:not(:last-of-type) .ld-link, +.ld-crumb__link:not(.ld-crumb__link--current)::part(anchor) { + --ld-link-col: var(--ld-col-neutral-600); + + font-weight: normal; + margin-right: calc(var(--ld-crumb-gap) + 0.5em); + + &:hover { + --ld-link-col: var(--ld-thm-primary-hover); + } + + &:focus:focus-visible { + --ld-link-col: var(--ld-thm-primary-focus); + } + + &:active { + --ld-link-col: var(--ld-thm-primary-active); + } +} + +.ld-breadcrumbs li:last-of-type .ld-link, +.ld-crumb__link--current { + cursor: default; + pointer-events: none; +} diff --git a/src/liquid/components/ld-breadcrumbs/ld-crumb/ld-crumb.tsx b/src/liquid/components/ld-breadcrumbs/ld-crumb/ld-crumb.tsx new file mode 100644 index 0000000000..b76f5d48d4 --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/ld-crumb/ld-crumb.tsx @@ -0,0 +1,40 @@ +import '../../../components' // type definitions for type checks and intelliSense +import { Component, h, Prop } from '@stencil/core' +import { getClassNames } from '../../../utils/getClassNames' + +/** + * @part link - Breadcrumb link + * @virtualProp ref - reference to component + * @virtualProp {string | number} key - for tracking the node's identity when working with lists + */ +@Component({ + tag: 'ld-crumb', + styleUrl: 'ld-crumb.css', + shadow: true, +}) +export class LdCrumb { + /** @internal */ + @Prop() current?: boolean + + /** The URL that the hyperlink points to. */ + @Prop() href?: string + + render() { + return ( +
  • + + + +
  • + ) + } +} diff --git a/src/liquid/components/ld-breadcrumbs/ld-crumb/readme.md b/src/liquid/components/ld-breadcrumbs/ld-crumb/readme.md new file mode 100644 index 0000000000..8a3175594c --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/ld-crumb/readme.md @@ -0,0 +1,50 @@ +--- +eleventyNavigation: + key: Crumb + parent: Breadcrumbs +layout: layout.njk +title: Crumb +permalink: components/ld-breadcrumbs/ld-crumb/ +--- + +# ld-crumb + +The `ld-crumb` component is a subcomponent for `ld-breadcrumbs`. + +Please refer to the [`ld-breadcrumbs` documentation](components/ld-breadcrumbs/) for usage examples. + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| -------- | --------- | -------------------------------------------------------- | ------------------ | ----------- | +| `href` | `href` | The URL that the hyperlink points to. | `string` | `undefined` | +| `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` | +| `ref` | `ref` | reference to component | `any` | `undefined` | + + +## Shadow Parts + +| Part | Description | +| -------- | --------------- | +| `"link"` | Breadcrumb link | + + +## Dependencies + +### Depends on + +- [ld-link](../../ld-link) + +### Graph +```mermaid +graph TD; + ld-crumb --> ld-link + style ld-crumb fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + + diff --git a/src/liquid/components/ld-breadcrumbs/readme.md b/src/liquid/components/ld-breadcrumbs/readme.md new file mode 100644 index 0000000000..707f6a1cfc --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/readme.md @@ -0,0 +1,109 @@ +--- +eleventyNavigation: + key: Breadcrumbs + parent: Components +layout: layout.njk +title: Breadcrumbs +permalink: components/ld-breadcrumbs/ +--- + + + + + + +# ld-breadcrumbs + +A breadcrumb trail consists of a list of links to the parent pages of the current page in hierarchical order. It helps users find their place within a website or web application. Breadcrumbs are often placed horizontally before a page's main content. + +This component is meant to be used in conjunction with the [`ld-crumb`](./ld-crumb/) component. + +## Examples + +### Basic + +{% example %} + + Components + Breadcrumbs + + + + + +{% endexample %} + +### With icons + +{% example %} + + + + Components + + + + Breadcrumbs + + + + + + +{% endexample %} + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| -------- | --------- | -------------------------------------------------------- | ------------------ | ----------- | +| `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` | +| `ref` | `ref` | reference to component | `any` | `undefined` | + + +## Shadow Parts + +| Part | Description | +| -------- | ---------------- | +| `"list"` | Breadcrumbs list | + + +---------------------------------------------- + + diff --git a/src/liquid/components/ld-breadcrumbs/test/__snapshots__/ld-breadcrumbs.spec.tsx.snap b/src/liquid/components/ld-breadcrumbs/test/__snapshots__/ld-breadcrumbs.spec.tsx.snap new file mode 100644 index 0000000000..70fa919090 --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/test/__snapshots__/ld-breadcrumbs.spec.tsx.snap @@ -0,0 +1,127 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ld-breadcrumbs renders 1`] = ` + + + + + + +
  • + + + +
  • +
    + foo +
    + + +
  • + + + +
  • +
    + bar +
    + + +
  • + + + +
  • +
    + qux +
    +
    +`; + +exports[`ld-breadcrumbs updates current after addition of a crumb 1`] = ` + + + + + + +
  • + + + +
  • +
    + foo +
    + + +
  • + + + +
  • +
    + bar +
    + + +
  • + + + +
  • +
    + qux +
    + + +
  • + + + +
  • +
    + baz +
    +
    +`; + +exports[`ld-breadcrumbs updates current after removal of current crumb 1`] = ` + + + + + + +
  • + + + +
  • +
    + foo +
    + + +
  • + + + +
  • +
    + bar +
    +
    +`; diff --git a/src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.e2e.ts b/src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.e2e.ts new file mode 100644 index 0000000000..7bd76fe2d6 --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.e2e.ts @@ -0,0 +1,171 @@ +import { + analyzeAccessibility, + getPageWithContent, +} from 'src/liquid/utils/e2e-tests' +import { LdIcon } from '../../ld-icon/ld-icon' +import { LdBreadcrumbs } from '../ld-breadcrumbs' +import { LdCrumb } from '../ld-crumb/ld-crumb' +import { LdLink } from '../../ld-link/ld-link' + +const components = [LdBreadcrumbs, LdCrumb, LdLink, LdIcon] + +describe('ld-breadcrumbs', () => { + it('renders as Web Component', async () => { + const page = await getPageWithContent( + ` + foo + bar + qux + ` + ) + + const results = await page.compareScreenshot() + expect(results).toMatchScreenshot() + }) + + it('renders as CSS Component', async () => { + const page = await getPageWithContent( + ``, + { + components, + } + ) + + const results = await page.compareScreenshot() + expect(results).toMatchScreenshot() + }) + + describe('accessibility', () => { + it('is accessible as a Web Component', async () => { + const page = await getPageWithContent( + ` + foo + bar + qux + ` + ) + page.waitForChanges() + + const accessibilityReport = await analyzeAccessibility(page, { + rules: { + // Disabling list rules here, because manual tests with screenreader + // resulted in expected behavior: The screenreader reads number of list items + // in breadcrumb trail list. + list: { enabled: false }, + listitem: { enabled: false }, + }, + }) + expect(accessibilityReport).toHaveNoAccessibilityIssues() + }) + + it('is accessible as a CSS Component', async () => { + const page = await getPageWithContent( + ``, + { + components, + } + ) + + page.waitForChanges() + const accessibilityReport = await analyzeAccessibility(page) + expect(accessibilityReport).toHaveNoAccessibilityIssues() + }) + }) + + it('renders with icons as Web Component', async () => { + const page = await getPageWithContent( + ` + + + foo + + + + bar + + + + qux + + `, + { + components, + } + ) + + const results = await page.compareScreenshot() + expect(results).toMatchScreenshot() + }) + + it('renders with icons as CSS component', async () => { + const page = await getPageWithContent( + ``, + { + components, + } + ) + + const results = await page.compareScreenshot() + expect(results).toMatchScreenshot() + }) +}) diff --git a/src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.spec.tsx b/src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.spec.tsx new file mode 100644 index 0000000000..b9a711d206 --- /dev/null +++ b/src/liquid/components/ld-breadcrumbs/test/ld-breadcrumbs.spec.tsx @@ -0,0 +1,61 @@ +import { newSpecPage } from '@stencil/core/testing' +import { LdBreadcrumbs } from '../ld-breadcrumbs' +import { LdCrumb } from '../ld-crumb/ld-crumb' +import { getTriggerableMutationObserver } from '../../../utils/mutationObserver' + +describe('ld-breadcrumbs', () => { + it('renders', async () => { + const page = await newSpecPage({ + components: [LdBreadcrumbs, LdCrumb], + html: ` + foo + bar + qux + `, + }) + await page.waitForChanges() + jest.advanceTimersByTime(0) + + expect(page.root).toMatchSnapshot() + }) + + it('updates current after addition of a crumb', async () => { + const page = await newSpecPage({ + components: [LdBreadcrumbs, LdCrumb], + html: ` + foo + bar + qux + `, + }) + await page.waitForChanges() + jest.advanceTimersByTime(0) + + const crumb = page.doc.createElement('ld-crumb') + crumb.innerHTML = 'baz' + + page.root.appendChild(crumb) + getTriggerableMutationObserver().trigger([]) + await page.waitForChanges() + + expect(page.root).toMatchSnapshot() + }) + + it('updates current after removal of current crumb', async () => { + const page = await newSpecPage({ + components: [LdBreadcrumbs, LdCrumb], + html: ` + foo + bar + qux + `, + }) + await page.waitForChanges() + + page.root.removeChild(page.root.querySelector('ld-crumb:last-of-type')) + getTriggerableMutationObserver().trigger([]) + await page.waitForChanges() + + expect(page.root).toMatchSnapshot() + }) +}) diff --git a/src/liquid/components/ld-checkbox/ld-checkbox.tsx b/src/liquid/components/ld-checkbox/ld-checkbox.tsx index fe39bbe60e..a22b1c3e48 100644 --- a/src/liquid/components/ld-checkbox/ld-checkbox.tsx +++ b/src/liquid/components/ld-checkbox/ld-checkbox.tsx @@ -222,6 +222,7 @@ export class LdCheckbox implements InnerFocusable, ClonesAttributes { type="checkbox" value={this.value} /> + {/* custom icon check */} '); transform: translateY(4%); } + /* custom icon calendar-picker */ &[type='time'] { min-width: var(--ld-input-time-min-width-md); diff --git a/src/liquid/components/ld-link/ld-link.css b/src/liquid/components/ld-link/ld-link.css index 48587404c5..9273c4c9e0 100644 --- a/src/liquid/components/ld-link/ld-link.css +++ b/src/liquid/components/ld-link/ld-link.css @@ -1,11 +1,13 @@ .ld-link { --ld-link-col: var(--ld-thm-primary); - --ld-link-chevron-width: 0.15em; + --ld-link-gap: 0.3em; + --ld-link-chevron-gap: 0em; + --ld-link-chevron-width: 0.135em; align-items: baseline; color: var(--ld-link-col); cursor: pointer; - gap: 0.3em; + gap: var(--ld-link-gap); font-family: var(--ld-font-body); font-size: inherit; font-weight: 700; @@ -32,8 +34,16 @@ cursor: default; opacity: 0.3; } + + > ld-icon, + > .ld-icon, + > ::slotted(ld-icon), + > ::slotted(.ld-icon) { + transform: translateY(10%); + } } +/* custom icon arrow-right */ .ld-link--chevron-start, .ld-link--chevron-end { display: inline-flex; @@ -42,31 +52,54 @@ &::after { content: ''; background-color: var(--ld-link-col); - border-radius: var(--ld-link-chevron-width); + border-radius: 99rem; display: block; flex-shrink: 0; height: 0.5em; transform-origin: right; width: var(--ld-link-chevron-width); } - - &::before { - transform: translateX(calc(0.15em + 50%)) translateY(-60%) rotate(-40deg); - } - &::after { - transform: translateX(calc(-0.15em - 50%)) translateY(20%) rotate(40deg); - } } .ld-link--chevron-start { + margin-left: calc(0.5 * var(--ld-link-chevron-gap)); + &::before, &::after { order: -1; } + + &::before { + transform: translateX( + calc(0.5 * (var(--ld-link-gap) - var(--ld-link-chevron-gap)) + 50%) + ) + translateY(-65%) rotate(-40deg); + } + &::after { + transform: translateX( + calc(-0.5 * var(--ld-link-gap) - 0.5 * var(--ld-link-chevron-gap) - 50%) + ) + translateY(20%) rotate(40deg); + } } .ld-link--chevron-end { + margin-right: calc(0.5 * var(--ld-link-chevron-gap)); + &::before, &::after { order: 1; } + + &::before { + transform: translateX( + calc(0.5 * (var(--ld-link-gap) + var(--ld-link-chevron-gap)) + 50%) + ) + translateY(-65%) rotate(-40deg); + } + &::after { + transform: translateX( + calc(-0.5 * var(--ld-link-gap) + 0.5 * var(--ld-link-chevron-gap) - 50%) + ) + translateY(20%) rotate(40deg); + } } diff --git a/src/liquid/components/ld-link/ld-link.tsx b/src/liquid/components/ld-link/ld-link.tsx index 225fe22466..96b42a9f3f 100644 --- a/src/liquid/components/ld-link/ld-link.tsx +++ b/src/liquid/components/ld-link/ld-link.tsx @@ -1,9 +1,11 @@ import '../../components' // type definitions for type checks and intelliSense -import { Component, Element, h, Prop, State } from '@stencil/core' +import { Component, Element, h, Method, Prop, State } from '@stencil/core' import { getClassNames } from 'src/liquid/utils/getClassNames' import { cloneAttributes } from '../../utils/cloneAttributes' /** + * @part anchor - the link anchor + * @virtualProp href - the URL that the hyperlink points to * @virtualProp ref - reference to component * @virtualProp {string | number} key - for tracking the node's identity when working with lists */ @@ -13,18 +15,22 @@ import { cloneAttributes } from '../../utils/cloneAttributes' styleUrl: 'ld-link.css', shadow: true, }) -export class LdLink implements ClonesAttributes { +export class LdLink implements ClonesAttributes, InnerFocusable { @Element() el: HTMLElement + private anchor: HTMLAnchorElement private attributesObserver: MutationObserver + /** Displays chevron icon. */ + @Prop() chevron?: 'start' | 'end' + /** * The disabled attribute sets `aria-disabled="true"` * on the rendered anchor element. */ @Prop() disabled?: boolean - /** Displays chevron icon. */ - @Prop() chevron?: 'start' | 'end' + /** Tab index of the input. */ + @Prop() ldTabindex: number | undefined /** * The `target` attributed can be used in conjunction with the `href` attribute. @@ -35,6 +41,12 @@ export class LdLink implements ClonesAttributes { @State() clonedAttributes + /** Sets focus on the anchor. */ + @Method() + async focusInner() { + this.anchor.focus() + } + componentWillLoad() { this.attributesObserver = cloneAttributes.call(this, [ 'iconStart', @@ -77,8 +89,11 @@ export class LdLink implements ClonesAttributes { aria-disabled={ this.disabled || this.el.ariaDisabled ? 'true' : undefined } + ref={(ref) => (this.anchor = ref)} rel={this.target === '_blank' ? 'noreferrer noopener' : undefined} disabled={this.disabled} + part="anchor focusable" + tabIndex={this.ldTabindex} > diff --git a/src/liquid/components/ld-link/readme.md b/src/liquid/components/ld-link/readme.md index 12d371bbaa..69b89534df 100644 --- a/src/liquid/components/ld-link/readme.md +++ b/src/liquid/components/ld-link/readme.md @@ -112,15 +112,50 @@ There are several ways to disable a link. The simplest one is by not using a `hr ## Properties -| Property | Attribute | Description | Type | Default | -| ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ----------- | -| `chevron` | `chevron` | Displays chevron icon. | `"end" \| "start"` | `undefined` | -| `disabled` | `disabled` | The disabled attribute sets `aria-disabled="true"` on the rendered anchor element. | `boolean` | `undefined` | -| `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` | -| `ref` | `ref` | reference to component | `any` | `undefined` | -| `target` | `target` | The `target` attributed can be used in conjunction with the `href` attribute. See [mdn docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target) for more information on the `target` attribute. | `"_blank" \| "_parent" \| "_self" \| "_top"` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ----------- | +| `chevron` | `chevron` | Displays chevron icon. | `"end" \| "start"` | `undefined` | +| `disabled` | `disabled` | The disabled attribute sets `aria-disabled="true"` on the rendered anchor element. | `boolean` | `undefined` | +| `href` | `href` | the URL that the hyperlink points to | `any` | `undefined` | +| `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` | +| `ldTabindex` | `ld-tabindex` | Tab index of the input. | `number` | `undefined` | +| `ref` | `ref` | reference to component | `any` | `undefined` | +| `target` | `target` | The `target` attributed can be used in conjunction with the `href` attribute. See [mdn docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target) for more information on the `target` attribute. | `"_blank" \| "_parent" \| "_self" \| "_top"` | `undefined` | +## Methods + +### `focusInner() => Promise` + +Sets focus on the anchor. + +#### Returns + +Type: `Promise` + + + + +## Shadow Parts + +| Part | Description | +| ---------- | --------------- | +| `"anchor"` | the link anchor | + + +## Dependencies + +### Used by + + - [ld-crumb](../ld-breadcrumbs/ld-crumb) + +### Graph +```mermaid +graph TD; + ld-crumb --> ld-link + style ld-link fill:#f9f,stroke:#333,stroke-width:4px +``` + ---------------------------------------------- diff --git a/src/liquid/components/ld-link/test/__snapshots__/ld-link.spec.tsx.snap b/src/liquid/components/ld-link/test/__snapshots__/ld-link.spec.tsx.snap index 039c67cd82..d3e9400e43 100644 --- a/src/liquid/components/ld-link/test/__snapshots__/ld-link.spec.tsx.snap +++ b/src/liquid/components/ld-link/test/__snapshots__/ld-link.spec.tsx.snap @@ -3,7 +3,7 @@ exports[`ld-link renders as disabled 1`] = ` - + @@ -14,7 +14,7 @@ exports[`ld-link renders as disabled 1`] = ` exports[`ld-link renders default 1`] = ` - + @@ -25,7 +25,7 @@ exports[`ld-link renders default 1`] = ` exports[`ld-link renders with href 1`] = ` - + @@ -36,7 +36,7 @@ exports[`ld-link renders with href 1`] = ` exports[`ld-link renders with target _blank and rel 1`] = ` - + diff --git a/src/liquid/components/ld-link/test/ld-link.spec.tsx b/src/liquid/components/ld-link/test/ld-link.spec.tsx index a230bfaf99..277c01b918 100644 --- a/src/liquid/components/ld-link/test/ld-link.spec.tsx +++ b/src/liquid/components/ld-link/test/ld-link.spec.tsx @@ -35,4 +35,18 @@ describe('ld-link', () => { }) expect(page.root).toMatchSnapshot() }) + + it('allows to set inner focus', async () => { + const page = await newSpecPage({ + components: [LdLink], + html: 'Link', + }) + const ldLink = page.root + const anchor = ldLink.shadowRoot.children[0] as HTMLAnchorElement + + anchor.focus = jest.fn() + await ldLink.focusInner() + + expect(anchor.focus).toHaveBeenCalled() + }) }) diff --git a/src/liquid/components/ld-modal/ld-modal.css b/src/liquid/components/ld-modal/ld-modal.css index 1d8b0c139a..c553016f16 100644 --- a/src/liquid/components/ld-modal/ld-modal.css +++ b/src/liquid/components/ld-modal/ld-modal.css @@ -130,6 +130,7 @@ dialog.ld-modal--blurry-backdrop, } } +/* custom icon cross */ .ld-modal__x { align-self: flex-start; appearance: none; diff --git a/src/liquid/components/ld-notification/ld-notification.tsx b/src/liquid/components/ld-notification/ld-notification.tsx index 78b01cc4a6..65debfbbd7 100644 --- a/src/liquid/components/ld-notification/ld-notification.tsx +++ b/src/liquid/components/ld-notification/ld-notification.tsx @@ -137,6 +137,7 @@ export class LdNotification { } part="btn-dismiss focusable" > + {/* custom icon cross */} + {/* custom icon cross */} (this.btnClearRef = el)} part="btn-clear focusable" > + {/* custom icon cross */} {!this.hasCustomIcon && ( + /* custom icon arrow-down */ (this.btnScrollLeftRef = el)} tabindex="-1" > + {/* custom icon arrow-left */} + {/* custom icon arrow-right */} { const defaultOptions = { rules: {} } + const finalOptions = Object.assign({}, defaultOptions, options) + const disabledRuleIds = [ // TODO: this should be disabled only for certain elements (ld-button), if possible 'aria-allowed-attr', @@ -108,11 +110,9 @@ export const analyzeAccessibility = async (page, options = {}) => { 'region', ] disabledRuleIds.forEach((ruleId) => { - defaultOptions.rules[ruleId] = { enabled: false } + finalOptions.rules[ruleId] = { enabled: false } }) - const finalOptions = Object.assign({}, defaultOptions, options) - // Inject the axe script in our page. await page.addScriptTag({ path: resolvePath(PATH_TO_AXE) }) // Make sure that axe is executed in the next tick after