Skip to content

Commit

Permalink
refactor(cc-tile-status-codes)!: migrate to the new smart component d…
Browse files Browse the repository at this point in the history
…esign

BREAKING CHANGE: the properties have changed
- `state`: new property containing the whole state
- `statusCodes`: 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

Fixes #924
  • Loading branch information
pdesoyres-cc committed Feb 2, 2024
1 parent aff6229 commit fc5dc71
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 61 deletions.
6 changes: 3 additions & 3 deletions demo-smart/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@
<li>
<a class="definition-link" href="?definition=cc-email-list.smart">cc-email-list.smart</a>
</li>
<!-- <li>-->
<!-- <a class="definition-link" href="?definition=cc-tile-status-codes.smart">cc-tile-status-codes.smart</a>-->
<!-- </li>-->
<li>
<a class="definition-link" href="?definition=cc-tile-status-codes.smart">cc-tile-status-codes.smart</a>
</li>
<li>
<a class="definition-link" href="?definition=cc-orga-member-list.smart">cc-orga-member-list.smart</a>
</li>
Expand Down
39 changes: 19 additions & 20 deletions src/components/cc-tile-status-codes/cc-tile-status-codes.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@ const COLORS = {
const SKELETON_STATUS_CODES = { 200: 1 };

/**
* @typedef {import('./cc-tile-status-codes.types.js').StatusCodesData} StatusCodesData
* @typedef {import('./cc-tile-status-codes.types.js').TileStatusCodesState} TileStatusCodesState
*/

/**
* A "tile" component to display HTTP response status codes in a pie chart (donut).
*
* ## Details
* * When `data` is nullish, a skeleton screen UI pattern is displayed (loading hint).
* * A short doc is available when the (i) button is clicked.
*
* @cssdisplay grid
Expand All @@ -48,8 +47,7 @@ export class CcTileStatusCodes extends LitElement {

static get properties () {
return {
error: { type: Boolean, reflect: true },
statusCodes: { type: Object, attribute: 'status-codes' },
state: { type: Object },
_docs: { type: Boolean, state: true },
_empty: { type: Boolean, state: true },
_skeleton: { type: Boolean, state: true },
Expand All @@ -59,11 +57,8 @@ export class CcTileStatusCodes extends LitElement {
constructor () {
super();

/** @type {boolean|null} Displays an error message. */
this.error = null;

/** @type {StatusCodesData|null} Sets data with the number of requests for each HTTP status code. */
this.statusCodes = null;
/** @type {TileStatusCodesState} Sets the status codes state. */
this.state = { type: 'loading' };

/** @type {boolean} */
this._docs = false;
Expand All @@ -79,9 +74,9 @@ export class CcTileStatusCodes extends LitElement {
this._docs = !this._docs;
}

firstUpdated () {
firstUpdated (changedProperties) {

if (this.error) {
if (this.state.type === 'error') {
return;
}

Expand Down Expand Up @@ -150,26 +145,29 @@ export class CcTileStatusCodes extends LitElement {
});
}

// updated and not udpate because we need this._chart before
// updated and not update because we need this._chart before
updated (changedProperties) {
if (changedProperties.has('state')) {

if (changedProperties.has('statusCodes') && !this.error) {
if (this.state.type === 'error') {
return;
}

this._skeleton = (this.statusCodes == null);
this._skeleton = this.state.type === 'loading';

const value = this._skeleton ? SKELETON_STATUS_CODES : this.statusCodes;
const statusCodes = this._skeleton ? SKELETON_STATUS_CODES : this.state.statusCodes;

this._empty = Object.keys(value).length === 0;
this._empty = Object.keys(statusCodes).length === 0;

// Raw status codes
this._labels = Object.keys(value);
this._labels = Object.keys(statusCodes);

// Status codes as categories (2xx, 3xx...)
this._chartLabels = this._skeleton
? this._labels.map(() => '???')
: this._labels.map((statusCode) => statusCode[0] + 'xx');

this._data = Object.values(value);
this._data = Object.values(statusCodes);

this._backgroundColor = this._skeleton
? this._labels.map(() => '#bbb')
Expand All @@ -194,8 +192,9 @@ export class CcTileStatusCodes extends LitElement {

render () {

const displayChart = (!this.error && !this._empty && !this._docs);
const displayError = (this.error && !this._docs);
const error = this.state.type === 'error';
const displayChart = (!error && !this._empty && !this._docs);
const displayError = (error && !this._docs);
const displayEmpty = (this._empty && !this._docs);
const displayDocs = (this._docs);

Expand Down
40 changes: 15 additions & 25 deletions src/components/cc-tile-status-codes/cc-tile-status-codes.smart.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,29 @@ import { getStatusCodesFromWarp10 } from '@clevercloud/client/esm/access-logs.js
import { getWarp10AccessLogsToken } from '@clevercloud/client/esm/api/v2/warp-10.js';
import { THIRTY_SECONDS } from '@clevercloud/client/esm/request.fetch-with-timeout.js';
import { ONE_DAY } from '@clevercloud/client/esm/with-cache.js';
import { defineSmartComponentWithObservables } from '../../lib/define-smart-component-with-observables.js';
import { LastPromise, unsubscribeWithSignal } from '../../lib/observables.js';
import { defineSmartComponent } from '../../lib/define-smart-component.js';
import { sendToApi, sendToWarp } from '../../lib/send-to-api.js';

defineSmartComponentWithObservables({
defineSmartComponent({
selector: 'cc-tile-status-codes',
params: {
apiConfig: { type: Object },
ownerId: { type: String },
appId: { type: String, required: false },
},
onConnect (container, component, context$, disconnectSignal) {

const statusCodes_lp = new LastPromise();

unsubscribeWithSignal(disconnectSignal, [

statusCodes_lp.error$.subscribe(console.error),
statusCodes_lp.error$.subscribe(() => (component.error = true)),
statusCodes_lp.value$.subscribe((statusCodes) => (component.statusCodes = statusCodes)),

context$.subscribe(({ apiConfig, ownerId, appId }) => {

component.error = false;
component.statusCodes = null;

if (apiConfig != null && ownerId != null) {
statusCodes_lp.push((signal) => fetchStatusCodes({ apiConfig, signal, ownerId, appId }));
}

}),

]);
onContextUpdate ({ context, updateComponent, signal }) {
const { apiConfig, ownerId, appId } = context;

updateComponent('state', { type: 'loading' });

fetchStatusCodes({ apiConfig, signal, ownerId, appId })
.then((statusCodes) => {
updateComponent('state', { type: 'loaded', statusCodes });
})
.catch((error) => {
console.log(error);
updateComponent('state', { type: 'error' });
});
},
});

Expand Down
22 changes: 11 additions & 11 deletions src/components/cc-tile-status-codes/cc-tile-status-codes.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,37 +26,37 @@ const conf = {
};

export const defaultStory = makeStory(conf, {
items: [{ statusCodes: DATA[0] }],
items: [{ state: { type: 'loaded', statusCodes: DATA[0] } }],
});

export const skeleton = makeStory(conf, {
items: [{}],
export const loading = makeStory(conf, {
items: [{ state: { type: 'loading' } }],
});

export const error = makeStory(conf, {
items: [{ error: true }],
items: [{ state: { type: 'error' } }],
});

export const empty = makeStory(conf, {
items: [{ statusCodes: [] }],
items: [{ state: { type: 'loaded', statusCodes: [] } }],
});

export const dataLoaded = makeStory(conf, {
items: [
{ statusCodes: DATA[0] },
{ statusCodes: DATA[1] },
{ statusCodes: DATA[2] },
{ state: { type: 'loaded', statusCodes: DATA[0] } },
{ state: { type: 'loaded', statusCodes: DATA[1] } },
{ state: { type: 'loaded', statusCodes: DATA[2] } },
],
});

export const simulations = makeStory(conf, {
items: [{}, {}],
simulations: [
storyWait(2000, ([component, componentError]) => {
component.statusCodes = DATA[0];
componentError.error = true;
component.state = { type: 'loaded', statusCodes: DATA[0] };
componentError.state = { type: 'error' };
}),
],
});

enhanceStoriesNames({ defaultStory, skeleton, error, empty, dataLoaded, simulations });
enhanceStoriesNames({ defaultStory, loading, error, empty, dataLoaded, simulations });
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
export type TileStatusCodesState = TileStatusCodesStateLoading | TileStatusCodesStateLoaded | TileStatusCodesStateError;

interface TileStatusCodesStateLoading {
type: 'loading';
}

interface TileStatusCodesStateLoaded {
type: 'loaded';
statusCodes: StatusCodesData;
}

interface TileStatusCodesStateError {
type: 'error';
}

interface StatusCodesData {
// Status code number as property.
// Number of requests as value.
[index: number]: number;
}


3 changes: 3 additions & 0 deletions web-dev-server.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const injectAuthForSmartComponentsPlugin = {
async transform (context) {

const {
WARP_10_HOST,
API_HOST,
API_OAUTH_TOKEN,
API_OAUTH_TOKEN_SECRET,
Expand All @@ -79,6 +80,7 @@ const injectAuthForSmartComponentsPlugin = {
updateRootContext({
apiConfig: {
WARP_10_HOST: '${WARP_10_HOST}',
API_HOST: '${API_HOST}',
API_OAUTH_TOKEN: '${API_OAUTH_TOKEN}',
API_OAUTH_TOKEN_SECRET: '${API_OAUTH_TOKEN_SECRET}',
Expand All @@ -95,6 +97,7 @@ const injectAuthForSmartComponentsPlugin = {
context.body += `
updateRootContext({
apiConfig: {
WARP_10_HOST: '${WARP_10_HOST}',
API_HOST: '${API_HOST}',
API_OAUTH_TOKEN: '${API_OAUTH_TOKEN}',
API_OAUTH_TOKEN_SECRET: '${API_OAUTH_TOKEN_SECRET}',
Expand Down

0 comments on commit fc5dc71

Please sign in to comment.