Skip to content

Commit

Permalink
refactor(sbb-clock, sbb-link-list, sbb-footer): migrate to lit (#2059)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeripeierSBB authored Oct 10, 2023
1 parent fbf67ee commit 73ab56a
Show file tree
Hide file tree
Showing 16 changed files with 389 additions and 339 deletions.
1 change: 1 addition & 0 deletions src/components/sbb-clock/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './sbb-clock';
47 changes: 0 additions & 47 deletions src/components/sbb-clock/sbb-clock.e2e.ts

This file was deleted.

62 changes: 51 additions & 11 deletions src/components/sbb-clock/sbb-clock.spec.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,64 @@
import { assert, expect, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js';
import './sbb-clock';
import { SbbClock } from './sbb-clock';
import { newSpecPage } from '@stencil/core/testing';

describe('sbb-clock', () => {
let element: SbbClock;

it('renders', async () => {
const { root } = await newSpecPage({
components: [SbbClock],
html: '<sbb-clock />',
});
element = await fixture(html`<sbb-clock></sbb-clock>`);
assert.instanceOf(element, SbbClock);

expect(element).dom.to.be.equal(`<sbb-clock></sbb-clock>`);

expect(root).toEqualHtml(`
<sbb-clock>
<mock:shadow-root>
expect(element).shadowDom.to.be.equal(
`
<div class="sbb-clock">
<span class="sbb-clock__face"></span>
<span class="sbb-clock__hand-hours"></span>
<span class="sbb-clock__hand-minutes sbb-clock__hand-minutes--no-transition"></span>
<span class="sbb-clock__hand-seconds"></span>
</div>
</mock:shadow-root>
</sbb-clock>
`);
`,
);
});

it('renders with a fixed time', async () => {
element = await fixture(html`<sbb-clock data-now="1674732600000"></sbb-clock>`);
assert.instanceOf(element, SbbClock);

expect(element).to.have.attribute('data-initialized');

expect(element).shadowDom.to.be.equal(`
<div class="sbb-clock">
<span class="sbb-clock__face">
<svg focusable="false" viewBox="0 0 105 105" xmlns="http://www.w3.org/2000/svg">
<g class="face">
<circle cx="52.5" cy="52.5" fill="#FFF" r="52.5"></circle>
<path d="M50.75 4h3.5v12h-3.5zM50.75 89h3.5v12h-3.5zM75.233 9.623l3.03 1.75-6 10.392-3.03-1.75zM32.734 83.233l3.03 1.75-6 10.392-3.03-1.75zM93.628 26.732l1.75 3.031-10.392 6-1.75-3.03zM20.017 69.234l1.75 3.031-10.392 6-1.75-3.03zM101 50.75v3.5H89v-3.5zM16 50.75v3.5H4v-3.5zM95.379 75.232l-1.75 3.031-10.392-6 1.75-3.03zM21.766 32.734l-1.75 3.031-10.392-6 1.75-3.03zM78.267 93.63l-3.03 1.75-6-10.393 3.03-1.75zM35.766 20.015l-3.03 1.75-6-10.392 3.03-1.75z"></path>
<g>
<path d="M56.873 4.19l1.392.147-.366 3.48-1.392-.145zM47.101 97.177l1.393.146-.366 3.481-1.392-.146zM61.896 4.914l1.37.29-.728 3.424-1.37-.29zM42.458 96.366l1.37.29-.728 3.424-1.37-.291zM66.825 6.157l1.332.432-1.082 3.33-1.331-.434zM37.931 95.085l1.332.432-1.082 3.33-1.331-.433zM71.584 7.906l1.28.569-1.424 3.197-1.28-.57zM33.56 93.32l1.278.569-1.423 3.197-1.28-.57zM80.44 12.852l1.133.823-2.058 2.831-1.132-.823zM25.481 88.494l1.133.822-2.057 2.832-1.133-.823zM84.43 15.986l1.04.937-2.342 2.6-1.04-.936zM21.87 85.469l1.04.936-2.341 2.601-1.04-.937zM88.072 19.522l.937 1.04-2.6 2.343-.937-1.04zM18.593 82.088l.937 1.04-2.601 2.342-.937-1.04zM91.328 23.425l.823 1.133-2.832 2.057-.823-1.132zM15.684 78.385l.823 1.132-2.832 2.058-.822-1.133zM96.52 32.128l.57 1.279-3.198 1.423-.57-1.278zM11.109 70.161l.569 1.279-3.197 1.423-.57-1.279zM98.407 36.85l.433 1.332-3.33 1.081-.432-1.331zM9.483 65.74l.432 1.33-3.329 1.082-.432-1.331zM99.795 41.726l.291 1.37-3.423.727-.29-1.37zM8.34 61.17l.292 1.37-3.424.728-.29-1.37zM100.66 46.73l.146 1.393-3.48.366-.147-1.392zM7.674 56.506l.146 1.392-3.48.366-.147-1.392zM100.811 56.873l-.146 1.392-3.48-.365.145-1.393zM7.821 47.101l-.146 1.392-3.48-.365.145-1.393zM100.09 61.895l-.291 1.369-3.424-.728.291-1.369zM8.631 42.46l-.291 1.37-3.423-.728.29-1.37zM98.84 66.827l-.432 1.331-3.329-1.081.433-1.332zM9.918 37.93l-.433 1.331-3.329-1.082.433-1.331zM97.098 71.585l-.569 1.28-3.197-1.424.57-1.28zM11.677 33.558l-.57 1.28-3.197-1.424.57-1.279zM92.149 80.439l-.823 1.133-2.832-2.058.823-1.132zM16.506 25.482l-.823 1.133-2.831-2.057.823-1.133zM89.017 84.431l-.937 1.04-2.6-2.341.936-1.04zM19.528 21.869l-.936 1.04-2.601-2.342.936-1.04zM85.48 88.076l-1.041.936-2.342-2.6 1.04-.937zM22.91 18.59l-1.04.937-2.341-2.601 1.04-.937zM81.574 91.328l-1.133.823-2.057-2.831 1.132-.823zM26.617 15.684l-1.133.823-2.057-2.832 1.132-.823zM72.873 96.524l-1.279.57-1.423-3.198 1.278-.57zM34.838 11.105l-1.279.57-1.423-3.198 1.279-.57zM68.151 98.405l-1.331.432-1.082-3.329 1.332-.432zM39.259 9.485l-1.332.433-1.081-3.33 1.331-.432zM63.272 99.799l-1.369.29-.728-3.422 1.37-.291zM43.83 8.337l-1.369.291-.727-3.423 1.37-.291zM58.27 100.662l-1.393.146-.366-3.48 1.393-.147zM48.494 7.672l-1.392.147-.366-3.481 1.392-.147z"></path>
</g>
</g>
</svg>
</span>
<span class="sbb-clock__hand-hours sbb-clock__hand-hours--initial-hour">
<svg focusable="false" viewBox="0 0 105 105" xmlns="http://www.w3.org/2000/svg">
<path d="M55.7 64.5h-6.4l.6-44h5.2z" id="mod_clock_svg_hours"></path>
</svg>
</span>
<span class="sbb-clock__hand-minutes sbb-clock__hand-minutes--no-transition" style="transform: rotateZ(180deg);">
<svg focusable="false" viewBox="0 0 105 105" xmlns="http://www.w3.org/2000/svg">
<path d="M55.1,64.5h-5.2l0.8-58h3.6L55.1,64.5z"></path>
</svg>
</span>
<span class="sbb-clock__hand-seconds sbb-clock__hand-seconds--initial-minute">
<svg focusable="false" viewBox="0 0 105 105" xmlns="http://www.w3.org/2000/svg">
<path d="M57.8,21.3c0-2.9-2.4-5.2-5.2-5.2s-5.3,2.3-5.3,5.2c0,2.7,2,4.8,4.5,5.2V69h1.5V26.5C55.8,26.2,57.8,24,57.8,21.3z"></path>
</svg>
</span>
</div>
`);
});
});
3 changes: 2 additions & 1 deletion src/components/sbb-clock/sbb-clock.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import { h, JSX } from 'jsx-dom';
import isChromatic from 'chromatic';
import readme from './readme.md?raw';
import type { Meta, StoryObj } from '@storybook/html';
import type { Meta, StoryObj } from '@storybook/web-components';
import type { InputType } from '@storybook/types';
import './sbb-clock';

const dataNow: InputType = {
control: {
Expand Down
140 changes: 72 additions & 68 deletions src/components/sbb-clock/sbb-clock.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Component, ComponentInterface, Element, h, Host, JSX, State } from '@stencil/core';

import clockFaceSVG from './assets/sbb_clock_face.svg';
import clockHandleHoursSVG from './assets/sbb_clock_hours.svg';
import clockHandleMinutesSVG from './assets/sbb_clock_minutes.svg';
import clockHandleSecondsSVG from './assets/sbb_clock_seconds.svg';
import clockFaceSVG from './assets/sbb_clock_face.svg?raw';
import clockHandleHoursSVG from './assets/sbb_clock_hours.svg?raw';
import clockHandleMinutesSVG from './assets/sbb_clock_minutes.svg?raw';
import clockHandleSecondsSVG from './assets/sbb_clock_seconds.svg?raw';
import { CSSResult, html, LitElement, TemplateResult } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { setAttributes } from '../../global/dom';
import { ref } from 'lit/directives/ref.js';
import Style from './sbb-clock.scss?lit&inline';

/** Number of hours on the clock face. */
const TOTAL_HOURS_ON_CLOCK_FACE = 12;
Expand Down Expand Up @@ -40,17 +43,14 @@ const ADD_EVENT_LISTENER_OPTIONS: AddEventListenerOptions = {
passive: true,
};

@Component({
shadow: true,
styleUrl: 'sbb-clock.scss',
tag: 'sbb-clock',
})
export class SbbClock implements ComponentInterface {
@customElement('sbb-clock')
export class SbbClock extends LitElement {
public static override styles: CSSResult = Style;

/** If it's false, the clock's hands are hidden; it's set to true when calculations are ready. */
@State() private _isInitialized = false;
@state() private _isInitialized = false;

/** Reference to the host element. */
@Element() private _element: HTMLElement;

/** Reference to the hour hand. */
private _clockHandHours: HTMLElement;
Expand Down Expand Up @@ -79,11 +79,11 @@ export class SbbClock implements ComponentInterface {
/** Move the minutes hand every minute. */
private _handMovement: ReturnType<typeof setInterval>;

private _handlePageVisibilityChange(): void {
private async _handlePageVisibilityChange(): Promise<void> {
if (document.visibilityState === 'hidden') {
this._stopClock();
} else if (!this._hasDataNow()) {
this._startClock();
await this._startClock();
}
}

Expand All @@ -104,15 +104,15 @@ export class SbbClock implements ComponentInterface {

private _removeHoursAnimationStyles(): void {
this._clockHandHours?.classList.remove('sbb-clock__hand-hours--initial-hour');
this._element.style.removeProperty('--sbb-clock-hours-animation-start-angle');
this._element.style.removeProperty('--sbb-clock-hours-animation-duration');
this.style.removeProperty('--sbb-clock-hours-animation-start-angle');
this.style.removeProperty('--sbb-clock-hours-animation-duration');
}

private _removeSecondsAnimationStyles(): void {
this._clockHandSeconds?.classList.remove('sbb-clock__hand-seconds--initial-minute');
this._clockHandMinutes?.classList.remove('sbb-clock__hand-minutes--no-transition');
this._element.style.removeProperty('--sbb-clock-seconds-animation-start-angle');
this._element.style.removeProperty('--sbb-clock-seconds-animation-duration');
this.style.removeProperty('--sbb-clock-seconds-animation-start-angle');
this.style.removeProperty('--sbb-clock-seconds-animation-duration');
}

/** Given the current date, calculates the hh/mm/ss values and the hh/mm/ss left to the next midnight. */
Expand Down Expand Up @@ -153,28 +153,22 @@ export class SbbClock implements ComponentInterface {
this._clockHandSeconds.style.animation = '';
}

this._element.style.setProperty(
this.style.setProperty(
'--sbb-clock-hours-animation-start-angle',
`${Math.ceil(this._hours * HOURS_ANGLE + this._minutes / 2)}deg`,
);
this._element.style.setProperty(
'--sbb-clock-hours-animation-duration',
`${hoursAnimationDuration}s`,
);
this._element.style.setProperty(
this.style.setProperty('--sbb-clock-hours-animation-duration', `${hoursAnimationDuration}s`);
this.style.setProperty(
'--sbb-clock-seconds-animation-start-angle',
`${Math.ceil(this._seconds * SBB_SECONDS_ANGLE)}deg`,
);
this._element.style.setProperty(
'--sbb-clock-seconds-animation-duration',
`${remainingSeconds}s`,
);
this.style.setProperty('--sbb-clock-seconds-animation-duration', `${remainingSeconds}s`);

this._setMinutesHand();

this._clockHandSeconds?.classList.add('sbb-clock__hand-seconds--initial-minute');
this._clockHandHours?.classList.add('sbb-clock__hand-hours--initial-hour');
this._element.style.setProperty('--sbb-clock-animation-play-state', 'running');
this.style.setProperty('--sbb-clock-animation-play-state', 'running');

this._isInitialized = true;
}
Expand Down Expand Up @@ -237,11 +231,11 @@ export class SbbClock implements ComponentInterface {

this._clockHandMinutes?.classList.add('sbb-clock__hand-minutes--no-transition');

this._element.style.setProperty('--sbb-clock-animation-play-state', 'paused');
this.style.setProperty('--sbb-clock-animation-play-state', 'paused');
}

/** Starts the clock by defining the hands starting position then starting the animations. */
private _startClock(): void {
private async _startClock(): Promise<void> {
this._clockHandHours?.addEventListener(
'animationend',
this._moveHoursHandFn,
Expand All @@ -253,64 +247,74 @@ export class SbbClock implements ComponentInterface {
ADD_EVENT_LISTENER_OPTIONS,
);

setTimeout(() => this._setHandsStartingPosition(), INITIAL_TIMEOUT_DURATION);
await new Promise(() =>
setTimeout(() => this._setHandsStartingPosition(), INITIAL_TIMEOUT_DURATION),
);
}

private _hasDataNow(): boolean {
const dataNow = +this._element.dataset?.now;
const dataNow = +this.dataset?.now;
return !isNaN(dataNow);
}

private _now(): Date {
if (this._hasDataNow()) {
return new Date(+this._element.dataset?.now);
return new Date(+this.dataset?.now);
}
return new Date();
}

public componentDidLoad(): void {
protected override async firstUpdated(): Promise<void> {
this._addEventListeners();

if (this._hasDataNow()) {
this._stopClock();
} else {
this._startClock();
await this._startClock();
}
}

public disconnectedCallback(): void {
public override disconnectedCallback(): void {
super.disconnectedCallback();
this._removeEventListeners();
}

public render(): JSX.Element {
protected override render(): TemplateResult {
const hostAttributes = { 'data-initialized': this._isInitialized };
return (
<Host {...hostAttributes}>
<div class="sbb-clock">
<span class="sbb-clock__face" innerHTML={clockFaceSVG} />
<span
class="sbb-clock__hand-hours"
innerHTML={clockHandleHoursSVG}
ref={(el): void => {
this._clockHandHours = el;
}}
/>
<span
class="sbb-clock__hand-minutes sbb-clock__hand-minutes--no-transition"
innerHTML={clockHandleMinutesSVG}
ref={(el): void => {
this._clockHandMinutes = el;
}}
/>
<span
class="sbb-clock__hand-seconds"
innerHTML={clockHandleSecondsSVG}
ref={(el): void => {
this._clockHandSeconds = el;
}}
/>
</div>
</Host>
);
setAttributes(this, hostAttributes);

return html`
<div class="sbb-clock">
<span class="sbb-clock__face" .innerHTML=${clockFaceSVG}></span>
<span
class="sbb-clock__hand-hours"
.innerHTML=${clockHandleHoursSVG}
${ref((e: HTMLSpanElement): void => {
this._clockHandHours = e;
})}
></span>
<span
class="sbb-clock__hand-minutes sbb-clock__hand-minutes--no-transition"
.innerHTML=${clockHandleMinutesSVG}
${ref((el: HTMLSpanElement): void => {
this._clockHandMinutes = el;
})}
></span>
<span
class="sbb-clock__hand-seconds"
.innerHTML=${clockHandleSecondsSVG}
${ref((el: HTMLSpanElement): void => {
this._clockHandSeconds = el;
})}
></span>
</div>
`;
}
}

declare global {
interface HTMLElementTagNameMap {
// eslint-disable-next-line @typescript-eslint/naming-convention
'sbb-clock': SbbClock;
}
}
1 change: 1 addition & 0 deletions src/components/sbb-footer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './sbb-footer';
13 changes: 6 additions & 7 deletions src/components/sbb-footer/sbb-footer.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { E2EElement, E2EPage, newE2EPage } from '@stencil/core/testing';
import { assert, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js';
import { SbbFooter } from './sbb-footer';

describe('sbb-footer', () => {
let element: E2EElement, page: E2EPage;
let element: SbbFooter;

it('renders', async () => {
page = await newE2EPage();
await page.setContent('<sbb-footer></sbb-footer>');

element = await page.find('sbb-footer');
expect(element).toHaveClass('hydrated');
element = await fixture(html`<sbb-footer></sbb-footer>`);
assert.instanceOf(element, SbbFooter);
});
});
Loading

0 comments on commit 73ab56a

Please sign in to comment.