Skip to content

Commit 0d9def9

Browse files
wolmirStephanie Roysroy3
authored
Add missing "Hide Column" option to the other headers (#2593)
* Add missing options to the other headers * Remove timestamp restriction * Refactor context-menu options in table header (#2643) * Move context menu stuff lower level component * Refactor TableHeaderCell * Extend sorting options to placeholders * Add test for placeholder options * Fix the options in the Experiment columns * Add a failling test for the parent/child mismatch * Add PathSelectionModel::unselect() * Unselect, rather than toggleStatus, when hiding a colum * Send the leaf column id for ctx options * Fix sorting from placeholder * Fix merge conflict * Revert demo Co-authored-by: Stephanie Roy <[email protected]> Co-authored-by: Stephanie Roy <[email protected]> * Reduce number of lines Co-authored-by: Stephanie Roy <[email protected]> Co-authored-by: Stephanie Roy <[email protected]>
1 parent c4add8c commit 0d9def9

File tree

10 files changed

+354
-143
lines changed

10 files changed

+354
-143
lines changed

extension/src/experiments/columns/model.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,4 +364,74 @@ describe('ColumnsModel', () => {
364364
expect(model.getColumnWidths()[changedColumnId]).toBe(expectedWidth)
365365
})
366366
})
367+
368+
describe('columns selection', () => {
369+
const expectStatus = (
370+
columns: Array<{ status: Status }>,
371+
expectedStatus: Status
372+
) => {
373+
for (const { status } of columns) {
374+
expect(status).toStrictEqual(expectedStatus)
375+
}
376+
}
377+
378+
it('should unselect the children of a unselected column', async () => {
379+
const mockMemento = buildMockMemento({
380+
[PersistenceKey.METRICS_AND_PARAMS_STATUS + exampleDvcRoot]: {}
381+
})
382+
const model = new ColumnsModel(
383+
exampleDvcRoot,
384+
mockMemento,
385+
mockedColumnsOrderOrStatusChanged
386+
)
387+
await model.transformAndSet(outputFixture)
388+
expect(model.getSelected()).toStrictEqual(columnsFixture)
389+
390+
const parentPath = 'params:params.yaml:process'
391+
model.toggleStatus(parentPath)
392+
const children = model.getChildren(parentPath)
393+
expectStatus(children, Status.UNSELECTED)
394+
})
395+
396+
it('should select the children of a selected column', async () => {
397+
const parentPath = 'params:params.yaml:process'
398+
const mockMemento = buildMockMemento({
399+
[PersistenceKey.METRICS_AND_PARAMS_STATUS + exampleDvcRoot]: {
400+
[parentPath]: Status.UNSELECTED
401+
}
402+
})
403+
const model = new ColumnsModel(
404+
exampleDvcRoot,
405+
mockMemento,
406+
mockedColumnsOrderOrStatusChanged
407+
)
408+
await model.transformAndSet(outputFixture)
409+
expect(model.getSelected()).not.toStrictEqual(columnsFixture)
410+
411+
model.toggleStatus(parentPath)
412+
413+
const children = model.getChildren(parentPath)
414+
expectStatus(children, Status.SELECTED)
415+
})
416+
417+
it('should unselect the children of an indeterminate column when it is unselected', async () => {
418+
const mockMemento = buildMockMemento({
419+
[PersistenceKey.METRICS_AND_PARAMS_STATUS + exampleDvcRoot]: {}
420+
})
421+
const model = new ColumnsModel(
422+
exampleDvcRoot,
423+
mockMemento,
424+
mockedColumnsOrderOrStatusChanged
425+
)
426+
await model.transformAndSet(outputFixture)
427+
expect(model.getSelected()).toStrictEqual(columnsFixture)
428+
429+
const parentPath = 'metrics:summary.json'
430+
model.toggleStatus('metrics:summary.json:val_loss')
431+
model.unselect(parentPath)
432+
433+
const children = model.getChildren(parentPath)
434+
expectStatus(children, Status.UNSELECTED)
435+
})
436+
})
367437
})

extension/src/experiments/webview/messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export class WebviewMessages {
276276
}
277277

278278
private hideTableColumn(path: string) {
279-
this.columns.toggleStatus(path)
279+
this.columns.unselect(path)
280280

281281
this.notifyChanged()
282282

extension/src/path/selection/model.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,11 @@ export abstract class PathSelectionModel<
5151

5252
public toggleStatus(path: string) {
5353
const status = this.getNextStatus(path)
54-
this.status[path] = status
55-
this.setAreChildrenSelected(path, status)
56-
this.setAreParentsSelected(path)
57-
this.persistStatus()
58-
this.statusChanged?.fire()
54+
return this.setStatus(path, status)
55+
}
5956

60-
return this.status[path]
57+
public unselect(path: string) {
58+
return this.setStatus(path, Status.UNSELECTED)
6159
}
6260

6361
public getTerminalNodeStatuses(parentPath?: string): Status[] {
@@ -90,6 +88,16 @@ export abstract class PathSelectionModel<
9088
}
9189
}
9290

91+
private setStatus(path: string, status: Status) {
92+
this.status[path] = status
93+
this.setAreChildrenSelected(path, status)
94+
this.setAreParentsSelected(path)
95+
this.persistStatus()
96+
this.statusChanged?.fire()
97+
98+
return this.status[path]
99+
}
100+
93101
private setAreChildrenSelected(path: string, status: Status) {
94102
return this.getChildren(path)?.map(element => {
95103
const path = element.path

extension/src/test/suite/experiments/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ suite('Experiments Test Suite', () => {
355355
it('should be able to handle a message to hide a table column', async () => {
356356
const { experiments, columnsModel } = buildExperiments(disposable)
357357

358-
const mockToggleStatus = stub(columnsModel, 'toggleStatus')
358+
const mockUnselect = stub(columnsModel, 'unselect')
359359
const mockSendTelemetryEvent = stub(Telemetry, 'sendTelemetryEvent')
360360
const webview = await experiments.showWebview()
361361
const mockMessageReceived = getMessageReceivedEmitter(webview)
@@ -366,8 +366,8 @@ suite('Experiments Test Suite', () => {
366366
type: MessageFromWebviewType.HIDE_EXPERIMENTS_TABLE_COLUMN
367367
})
368368

369-
expect(mockToggleStatus).to.be.calledOnce
370-
expect(mockToggleStatus).to.be.calledWithExactly(mockColumnId)
369+
expect(mockUnselect).to.be.calledOnce
370+
expect(mockUnselect).to.be.calledWithExactly(mockColumnId)
371371

372372
expect(mockSendTelemetryEvent).to.be.calledWithExactly(
373373
EventName.VIEWS_EXPERIMENTS_TABLE_HIDE_COLUMN,

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

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,11 +760,82 @@ describe('App', () => {
760760
advanceTimersByTime(100)
761761

762762
const menuitems = screen.getAllByRole('menuitem')
763-
expect(menuitems).toHaveLength(2)
763+
expect(menuitems).toHaveLength(3)
764764

765765
fireEvent.keyDown(paramsFileHeader, { bubbles: true, key: 'Escape' })
766766
expect(screen.queryAllByRole('menuitem')).toHaveLength(0)
767767
})
768+
769+
it('should have the same options in the empty placeholders', () => {
770+
renderTableWithPlaceholder()
771+
const header = screen.getByTestId('header-Created')
772+
const placeholders = screen.getAllByTestId(/header-Created.+placeholder/)
773+
const entireColumn = [header, ...placeholders]
774+
775+
expect(entireColumn).toHaveLength(5)
776+
777+
for (const segment of entireColumn) {
778+
fireEvent.contextMenu(segment, { bubbles: true })
779+
jest.advanceTimersByTime(100)
780+
const menuitems = screen
781+
.getAllByRole('menuitem')
782+
.map(item => item.textContent)
783+
784+
expect(menuitems).toStrictEqual([
785+
'Hide Column',
786+
'Set Max Header Height',
787+
'Sort Ascending',
788+
'Sort Descending'
789+
])
790+
791+
fireEvent.keyDown(segment, { bubbles: true, key: 'Escape' })
792+
}
793+
})
794+
795+
it('should have the same options in the empty placeholders of the Experiment column', () => {
796+
renderTableWithPlaceholder()
797+
const header = screen.getByTestId('header-id')
798+
const placeholders = screen.getAllByTestId(/header-id.+placeholder/)
799+
const entireColumn = [header, ...placeholders]
800+
801+
expect(entireColumn).toHaveLength(5)
802+
803+
for (const segment of entireColumn) {
804+
fireEvent.contextMenu(segment, { bubbles: true })
805+
jest.advanceTimersByTime(100)
806+
const menuitems = screen
807+
.getAllByRole('menuitem')
808+
.map(item => item.textContent)
809+
810+
expect(menuitems).toStrictEqual(['Set Max Header Height'])
811+
812+
fireEvent.keyDown(segment, { bubbles: true, key: 'Escape' })
813+
}
814+
})
815+
816+
describe('Hiding a column from its empty placeholder', () => {
817+
it('should send the column id and not the placeholder id as the message payload', () => {
818+
renderTableWithPlaceholder()
819+
const placeholders = screen.getAllByTestId(
820+
/header-Created.+placeholder/
821+
)
822+
const placeholder = placeholders[0]
823+
fireEvent.contextMenu(placeholder, { bubbles: true })
824+
jest.advanceTimersByTime(100)
825+
826+
const hideOption = screen.getByText('Hide Column')
827+
828+
mockPostMessage.mockClear()
829+
830+
fireEvent.click(hideOption)
831+
832+
expect(mockPostMessage).toHaveBeenCalledTimes(1)
833+
expect(mockPostMessage).toHaveBeenCalledWith({
834+
payload: 'Created',
835+
type: MessageFromWebviewType.HIDE_EXPERIMENTS_TABLE_COLUMN
836+
})
837+
})
838+
})
768839
})
769840

770841
describe('Row Context Menu', () => {

webview/src/experiments/components/table/Table.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import { MessageFromWebviewType } from 'dvc/src/webview/contract'
1818
import React from 'react'
1919
import { TableInstance } from 'react-table'
2020
import tableDataFixture from 'dvc/src/test/fixtures/expShow/base/tableData'
21-
import { SortOrder } from './header/TableHeader'
2221
import { Table } from './Table'
2322
import styles from './styles.module.scss'
23+
import { SortOrder } from './header/ContextMenuContent'
2424
import { ExperimentsTable } from '../Experiments'
2525
import * as ColumnOrder from '../../hooks/useColumnOrder'
2626
import { vsCodeApi } from '../../../shared/api'

0 commit comments

Comments
 (0)