Skip to content

Commit 53f21f3

Browse files
authored
Show middle state badges for running, starred and checked sub-rows (#2004)
* Add middle states indicator for exp table Add some stars to the Table stories data * Remove styles and coalescing operator
1 parent 1d75224 commit 53f21f3

File tree

11 files changed

+476
-52
lines changed

11 files changed

+476
-52
lines changed

webview/src/experiments/components/App.test.tsx

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
import { getRow } from '../../test/queries'
4343
import { dragAndDrop } from '../../test/dragDrop'
4444
import { DragEnterDirection } from '../../shared/components/dragDrop/util'
45+
import { setExperimentsAsStarred } from '../../test/tableDataFixture'
4546

4647
jest.mock('../../shared/api')
4748
jest.mock('../../util/styles')
@@ -372,6 +373,129 @@ describe('App', () => {
372373
})
373374
})
374375

376+
describe('Sub-rows middle states indicators', () => {
377+
const renderWithAllRowsExpanded = () => {
378+
render(<App />)
379+
380+
fireEvent(
381+
window,
382+
new MessageEvent('message', {
383+
data: {
384+
data: tableDataFixture,
385+
type: MessageToWebviewType.SET_DATA
386+
}
387+
})
388+
)
389+
}
390+
391+
const getMiddleStatesTestRow = () => {
392+
return getRow('4fb124a')
393+
}
394+
395+
const getCountIndicators = (element: HTMLElement) => {
396+
return within(element).queryAllByRole('marquee')
397+
}
398+
399+
const getCountIndicatorById = (row: HTMLElement, rowActionId: string) => {
400+
const checkboxRowAction = within(row).getByTestId(rowActionId)
401+
402+
return within(checkboxRowAction).queryByRole('marquee')
403+
}
404+
405+
const getCheckboxCountIndicator = (row: HTMLElement) => {
406+
return getCountIndicatorById(row, 'row-action-checkbox')
407+
}
408+
409+
const clickRowCheckbox = (label: string) => {
410+
const checkbox = within(getRow(label)).getByRole('checkbox')
411+
412+
fireEvent.click(checkbox)
413+
}
414+
415+
const selectSomeSubRows = () => {
416+
clickRowCheckbox('d1343a8')
417+
clickRowCheckbox('1ee5f2e')
418+
419+
return 2
420+
}
421+
422+
const starSomeSubRows = () => {
423+
const starredFixture = setExperimentsAsStarred(tableDataFixture, [
424+
'd1343a8',
425+
'1ee5f2e'
426+
])
427+
428+
fireEvent(
429+
window,
430+
new MessageEvent('message', {
431+
data: {
432+
data: starredFixture,
433+
type: MessageToWebviewType.SET_DATA
434+
}
435+
})
436+
)
437+
438+
return 2
439+
}
440+
441+
const toggleExpansion = (row: HTMLElement) => {
442+
const expandButton = within(row).getByTitle('Contract Row')
443+
444+
fireEvent.click(expandButton)
445+
}
446+
447+
it('should be hidden when the parent row is expanded', () => {
448+
renderWithAllRowsExpanded()
449+
const row = getMiddleStatesTestRow()
450+
const indicators = getCountIndicators(row)
451+
expect(indicators).toHaveLength(0)
452+
})
453+
454+
describe('Checkbox selection counter', () => {
455+
it('should not be visible if no sub-row was checked', () => {
456+
renderWithAllRowsExpanded()
457+
const row = getMiddleStatesTestRow()
458+
const indicator = getCheckboxCountIndicator(row)
459+
expect(indicator).not.toBeInTheDocument()
460+
})
461+
462+
it('should display the correct number of checked sub-rows when the parent is collapsed', () => {
463+
renderWithAllRowsExpanded()
464+
const row = getMiddleStatesTestRow()
465+
const numberOfSubrowsSelected = selectSomeSubRows()
466+
expect(getCheckboxCountIndicator(row)).not.toBeInTheDocument()
467+
toggleExpansion(row)
468+
const collapsed = getMiddleStatesTestRow()
469+
expect(getCheckboxCountIndicator(collapsed)).toHaveTextContent(
470+
`${numberOfSubrowsSelected}`
471+
)
472+
})
473+
})
474+
475+
describe('Stars counter', () => {
476+
it('should not be visible if no sub-row was starred', () => {
477+
renderWithAllRowsExpanded()
478+
const row = getMiddleStatesTestRow()
479+
const indicator = getCountIndicatorById(row, 'row-action-star')
480+
expect(indicator).not.toBeInTheDocument()
481+
})
482+
483+
it('should display the correct number of starred sub-rows when the parent is collapsed', () => {
484+
renderWithAllRowsExpanded()
485+
const row = getMiddleStatesTestRow()
486+
const numberOfSubrowsStarred = starSomeSubRows()
487+
expect(
488+
getCountIndicatorById(row, 'row-action-star')
489+
).not.toBeInTheDocument()
490+
toggleExpansion(row)
491+
const collapsed = getMiddleStatesTestRow()
492+
expect(
493+
getCountIndicatorById(collapsed, 'row-action-star')
494+
).toHaveTextContent(`${numberOfSubrowsStarred}`)
495+
})
496+
})
497+
})
498+
375499
describe('Toggle experiment status', () => {
376500
it('should send a message to the extension to toggle an experiment when the row is clicked', () => {
377501
render(<App />)

webview/src/experiments/components/Experiments.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ const getColumns = (columns: Column[]): TableColumn<Row>[] =>
9494
Header: ExperimentHeader,
9595
accessor: 'id',
9696
id: 'id',
97-
width: 150
97+
minWidth: 250,
98+
width: 250
9899
},
99100
{
100101
Cell: ({ value }: { value: string }) => {

webview/src/experiments/components/table/Cell.tsx

Lines changed: 111 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React from 'react'
22
import cx from 'classnames'
33
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react'
4+
import { Indicator, IndicatorWithJustTheCounter } from './Indicators'
45
import styles from './styles.module.scss'
56
import { CellProp, RowProp } from './interfaces'
67
import ClockIcon from '../../../shared/components/icons/Clock'
78
import { clickAndEnterProps } from '../../../util/props'
89
import { StarFull, StarEmpty } from '../../../shared/components/icons'
10+
import { pluralize } from '../../../util/strings'
911

1012
const RowExpansionButton: React.FC<RowProp> = ({ row }) =>
1113
row.canExpand ? (
@@ -31,27 +33,115 @@ const RowExpansionButton: React.FC<RowProp> = ({ row }) =>
3133
<span className={styles.rowArrowContainer} />
3234
)
3335

34-
export const FirstCell: React.FC<
35-
CellProp & {
36-
bulletColor?: string
37-
isRowSelected: boolean
38-
toggleExperiment: () => void
39-
toggleRowSelection: () => void
40-
toggleStarred: () => void
36+
export type RowActionsProps = {
37+
isRowSelected: boolean
38+
showSubRowStates: boolean
39+
starred?: boolean
40+
subRowStates: {
41+
selections: number
42+
stars: number
43+
plotSelections: number
4144
}
42-
> = ({
43-
cell,
44-
bulletColor,
45+
toggleRowSelection: () => void
46+
toggleStarred: () => void
47+
}
48+
49+
export type RowActionProps = {
50+
showSubRowStates: boolean
51+
subRowsAffected: number
52+
actionAppliedLabel: string
53+
children: React.ReactElement
54+
hidden?: boolean
55+
testId?: string
56+
}
57+
58+
export const RowAction: React.FC<RowActionProps> = ({
59+
showSubRowStates,
60+
subRowsAffected,
61+
actionAppliedLabel,
62+
children,
63+
hidden,
64+
testId
65+
}) => {
66+
const count = (showSubRowStates && subRowsAffected) || 0
67+
68+
return (
69+
<div
70+
className={cx(styles.rowActions, hidden && styles.hidden)}
71+
data-testid={testId}
72+
>
73+
<Indicator
74+
count={count}
75+
tooltipContent={
76+
count &&
77+
`${count} ${pluralize('sub-row', count)} ${actionAppliedLabel}.`
78+
}
79+
>
80+
{children}
81+
</Indicator>
82+
</div>
83+
)
84+
}
85+
86+
export const RowActions: React.FC<RowActionsProps> = ({
4587
isRowSelected,
46-
toggleExperiment,
88+
showSubRowStates,
89+
starred,
90+
subRowStates: { selections, stars },
4791
toggleRowSelection,
4892
toggleStarred
4993
}) => {
94+
return (
95+
<>
96+
<RowAction
97+
showSubRowStates={showSubRowStates}
98+
subRowsAffected={selections}
99+
actionAppliedLabel={'selected'}
100+
testId={'row-action-checkbox'}
101+
>
102+
<VSCodeCheckbox
103+
{...clickAndEnterProps(toggleRowSelection)}
104+
checked={isRowSelected}
105+
/>
106+
</RowAction>
107+
<RowAction
108+
showSubRowStates={showSubRowStates}
109+
subRowsAffected={stars}
110+
actionAppliedLabel={'starred'}
111+
testId={'row-action-star'}
112+
>
113+
<div
114+
className={styles.starSwitch}
115+
role="switch"
116+
aria-checked={starred}
117+
tabIndex={0}
118+
{...clickAndEnterProps(toggleStarred)}
119+
data-testid="star-icon"
120+
>
121+
{starred && <StarFull />}
122+
{!starred && <StarEmpty />}
123+
</div>
124+
</RowAction>
125+
</>
126+
)
127+
}
128+
129+
export const FirstCell: React.FC<
130+
CellProp &
131+
RowActionsProps & {
132+
bulletColor?: string
133+
toggleExperiment: () => void
134+
}
135+
> = ({ cell, bulletColor, toggleExperiment, ...rowActionsProps }) => {
50136
const { row, isPlaceholder } = cell
51137
const {
52-
original: { starred, queued }
138+
original: { queued }
53139
} = row
54140

141+
const {
142+
subRowStates: { plotSelections }
143+
} = rowActionsProps
144+
55145
return (
56146
<div
57147
{...cell.getCellProps({
@@ -63,29 +153,21 @@ export const FirstCell: React.FC<
63153
})}
64154
>
65155
<div className={styles.innerCell}>
66-
<div className={styles.rowActions}>
67-
<VSCodeCheckbox
68-
{...clickAndEnterProps(toggleRowSelection)}
69-
checked={isRowSelected}
70-
/>
71-
<div
72-
className={styles.starSwitch}
73-
role="switch"
74-
aria-checked={starred}
75-
tabIndex={0}
76-
{...clickAndEnterProps(toggleStarred)}
77-
data-testid="star-icon"
78-
>
79-
{starred && <StarFull />}
80-
{!starred && <StarEmpty />}
81-
</div>
82-
</div>
156+
<RowActions {...rowActionsProps} />
83157
<RowExpansionButton row={row} />
84158
<span
85159
className={styles.bullet}
86160
style={{ color: bulletColor }}
87161
{...clickAndEnterProps(toggleExperiment)}
88162
>
163+
<IndicatorWithJustTheCounter
164+
aria-label={'Sub-rows selected for plots'}
165+
count={plotSelections}
166+
tooltipContent={`${plotSelections} ${pluralize(
167+
'sub-row',
168+
plotSelections
169+
)} selected for plots.`}
170+
/>
89171
{queued && <ClockIcon />}
90172
</span>
91173
{isPlaceholder ? null : (

0 commit comments

Comments
 (0)