Skip to content

Commit

Permalink
refactor(sbb-loading-indicator): split variants into two components a…
Browse files Browse the repository at this point in the history
…nd add missing sizes (#3211)

BREAKING CHANGE: The `sbb-loading-indicator` component no longer
supports the `circle` variant, to achieve this look use
`sbb-loading-indicator-circle` instead. For any other case where it is
used in its `window` variant just remove the `variant` property as it is
no longer needed.
  • Loading branch information
MarioCastigliano authored Dec 2, 2024
1 parent 65b667b commit d450f49
Show file tree
Hide file tree
Showing 18 changed files with 507 additions and 329 deletions.
7 changes: 3 additions & 4 deletions src/elements/button/common/common-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { html, unsafeStatic } from 'lit/static-html.js';
import { sbbSpread } from '../../../storybook/helpers/spread.js';

import '../../icon.js';
import '../../loading-indicator.js';
import '../../loading-indicator-circle.js';

/* eslint-disable lit/binding-positions, @typescript-eslint/naming-convention */
const Template = ({ tag, text, ...args }: Args): TemplateResult => html`
Expand All @@ -38,10 +38,9 @@ const IconSlotTemplate = ({

const LoadingIndicatorTemplate = ({ tag, text, ...args }: Args): TemplateResult => html`
<${unsafeStatic(tag)} ${sbbSpread(args)}>
<sbb-loading-indicator
<sbb-loading-indicator-circle
slot="icon"
variant="circle"
></sbb-loading-indicator>
></sbb-loading-indicator-circle>
${text}
</${unsafeStatic(tag)}>
`;
Expand Down
1 change: 1 addition & 0 deletions src/elements/loading-indicator-circle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './loading-indicator-circle/loading-indicator-circle.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* @web/test-runner snapshot v1 */
export const snapshots = {};

snapshots["sbb-loading-indicator-circle renders with variant `circle` DOM"] =
`<sbb-loading-indicator-circle
aria-busy="true"
color="default"
role="progressbar"
>
</sbb-loading-indicator-circle>
`;
/* end snapshot sbb-loading-indicator-circle renders with variant `circle` DOM */

snapshots["sbb-loading-indicator-circle renders with variant `circle` Shadow DOM"] =
`<span class="sbb-loading-indicator">
<span class="sbb-loading-indicator__animated-element">
</span>
</span>
`;
/* end snapshot sbb-loading-indicator-circle renders with variant `circle` Shadow DOM */

snapshots["sbb-loading-indicator-circle renders with variant `circle` A11y tree Chrome"] =
`<p>
{
"role": "WebArea",
"name": ""
}
</p>
`;
/* end snapshot sbb-loading-indicator-circle renders with variant `circle` A11y tree Chrome */

snapshots["sbb-loading-indicator-circle renders with variant `circle` A11y tree Firefox"] =
`<p>
{
"role": "document",
"name": ""
}
</p>
`;
/* end snapshot sbb-loading-indicator-circle renders with variant `circle` A11y tree Firefox */

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
@use '../core/styles' as sbb;

// Box-sizing rules contained in typography are not traversing Shadow DOM boundaries. We need to include box-sizing mixin in every component.
@include sbb.box-sizing;

:host {
display: inline-block;
line-height: 0;

--sbb-loading-indicator-circle-color: var(--sbb-color-red);
--sbb-loading-indicator-circle-padding: #{sbb.px-to-rem-build(2)};
--sbb-loading-indicator-circle-duration: var(--sbb-disable-animation-zero-duration, 1.5s);
--sbb-loading-indicator-circle-background-color: var(--sbb-color-white);
--sbb-loading-indicator-circle-background: conic-gradient(
from 90deg,
var(--sbb-loading-indicator-circle-background-color),
var(--sbb-loading-indicator-circle-color)
);
--sbb-loading-indicator-circle-animated-width: 0.1875em;
--sbb-loading-indicator-circle-animated-height: 0.1875em;
--sbb-loading-indicator-circle-animated-border-radius: 50%;

@include sbb.if-forced-colors {
--sbb-loading-indicator-circle-color: CanvasText !important;
--sbb-loading-indicator-circle-animated-width: 50%;
--sbb-loading-indicator-circle-animated-height: 100%;
--sbb-loading-indicator-circle-animated-border-radius: 0;
--sbb-loading-indicator-circle-background: transparent;
}
}

:host([color='smoke']) {
--sbb-loading-indicator-circle-color: var(--sbb-color-smoke);
}

:host([color='white']) {
--sbb-loading-indicator-circle-color: var(--sbb-color-white);
}

:host([color='white']) {
--sbb-loading-indicator-circle-background-color: var(--sbb-color-iron);
}

.sbb-loading-indicator {
display: inline-flex;
padding: var(--sbb-loading-indicator-circle-padding);
vertical-align: middle;
line-height: 1;
}

.sbb-loading-indicator__animated-element {
width: 1.25em;
height: 1.25em;
display: inline-block;
color: transparent;
position: relative;
margin: 0 auto;
overflow: hidden;
border-radius: 50%;
background: var(--sbb-loading-indicator-circle-background);
mask: radial-gradient(
circle 0.4375em,
#0000 98%,
var(--sbb-loading-indicator-circle-background-color)
);
animation: rotation var(--sbb-loading-indicator-circle-duration) infinite linear;

// Rounded start of strong color part
&::after {
content: '';
width: var(--sbb-loading-indicator-circle-animated-width);
height: var(--sbb-loading-indicator-circle-animated-height);
background: var(--sbb-loading-indicator-circle-color);
border-radius: var(--sbb-loading-indicator-circle-animated-border-radius);
position: absolute;
top: 50%;
right: 0;
translate: 0 -50%;
overflow: hidden;
margin: auto;
}
}

@keyframes rotation {
from {
rotate: 0deg;
}

to {
rotate: 359deg;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect } from '@open-wc/testing';
import { html } from 'lit/static-html.js';

import { fixture, testA11yTreeSnapshot } from '../core/testing/private.js';

import type { SbbLoadingIndicatorCircleElement } from './loading-indicator-circle.js';

import './loading-indicator-circle.js';

describe(`sbb-loading-indicator-circle`, () => {
let element: SbbLoadingIndicatorCircleElement;

describe('renders with variant `circle`', () => {
beforeEach(async () => {
element = await fixture(html`<sbb-loading-indicator-circle></sbb-loading-indicator-circle>`);
});

it('DOM', async () => {
await expect(element).dom.to.be.equalSnapshot();
});

it('Shadow DOM', async () => {
await expect(element).shadowDom.to.be.equalSnapshot();
});

testA11yTreeSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { assert } from '@open-wc/testing';
import { html } from 'lit/static-html.js';

import { fixture } from '../core/testing/private.js';

import { SbbLoadingIndicatorCircleElement } from './loading-indicator-circle.js';

describe(`sbb-loading-indicator-circle`, () => {
let element: SbbLoadingIndicatorCircleElement;

it('renders', async () => {
element = await fixture(html`<sbb-loading-indicator-circle></sbb-loading-indicator-circle>`);
assert.instanceOf(element, SbbLoadingIndicatorCircleElement);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { assert } from '@open-wc/testing';
import { html } from 'lit';

import { ssrHydratedFixture } from '../core/testing/private.js';

import { SbbLoadingIndicatorCircleElement } from './loading-indicator-circle.js';

describe(`sbb-loading-indicator-circle ssr`, () => {
let root: SbbLoadingIndicatorCircleElement;

beforeEach(async () => {
root = await ssrHydratedFixture(
html`<sbb-loading-indicator-circle></sbb-loading-indicator-circle>`,
{
modules: ['./loading-indicator-circle.js'],
},
);
});

it('renders', () => {
assert.instanceOf(root, SbbLoadingIndicatorCircleElement);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type { InputType, StoryContext } from '@storybook/types';
import type { Args, ArgTypes, Meta, StoryObj } from '@storybook/web-components';
import type { TemplateResult } from 'lit';
import { html } from 'lit';

import { sbbSpread } from '../../storybook/helpers/spread.js';

import type { SbbLoadingIndicatorCircleElement } from './loading-indicator-circle.js';
import readme from './readme.md?raw';

import './loading-indicator-circle.js';
import '../button/button.js';
import '../title.js';
import '../card.js';

const createLoadingIndicator = (event: Event): void => {
const loader: SbbLoadingIndicatorCircleElement = document.createElement(
'sbb-loading-indicator-circle',
);
const container = (event.currentTarget as HTMLElement).parentElement!.querySelector(
'.loader-container',
)!;
loader.setAttribute('aria-label', 'Loading, please wait');
container.append(loader);
setTimeout(() => {
const p = document.createElement('p');
p.textContent = "Loading complete. Here's your data: ...";
container.append(p);
loader.remove();
}, 5000);
};

const TemplateAccessibility = (): TemplateResult => html`
<sbb-card color="milk">
Turn on your screen-reader and click the button to make the loading indicator appear.
</sbb-card>
<br />
<sbb-button @click=${(event: Event) => createLoadingIndicator(event)}> Show loader </sbb-button>
<div
class="loader-container"
aria-live="polite"
style="padding-block: var(--sbb-spacing-fixed-4x)"
></div>
`;

const Template = (args: Args): TemplateResult => html`
<p>
<sbb-loading-indicator-circle ${sbbSpread(args)}></sbb-loading-indicator-circle> Inline loading
indicator
</p>
<sbb-title level="4">
<sbb-loading-indicator-circle ${sbbSpread(args)}></sbb-loading-indicator-circle> Adaptive to
font size
</sbb-title>
`;

const color: InputType = {
control: {
type: 'inline-radio',
},
options: ['default', 'smoke', 'white'],
};

const defaultArgTypes: ArgTypes = {
color,
};

const defaultArgs: Args = {
color: color.options![0],
};

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

export const Accessibility: StoryObj = {
render: TemplateAccessibility,
argTypes: defaultArgTypes,
args: { ...defaultArgs },
};

const meta: Meta = {
decorators: [
(story, context) => {
if (context.args.color === 'white') {
return html`<div
style="color: var(--sbb-color-white); --sbb-title-text-color-normal-override: var(--sbb-color-white)"
>
${story()}
</div>`;
}
return story();
},
],
parameters: {
backgroundColor: (context: StoryContext) =>
context.args.color === 'white' ? 'var(--sbb-color-iron)' : 'var(--sbb-color-white)',
docs: {
extractComponentDescription: () => readme,
},
},
title: 'elements/sbb-loading-indicator-circle',
};

export default meta;
38 changes: 38 additions & 0 deletions src/elements/loading-indicator-circle/loading-indicator-circle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { CSSResultGroup, TemplateResult } from 'lit';
import { html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';

import { hostAttributes } from '../core/decorators.js';

import style from './loading-indicator-circle.scss?lit&inline';

/**
* It displays a circle loading indicator.
*/
export
@customElement('sbb-loading-indicator-circle')
@hostAttributes({
role: 'progressbar',
'aria-busy': 'true',
})
class SbbLoadingIndicatorCircleElement extends LitElement {
public static override styles: CSSResultGroup = style;

/** Color variant. */
@property({ reflect: true }) public accessor color: 'default' | 'smoke' | 'white' = 'default';

protected override render(): TemplateResult {
return html`
<span class="sbb-loading-indicator">
<span class="sbb-loading-indicator__animated-element"></span>
</span>
`;
}
}

declare global {
interface HTMLElementTagNameMap {
// eslint-disable-next-line @typescript-eslint/naming-convention
'sbb-loading-indicator-circle': SbbLoadingIndicatorCircleElement;
}
}
Loading

0 comments on commit d450f49

Please sign in to comment.