From b4a72b5641e9cae6b6844f1b2e0152be1edef509 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 15 Jun 2023 18:08:49 -0700 Subject: [PATCH 1/5] Basic recommendation-chart story with knobs --- .../charts/recommendation-chart.stories.js | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 ui/stories/charts/recommendation-chart.stories.js diff --git a/ui/stories/charts/recommendation-chart.stories.js b/ui/stories/charts/recommendation-chart.stories.js new file mode 100644 index 00000000000..c3a42c7a8c0 --- /dev/null +++ b/ui/stories/charts/recommendation-chart.stories.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import hbs from 'htmlbars-inline-precompile'; +import DelayedTruth from '../utils/delayed-truth'; +import { withKnobs, optionsKnob, number } from '@storybook/addon-knobs'; + +export default { + title: 'Charts/Recomendation CHart', + decorators: [withKnobs], +}; + +export let Configurable = () => { + return { + template: hbs` + + {{#if delayedTruth.complete}} + + {{/if}} + `, + context: contextFactory(), + }; +}; + +function contextFactory() { + const numberConfig = { range: true, min: 0, max: 1000, step: 1 }; + return { + delayedTruth: DelayedTruth.create(), + resource: optionsKnob( + 'Resource', + { Cpu: 'CPU', Memory: 'MemoryMB' }, + 'CPU', + { display: 'inline-radio' } + ), + current: number('Current', 100, numberConfig), + recommendedValue: number('Recommendation', 300, numberConfig), + stats: { + mean: number('Stat: mean', 150, numberConfig), + p99: number('Stat: p99', 600, numberConfig), + max: number('Stat: max', 650, numberConfig), + }, + }; +} From 7e1516b436be1c35caa5da81ca666cb39ff01563 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 15 Jun 2023 18:39:00 -0700 Subject: [PATCH 2/5] Standard usage story --- .../charts/recommendation-chart.stories.js | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/ui/stories/charts/recommendation-chart.stories.js b/ui/stories/charts/recommendation-chart.stories.js index c3a42c7a8c0..33455a2d540 100644 --- a/ui/stories/charts/recommendation-chart.stories.js +++ b/ui/stories/charts/recommendation-chart.stories.js @@ -5,10 +5,15 @@ import hbs from 'htmlbars-inline-precompile'; import DelayedTruth from '../utils/delayed-truth'; -import { withKnobs, optionsKnob, number } from '@storybook/addon-knobs'; +import { + withKnobs, + optionsKnob, + number, + boolean, +} from '@storybook/addon-knobs'; export default { - title: 'Charts/Recomendation CHart', + title: 'Charts/Recomendation Chart', decorators: [withKnobs], }; @@ -22,6 +27,7 @@ export let Configurable = () => { @currentValue={{current}} @recommendedValue={{recommendedValue}} @stats={{stats}} + @disabled={{disabled}} /> {{/if}} `, @@ -29,6 +35,65 @@ export let Configurable = () => { }; }; +export let Standard = () => { + return { + template: hbs` + +
+ {{#if delayedTruth.complete}} + + +
+ + + {{/if}} +
+ `, + context: { + delayedTruth: DelayedTruth.create(), + cpu: { + current: 100, + recommendedValue: 600, + stats: { + mean: 300, + p99: 500, + max: 525, + }, + }, + mem: { + current: 2048, + recommendedValue: 256, + stats: { + mean: 140, + p99: 215, + max: 225, + }, + }, + }, + }; +}; + function contextFactory() { const numberConfig = { range: true, min: 0, max: 1000, step: 1 }; return { @@ -46,5 +111,6 @@ function contextFactory() { p99: number('Stat: p99', 600, numberConfig), max: number('Stat: max', 650, numberConfig), }, + disabled: boolean('Disabled', false), }; } From ea713212bbbbfe1a59260a045a919dc1382db26e Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 22 Jun 2023 15:03:32 -0700 Subject: [PATCH 3/5] TopoViz child component stories --- ui/stories/charts/topo-viz.stories.js | 99 +++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 ui/stories/charts/topo-viz.stories.js diff --git a/ui/stories/charts/topo-viz.stories.js b/ui/stories/charts/topo-viz.stories.js new file mode 100644 index 00000000000..c0d23f2fcc5 --- /dev/null +++ b/ui/stories/charts/topo-viz.stories.js @@ -0,0 +1,99 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import hbs from 'htmlbars-inline-precompile'; +import DelayedTruth from '../utils/delayed-truth'; +import { withKnobs, boolean } from '@storybook/addon-knobs'; +import { scaleLinear } from 'd3-scale'; +import faker from 'faker'; + +export default { + title: 'Charts/Topo Viz', + decorators: [withKnobs], +}; + +const nodeGen = (name, datacenter, memory, cpu, allocations = []) => ({ + datacenter, + memory, + cpu, + node: { name, isEligible: true, isDraining: false }, + allocations: allocations.map((alloc) => ({ + memory: alloc.memory, + cpu: alloc.cpu, + memoryPercent: alloc.memory / memory, + cpuPercent: alloc.cpu / cpu, + allocation: { + id: faker.random.uuid(), + isScheduled: true, + clientStatus: alloc.clientStatus, + }, + })), +}); + +export let Node = () => ({ + template: hbs` + + {{#if delayedTruth.complete}} + + {{/if}} + `, + context: { + delayedTruth: DelayedTruth.create(), + isDense: boolean('isDense', false), + heightScale: scaleLinear().range([15, 40]).domain([100, 1000]), + node: nodeGen('Node One', 'dc1', 1000, 1000, [ + { memory: 100, cpu: 100, clientStatus: 'pending' }, + { memory: 250, cpu: 300, clientStatus: 'running' }, + { memory: 300, cpu: 200, clientStatus: 'running' }, + ]), + }, +}); + +export let Datacenter = () => ({ + template: hbs` + + {{#if delayedTruth.complete}} + + {{/if}} + `, + context: { + delayedTruth: DelayedTruth.create(), + isSingleColumn: boolean('isSingleColumn', false), + isDense: boolean('isDense', false), + heightScale: scaleLinear().range([15, 40]).domain([100, 1000]), + dc: { + name: 'dc1', + nodes: [ + nodeGen('Node One', 'dc1', 1000, 1000, [ + { memory: 100, cpu: 100, clientStatus: 'pending' }, + { memory: 250, cpu: 300, clientStatus: 'running' }, + { memory: 300, cpu: 200, clientStatus: 'running' }, + ]), + nodeGen('And Two', 'dc1', 500, 1000, [ + { memory: 100, cpu: 100, clientStatus: 'pending' }, + { memory: 250, cpu: 300, clientStatus: 'running' }, + { memory: 100, cpu: 100, clientStatus: 'running' }, + { memory: 100, cpu: 100, clientStatus: 'running' }, + { memory: 100, cpu: 100, clientStatus: 'running' }, + ]), + nodeGen('Three', 'dc1', 500, 500, [ + { memory: 100, cpu: 300, clientStatus: 'running' }, + { memory: 300, cpu: 200, clientStatus: 'pending' }, + ]), + ], + }, + }, +}); + +export let FullViz = () => {}; From e7952b6e2582ae867d2982512d06f10e7a03eef7 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 22 Jun 2023 16:17:17 -0700 Subject: [PATCH 4/5] Full TopoViz story --- ui/stories/charts/topo-viz.stories.js | 65 ++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/ui/stories/charts/topo-viz.stories.js b/ui/stories/charts/topo-viz.stories.js index c0d23f2fcc5..8a1c4df4e3d 100644 --- a/ui/stories/charts/topo-viz.stories.js +++ b/ui/stories/charts/topo-viz.stories.js @@ -32,6 +32,43 @@ const nodeGen = (name, datacenter, memory, cpu, allocations = []) => ({ })), }); +const nodeModelGen = (datacenter, id, name, resources = '2000/1000') => { + const [cpu, memory] = resources.split('/'); + return { + datacenter, + id, + name, + isEligible: true, + isDraining: false, + resources: { cpu, memory }, + }; +}; + +const allocModelGen = ( + id, + taskGroupName, + clientStatus, + nodeId, + jobId, + resources = '100/100' +) => { + const [cpu, memory] = resources.split('/'); + return { + id, + taskGroupName, + clientStatus, + isScheduled: true, + allocatedResources: { cpu, memory }, + belongsTo(t) { + return { + id() { + return t === 'node' ? nodeId : jobId; + }, + }; + }, + }; +}; + export let Node = () => ({ template: hbs` @@ -96,4 +133,30 @@ export let Datacenter = () => ({ }, }); -export let FullViz = () => {}; +export let FullViz = () => ({ + template: hbs` + {{#if delayedTruth.complete}} + + {{/if}} + `, + context: { + delayedTruth: DelayedTruth.create(), + nodes: [ + nodeModelGen('dc1', '1', 'pdx-1', '2000/1000'), + nodeModelGen('dc1', '2', 'pdx-2', '2000/1000'), + nodeModelGen('dc1', '3', 'pdx-3', '2000/3000'), + nodeModelGen('dc2', '4', 'yyz-1', '2000/1000'), + nodeModelGen('dc2', '5', 'yyz-2', '2000/2000'), + ], + allocations: [ + allocModelGen('1', 'name', 'running', '1', 'job-1', '200/500'), + allocModelGen('1', 'name', 'running', '5', 'job-1', '200/500'), + ], + setAllocation() { + console.log('hmm'); + }, + }, +}); From 2e2e396d26c945576c0fd7f4905c11cbb28abd4a Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 22 Jun 2023 16:55:36 -0700 Subject: [PATCH 5/5] TopoViz story that is sourced from Mirage Unfortunately due to the split build nature of the ember app and storybook it isn't possible to import mirage in the storybook context to control scenarios via a knob :( --- ui/stories/charts/topo-viz.stories.js | 37 +++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/ui/stories/charts/topo-viz.stories.js b/ui/stories/charts/topo-viz.stories.js index 8a1c4df4e3d..dc4bb327732 100644 --- a/ui/stories/charts/topo-viz.stories.js +++ b/ui/stories/charts/topo-viz.stories.js @@ -6,6 +6,8 @@ import hbs from 'htmlbars-inline-precompile'; import DelayedTruth from '../utils/delayed-truth'; import { withKnobs, boolean } from '@storybook/addon-knobs'; +import { getOwner } from '@ember/application'; +import { tracked } from '@glimmer/tracking'; import { scaleLinear } from 'd3-scale'; import faker from 'faker'; @@ -155,8 +157,39 @@ export let FullViz = () => ({ allocModelGen('1', 'name', 'running', '1', 'job-1', '200/500'), allocModelGen('1', 'name', 'running', '5', 'job-1', '200/500'), ], - setAllocation() { - console.log('hmm'); + }, +}); + +export let EmberData = () => ({ + template: hbs` +
+

This visualization uses data from mirage.

+

Change the mirage scenario to see different cluster states visualized.

+
+ {{#if (and delayedTruth.complete nodes allocations)}} + + {{/if}} + `, + context: { + delayedTruth: DelayedTruth.create(), + nodes: tracked([]), + allocations: tracked([]), + + async init() { + this._super(...arguments); + + const owner = getOwner(this); + const store = owner.lookup('service:store'); + + this.nodes = await store.query('node', { resources: true }); + this.allocations = await store.query('allocation', { + resources: true, + task_states: false, + namespace: '*', + }); }, }, });