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

refactor(sbb-loading-indicator): split variants into two components and add missing sizes #3211

Merged
merged 15 commits into from
Dec 2, 2024
Merged
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;
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