Skip to content

Commit

Permalink
feat(components): add a post-logo component
Browse files Browse the repository at this point in the history
  • Loading branch information
alizedebray committed Jul 26, 2024
1 parent edf71f7 commit a1e0027
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 10 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.
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
19 changes: 19 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,19 @@
@use '@swisspost/design-system-styles/variables/color';
@use '@swisspost/design-system-styles/mixins/utilities';

post-logo {
display: inline-block;
height: 100%;

.logo,
svg {
display: block;
height: 100%;
aspect-ratio: 1/1;
}

.description {
@include utilities.visuallyhidden;
}
}

62 changes: 62 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,62 @@
import { Component, Host, h, Prop, Watch, Element } from '@stencil/core';
import { checkEmptyOrUrl } from '@/utils';
import { version } from '@root/package.json';

/**
* @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',
})
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);
});
});
});
28 changes: 28 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,28 @@
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

### 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} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { StoryContext, StoryObj } from '@storybook/web-components';
import meta from './logo.stories';
import { html } from 'lit';

const { id, ...metaWithoutId } = meta;

export default {
...metaWithoutId,
title: 'Snapshots',
};

type Story = StoryObj<HTMLPostLogoElement>;

export const PostLogo: Story = {
render: (_args: Partial<HTMLPostLogoElement>, context: StoryContext<HTMLPostLogoElement>) => {
return html`
<div class="row">
<div class="col">
<p>Images</p>
</div>
<div class="col">
<p>Links</p>
</div>
</div>
${['big', 'huge', 'giant'].map(
height => html`
<div class="row h-${height}">
${['', 'https://www.post.ch'].map(url =>
['white', 'dark'].map(
bg => html`
<div class="col p-small-regular bg-${bg}">
${meta.render?.({ ...context.args, url }, context)}
</div>
`,
),
)}
</div>
`,
)}
`;
},
};
46 changes: 46 additions & 0 deletions packages/documentation/src/stories/components/logo/logo.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import { MetaComponent } from '@root/types';
import { ifDefined } from 'lit/directives/if-defined.js';

const meta: MetaComponent<HTMLPostLogoElement> = {
id: '73066e1c-0720-4a9b-8f81-a29d4250872a',
title: 'Components/Post Logo',
tags: ['package:WebComponents'],
component: 'post-logo',
render: renderLogo(),
parameters: {
design: {},
},
argTypes: {
url: {
control: {
type: 'text',
},
},
},
};

export default meta;

// RENDERER
function renderLogo(pageTitle = '[page title]') {
return (args: Partial<HTMLPostLogoElement>) => {
const url = args.url || undefined;
const description = `Logo of the Post${url ? `, To ${pageTitle}` : ''}`;

return html` <post-logo url=${ifDefined(url)}>${description}</post-logo> `;
};
}

// STORIES
type Story = StoryObj;

export const Default: Story = {};

export const Link: Story = {
args: {
url: 'https://www.post.ch/en',
},
render: renderLogo('the homepage'),
};
Loading

0 comments on commit a1e0027

Please sign in to comment.