Skip to content

Commit

Permalink
feat(styles, docs): update badges (#2860)
Browse files Browse the repository at this point in the history
  • Loading branch information
alizedebray authored Apr 5, 2024
1 parent 407751e commit 4bcc9f7
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 303 deletions.
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.
10 changes: 10 additions & 0 deletions packages/documentation/src/shared/decorators/dark-background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StoryContext, StoryFn } from '@storybook/web-components';
import { html } from 'lit';

export function coloredBackground(story: StoryFn, context: StoryContext, color: string) {
return html`
<div class="bg-${color}" style="margin: -40px -30px; padding: 40px 30px;">
${story(context.args, context)}
</div>
`;
}
37 changes: 16 additions & 21 deletions packages/documentation/src/stories/components/badge/badge.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,9 @@ import StylesPackageImport from '../../../shared/styles-package-import.mdx';
# Badge

<div className="lead">
Documentation and examples for badges, our small count and labeling component.
Highlight a numerical characteristic or mark an item with a status.
</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.
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.

<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
241 changes: 75 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,41 @@
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';
import { coloredBackground } from '../../../shared/decorators/dark-background';

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 +44,51 @@ 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, context: StoryContext) {
const { args } = context;
const isLight = ['bg-white', 'bg-light', 'bg-gray'].includes(args.background as string);
return isLight ? coloredBackground(story, context, 'dark') : story(args, context);
}

// 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 +97,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-info' })}
${renderBadge({ ...args, background: 'bg-success' })}
${renderBadge({ ...args, background: 'bg-warning' })}
${renderBadge({ ...args, background: 'bg-yellow' })}
`,
};

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
<div class="badge bg-gray">1</div>
</div>
<div class="position-relative d-inline">
<post-icon name="2026" class="fs-large"></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>
`,
],
};
Loading

0 comments on commit 4bcc9f7

Please sign in to comment.