Skip to content

Commit bf8d1f5

Browse files
committed
fix: support arranging dashboard grids into rows
1 parent 9a76acc commit bf8d1f5

File tree

4 files changed

+84
-25
lines changed

4 files changed

+84
-25
lines changed

Diff for: plugins/plugin-codeflare-dashboard/src/components/Dashboard/index.tsx

+71-18
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type Props = {
6868
/** Scale up the grid? [default: 1] */
6969
scale?: number
7070

71-
grids: GridSpec[]
71+
grids: (null | GridSpec)[]
7272
}
7373

7474
type State = {
@@ -95,9 +95,12 @@ type State = {
9595
}
9696

9797
type GridProps = {
98+
/** Position of legend w.r.t. the grid UI [default: "below"] */
99+
legendPosition?: "right" | "below"
100+
98101
scale: Props["scale"]
99-
title: Props["grids"][number]["title"]
100-
states: Props["grids"][number]["states"]
102+
title: NonNullable<Props["grids"][number]>["title"]
103+
states: NonNullable<Props["grids"][number]>["states"]
101104
workers: State["workers"][number]
102105
}
103106

@@ -136,6 +139,8 @@ class Grid extends React.PureComponent<GridProps> {
136139
}
137140

138141
private get emptyCell(): TextProps {
142+
// TODO in light terminal themes, white-dim is a better choice
143+
// than gray-dim
139144
return this.cellFor({ color: "gray", dimColor: true })
140145
}
141146

@@ -199,16 +204,20 @@ class Grid extends React.PureComponent<GridProps> {
199204
return (
200205
<Box flexDirection="column">
201206
{A.filter(Boolean).map((AA, ridx) => (
207+
/* legend row */
202208
<Box key={ridx} flexDirection="row" justifyContent="space-around">
203209
{AA.filter(Boolean).map((_, cidx) => (
210+
/* legend entry (i.e. legend column) */
204211
<Box key={_.state} {...outerBoxProps}>
205212
<Box {...innerBoxProps} marginLeft={1}>
213+
{/* legend entry label */}
206214
<Box>
207215
<Text {..._.style} bold>
208216
{_.state}
209217
</Text>
210218
</Box>
211219

220+
{/* legend entry value */}
212221
<Box {...valueProps}>
213222
<Text {..._.style}>{C[ridx][cidx] /*.toString().padStart(maxLen)*/}</Text>
214223
</Box>
@@ -250,22 +259,45 @@ class Grid extends React.PureComponent<GridProps> {
250259
return <Text>{this.props.title}</Text>
251260
}
252261

262+
private get legendPosition() {
263+
return this.props.legendPosition
264+
}
265+
253266
public render() {
267+
const flexDirection = this.legendPosition === "below" ? "column" : "row"
268+
const alignItems = this.legendPosition === "below" ? "center" : "center"
269+
const legendBoxProps = this.legendPosition === "below" ? { marginTop: 1 } : { marginLeft: 2 }
270+
254271
return (
255-
<Box flexDirection="column" alignItems="center" justifyContent="center" paddingTop={1} paddingBottom={1}>
256-
{this.title()}
257-
{this.grid()}
258-
<Box marginTop={1}>{this.legend()}</Box>
272+
<Box
273+
flexDirection={flexDirection}
274+
alignItems={alignItems}
275+
justifyContent="center"
276+
paddingTop={1}
277+
paddingBottom={1}
278+
>
279+
{/* title and grid */}
280+
<Box flexDirection="column" alignItems="center">
281+
{this.title()}
282+
{this.grid()}
283+
</Box>
284+
285+
{/* legend */}
286+
<Box {...legendBoxProps}>{this.legend()}</Box>
259287
</Box>
260288
)
261289
}
262290
}
263291

264292
export default class Dashboard extends React.PureComponent<Props, State> {
293+
private get grids(): GridSpec[] {
294+
return this.props.grids.filter((_) => _ !== null) as GridSpec[]
295+
}
296+
265297
public componentDidMount() {
266298
this.setState({
267299
workers: [],
268-
watchers: this.props.grids.map((props, gridIdx) =>
300+
watchers: this.grids.map((props, gridIdx) =>
269301
props.initWatcher((model: UpdatePayload) => this.onUpdate(gridIdx, model))
270302
),
271303
agoInterval: setInterval(() => this.setState((curState) => ({ iter: (curState?.iter || 0) + 1 })), 5 * 1000),
@@ -388,26 +420,47 @@ export default class Dashboard extends React.PureComponent<Props, State> {
388420
// eslint-disable-next-line no-control-regex
389421
rows.push(<Text key={line + "-" + n}>{line.replace(/\x1b\x5B\[2J/g, "")}</Text>)
390422
}
391-
return <React.Fragment>{rows}</React.Fragment>
423+
return (
424+
<Box marginTop={1} flexDirection="column">
425+
{rows}
426+
</Box>
427+
)
428+
}
429+
}
430+
431+
private gridRows() {
432+
const rows: { widx: number; grid: NonNullable<Props["grids"][number]> }[][] = []
433+
for (let idx = 0, ridx = 0, widx = 0; idx < this.props.grids.length; idx++) {
434+
const grid = this.props.grids[idx]
435+
if (grid === null) {
436+
ridx++
437+
} else {
438+
if (!rows[ridx]) {
439+
rows[ridx] = []
440+
}
441+
rows[ridx].push({ grid, widx: widx++ })
442+
}
392443
}
444+
return rows
393445
}
394446

395447
private body() {
396-
return (
397-
<Box justifyContent="space-around">
398-
{this.props.grids.map((props, idx) => (
399-
<Box key={props.title} marginLeft={2}>
448+
return this.gridRows().map((row, ridx) => (
449+
<Box key={ridx} justifyContent="space-around">
450+
{row.map(({ grid, widx }) => (
451+
<Box key={grid.title} marginLeft={2}>
400452
<Grid
401-
key={props.title}
402-
title={props.title}
453+
key={grid.title}
454+
title={grid.title}
403455
scale={this.props.scale}
404-
states={props.states}
405-
workers={this.state?.workers[idx] || []}
456+
states={grid.states}
457+
workers={this.state?.workers[widx] || []}
458+
legendPosition={row.length === 1 ? "right" : "below"}
406459
/>
407460
</Box>
408461
))}
409462
</Box>
410-
)
463+
))
411464
}
412465

413466
public render() {

Diff for: plugins/plugin-codeflare-dashboard/src/controller/dashboard/db.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import Dashboard, { GridSpec } from "../../components/Dashboard/index.js"
2323
export default function db(
2424
profile: string,
2525
jobId: string,
26-
grids: null | GridSpec | GridSpec[],
26+
grids: null | GridSpec | (null | GridSpec)[],
2727
opts: Pick<Options, "scale">
2828
) {
2929
if (!grids || (Array.isArray(grids) && grids.length === 0)) {

Diff for: plugins/plugin-codeflare-dashboard/src/controller/dashboard/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,16 @@ export default async function dashboard(args: Arguments<Options>, cmd: "db" | "d
103103
: utilization(kind, tails, { demo, theme })
104104
}
105105

106-
const gridForA = async (kind: KindA): Promise<null | GridSpec | GridSpec[]> => {
106+
const gridForA = async (kind: KindA): Promise<null | GridSpec | (null | GridSpec)[]> => {
107107
if (kind === "all") {
108-
const grids = await Promise.all([
108+
return Promise.all([
109109
gridFor("status"),
110+
null, // newline
110111
gridFor("cpu%"),
111112
gridFor("mem%"),
112113
gridFor("gpu%"),
113114
gridFor("gpumem%"),
114115
])
115-
return grids.filter(Boolean)
116116
} else if (isSupportedGrid(kind)) {
117117
return gridFor(kind)
118118
} else {

Diff for: plugins/plugin-codeflare-dashboard/src/controller/dashboard/utilization.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ import { isValidTheme, themes } from "./themes/utilization.js"
2424
import { OnData, Worker, GridSpec } from "../../components/Dashboard/index.js"
2525
import { SupportedUtilizationGrid, defaultUtilizationThemes, providerFor } from "./grids.js"
2626

27-
type WorkerState = "<20%" | "<40%" | "<60%" | "<80%" | "<100%"
27+
/**
28+
* The discrete/quantized utilization states. Note: this currently is
29+
* assumed to be parallel to the ./themes/utilization.ts arrays.
30+
*/
31+
const states = ["<20%", "<40%", "<60%", "<80%", "<100%"]
2832

29-
const states: WorkerState[] = ["<20%", "<40%", "<60%", "<80%", "<100%"]
33+
/** Type declaration for quantized utilization states */
34+
type WorkerState = (typeof states)[number]
3035

3136
/**
3237
* Maintain a model of live data from a given set of file streams
@@ -40,7 +45,8 @@ class Live {
4045
private stateFor(util: string): WorkerState {
4146
const percent = parseInt(util.replace(/%$/, ""), 10)
4247
const bucketWidth = ~~(100 / states.length)
43-
return states[Math.min(~~(percent / bucketWidth), states.length - 1)]
48+
const bucketIdx = Math.min(~~(percent / bucketWidth), states.length - 1)
49+
return states[bucketIdx]
4450
}
4551

4652
public constructor(

0 commit comments

Comments
 (0)