Skip to content

Commit

Permalink
feat(sbb-status): component implementation (#2262)
Browse files Browse the repository at this point in the history
Co-authored-by: Davide Mininni <[email protected]>
Co-authored-by: Jeremias Peier <[email protected]>
  • Loading branch information
3 people authored Dec 25, 2023
1 parent 6e927de commit 8e1da55
Show file tree
Hide file tree
Showing 7 changed files with 425 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/components/status/__snapshots__/status.spec.snap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* @web/test-runner snapshot v1 */
export const snapshots = {};

snapshots["sbb-status renders"] =
`<div class="sbb-status">
<sbb-icon
aria-hidden="true"
class="sbb-status__icon"
data-namespace="default"
name="circle-information-small"
role="img"
>
</sbb-icon>
<span class="sbb-status__content">
<p class="sbb-status__content-slot">
<slot>
</slot>
</p>
</span>
</div>
`;
/* end snapshot sbb-status renders */

snapshots["sbb-status renders with the status title"] =
`<div class="sbb-status">
<sbb-icon
aria-hidden="true"
class="sbb-status__icon"
data-namespace="default"
name="circle-information-small"
role="img"
>
</sbb-icon>
<span class="sbb-status__content">
<sbb-title
aria-level="3"
class="sbb-status__title"
level="3"
role="heading"
visual-level="5"
>
<slot name="title">
Title
</slot>
</sbb-title>
<p class="sbb-status__content-slot">
<slot>
</slot>
</p>
</span>
</div>
`;
/* end snapshot sbb-status renders with the status title */

58 changes: 58 additions & 0 deletions src/components/status/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
The `sbb-status` is a component that has the purpose to show the user short messages to update the current status.
The status element displays a brief text message preceded by the status icon.

The `sbb-status` is structured in the following way:

- Icon: informs user about the current status type
- Title (optional): gives user an overview of the message
- Message: provide the information to the user

```html
<sbb-status> Status info text </sbb-status>
```

## Variants

The `sbb-status` supports four types: `info` (default), `success`, `warn` and `error`, based on the type of the information displayed.

```html
<sbb-status type="info">...</sbb-status>

<sbb-status type="success">...</sbb-status>

<sbb-status type="warn">...</sbb-status>

<sbb-status type="error">...</sbb-status>
```

## Style

The `sbb-status` use default message colors, based on the chosen `type`.

## Accessibility

The message text is wrapped into a `<p>` element to guarantee the semantic meaning.
Avoid slotting block elements (e.g. `<div>`) as this violates semantic rules and can have negative effects on screen-readers.

If needed, the `role="status"` attribute can be added on the component's tag.

```html
<sbb-status role="status" type="error"> An error occurred. </sbb-status>
```

<!-- Auto Generated Below -->

## Properties

| Name | Attribute | Privacy | Type | Default | Description |
| -------------- | --------------- | ------- | --------------------- | -------- | ---------------------------------------------------------------------------------- |
| `type` | `type` | public | `SbbStatusType` | `'info'` | The type of the status. |
| `titleContent` | `title-content` | public | `string \| undefined` | | Content of title. |
| `titleLevel` | `title-level` | public | `TitleLevel` | `'3'` | Level of title, it will be rendered as heading tag (e.g. h3). Defaults to level 3. |

## Slots

| Name | Description |
| ------- | ---------------------------------------------------------- |
| | Use the unnamed slot to add content to the status message. |
| `title` | Use this to provide a title for the status (optional). |
16 changes: 16 additions & 0 deletions src/components/status/status.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { assert, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js';

import { SbbStatusElement } from './status';

describe('sbb-status', () => {
let element: SbbStatusElement;

beforeEach(async () => {
element = await fixture(html`<sbb-status> Status info text </sbb-status>`);
});

it('renders', async () => {
assert.instanceOf(element, SbbStatusElement);
});
});
71 changes: 71 additions & 0 deletions src/components/status/status.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
@use '../core/styles' as sbb;

// Default component properties, defined for :host. Properties which can not
// travel the shadow boundary are defined through this mixin
@include sbb.host-component-properties;

:host {
--sbb-status-gap: var(--sbb-spacing-fixed-1x);
--sbb-status-icon-color: var(--sbb-color-iron-default);
--_sbb-status-icon-font-size: var(--sbb-font-size-text-s);
--sbb-status-text-color: var(--sbb-color-iron-default);
--sbb-status-text-size: var(--sbb-font-size-text-s);
}

:host([type='error']) {
--sbb-status-text-color: var(--sbb-color-red125-default);
}

:host(:is([type='error'], [type='error'][data-has-title])) {
--sbb-status-icon-color: var(--sbb-color-red125-default);
}

:host([type='success']) {
--sbb-status-text-color: var(--sbb-color-green-default);
}

:host(:is([type='success'], [type='success'][data-has-title])) {
--sbb-status-icon-color: var(--sbb-color-green-default);
}

:host([type='warning']) {
--sbb-status-icon-color: var(--sbb-color-charcoal-default);
--sbb-status-text-color: var(--sbb-color-charcoal-default);
}

:host([data-has-title]) {
--sbb-status-text-color: var(--sbb-color-granite-default);
--sbb-status-icon-color: var(--sbb-color-charcoal-default);
--sbb-status-gap: var(--sbb-spacing-responsive-xxxs);
--_sbb-status-icon-font-size: var(--sbb-font-size-title-5);
--sbb-status-text-size: var(--sbb-typo-scale-0-875x);
}

.sbb-status {
display: flex;
gap: var(--sbb-status-gap);
color: var(--sbb-status-text-color);
font-size: var(--sbb-status-text-size);
}

.sbb-status__icon {
color: var(--sbb-status-icon-color);
margin-block-start: calc(
(
(var(--_sbb-status-icon-font-size) * var(--sbb-typo-line-height-body-text)) - var(
--sbb-size-icon-ui-small
)
) / 2
);
}

.sbb-status__title {
margin-block: 0;
}

.sbb-status__content-slot {
// Reset paragraph styles
display: inline;
margin: 0;
padding: 0;
}
27 changes: 27 additions & 0 deletions src/components/status/status.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { expect, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js';
import './status';
import '../icon';

describe('sbb-status', () => {
it('renders', async () => {
const root = await fixture(html` <sbb-status type="info"> Status info text </sbb-status>`);

expect(root).dom.to.be.equal(`<sbb-status type="info">Status info text</sbb-status>`);

await expect(root).shadowDom.to.be.equalSnapshot();
});

it('renders with the status title', async () => {
const root = await fixture(
html` <sbb-status type="info" title-content="Title"> Status info text </sbb-status>`,
);

expect(root).dom.to.be.equal(`
<sbb-status type="info" title-content="Title" data-has-title="">
Status info text
</sbb-status>
`);
await expect(root).shadowDom.to.be.equalSnapshot();
});
});
111 changes: 111 additions & 0 deletions src/components/status/status.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { withActions } from '@storybook/addon-actions/decorator';
import type { InputType } from '@storybook/types';
import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-components';
import { html, TemplateResult } from 'lit';

import { sbbSpread } from '../core/dom';

import readme from './readme.md?raw';

import './status';

const type: InputType = {
control: {
type: 'select',
},
options: ['info', 'success', 'warning', 'error'],
};

const titleContent: InputType = {
control: {
type: 'text',
},
};

const text: InputType = {
control: {
type: 'text',
},
};

const defaultArgTypes: ArgTypes = {
type,
'title-content': titleContent,
text,
};

const defaultArgs: Args = {
type: 'info',
'title-content': undefined,
text: 'Status info text',
};

const Template = ({ text, ...args }: Args): TemplateResult => html`
<sbb-status ${sbbSpread(args)}> ${text} </sbb-status>
`;

export const info: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs },
};

export const success: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs, type: 'success' },
};

export const warning: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs, type: 'warning' },
};

export const error: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs, type: 'error' },
};

export const infoWithTitle: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs, 'title-content': 'Title' },
};

export const successWithTitle: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs, type: 'success', 'title-content': 'Success!' },
};

export const warningWithTitle: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs, type: 'warning', 'title-content': 'Warning!' },
};

export const errorWithTitle: StoryObj = {
render: Template,
argTypes: defaultArgTypes,
args: { ...defaultArgs, type: 'error', 'title-content': 'Error!' },
};

const meta: Meta = {
decorators: [
(story) => html` <div style="padding: 2rem;">${story()}</div> `,
withActions as Decorator,
],
parameters: {
backgrounds: {
disable: true,
},
docs: {
extractComponentDescription: () => readme,
},
},
title: 'components/sbb-status',
};

export default meta;
Loading

0 comments on commit 8e1da55

Please sign in to comment.