Skip to content

Commit 5b17f99

Browse files
authored
Refactor experiment table header context menu (#4407)
1 parent 108f11b commit 5b17f99

File tree

7 files changed

+63
-120
lines changed

7 files changed

+63
-120
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import React from 'react'
1515
import tableDataFixture from 'dvc/src/test/fixtures/expShow/base/tableData'
1616
import { EXPERIMENT_WORKSPACE_ID } from 'dvc/src/cli/dvc/contract'
1717
import styles from './styles.module.scss'
18-
import { SortOrder } from './header/ContextMenuContent'
18+
import { SortOrder } from './header/util'
1919
import { ExperimentsTable } from '../Experiments'
2020
import { vsCodeApi } from '../../../shared/api'
2121
import {

webview/src/experiments/components/table/header/ContextMenuContent.tsx

Lines changed: 15 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,21 @@
11
import { ColumnType, Experiment } from 'dvc/src/experiments/webview/contract'
22
import { MessageFromWebviewType } from 'dvc/src/webview/contract'
3-
import { VSCodeDivider } from '@vscode/webview-ui-toolkit/react'
43
import React, { useMemo } from 'react'
54
import { Header } from '@tanstack/react-table'
65
import { useSelector } from 'react-redux'
76
import { SortDefinition } from 'dvc/src/experiments/model/sortBy'
7+
import { SortOrder, getSortDetails, isFromExperimentColumn } from './util'
88
import { MessagesMenu } from '../../../../shared/components/messagesMenu/MessagesMenu'
99
import { MessagesMenuOptionProps } from '../../../../shared/components/messagesMenu/MessagesMenuOption'
1010
import { ExperimentsState } from '../../../store'
1111
import { ColumnWithGroup } from '../../../util/buildColumns'
1212

13-
export enum SortOrder {
14-
ASCENDING = 'Sort Ascending',
15-
DESCENDING = 'Sort Descending',
16-
NONE = 'Remove Sort'
17-
}
18-
19-
const possibleOrders = {
20-
false: SortOrder.ASCENDING,
21-
true: SortOrder.DESCENDING,
22-
undefined: SortOrder.NONE
23-
} as const
24-
25-
const isFromExperimentColumn = (header: Header<Experiment, unknown>) =>
26-
header.column.id === 'id' || header.column.id.startsWith('id_placeholder')
27-
2813
const sortOption = (
2914
label: SortOrder,
3015
currentSort: SortOrder,
3116
columnId: string,
32-
isSortable: boolean
17+
isSortable: boolean,
18+
divider?: boolean
3319
) => {
3420
const sortOrder = currentSort
3521
const disabled = !isSortable || sortOrder === label
@@ -53,6 +39,7 @@ const sortOption = (
5339

5440
return {
5541
disabled,
42+
divider,
5643
id: label,
5744
label,
5845
message
@@ -63,36 +50,14 @@ interface HeaderMenuProps {
6350
header: Header<Experiment, unknown>
6451
}
6552

66-
const getSortOptions = (
67-
header: Header<Experiment, unknown>,
68-
sorts: SortDefinition[]
69-
) => {
70-
const isNotExperiments = !isFromExperimentColumn(header)
71-
const isSortable = isNotExperiments && header.column.columns.length <= 1
72-
const baseColumn =
73-
header.headerGroup.headers.find(
74-
h => h.column.id === header.placeholderId
75-
) || header.column
76-
const sort = sorts.find(sort => sort.path === baseColumn.id)
77-
78-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
79-
const sortOrder: SortOrder = possibleOrders[`${sort?.descending}`]
80-
81-
const sortOptions = [
82-
sortOption(SortOrder.ASCENDING, sortOrder, baseColumn.id, isSortable),
83-
sortOption(SortOrder.DESCENDING, sortOrder, baseColumn.id, isSortable),
84-
sortOption(SortOrder.NONE, sortOrder, baseColumn.id, isSortable)
85-
]
86-
87-
return { isSortable, sortOptions, sortOrder }
88-
}
89-
90-
export const getMenuOptions = (
53+
const getMenuOptions = (
9154
header: Header<Experiment, unknown>,
9255
sorts: SortDefinition[]
93-
) => {
56+
): MessagesMenuOptionProps[] => {
9457
const leafColumn = header.column
95-
const menuOptions: MessagesMenuOptionProps[] = [
58+
const { id, isSortable, sortOrder } = getSortDetails(header, sorts)
59+
60+
return [
9661
{
9762
disabled: isFromExperimentColumn(header),
9863
id: 'hide',
@@ -143,30 +108,19 @@ export const getMenuOptions = (
143108
message: {
144109
type: MessageFromWebviewType.SELECT_FIRST_COLUMNS
145110
}
146-
}
111+
},
112+
sortOption(SortOrder.ASCENDING, sortOrder, id, isSortable, true),
113+
sortOption(SortOrder.DESCENDING, sortOrder, id, isSortable),
114+
sortOption(SortOrder.NONE, sortOrder, id, isSortable)
147115
]
148-
149-
const { isSortable, sortOptions, sortOrder } = getSortOptions(header, sorts)
150-
151-
return { isSortable, menuOptions, sortOptions, sortOrder }
152116
}
153117

154118
export const ContextMenuContent: React.FC<HeaderMenuProps> = ({ header }) => {
155119
const { sorts } = useSelector((state: ExperimentsState) => state.tableData)
156120

157-
const { menuOptions, sortOptions } = useMemo(() => {
121+
const menuOptions = useMemo(() => {
158122
return getMenuOptions(header, sorts)
159123
}, [header, sorts])
160124

161-
return (
162-
<div>
163-
<MessagesMenu options={menuOptions} />
164-
{sortOptions.length > 0 && (
165-
<>
166-
<VSCodeDivider />
167-
<MessagesMenu options={sortOptions} />
168-
</>
169-
)}
170-
</div>
171-
)
125+
return <MessagesMenu options={menuOptions} />
172126
}

webview/src/experiments/components/table/header/MergeHeaderGroups.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22
import cx from 'classnames'
33
import { Experiment } from 'dvc/src/experiments/webview/contract'
44
import { HeaderGroup, Header } from '@tanstack/react-table'
5-
import { TableHeader } from './TableHeader'
5+
import { TableHeaderCell } from './TableHeaderCell'
66
import styles from '../styles.module.scss'
77
import { DragFunction } from '../../../../shared/components/dragDrop/Draggable'
88

@@ -30,7 +30,7 @@ export const MergedHeaderGroups: React.FC<{
3030
return (
3131
<tr className={cx(styles.experimentsTr, styles.headRow)}>
3232
{headerGroup.headers.map((header: Header<Experiment, unknown>) => (
33-
<TableHeader
33+
<TableHeaderCell
3434
setExpColumnNeedsShadow={setExpColumnNeedsShadow}
3535
key={header.id}
3636
header={header}

webview/src/experiments/components/table/header/TableHeader.tsx

Lines changed: 0 additions & 50 deletions
This file was deleted.

webview/src/experiments/components/table/header/TableHeaderCell.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { Header } from '@tanstack/react-table'
55
import cx from 'classnames'
66
import { useInView } from 'react-intersection-observer'
77
import { TableHeaderCellContents } from './TableHeaderCellContents'
8-
import { ContextMenuContent, getMenuOptions } from './ContextMenuContent'
8+
import { ContextMenuContent } from './ContextMenuContent'
9+
import { getSortDetails } from './util'
910
import styles from '../styles.module.scss'
1011
import { isExperimentColumn, isFirstLevelHeader } from '../../../util/columns'
1112
import { ExperimentsState } from '../../../store'
@@ -80,7 +81,6 @@ const WithExpColumnNeedsShadowUpdates: React.FC<{
8081

8182
export const TableHeaderCell: React.FC<{
8283
header: Header<Experiment, unknown>
83-
hasFilter: boolean
8484
onDragEnter: DragFunction
8585
onDragEnd: DragFunction
8686
onDragStart: DragFunction
@@ -91,7 +91,6 @@ export const TableHeaderCell: React.FC<{
9191
onlyOneLine?: boolean
9292
}> = ({
9393
header,
94-
hasFilter,
9594
onDragEnter,
9695
onDragEnd,
9796
onDragStart,
@@ -111,13 +110,17 @@ export const TableHeaderCell: React.FC<{
111110
const headerDropTargetId = useSelector(
112111
(state: ExperimentsState) => state.headerDropTarget
113112
)
114-
const { sorts } = useSelector((state: ExperimentsState) => state.tableData)
113+
const { filters, sorts } = useSelector(
114+
(state: ExperimentsState) => state.tableData
115+
)
115116

116117
const { isSortable, sortOrder } = useMemo(() => {
117-
return getMenuOptions(header, sorts)
118+
return getSortDetails(header, sorts)
118119
}, [header, sorts])
119120
const isDraggable = !isPlaceholder && !isExperimentColumn(id)
120121

122+
const hasFilter = !!(header.id && filters.includes(header.id))
123+
121124
const canResize = getCanResize() && !isPlaceholder
122125
const resizerHeight = calcResizerHeight(header)
123126

webview/src/experiments/components/table/header/TableHeaderCellContents.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import React, { useEffect } from 'react'
22
import cx from 'classnames'
33
import { Experiment } from 'dvc/src/experiments/webview/contract'
44
import { flexRender, Header } from '@tanstack/react-table'
5-
import { SortOrder } from './ContextMenuContent'
65
import { ColumnResizer, ResizerHeight } from './ColumnResizer'
6+
import { SortOrder } from './util'
77
import styles from '../styles.module.scss'
88
import {
99
Draggable,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Header } from '@tanstack/react-table'
2+
import { SortDefinition } from 'dvc/src/experiments/model/sortBy'
3+
import { Experiment } from 'dvc/src/experiments/webview/contract'
4+
5+
export enum SortOrder {
6+
ASCENDING = 'Sort Ascending',
7+
DESCENDING = 'Sort Descending',
8+
NONE = 'Remove Sort'
9+
}
10+
11+
const possibleOrders = {
12+
false: SortOrder.ASCENDING,
13+
true: SortOrder.DESCENDING,
14+
undefined: SortOrder.NONE
15+
} as const
16+
17+
export const isFromExperimentColumn = (header: Header<Experiment, unknown>) =>
18+
header.column.id === 'id' || header.column.id.startsWith('id_placeholder')
19+
20+
export const getSortDetails = (
21+
header: Header<Experiment, unknown>,
22+
sorts: SortDefinition[]
23+
): { id: string; isSortable: boolean; sortOrder: SortOrder } => {
24+
const isNotExperiments = !isFromExperimentColumn(header)
25+
const isSortable = isNotExperiments && header.column.columns.length <= 1
26+
const baseColumn =
27+
header.headerGroup.headers.find(
28+
h => h.column.id === header.placeholderId
29+
) || header.column
30+
const sort = sorts.find(sort => sort.path === baseColumn.id)
31+
32+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
33+
const sortOrder: SortOrder = possibleOrders[`${sort?.descending}`]
34+
35+
return { id: baseColumn.id, isSortable, sortOrder }
36+
}

0 commit comments

Comments
 (0)