Skip to content

Commit

Permalink
feat(components): add a post-logo component (#3354)
Browse files Browse the repository at this point in the history
  • Loading branch information
alizedebray authored Aug 12, 2024
1 parent a767c23 commit 1b7d47c
Show file tree
Hide file tree
Showing 21 changed files with 418 additions and 21 deletions.
8 changes: 8 additions & 0 deletions .changeset/swift-geckos-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@swisspost/design-system-documentation': minor
'@swisspost/design-system-components': minor
'@swisspost/design-system-components-angular': minor
'@swisspost/design-system-components-react': minor
---

Added the post-logo component, which enables displaying the Post's logo either as a clickable link or as a simple image.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ <h2>Post Icon</h2>
<post-icon name="1001"></post-icon>
</div>

<div class="my-4">
<h2>Post Logo</h2>
<post-logo url="https://www.post.ch/en">Logo of the Post, To the homepage</post-logo>
</div>

<div class="my-4">
<h2>Post Popover</h2>
<button class="btn btn-secondary btn-large" data-popover-target="popover-one">
Expand Down
72 changes: 72 additions & 0 deletions packages/components/cypress/e2e/logo.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const LOGO_ID = '73066e1c-0720-4a9b-8f81-a29d4250872a';

describe('logo', () => {
describe('default', () => {
beforeEach(() => {
cy.getComponent('logo', LOGO_ID);
});

it('should render', () => {
cy.get('@logo').should('exist');
});

it('should not be a link', () => {
cy.get('@logo').shadow().find('a').should('not.exist');
});

it('should contain text', () => {
cy.get('@logo').invoke('text').should('not.be.empty');
});

it('should fit the parent height', () => {
cy.get('@logo')
.parent()
.invoke('innerHeight')
.then(parentHeight => {
cy.get('@logo').invoke('outerHeight').should('eq', parentHeight);
});
});
});

describe('link', () => {
beforeEach(() => {
cy.getComponent('logo', LOGO_ID, 'link');
});

it('should be a link', () => {
cy.get('@logo').shadow().find('a').as('link');
cy.get('@link').should('exist');
cy.get('@link').invoke('attr', 'href').should('not.be.empty');
});

it('should contain text', () => {
cy.get('@logo').invoke('text').should('not.be.empty');
});
});

describe('custom height', () => {
beforeEach(() => {
cy.getComponent('logo', LOGO_ID, 'height');
});

it('should not fit the parent height', () => {
cy.get('@logo')
.parent()
.invoke('innerHeight')
.then(parentHeight => {
cy.get('@logo').invoke('outerHeight').should('not.eq', parentHeight);
});
});

it('should have its own height', () => {
cy.get('@logo').invoke('css', 'height').should('not.be.undefined');
});
});
});

describe('Accessibility', () => {
it('Has no detectable a11y violations on load for all variants', () => {
cy.getSnapshots('post-logo');
cy.checkA11y('#root-inner');
});
});
21 changes: 21 additions & 0 deletions packages/components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ export namespace Components {
*/
"scale"?: number | null;
}
interface PostLogo {
/**
* The URL to which the user is redirected upon clicking the logo.
*/
"url": string | URL;
}
interface PostPopover {
/**
* Show a little indicator arrow
Expand Down Expand Up @@ -422,6 +428,12 @@ declare global {
prototype: HTMLPostIconElement;
new (): HTMLPostIconElement;
};
interface HTMLPostLogoElement extends Components.PostLogo, HTMLStencilElement {
}
var HTMLPostLogoElement: {
prototype: HTMLPostLogoElement;
new (): HTMLPostLogoElement;
};
interface HTMLPostPopoverElement extends Components.PostPopover, HTMLStencilElement {
}
var HTMLPostPopoverElement: {
Expand Down Expand Up @@ -512,6 +524,7 @@ declare global {
"post-collapsible": HTMLPostCollapsibleElement;
"post-collapsible-trigger": HTMLPostCollapsibleTriggerElement;
"post-icon": HTMLPostIconElement;
"post-logo": HTMLPostLogoElement;
"post-popover": HTMLPostPopoverElement;
"post-popovercontainer": HTMLPostPopovercontainerElement;
"post-rating": HTMLPostRatingElement;
Expand Down Expand Up @@ -668,6 +681,12 @@ declare namespace LocalJSX {
*/
"scale"?: number | null;
}
interface PostLogo {
/**
* The URL to which the user is redirected upon clicking the logo.
*/
"url"?: string | URL;
}
interface PostPopover {
/**
* Show a little indicator arrow
Expand Down Expand Up @@ -780,6 +799,7 @@ declare namespace LocalJSX {
"post-collapsible": PostCollapsible;
"post-collapsible-trigger": PostCollapsibleTrigger;
"post-icon": PostIcon;
"post-logo": PostLogo;
"post-popover": PostPopover;
"post-popovercontainer": PostPopovercontainer;
"post-rating": PostRating;
Expand Down Expand Up @@ -807,6 +827,7 @@ declare module "@stencil/core" {
* @class PostIcon - representing a stencil component
*/
"post-icon": LocalJSX.PostIcon & JSXBase.HTMLAttributes<HTMLPostIconElement>;
"post-logo": LocalJSX.PostLogo & JSXBase.HTMLAttributes<HTMLPostLogoElement>;
"post-popover": LocalJSX.PostPopover & JSXBase.HTMLAttributes<HTMLPostPopoverElement>;
"post-popovercontainer": LocalJSX.PostPopovercontainer & JSXBase.HTMLAttributes<HTMLPostPopovercontainerElement>;
"post-rating": LocalJSX.PostRating & JSXBase.HTMLAttributes<HTMLPostRatingElement>;
Expand Down
17 changes: 17 additions & 0 deletions packages/components/src/components/post-logo/post-logo.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@use '@swisspost/design-system-styles/variables/color';
@use '@swisspost/design-system-styles/mixins/utilities';

:host,
.logo {
display: inline-block;

&,
.logo > svg {
height: 100%;
}
}

.description {
@include utilities.visuallyhidden;
}

63 changes: 63 additions & 0 deletions packages/components/src/components/post-logo/post-logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Component, Element, h, Host, Prop, Watch } from '@stencil/core';
import { version } from '@root/package.json';
import { checkEmptyOrUrl } from '@/utils';

/**
* @slot default - Slot for placing hidden descriptive text. If `url` is set, this text will serve as the accessible name of the link; otherwise, it will be used as the title of the SVG.
*/
@Component({
tag: 'post-logo',
styleUrl: 'post-logo.scss',
shadow: true,
})
export class PostLogo {
@Element() host: HTMLPostLogoElement;

/**
* The URL to which the user is redirected upon clicking the logo.
*/
@Prop() url: string | URL;

@Watch('url')
validateUrl() {
checkEmptyOrUrl(this.url, 'The "url" property of the post-logo is invalid');
}

connectedCallback() {
this.validateUrl();
this.checkDescription();
}

private checkDescription() {
if (!this.host.textContent) {
console.warn(
'Be sure to add descriptive text in the post-logo to ensure good accessibility of the component.',
);
}
}

render() {
const logoLink = this.url && (typeof this.url === 'string' ? this.url : this.url.href);
const LogoTag = logoLink ? 'a' : 'span';

return (
<Host data-version={version}>
<LogoTag class="logo" {...(logoLink ? { href: logoLink } : {})}>
<span class="description">
<slot onSlotchange={() => this.checkDescription()}></slot>
</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" aria-hidden="true">
<g id="Logo">
<rect fill="#ffcc00" x="0" y="0" width="72" height="72" />
<polygon
fill="#ff0000"
points="34,32.3 34,19 19.7,19 19.7,29.1 10,29.1 10,42.9 19.7,42.9 19.7,53 34,53 34,39.7 30.6,39.7 30.6,49.8 23.1,49.8 23.1,39.7 13.4,39.7 13.4,32.3 23.1,32.3 23.1,22.2 30.6,22.2 30.6,32.3"
/>
<path d="M53.56234,31.10526c0,2.41272-1.99154,4.29475-4.51723,4.29475H45.2v-8.3h3.84511C51.66802,27.1,53.56234,28.78889,53.56234,31.10526z M50.69666,19H36v34h9.2V42.9h5.49666c6.75131,0,11.9971-5.15137,11.9971-11.8057C62.69376,24.39136,57.35099,19,50.69666,19z" />
</g>
</svg>
</LogoTag>
</Host>
);
}
}
24 changes: 24 additions & 0 deletions packages/components/src/components/post-logo/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# post-logo



<!-- Auto Generated Below -->


## Properties

| Property | Attribute | Description | Type | Default |
| -------- | --------- | --------------------------------------------------------------- | --------------- | ----------- |
| `url` | `url` | The URL to which the user is redirected upon clicking the logo. | `URL \| string` | `undefined` |


## Slots

| Slot | Description |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"default"` | Slot for placing hidden descriptive text. If `url` is set, this text will serve as the accessible name of the link; otherwise, it will be used as the title of the SVG. |


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

*Built with [StencilJS](https://stenciljs.com/)*
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { PostCardControl } from './components/post-card-control/post-card-contro
export { PostCollapsible } from './components/post-collapsible/post-collapsible';
export { PostCollapsibleTrigger } from './components/post-collapsible-trigger/post-collapsible-trigger';
export { PostIcon } from './components/post-icon/post-icon';
export { PostLogo } from './components/post-logo/post-logo';
export { PostPopover } from './components/post-popover/post-popover';
export { PostPopovercontainer } from './components/post-popovercontainer/post-popovercontainer';
export { PostRating } from './components/post-rating/post-rating';
Expand Down
9 changes: 9 additions & 0 deletions packages/components/src/utils/property-checkers/check-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function checkUrl(value: unknown, error: string) {
if (typeof value !== 'string' && !(value instanceof URL)) throw new Error(error);

try {
new URL(value);
} catch (e) {
throw new Error(error);
}
}
3 changes: 3 additions & 0 deletions packages/components/src/utils/property-checkers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { emptyOr } from './empty-or';
import { checkOneOf } from './check-one-of';
import { checkPattern } from './check-pattern';
import { checkType } from './check-type';
import { checkUrl } from './check-url';

export const checkEmptyOrOneOf = emptyOr(checkOneOf);
export const checkEmptyOrPattern = emptyOr(checkPattern);
export const checkEmptyOrType = emptyOr(checkType);
export const checkEmptyOrUrl = emptyOr(checkUrl);

export * from './check-non-empty';
export * from './check-one-of';
export * from './check-pattern';
export * from './check-type';
export * from './check-url';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { checkUrl } from '../check-url';

describe('checkUrl', () => {
const errorMessage = 'Invalid URL';

test('should not throw an error if the value is an URL string or an URL object', () => {
['https://www.example.com', new URL('https://www.example.com')].forEach(validUrl => {
expect(() => checkUrl(validUrl, errorMessage)).not.toThrow();
});
});

test('should throw an error if the value is not an URL string or an URL object', () => {
[
'',
'invalid-url',
123,
true,
null,
undefined,
['https://www.example.com'],
{ url: 'https://www.example.com' },
() => 'https://www.example.com',
].forEach(invalidUrl => {
expect(() => checkUrl(invalidUrl, errorMessage)).toThrow(errorMessage);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
describe('Logo', () => {
it('default', () => {
cy.visit('/iframe.html?id=snapshots--post-logo');
cy.get('post-logo.hydrated', { timeout: 30000 }).should('be.visible');
cy.percySnapshot('Logos', { widths: [1440] });
});
});
36 changes: 36 additions & 0 deletions packages/documentation/src/stories/components/logo/logo.docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Canvas, Controls, Meta } from '@storybook/blocks';
import * as logoStories from './logo.stories';

<Meta of={logoStories} />

# Post Logo

<p className="lead">Display the Post logo as a clickable link or as a simple image.</p>

<Canvas sourceState="shown" of={logoStories.Default} />
<div className="hide-col-default">
<Controls of={logoStories.Default} />
</div>

## Installation

The `<post-logo>` element is part of the `@swisspost/design-system-components` package.
For more information, read the [getting started with components guide](/?path=/docs/edfb619b-fda1-4570-bf25-20830303d483--docs).


## Examples

### Height

By default, the logo will be 100% of the height of its parent.
You can also set the height on the logo using one of the <a href="/?path=/docs/e728de1f-0d71-4317-8bb8-cbef0bf8d5db--docs">sizing utility classes</a>.

<Canvas of={logoStories.Height} />

### Link

You can use the logo as a link by setting its `url` property.
Ensure that the descriptive text clearly indicates the destination the user will be redirected to after clicking the logo.

<Canvas of={logoStories.Link} />

Loading

0 comments on commit 1b7d47c

Please sign in to comment.