From 654dbba667e9132b77c67adb448204037c49cd8c Mon Sep 17 00:00:00 2001 From: Helene Amouzou Date: Thu, 28 Mar 2024 11:51:39 +0100 Subject: [PATCH] refactor(cc-heptapod-info)!: rework properties to avoid impossible states BREAKING CHANGE: the properties have changed - `state`: new property containing the whole state - `statistics`: property has been deleted as it is now part of the state - `error`: property has been deleted as it is now part of the state --- .../cc-heptapod-info/cc-heptapod-info.js | 76 ++++++++++--------- .../cc-heptapod-info.stories.js | 50 +++++++++--- .../cc-heptapod-info.types.d.ts | 22 +++++- 3 files changed, 100 insertions(+), 48 deletions(-) diff --git a/src/components/cc-heptapod-info/cc-heptapod-info.js b/src/components/cc-heptapod-info/cc-heptapod-info.js index 8640da727..381d6c3c6 100644 --- a/src/components/cc-heptapod-info/cc-heptapod-info.js +++ b/src/components/cc-heptapod-info/cc-heptapod-info.js @@ -18,40 +18,32 @@ const HEPTAPOD_LOGO_URL = 'https://assets.clever-cloud.com/logos/heptapod.svg'; /** * @typedef {import('./cc-heptapod-info.types.js').Statistics} Statistics + * @typedef {import('./cc-heptapod-info.types.js').HeptapodInfoState} HeptapodInfoState + * @typedef {import('./cc-heptapod-info.types.js').HeptapodInfoStateLoaded} HeptapodInfoStateLoaded + * @typedef {import('./cc-heptapod-info.types.js').HeptapodInfoStateLoading} HeptapodInfoStateLoading */ /** * A component that shows a summary of our Heptapod SaaS offer. * - * ## Details - * - * * When `statistics` is nullish, a skeleton screen UI pattern is displayed (loading hint). - * * @cssdisplay block */ export class CcHeptapodInfo extends LitElement { static get properties () { return { - error: { type: Boolean, reflect: true }, - statistics: { type: Object }, + state: { type: Object }, }; } constructor () { super(); - /** @type {boolean} Displays an error message. */ - this.error = false; - - /** @type {Statistics|null} Sets the usage statistics of this heptapod SaaS or `"not-used"` to display a message explaining the service is not used. */ - this.statistics = null; + /** @type {HeptapodInfoState} Set the state of the component. */ + this.state = { type: 'loading' }; } render () { - const skeleton = (this.statistics == null); - const statistics = skeleton ? SKELETON_STATISTICS : this.statistics; - const isNotUsed = (this.statistics === 'not-used'); return html` @@ -67,39 +59,49 @@ export class CcHeptapodInfo extends LitElement { ${i18n('cc-heptapod-info.description')} - ${!this.error && !isNotUsed ? html` -
-
-
${statistics.privateActiveUsers}
-
${i18n('cc-heptapod-info.private-active-users-description')}
-
-
-
${statistics.publicActiveUsers}
-
${i18n('cc-heptapod-info.public-active-users-description')}
-
-
-
${i18n('cc-heptapod-info.storage-bytes', statistics)}
-
${i18n('cc-heptapod-info.storage-description')}
-
-
-
${i18n('cc-heptapod-info.price-value', statistics)}
-
${i18n('cc-heptapod-info.price-description')}
-
-
+ ${this.state.type === 'error' ? html` + ` : ''} - ${!this.error && isNotUsed ? html` + ${this.state.type === 'not-used' ? html`
${i18n('cc-heptapod-info.not-in-use')}
` : ''} - ${this.error ? html` - - ` : ''} + ${this.state.type === 'loading' ? this._renderStatistics(SKELETON_STATISTICS, true) : ''} + ${this.state.type === 'loaded' ? this._renderStatistics(this.state.statistics, false) : ''}
`; } + /** + * @param {Statistics} statistics + * @param {boolean} skeleton + * @private + */ + _renderStatistics (statistics, skeleton) { + return html` +
+
+
${statistics.privateActiveUsers}
+
${i18n('cc-heptapod-info.private-active-users-description')}
+
+
+
${statistics.publicActiveUsers}
+
${i18n('cc-heptapod-info.public-active-users-description')}
+
+
+
${i18n('cc-heptapod-info.storage-bytes', statistics)}
+
${i18n('cc-heptapod-info.storage-description')}
+
+
+
${i18n('cc-heptapod-info.price-value', statistics)}
+
${i18n('cc-heptapod-info.price-description')}
+
+
+ `; + } + static get styles () { return [ skeletonStyles, diff --git a/src/components/cc-heptapod-info/cc-heptapod-info.stories.js b/src/components/cc-heptapod-info/cc-heptapod-info.stories.js index 92b0b0279..3de259027 100644 --- a/src/components/cc-heptapod-info/cc-heptapod-info.stories.js +++ b/src/components/cc-heptapod-info/cc-heptapod-info.stories.js @@ -17,6 +17,16 @@ const conf = { `, }; +/** + * @typedef {import('./cc-heptapod-info.js').CcHeptapodInfo} CcHeptapodInfo + * @typedef {import('./cc-heptapod-info.types.js').HeptapodInfoStateLoaded} HeptapodInfoStateLoaded + * @typedef {import('./cc-heptapod-info.types.js').HeptapodInfoStateLoading} HeptapodInfoStateLoading + * @typedef {import('./cc-heptapod-info.types.js').HeptapodInfoStateError} HeptapodInfoStateError + * @typedef {import('./cc-heptapod-info.types.js').HeptapodInfoStateNotUsed} HeptapodInfoStateNotUsed + * @typedef {import('./cc-heptapod-info.types.js').Statistics} Statistics + */ + +/** @type {Statistics} */ const statistics = { privateActiveUsers: 12, publicActiveUsers: 120, @@ -26,28 +36,48 @@ const statistics = { }; export const defaultStory = makeStory(conf, { - items: [{ statistics }], + items: [{ + /** @type {HeptapodInfoStateLoaded} */ + state: { + type: 'loaded', + statistics: statistics, + }, + }], }); -export const skeleton = makeStory(conf, { - items: [{}], +export const loading = makeStory(conf, { + items: [{ + /** @type {HeptapodInfoStateLoading} */ + state: { type: 'loading' }, + }], }); export const notUsed = makeStory(conf, { - items: [{ statistics: 'not-used' }], + items: [{ + /** @type {HeptapodInfoStateNotUsed} */ + state: { type: 'not-used' }, + }], }); export const error = makeStory(conf, { - items: [{ error: true }], + items: [{ + /** @type {HeptapodInfoStateError} */ + state: { type: 'error' }, + }], }); export const simulations = makeStory(conf, { items: [{}, {}, {}], simulations: [ - storyWait(2000, ([component, componentNotUsed, componentError]) => { - component.statistics = statistics; - componentNotUsed.statistics = 'not-used'; - componentError.error = true; - }), + storyWait(2000, + /** @param {CcHeptapodInfo[]} components */ + ([component, componentNotUsed, componentError]) => { + component.state = { + type: 'loaded', + statistics: statistics, + }; + componentNotUsed.state = { type: 'not-used' }; + componentError.state = { type: 'error' }; + }), ], }); diff --git a/src/components/cc-heptapod-info/cc-heptapod-info.types.d.ts b/src/components/cc-heptapod-info/cc-heptapod-info.types.d.ts index 22cae7a39..ca43673c8 100644 --- a/src/components/cc-heptapod-info/cc-heptapod-info.types.d.ts +++ b/src/components/cc-heptapod-info/cc-heptapod-info.types.d.ts @@ -1,6 +1,26 @@ -interface Statistics { +export type HeptapodInfoState = HeptapodInfoStateLoaded | HeptapodInfoStateLoading | HeptapodInfoStateError | HeptapodInfoStateNotUsed; + +export interface HeptapodInfoStateLoaded { + type : 'loaded'; + statistics: Statistics; +} + +export interface HeptapodInfoStateLoading { + type : 'loading'; +} + +export interface HeptapodInfoStateError { + type : 'error'; +} + +export interface HeptapodInfoStateNotUsed { + type : 'not-used'; +} + +export interface Statistics { privateActiveUsers: number; publicActiveUsers: number; storage: number; price: number; } +