From 9e529ba22865bca242724fe494e9f4eafffcdc95 Mon Sep 17 00:00:00 2001 From: Wolmir Nemitz Date: Tue, 5 Jul 2022 12:02:46 -0300 Subject: [PATCH 1/8] Clear row selection on Esc --- .../src/experiments/components/App.test.tsx | 29 +++++++++++++++++++ .../experiments/components/table/Table.tsx | 12 +++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index 2077b57775..ef7e3e7fc6 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -842,6 +842,35 @@ describe('App', () => { const itemLabels = menuitems.map(item => item.textContent) expect(itemLabels).toContain('Star Experiments') }) + + it('should clear the row selection when the Escape key is pressed', () => { + render() + + fireEvent( + window, + new MessageEvent('message', { + data: { + data: tableDataFixture, + type: MessageToWebviewType.SET_DATA + } + }) + ) + + const firstRowCheckbox = within(getRow('4fb124a')).getByRole('checkbox') + fireEvent.click(firstRowCheckbox) + + const tailRow = within(getRow('42b8736')).getByRole('checkbox') + fireEvent.click(tailRow, { shiftKey: true }) + + const selectedRows = () => + screen.queryAllByRole('row', { selected: true }) + expect(selectedRows().length).toBe(4) + + fireEvent.keyUp(tailRow, { bubbles: true, key: 'Escape' }) + + jest.advanceTimersByTime(100) + expect(selectedRows().length).toBe(0) + }) }) describe('Star Experiments', () => { diff --git a/webview/src/experiments/components/table/Table.tsx b/webview/src/experiments/components/table/Table.tsx index b1c565328d..f187fd6f49 100644 --- a/webview/src/experiments/components/table/Table.tsx +++ b/webview/src/experiments/components/table/Table.tsx @@ -161,7 +161,17 @@ export const Table: React.FC = ({ return (
-
+
{ + if (e.key === 'Escape') { + clearSelectedRows?.() + } + }} + > Date: Tue, 5 Jul 2022 14:46:39 -0300 Subject: [PATCH 2/8] Fix bulk-selection --- .../components/table/RowSelectionContext.tsx | 26 ++++++++++++++-- .../experiments/components/table/Table.tsx | 30 +++++++++---------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/webview/src/experiments/components/table/RowSelectionContext.tsx b/webview/src/experiments/components/table/RowSelectionContext.tsx index 1f7f87a230..d6f6517c72 100644 --- a/webview/src/experiments/components/table/RowSelectionContext.tsx +++ b/webview/src/experiments/components/table/RowSelectionContext.tsx @@ -3,8 +3,9 @@ import { RowProp } from './interfaces' export interface RowSelectionContextValue { selectedRows: Record - toggleRowSelected: ((row: RowProp) => void) | undefined - batchSelection: ((batch: RowProp[]) => void) | undefined + lastSelectedRow?: RowProp + toggleRowSelected?: (row: RowProp) => void + batchSelection?: (batch: RowProp[]) => void clearSelectedRows: (() => void) | undefined } @@ -22,6 +23,8 @@ export const RowSelectionProvider: React.FC<{ children: React.ReactNode }> = ({ Record >({}) + const [selectionHistory, setSelectionHistory] = useState([]) + const toggleRowSelected = (rowProp: RowProp) => { const { row: { @@ -32,6 +35,7 @@ export const RowSelectionProvider: React.FC<{ children: React.ReactNode }> = ({ ...selectedRows, [id]: selectedRows[id] ? undefined : rowProp }) + setSelectionHistory([id, ...selectionHistory]) } const batchSelection = (batch: RowProp[]) => { @@ -47,17 +51,35 @@ export const RowSelectionProvider: React.FC<{ children: React.ReactNode }> = ({ } setSelectedRows(selectedRowsCopy) + setSelectionHistory([ + ...batch.map( + ({ + row: { + values: { id } + } + }) => id + ), + ...selectionHistory + ]) } const clearSelectedRows = () => { setSelectedRows({}) + setSelectionHistory([]) } + const lastSelectedRow = React.useMemo(() => { + const lastSelectedId = selectionHistory.find(id => selectedRows[id]) ?? '' + + return selectedRows[lastSelectedId] + }, [selectedRows, selectionHistory]) + return ( = ({ filteredCounts } = tableData - const { clearSelectedRows, batchSelection, selectedRows } = + const { clearSelectedRows, batchSelection, lastSelectedRow } = React.useContext(RowSelectionContext) const tableRef = useRef(null) @@ -139,24 +139,22 @@ export const Table: React.FC = ({ useClickOutside(tableRef, clickOutsideHandler) const batchRowSelection = React.useCallback( - ({ row: { flatIndex } }: RowProp) => { - const firstSelection = flatRows.find( - ({ values: { id } }) => selectedRows[id] - ) + ({ row: { id } }: RowProp) => { + const lastSelectedRowId = lastSelectedRow?.row.id ?? '' + const lastIndex = + flatRows.findIndex(flatRow => flatRow.id === lastSelectedRowId) || 1 + const selectedIndex = + flatRows.findIndex(flatRow => flatRow.id === id) || 1 + const rangeStart = Math.min(lastIndex, selectedIndex) + const rangeEnd = Math.max(lastIndex, selectedIndex) - const firstIndex = firstSelection?.flatIndex || 1 + const batch = flatRows + .slice(rangeStart, rangeEnd + 1) + .map(row => ({ row })) - if (flatIndex >= firstIndex) { - const batch = flatRows - .filter( - row => row.flatIndex > firstIndex && row.flatIndex <= flatIndex - ) - .map(row => ({ row })) - - batchSelection?.(batch) - } + batchSelection?.(batch) }, - [selectedRows, flatRows, batchSelection] + [flatRows, batchSelection, lastSelectedRow] ) return ( From 7dfd91509d76cb8dee7f4f207710a41c41b19b26 Mon Sep 17 00:00:00 2001 From: Wolmir Nemitz Date: Tue, 5 Jul 2022 16:15:51 -0300 Subject: [PATCH 3/8] Add ctx-menu option to clear row selection --- .../src/experiments/components/App.test.tsx | 37 +++++++++++++++++++ .../src/experiments/components/table/Row.tsx | 8 +++- .../messagesMenu/MessagesMenuOption.tsx | 8 ++-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index ef7e3e7fc6..f7d240d7a6 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -843,6 +843,43 @@ describe('App', () => { expect(itemLabels).toContain('Star Experiments') }) + it('should present the Clear selected rows option when multiple rows are selected', () => { + render() + + fireEvent( + window, + new MessageEvent('message', { + data: { + data: { + ...tableDataFixture, + hasRunningExperiment: false + }, + type: MessageToWebviewType.SET_DATA + } + }) + ) + + const firstRowCheckbox = within(getRow('4fb124a')).getByRole('checkbox') + fireEvent.click(firstRowCheckbox) + + const tailRow = within(getRow('42b8736')).getByRole('checkbox') + fireEvent.click(tailRow, { shiftKey: true }) + + const selectedRows = () => + screen.queryAllByRole('row', { selected: true }) + expect(selectedRows().length).toBe(4) + + const target = screen.getByText('4fb124a') + fireEvent.contextMenu(target, { bubbles: true }) + + jest.advanceTimersByTime(100) + const clearOption = screen.getByText('Clear row selection') + fireEvent.click(clearOption) + + jest.advanceTimersByTime(100) + expect(selectedRows().length).toBe(0) + }) + it('should clear the row selection when the Escape key is pressed', () => { render() diff --git a/webview/src/experiments/components/table/Row.tsx b/webview/src/experiments/components/table/Row.tsx index c84b630dff..bcb5acbe0a 100644 --- a/webview/src/experiments/components/table/Row.tsx +++ b/webview/src/experiments/components/table/Row.tsx @@ -92,7 +92,13 @@ const getMultiSelectMenuOptions = (selectedRowsList: RowProp[]) => { MessageFromWebviewType.REMOVE_EXPERIMENT, hideRemoveOption, true - ) + ), + { + divider: true, + id: 'clear-selection', + keyboardShortcut: 'Esc', + label: 'Clear row selection' + } ] } diff --git a/webview/src/shared/components/messagesMenu/MessagesMenuOption.tsx b/webview/src/shared/components/messagesMenu/MessagesMenuOption.tsx index ff4ff5f1d0..f8cf4087b2 100644 --- a/webview/src/shared/components/messagesMenu/MessagesMenuOption.tsx +++ b/webview/src/shared/components/messagesMenu/MessagesMenuOption.tsx @@ -7,16 +7,17 @@ import { sendMessage } from '../../vscode' export interface MessagesMenuOptionProps { id: string label: string - message: MessageFromWebview + message?: MessageFromWebview hidden?: boolean divider?: boolean + keyboardShortcut?: string } export const MessagesMenuOption: React.FC< MessagesMenuOptionProps & { onOptionSelected?: () => void } -> = ({ label, message, divider, onOptionSelected }) => { +> = ({ label, message, divider, onOptionSelected, keyboardShortcut }) => { const sendTheMessage = () => { - sendMessage(message) + !!message && sendMessage(message) onOptionSelected?.() } @@ -44,6 +45,7 @@ export const MessagesMenuOption: React.FC< > {label}
+ {keyboardShortcut &&
{keyboardShortcut}
}
) From c335c91e2a3c4539b60ad6176ea47fbd24199981 Mon Sep 17 00:00:00 2001 From: Wolmir Nemitz Date: Tue, 5 Jul 2022 18:37:10 -0300 Subject: [PATCH 4/8] Enable specific options when an experiment is running --- .../src/experiments/components/table/Row.tsx | 93 ++++++++++++++----- .../experiments/components/table/Table.tsx | 10 +- .../components/table/interfaces.ts | 1 + 3 files changed, 78 insertions(+), 26 deletions(-) diff --git a/webview/src/experiments/components/table/Row.tsx b/webview/src/experiments/components/table/Row.tsx index bcb5acbe0a..1c55957140 100644 --- a/webview/src/experiments/components/table/Row.tsx +++ b/webview/src/experiments/components/table/Row.tsx @@ -46,7 +46,10 @@ const experimentMenuOption = ( } as MessagesMenuOptionProps } -const getMultiSelectMenuOptions = (selectedRowsList: RowProp[]) => { +const getMultiSelectMenuOptions = ( + selectedRowsList: RowProp[], + hasRunningExperiment: boolean +) => { const unstarredExperiments = selectedRowsList.filter( ({ row: { @@ -67,7 +70,8 @@ const getMultiSelectMenuOptions = (selectedRowsList: RowProp[]) => { .filter(value => value.row.depth === 1) .map(value => value.row.values.id) - const hideRemoveOption = removableRowIds.length !== selectedRowsList.length + const hideRemoveOption = + removableRowIds.length !== selectedRowsList.length || hasRunningExperiment const toggleStarOption = (ids: string[], label: string) => experimentMenuOption( @@ -102,57 +106,89 @@ const getMultiSelectMenuOptions = (selectedRowsList: RowProp[]) => { ] } +const getWorkspaceOptions = ( + withId: ( + label: string, + type: MessageFromWebviewType, + hidden?: boolean, + divider?: boolean + ) => MessagesMenuOptionProps, + isWorkspace: boolean, + projectHasCheckpoints: boolean, + hideVaryAndRun: boolean, + depth: number +) => { + const isNotCheckpoint = depth <= 1 || isWorkspace + + return [ + withId( + projectHasCheckpoints ? 'Modify and Resume' : 'Modify and Run', + MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_AND_RUN, + !isNotCheckpoint, + !hideVaryAndRun + ), + withId( + 'Modify, Reset and Run', + MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_RESET_AND_RUN, + !isNotCheckpoint || !projectHasCheckpoints + ), + withId( + 'Modify and Queue', + MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_AND_QUEUE, + !isNotCheckpoint + ) + ] +} + const getSingleSelectMenuOptions = ( id: string, isWorkspace: boolean, projectHasCheckpoints: boolean, + hasRunningExperiment: boolean, depth: number, queued?: boolean, starred?: boolean ) => { - const isNotCheckpoint = depth <= 1 || isWorkspace - const canApplyOrCreateBranch = queued || isWorkspace || depth <= 0 + const hideApplyAndCreateBranch = queued || isWorkspace || depth <= 0 const withId = ( label: string, type: MessageFromWebviewType, hidden?: boolean, divider?: boolean - ) => experimentMenuOption(id, label, type, hidden, divider) + ) => + experimentMenuOption( + id, + label, + type, + hidden || hasRunningExperiment, + divider + ) return [ withId( 'Apply to Workspace', MessageFromWebviewType.APPLY_EXPERIMENT_TO_WORKSPACE, - canApplyOrCreateBranch + hideApplyAndCreateBranch ), withId( 'Create new Branch', MessageFromWebviewType.CREATE_BRANCH_FROM_EXPERIMENT, - canApplyOrCreateBranch - ), - withId( - projectHasCheckpoints ? 'Modify and Resume' : 'Modify and Run', - MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_AND_RUN, - !isNotCheckpoint, - !canApplyOrCreateBranch + hideApplyAndCreateBranch ), - withId( - 'Modify, Reset and Run', - MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_RESET_AND_RUN, - !isNotCheckpoint || !projectHasCheckpoints - ), - withId( - 'Modify and Queue', - MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_AND_QUEUE, - !isNotCheckpoint + ...getWorkspaceOptions( + withId, + isWorkspace, + projectHasCheckpoints, + hideApplyAndCreateBranch, + depth ), experimentMenuOption( [id], starred ? 'Unstar Experiment' : 'Star Experiment', MessageFromWebviewType.TOGGLE_EXPERIMENT_STAR, isWorkspace, - true + !hasRunningExperiment ), withId( 'Remove', @@ -167,6 +203,7 @@ const getContextMenuOptions = ( id: string, isWorkspace: boolean, projectHasCheckpoints: boolean, + hasRunningExperiment: boolean, depth: number, selectedRows: Record, queued?: boolean, @@ -179,12 +216,13 @@ const getContextMenuOptions = ( return cond( isFromSelection && selectedRowsList.length > 1, - () => getMultiSelectMenuOptions(selectedRowsList), + () => getMultiSelectMenuOptions(selectedRowsList, hasRunningExperiment), () => getSingleSelectMenuOptions( id, isWorkspace, projectHasCheckpoints, + hasRunningExperiment, depth, queued, starred @@ -193,6 +231,7 @@ const getContextMenuOptions = ( } export const RowContextMenu: React.FC = ({ + hasRunningExperiment = false, projectHasCheckpoints = false, row: { original: { queued, starred }, @@ -210,6 +249,7 @@ export const RowContextMenu: React.FC = ({ id, isWorkspace, projectHasCheckpoints, + hasRunningExperiment, depth, selectedRows, queued, @@ -222,7 +262,8 @@ export const RowContextMenu: React.FC = ({ depth, id, projectHasCheckpoints, - selectedRows + selectedRows, + hasRunningExperiment ]) return ( @@ -273,6 +314,7 @@ export const RowContent: React.FC< changes, contextMenuDisabled, projectHasCheckpoints, + hasRunningExperiment, batchRowSelection }): JSX.Element => { const { @@ -325,6 +367,7 @@ export const RowContent: React.FC< } > diff --git a/webview/src/experiments/components/table/Table.tsx b/webview/src/experiments/components/table/Table.tsx index 76d9f85d16..5edc0b4cea 100644 --- a/webview/src/experiments/components/table/Table.tsx +++ b/webview/src/experiments/components/table/Table.tsx @@ -14,6 +14,7 @@ export const NestedRow: React.FC< instance, contextMenuDisabled, projectHasCheckpoints, + hasRunningExperiment, batchRowSelection }) => { instance.prepareRow(row) @@ -23,6 +24,7 @@ export const NestedRow: React.FC< className={styles.nestedRow} contextMenuDisabled={contextMenuDisabled} projectHasCheckpoints={projectHasCheckpoints} + hasRunningExperiment={hasRunningExperiment} batchRowSelection={batchRowSelection} /> ) @@ -35,6 +37,7 @@ export const ExperimentGroup: React.FC< instance, contextMenuDisabled, projectHasCheckpoints, + hasRunningExperiment, batchRowSelection }) => { instance.prepareRow(row) @@ -50,6 +53,7 @@ export const ExperimentGroup: React.FC< instance={instance} contextMenuDisabled={contextMenuDisabled} projectHasCheckpoints={projectHasCheckpoints} + hasRunningExperiment={hasRunningExperiment} batchRowSelection={batchRowSelection} /> {row.isExpanded && @@ -60,6 +64,7 @@ export const ExperimentGroup: React.FC< key={row.id} contextMenuDisabled={contextMenuDisabled} projectHasCheckpoints={projectHasCheckpoints} + hasRunningExperiment={hasRunningExperiment} batchRowSelection={batchRowSelection} /> ))} @@ -75,6 +80,7 @@ export const TableBody: React.FC< changes, contextMenuDisabled, projectHasCheckpoints, + hasRunningExperiment, batchRowSelection }) => { instance.prepareRow(row) @@ -93,6 +99,7 @@ export const TableBody: React.FC< ))} @@ -183,7 +191,7 @@ export const Table: React.FC = ({ instance={instance} key={row.id} changes={changes} - contextMenuDisabled={hasRunningExperiment} + hasRunningExperiment={hasRunningExperiment} projectHasCheckpoints={hasCheckpoints} batchRowSelection={batchRowSelection} /> diff --git a/webview/src/experiments/components/table/interfaces.ts b/webview/src/experiments/components/table/interfaces.ts index 02320bae31..8dc2626d3e 100644 --- a/webview/src/experiments/components/table/interfaces.ts +++ b/webview/src/experiments/components/table/interfaces.ts @@ -16,6 +16,7 @@ export interface WithChanges { export interface RowProp { row: Row contextMenuDisabled?: boolean + hasRunningExperiment?: boolean projectHasCheckpoints?: boolean } From 4aa35837a2ce1fc5c172a2805c1001c6a06f5e20 Mon Sep 17 00:00:00 2001 From: Wolmir Nemitz Date: Wed, 6 Jul 2022 14:21:43 -0300 Subject: [PATCH 5/8] Prevent the bulk selection of collapsed checkpoints --- .../src/experiments/components/App.test.tsx | 36 +++++++++++++++++++ .../experiments/components/table/Table.tsx | 10 ++++++ 2 files changed, 46 insertions(+) diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index f7d240d7a6..95c0d059d0 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -843,6 +843,42 @@ describe('App', () => { expect(itemLabels).toContain('Star Experiments') }) + it('should not include collapsed experiments in the bulk selection', () => { + render() + + fireEvent( + window, + new MessageEvent('message', { + data: { + data: { + ...tableDataFixture, + hasRunningExperiment: false + }, + type: MessageToWebviewType.SET_DATA + } + }) + ) + + const testBranchContractButton = within(getRow('42b8736')).getByTitle( + 'Contract Row' + ) + fireEvent.click(testBranchContractButton) + + jest.advanceTimersByTime(100) + const firstRowCheckbox = within(getRow('4fb124a')).getByRole('checkbox') + fireEvent.click(firstRowCheckbox) + + const tailRow = within(getRow('22e40e1')).getByRole('checkbox') + fireEvent.click(tailRow, { shiftKey: true }) + + fireEvent.click(testBranchContractButton) + + jest.advanceTimersByTime(100) + expect(getRow('42b8736')).toHaveAttribute('aria-selected', 'true') + expect(getRow('2173124')).not.toHaveAttribute('aria-selected', 'true') + expect(getRow('9523bde')).not.toHaveAttribute('aria-selected', 'true') + }) + it('should present the Clear selected rows option when multiple rows are selected', () => { render() diff --git a/webview/src/experiments/components/table/Table.tsx b/webview/src/experiments/components/table/Table.tsx index 5edc0b4cea..469c4c5122 100644 --- a/webview/src/experiments/components/table/Table.tsx +++ b/webview/src/experiments/components/table/Table.tsx @@ -156,8 +156,18 @@ export const Table: React.FC = ({ const rangeStart = Math.min(lastIndex, selectedIndex) const rangeEnd = Math.max(lastIndex, selectedIndex) + const collapsedIds = flatRows + .filter(flatRow => !flatRow.isExpanded) + .map(flatRow => flatRow.id) + const batch = flatRows .slice(rangeStart, rangeEnd + 1) + .filter( + flatRow => + !collapsedIds.some(collapsedId => + flatRow.id.startsWith(`${collapsedId}.`) + ) + ) .map(row => ({ row })) batchSelection?.(batch) From 352e7fb2ffb2d8a2768a2932498da48a6e940005 Mon Sep 17 00:00:00 2001 From: Wolmir Nemitz Date: Wed, 6 Jul 2022 15:18:18 -0300 Subject: [PATCH 6/8] Fix function name --- webview/src/experiments/components/table/Row.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webview/src/experiments/components/table/Row.tsx b/webview/src/experiments/components/table/Row.tsx index 1c55957140..0332dfc593 100644 --- a/webview/src/experiments/components/table/Row.tsx +++ b/webview/src/experiments/components/table/Row.tsx @@ -106,7 +106,7 @@ const getMultiSelectMenuOptions = ( ] } -const getWorkspaceOptions = ( +const getRunResumeOptions = ( withId: ( label: string, type: MessageFromWebviewType, @@ -176,7 +176,7 @@ const getSingleSelectMenuOptions = ( MessageFromWebviewType.CREATE_BRANCH_FROM_EXPERIMENT, hideApplyAndCreateBranch ), - ...getWorkspaceOptions( + ...getRunResumeOptions( withId, isWorkspace, projectHasCheckpoints, From 06054118d6e712ead64b44af30c8b860f96f3b4a Mon Sep 17 00:00:00 2001 From: Wolmir Nemitz Date: Wed, 6 Jul 2022 15:23:18 -0300 Subject: [PATCH 7/8] Change the options order to match the tree options --- webview/src/experiments/components/App.test.tsx | 2 +- webview/src/experiments/components/table/Row.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index 95c0d059d0..eb1ca4e5ab 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -746,8 +746,8 @@ describe('App', () => { const menuitems = screen.getAllByRole('menuitem') const itemLabels = menuitems.map(item => item.textContent) expect(itemLabels).toStrictEqual([ - 'Modify and Resume', 'Modify, Reset and Run', + 'Modify and Resume', 'Modify and Queue', 'Star Experiment' ]) diff --git a/webview/src/experiments/components/table/Row.tsx b/webview/src/experiments/components/table/Row.tsx index 0332dfc593..7991374c5a 100644 --- a/webview/src/experiments/components/table/Row.tsx +++ b/webview/src/experiments/components/table/Row.tsx @@ -121,17 +121,17 @@ const getRunResumeOptions = ( const isNotCheckpoint = depth <= 1 || isWorkspace return [ + withId( + 'Modify, Reset and Run', + MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_RESET_AND_RUN, + !isNotCheckpoint || !projectHasCheckpoints + ), withId( projectHasCheckpoints ? 'Modify and Resume' : 'Modify and Run', MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_AND_RUN, !isNotCheckpoint, !hideVaryAndRun ), - withId( - 'Modify, Reset and Run', - MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_RESET_AND_RUN, - !isNotCheckpoint || !projectHasCheckpoints - ), withId( 'Modify and Queue', MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_AND_QUEUE, From 40c4f5657fbf480c0472cb7c13d5946c42d7f960 Mon Sep 17 00:00:00 2001 From: Wolmir Nemitz Date: Wed, 6 Jul 2022 15:38:38 -0300 Subject: [PATCH 8/8] Add some more tests for bulk selection --- .../src/experiments/components/App.test.tsx | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index eb1ca4e5ab..7eede5b21e 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -843,6 +843,35 @@ describe('App', () => { expect(itemLabels).toContain('Star Experiments') }) + it('should allow batch selection from the bottom up too', () => { + render() + + fireEvent( + window, + new MessageEvent('message', { + data: { + data: { + ...tableDataFixture, + hasRunningExperiment: false + }, + type: MessageToWebviewType.SET_DATA + } + }) + ) + + const firstRowCheckbox = within(getRow('4fb124a')).getByRole('checkbox') + const tailRow = within(getRow('42b8736')).getByRole('checkbox') + fireEvent.click(tailRow) + fireEvent.click(firstRowCheckbox, { shiftKey: true }) + + const selectedRows = () => screen.getAllByRole('row', { selected: true }) + expect(selectedRows()).toHaveLength(4) + + const anotherRow = within(getRow('2173124')).getByRole('checkbox') + fireEvent.click(anotherRow, { shiftKey: true }) + expect(selectedRows()).toHaveLength(5) + }) + it('should not include collapsed experiments in the bulk selection', () => { render()