Skip to content

Commit

Permalink
feat(components): create post-list component (#3812)
Browse files Browse the repository at this point in the history
Co-authored-by: Oliver Schürch <[email protected]>
Co-authored-by: Alizé Debray <[email protected]>
  • Loading branch information
3 people authored Nov 7, 2024
1 parent ab8136b commit c5bf1de
Show file tree
Hide file tree
Showing 16 changed files with 582 additions and 212 deletions.
6 changes: 6 additions & 0 deletions .changeset/fast-bats-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@swisspost/design-system-documentation': minor
'@swisspost/design-system-components': minor
---

Created the `post-list` and `post-list-item` components.
60 changes: 60 additions & 0 deletions packages/components/cypress/e2e/list.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
describe('PostList Component', { baseUrl: null, includeShadowDom: false }, () => {
beforeEach(() => {
// Visit the page where the component is rendered
cy.visit('./cypress/fixtures/post-list.test.html');
});

it('should render the post-list component', () => {
// Check if the post-list component is rendered
cy.get('post-list').should('exist');
});

it('should have an id for the first div in post-list', () => {
// Ensure the first div inside post-list has an id attribute
cy.get('post-list')
.find('div')
.first()
.should('have.attr', 'id')
.and('not.be.empty')
.then($div => {
const id = $div['id'];
cy.log(`First div ID: ${id}`);
});
});

it('should throw an error if the title is missing', () => {
// Check for the mandatory title accessibility error if no title is provided
cy.on('uncaught:exception', err => {
expect(err.message).to.include(
'Please provide a title to the list component. Title is mandatory for accessibility purposes.',
);
return false;
});
cy.get('post-list').within(() => {
cy.get('[slot="post-list-item"]').first().invoke('remove');
});
});

it('should hide the title when title-hidden is set', () => {
// Set the `title-hidden` property and check if the title is hidden
cy.get('post-list').invoke('attr', 'title-hidden', true);
cy.get('post-list div').first().should('have.class', 'visually-hidden');
});

it('should render horizontally when the horizontal attribute is set', () => {
// Set the `horizontal` property and verify if the list has a horizontal layout
cy.get('post-list').invoke('attr', 'horizontal', true);
cy.get('post-list').should('have.attr', 'horizontal', 'true');
});

it('should ensure post-list-item components have the correct slot and role', () => {
// Verify that the `post-list-item` components have the correct slot and role attributes
cy.get('post-list').within(() => {
cy.get('post-list-item').each($el => {
cy.wrap($el)
.should('have.attr', 'slot', 'post-list-item')
.and('have.attr', 'role', 'listitem');
});
});
});
});
19 changes: 19 additions & 0 deletions packages/components/cypress/fixtures/post-list.test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="../../dist/post-components/post-components.esm.js" type="module"></script>
</head>
<body>
<post-list>
<h3>TITLE</h3>
<post-list-item><post-tag>Title 1</post-tag></post-list-item>
<post-list-item><post-tag>Title 2</post-tag></post-list-item>
<post-list-item><post-tag>Title 3</post-tag></post-list-item>
<post-list-item><post-tag>Title 4</post-tag></post-list-item>
<post-list-item><post-tag>Title 5</post-tag></post-list-item>
</post-list>
</body>
</html>
42 changes: 42 additions & 0 deletions packages/components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,18 @@ export namespace Components {
*/
"url": string;
}
interface PostList {
/**
* The list can become horizontal by setting `horizontal="true"` or just `horizontal`
*/
"horizontal": boolean;
/**
* If `true`, the list title will be hidden. Otherwise, it will be displayed.`
*/
"titleHidden": boolean;
}
interface PostListItem {
}
interface PostLogo {
/**
* The URL to which the user is redirected upon clicking the logo.
Expand Down Expand Up @@ -500,6 +512,18 @@ declare global {
prototype: HTMLPostLanguageOptionElement;
new (): HTMLPostLanguageOptionElement;
};
interface HTMLPostListElement extends Components.PostList, HTMLStencilElement {
}
var HTMLPostListElement: {
prototype: HTMLPostListElement;
new (): HTMLPostListElement;
};
interface HTMLPostListItemElement extends Components.PostListItem, HTMLStencilElement {
}
var HTMLPostListItemElement: {
prototype: HTMLPostListItemElement;
new (): HTMLPostListItemElement;
};
interface HTMLPostLogoElement extends Components.PostLogo, HTMLStencilElement {
}
var HTMLPostLogoElement: {
Expand Down Expand Up @@ -598,6 +622,8 @@ declare global {
"post-collapsible-trigger": HTMLPostCollapsibleTriggerElement;
"post-icon": HTMLPostIconElement;
"post-language-option": HTMLPostLanguageOptionElement;
"post-list": HTMLPostListElement;
"post-list-item": HTMLPostListItemElement;
"post-logo": HTMLPostLogoElement;
"post-popover": HTMLPostPopoverElement;
"post-popovercontainer": HTMLPostPopovercontainerElement;
Expand Down Expand Up @@ -795,6 +821,18 @@ declare namespace LocalJSX {
*/
"url"?: string;
}
interface PostList {
/**
* The list can become horizontal by setting `horizontal="true"` or just `horizontal`
*/
"horizontal"?: boolean;
/**
* If `true`, the list title will be hidden. Otherwise, it will be displayed.`
*/
"titleHidden"?: boolean;
}
interface PostListItem {
}
interface PostLogo {
/**
* The URL to which the user is redirected upon clicking the logo.
Expand Down Expand Up @@ -919,6 +957,8 @@ declare namespace LocalJSX {
"post-collapsible-trigger": PostCollapsibleTrigger;
"post-icon": PostIcon;
"post-language-option": PostLanguageOption;
"post-list": PostList;
"post-list-item": PostListItem;
"post-logo": PostLogo;
"post-popover": PostPopover;
"post-popovercontainer": PostPopovercontainer;
Expand Down Expand Up @@ -949,6 +989,8 @@ declare module "@stencil/core" {
*/
"post-icon": LocalJSX.PostIcon & JSXBase.HTMLAttributes<HTMLPostIconElement>;
"post-language-option": LocalJSX.PostLanguageOption & JSXBase.HTMLAttributes<HTMLPostLanguageOptionElement>;
"post-list": LocalJSX.PostList & JSXBase.HTMLAttributes<HTMLPostListElement>;
"post-list-item": LocalJSX.PostListItem & JSXBase.HTMLAttributes<HTMLPostListItemElement>;
"post-logo": LocalJSX.PostLogo & JSXBase.HTMLAttributes<HTMLPostLogoElement>;
"post-popover": LocalJSX.PostPopover & JSXBase.HTMLAttributes<HTMLPostPopoverElement>;
"post-popovercontainer": LocalJSX.PostPopovercontainer & JSXBase.HTMLAttributes<HTMLPostPopovercontainerElement>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
display: flex;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Component, Element, Host, h } from '@stencil/core';

/**
* @slot default- Slot for placing the content of the list item.
*/

@Component({
tag: 'post-list-item',
styleUrl: 'post-list-item.scss',
shadow: true,
})
export class PostListItem {
@Element() host: HTMLPostListItemElement;

connectedCallback() {
this.host.setAttribute('slot', 'post-list-item');
}

render() {
return (
<Host role="listitem">
<slot></slot>
</Host>
);
}
}
17 changes: 17 additions & 0 deletions packages/components/src/components/post-list-item/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# post-list-item



<!-- Auto Generated Below -->


## Slots

| Slot | Description |
| ----------------------------------------------------------- | ----------- |
| `"default- Slot for placing the content of the list item."` | |


----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
27 changes: 27 additions & 0 deletions packages/components/src/components/post-list/post-list.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@use '@swisspost/design-system-styles/core' as post;

post-list {
display: flex;
gap: var(--post-list-title-gap);
flex-direction: column;
align-items: flex-start;

& > div[role='list'] {
flex-direction: column;
display: flex;
gap: var(--post-list-item-gap);
}

&[horizontal]:not([horizontal='false']) {
flex-direction: row;
align-items: baseline;
& > div[role='list'] {
flex-direction: row;
align-items: center;
}
}

> .list-title.visually-hidden {
@include post.visually-hidden();
}
}
69 changes: 69 additions & 0 deletions packages/components/src/components/post-list/post-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Component, Element, Prop, Host, State, h } from '@stencil/core';
import { version } from '@root/package.json';

/**
* @slot default - Slot for placing the list title.
* @slot post-list-item - Slot for placing post-list-item components.
*/

@Component({
tag: 'post-list',
styleUrl: 'post-list.scss',
shadow: false,
})
export class PostList {
@Element() host: HTMLPostListElement;

/**
* The unique title of the list that is also referenced in the labelledby
*/
@State() titleId: string;

/**
* If `true`, the list title will be hidden. Otherwise, it will be displayed.`
*/
@Prop() readonly titleHidden: boolean = false;

/**
* The list can become horizontal by setting `horizontal="true"` or just `horizontal`
*/
@Prop() readonly horizontal: boolean = false;

titleEl: HTMLElement;

componentWillLoad() {
/**
* Get the id set on the host element or use a random id by default
*/
this.titleId = `list-${crypto.randomUUID()}`;
}

componentDidLoad() {
this.checkTitle();
}

private checkTitle() {
if (!this.titleEl.innerText) {
throw new Error(
'Please provide a title to the list component. Title is mandatory for accessibility purposes.',
);
}
}

render() {
return (
<Host data-version={version}>
<div
ref={el => (this.titleEl = el)}
id={this.titleId}
class={`list-title${this.titleHidden ? ' visually-hidden' : ''}`}
>
<slot></slot>
</div>
<div role="list" aria-labelledby={this.titleId}>
<slot name="post-list-item"></slot>
</div>
</Host>
);
}
}
26 changes: 26 additions & 0 deletions packages/components/src/components/post-list/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# post-list



<!-- Auto Generated Below -->


## Properties

| Property | Attribute | Description | Type | Default |
| ------------- | -------------- | ---------------------------------------------------------------------------------- | --------- | ------- |
| `horizontal` | `horizontal` | The list can become horizontal by setting `horizontal="true"` or just `horizontal` | `boolean` | `false` |
| `titleHidden` | `title-hidden` | If `true`, the list title will be hidden. Otherwise, it will be displayed.` | `boolean` | `false` |


## Slots

| Slot | Description |
| ------------------ | ------------------------------------------- |
| `"default"` | Slot for placing the list title. |
| `"post-list-item"` | Slot for placing post-list-item components. |


----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
2 changes: 2 additions & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ export { PostTabHeader } from './components/post-tab-header/post-tab-header';
export { PostTabPanel } from './components/post-tab-panel/post-tab-panel';
export { PostTooltip } from './components/post-tooltip/post-tooltip';
export { PostTag } from './components/post-tag/post-tag';
export { PostList } from './components/post-list/post-list';
export { PostListItem } from './components/post-list-item/post-list-item';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
describe('List', () => {
it('default', () => {
cy.visit('/iframe.html?id=snapshots--post-list');
cy.get('list-example', { timeout: 30000 }).should('be.visible');
cy.percySnapshot('List', { widths: [1440] });
});
});
Loading

0 comments on commit c5bf1de

Please sign in to comment.