Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(styles, docs): update badges #2860

Merged
merged 17 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/dry-wasps-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@swisspost/design-system-styles': major
'@swisspost/design-system-documentation': patch
---

Restricted the badge to showing counts only and updated the styles accordingly.
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,6 @@ import StylesPackageImport from '../../../shared/styles-package-import.mdx';
Documentation and examples for badges, our small count and labeling component.
</div>

<PostAlert type="warning">
<p slot="heading">Badge is deprecated</p>
<p>
The current `badge` component is deprecated and will be replaced with the `chip` component which will behave similarly to the current `badge` component.
There will also be a new `badge` component, but with a different behavior. Check out the new components in figma:
[Chip](https://www.figma.com/file/xZ0IW0MJO0vnFicmrHiKaY/Components-Post?type=design&node-id=11194-50777&mode=design&t=SeiMf2Vt3dmUnpMT-0)
/
[Badge](https://www.figma.com/file/xZ0IW0MJO0vnFicmrHiKaY/Components-Post?type=design&node-id=11038-51303&mode=design&t=SeiMf2Vt3dmUnpMT-0)
</p>
</PostAlert>

<Canvas sourceState="shown" of={badgeStories.Default} />
<div className="hide-col-default">
<Controls of={badgeStories.Default} />
Expand All @@ -31,17 +20,23 @@ import StylesPackageImport from '../../../shared/styles-package-import.mdx';

## Examples

### Checkable
### Colors

You can change the badge color simply by applying a `.bg-*` class to it.
See all available classes in the [background utilities documentation](/?path=/docs/60852fac-a861-4415-8276-bd38d68653bb--docs).

<Canvas of={badgeStories.Colors} />

### Large number

Checkable badges are nothing more than personalized checkboxes.
Therefore, you can handle then like standard checkboxes.
A large number will extend the badge with.
alizedebray marked this conversation as resolved.
Show resolved Hide resolved
To prevent the badge from being too large you can simply use the `+` notation as shown hereafter.

<Canvas of={badgeStories.Checkable} />
<Canvas of={badgeStories.LargeNumber} />

### Dismissible
### Position

Dismissible badges include a close button, allowing users to easily clear them from the page.
Because the close button does not have a visible text,
it should always include a visually hidden label to ensure accessibility for assistive technology users.
As inline elements, the badges can be placed inside other element such as tags.
They can also be place absolutely to appear above other elements such as icons for example.
alizedebray marked this conversation as resolved.
Show resolved Hide resolved

<Canvas of={badgeStories.Dismissible} />
<Canvas of={badgeStories.Position} />
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,11 @@ export const Badge: Story = {
bg => html`
<div class="${bg} d-flex flex-wrap align-items-start gap-regular p-regular">
${bombArgs({
text: [
'Malakceptebla Insigno',
'Contentus momentus vero siteos et accusam iretea et justo.',
],
showNumber: [true, false],
size: context.argTypes.size.options,
interactionType: context.argTypes.interactionType.options,
nestedBadge: [false, true],
checked: [false, true],
dismissed: [false],
background: context.argTypes.background.options,
})
.filter(args => !(args.interactionType !== 'checkable' && args.checked === true))
.filter(args => !(!args.showNumber && args.size === 'small'))
.map((args: Args) => meta.render?.({ ...context.args, ...args }, context))}
</div>
`,
Expand Down
239 changes: 73 additions & 166 deletions packages/documentation/src/stories/components/badge/badge.stories.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
import { useArgs } from '@storybook/preview-api';
import type { Args, StoryContext, StoryObj } from '@storybook/web-components';
import { Args, StoryContext, StoryFn, StoryObj } from '@storybook/web-components';
import { html, nothing } from 'lit';
import { mapClasses } from '../../../utils';
import { MetaComponent } from '../../../../types';
import backgroundColors from '../../../shared/background-colors.module.scss';

const meta: MetaComponent = {
id: 'bec68e8b-445e-4760-8bd7-1b9970206d8d',
title: 'Components/Badge',
tags: ['package:HTML'],
render: renderBadge,
decorators: [externalControl],
parameters: {
badges: [],
},
decorators: [adaptiveBackground],
args: {
text: 'Insigno',
size: 'default',
nestedBadge: false,
interactionType: 'none',
checked: false,
dismissed: false,
showNumber: true,
number: 1,
size: 'large',
},
argTypes: {
text: {
name: 'Text',
description: 'The text contained in the badge.',
showNumber: {
name: 'Show Number',
description: 'If `true`, the badge contains a number otherwise it is empty.',
control: {
type: 'boolean',
},
table: {
category: 'Content',
},
},
number: {
name: 'Number',
description: 'The number contained in the badge.',
if: {
arg: 'showNumber',
truthy: true,
},
control: {
type: 'text',
type: 'number',
min: 0,
},
table: {
category: 'Content',
Expand All @@ -35,161 +43,50 @@ const meta: MetaComponent = {
size: {
name: 'Size',
description: 'The size of the badge.',
if: {
arg: 'showNumber',
truthy: true,
},
control: {
type: 'radio',
labels: {
'default': 'Default',
'large': 'Large',
'badge-sm': 'Small',
},
},
options: ['default', 'badge-sm'],
options: ['large', 'badge-sm'],
table: {
category: 'General',
},
},
nestedBadge: {
name: 'Nested Badge',
description: 'If `true`, a nested badge is displayed inside the main badge.',
background: {
name: 'Backround',
description: 'You can use the Background classes to color the cards',
control: {
type: 'boolean',
type: 'select',
},
options: Object.keys(backgroundColors),
table: {
category: 'General',
},
},
interactionType: {
name: 'Interaction Type',
description: 'Defines how the badge can be interacted with.',
control: {
type: 'inline-radio',
labels: {
none: 'None',
checkable: 'Checkable',
dismissible: 'Dismissible',
},
},
options: ['none', 'checkable', 'dismissible'],
table: {
category: 'Interactions',
},
},
checked: {
name: 'Checked',
description: 'If `true`, the badge is checked otherwise it is unchecked.',
if: {
arg: 'interactionType',
eq: 'checkable',
},
control: {
type: 'boolean',
},
table: {
category: 'Interactions',
},
},
dismissed: {
name: 'Dismissed',
description: 'If `true`, the badge is removed from the page otherwise it is displayed.',
if: {
arg: 'interactionType',
eq: 'dismissible',
},
control: {
type: 'boolean',
},
table: {
category: 'Interactions',
},
},
},
};

export default meta;

// DECORATORS
function externalControl(story: any, { args }: StoryContext) {
const [_, updateArgs] = useArgs();

const button = html`
<a
href="#"
@click="${(e: Event) => {
e.preventDefault();
updateArgs({ dismissed: false });
}}"
>
Show badge
</a>
`;

return html` ${args.dismissed ? button : nothing} ${story()} `;
function adaptiveBackground(story: StoryFn, { args, context }: StoryContext) {
const bgClass = args.background === 'bg-white' ? `p-2 bg-dark` : 'p-2';
return html`<div class=${bgClass}>${story(args, context)}</div>`;
alizedebray marked this conversation as resolved.
Show resolved Hide resolved
}

// RENDERER
function getDefaultContent(args: Args) {
return html`
<span>${args.text}</span>
${args.nestedBadge ? html` <span class="badge">10</span> ` : nothing}
`;
}

function getCheckableContent(args: Args, updateArgs: (args: Args) => void, context: StoryContext) {
const checkboxId = `badge-example--${context.name.replace(/ /g, '-').toLowerCase()}`;
const labelClasses = mapClasses({
'badge-check-label': true,
[args.size]: args.size !== 'default',
});

const handleChange = (e: Event) => {
updateArgs({ checked: !args.checked });

if (document.activeElement === e.target) {
setTimeout(() => {
const element: HTMLInputElement | null = document.querySelector(`#${checkboxId}`);
if (element) element.focus();
}, 25);
}
};

return html`
<input
id="${checkboxId}"
class="badge-check-input"
type="checkbox"
?checked="${args.checked}"
@change="${handleChange}"
/>
<label class="${labelClasses}" for="${checkboxId}">${getDefaultContent(args)}</label>
`;
}

function getDismissButton(updateArgs: (args: Args) => void) {
return html`
<button class="btn-close" @click="${() => updateArgs({ dismissed: true })}">
<span class="visually-hidden">Forigi insignon</span>
</button>
`;
}

function renderBadge(args: Args, context: StoryContext) {
const [_, updateArgs] = useArgs();

if (args.dismissed) return html` ${nothing} `;

const isCheckable = args.interactionType === 'checkable';
const isDismissible = args.interactionType === 'dismissible';

const badgeClasses = mapClasses({
'badge': !isCheckable,
'badge-check': isCheckable,
[args.size]: args.size !== 'default' && !isCheckable,
});

function renderBadge(args: Args) {
const sizingClass = args.showNumber && args.size !== 'large' ? ` ${args.size}` : '';
const bgClass = args.background && args.background !== 'bg-danger' ? ` ${args.background}` : '';
return html`
<div class="${badgeClasses}">
${isCheckable ? getCheckableContent(args, updateArgs, context) : getDefaultContent(args)}
${isDismissible ? getDismissButton(updateArgs) : nothing}
</div>
<div class=${`badge${sizingClass}${bgClass}`}>${args.showNumber ? args.number : nothing}</div>
`;
}

Expand All @@ -198,26 +95,36 @@ type Story = StoryObj;

export const Default: Story = {};

export const Checkable: Story = {
parameters: {
controls: {
exclude: ['Interaction Type'],
},
},
args: {
text: 'Kontrolebla Insigno',
interactionType: 'checkable',
},
export const Colors: Story = {
render: args => html`
${renderBadge({ ...args, background: 'bg-success' })}
${renderBadge({ ...args, background: 'bg-warning' })}
${renderBadge({ ...args, background: 'bg-yellow' })}
${renderBadge({ ...args, background: 'bg-light' })}
`,
};

export const Dismissible: Story = {
parameters: {
controls: {
exclude: ['Interaction Type'],
},
},
args: {
text: 'Malakceptebla Insigno',
interactionType: 'dismissible',
},
export const LargeNumber: Story = {
render: args => html`
${renderBadge({ ...args, number: 256 })} ${renderBadge({ ...args, number: '+99' })}
`,
};

export const Position: Story = {
render: args => html`
<div class="chip">
Filter
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chip example is not working currently, this will be solved by the chip PR: #2855

<div class="badge bg-gray">1</div>
</div>

<div class="position-relative d-inline">
<post-icon name="2026" scale="1.5"></post-icon>
<div class="badge badge-sm position-absolute top-0 start-100 translate-middle">3</div>
</div>
`,
decorators: [
(story: StoryFn, { args, context }: StoryContext) => html`
<div class="d-flex gap-large">${story(args, context)}</div>
`,
],
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Args, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import { useArgs } from '@storybook/preview-api';
import scss from './card-button.module.scss';
import { MetaComponent } from '../../../../../types';
import backgroundColors from '../../../../shared/background-colors.module.scss';

const meta: MetaComponent = {
id: '6f8f76ec-a2b5-4eb0-87f7-4021e1a5b8d0',
Expand Down Expand Up @@ -64,7 +64,7 @@ const meta: MetaComponent = {
control: {
type: 'select',
},
options: Object.keys(scss),
options: Object.keys(backgroundColors),
table: {
category: 'General',
},
Expand Down
Loading
Loading