Skip to content

Commit

Permalink
Merge pull request #1050 from CleverCloud/cc-heptapod-info/state-migr…
Browse files Browse the repository at this point in the history
…ation

refactor(cc-heptapod-info)!: rework properties to avoid impossible st…
  • Loading branch information
florian-sanders-cc authored Jun 12, 2024
2 parents 9ff07c1 + 654dbba commit e3c5816
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 48 deletions.
76 changes: 39 additions & 37 deletions src/components/cc-heptapod-info/cc-heptapod-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
<cc-block>
Expand All @@ -67,39 +59,49 @@ export class CcHeptapodInfo extends LitElement {
${i18n('cc-heptapod-info.description')}
</div>
${!this.error && !isNotUsed ? html`
<div class="pricing">
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${statistics.privateActiveUsers}</div>
<div>${i18n('cc-heptapod-info.private-active-users-description')}</div>
</div>
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${statistics.publicActiveUsers}</div>
<div>${i18n('cc-heptapod-info.public-active-users-description')}</div>
</div>
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${i18n('cc-heptapod-info.storage-bytes', statistics)}</div>
<div>${i18n('cc-heptapod-info.storage-description')}</div>
</div>
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${i18n('cc-heptapod-info.price-value', statistics)}</div>
<div>${i18n('cc-heptapod-info.price-description')}</div>
</div>
</div>
${this.state.type === 'error' ? html`
<cc-notice intent="warning" message="${i18n('cc-heptapod-info.error-loading')}"></cc-notice>
` : ''}
${!this.error && isNotUsed ? html`
${this.state.type === 'not-used' ? html`
<div class="no-statistics">${i18n('cc-heptapod-info.not-in-use')}</div>
` : ''}
${this.error ? html`
<cc-notice intent="warning" message="${i18n('cc-heptapod-info.error-loading')}"></cc-notice>
` : ''}
${this.state.type === 'loading' ? this._renderStatistics(SKELETON_STATISTICS, true) : ''}
${this.state.type === 'loaded' ? this._renderStatistics(this.state.statistics, false) : ''}
</cc-block>
`;
}

/**
* @param {Statistics} statistics
* @param {boolean} skeleton
* @private
*/
_renderStatistics (statistics, skeleton) {
return html`
<div class="pricing">
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${statistics.privateActiveUsers}</div>
<div>${i18n('cc-heptapod-info.private-active-users-description')}</div>
</div>
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${statistics.publicActiveUsers}</div>
<div>${i18n('cc-heptapod-info.public-active-users-description')}</div>
</div>
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${i18n('cc-heptapod-info.storage-bytes', statistics)}</div>
<div>${i18n('cc-heptapod-info.storage-description')}</div>
</div>
<div class="pricing-item">
<div class="pricing-item-value ${classMap({ skeleton })}">${i18n('cc-heptapod-info.price-value', statistics)}</div>
<div>${i18n('cc-heptapod-info.price-description')}</div>
</div>
</div>
`;
}

static get styles () {
return [
skeletonStyles,
Expand Down
50 changes: 40 additions & 10 deletions src/components/cc-heptapod-info/cc-heptapod-info.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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' };
}),
],
});
22 changes: 21 additions & 1 deletion src/components/cc-heptapod-info/cc-heptapod-info.types.d.ts
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit e3c5816

Please sign in to comment.