diff --git a/webview/src/experiments/components/Experiments.tsx b/webview/src/experiments/components/Experiments.tsx
index 10e6832ec2..821fde2774 100644
--- a/webview/src/experiments/components/Experiments.tsx
+++ b/webview/src/experiments/components/Experiments.tsx
@@ -26,7 +26,6 @@ import buildDynamicColumns from '../util/buildDynamicColumns'
import { sendMessage } from '../../shared/vscode'
import { WebviewWrapper } from '../../shared/components/webviewWrapper/WebviewWrapper'
import { GetStarted } from '../../shared/components/getStarted/GetStarted'
-import { DragDropProvider } from '../../shared/components/dragDrop/DragDropContext'
import { EmptyState } from '../../shared/components/emptyState/EmptyState'
const DEFAULT_COLUMN_WIDTH = 90
@@ -224,11 +223,9 @@ export const ExperimentsTable: React.FC<{
}
return (
-
-
-
-
-
+
+
+
)
}
diff --git a/webview/src/experiments/components/table/Table.test.tsx b/webview/src/experiments/components/table/Table.test.tsx
index 4e81c21771..26b949a319 100644
--- a/webview/src/experiments/components/table/Table.test.tsx
+++ b/webview/src/experiments/components/table/Table.test.tsx
@@ -3,6 +3,7 @@
*/
/* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "expectHeaders"] }] */
import '@testing-library/jest-dom/extend-expect'
+import { configureStore } from '@reduxjs/toolkit'
import {
cleanup,
fireEvent,
@@ -10,6 +11,7 @@ import {
render,
screen
} from '@testing-library/react'
+import { Provider } from 'react-redux'
import { Experiment, TableData } from 'dvc/src/experiments/webview/contract'
import { MessageFromWebviewType } from 'dvc/src/webview/contract'
import React from 'react'
@@ -20,7 +22,6 @@ import { Table } from './Table'
import styles from './styles.module.scss'
import { ExperimentsTable } from '../Experiments'
import * as ColumnOrder from '../../hooks/useColumnOrder'
-
import { vsCodeApi } from '../../../shared/api'
import {
expectHeaders,
@@ -29,6 +30,7 @@ import {
} from '../../../test/sort'
import { dragAndDrop } from '../../../test/dragDrop'
import { DragEnterDirection } from '../../../shared/components/dragDrop/util'
+import { experimentsReducers } from '../../store'
import { customQueries } from '../../../test/queries'
jest.mock('../../../shared/api')
@@ -123,14 +125,23 @@ describe('Table', () => {
}
const renderTable = (testData = {}, tableInstance = instance) => {
const tableData = { ...dummyTableData, ...testData }
- return render(
)
+ return render(
+
+
+
+ )
}
const renderExperimentsTable = (
data: TableData = sortingTableDataFixture
) => {
- return render(, {
- queries: { ...queries, ...customQueries }
- })
+ return render(
+
+
+ ,
+ {
+ queries: { ...queries, ...customQueries }
+ }
+ )
}
beforeAll(() => {
@@ -335,7 +346,11 @@ describe('Table', () => {
...sortingTableDataFixture,
columnWidths
}
- render()
+ render(
+
+
+
+ )
const [experimentColumnResizeHandle] = await screen.findAllByRole(
'separator'
)
diff --git a/webview/src/experiments/index.tsx b/webview/src/experiments/index.tsx
index 95de9279c6..75ea6a1fe1 100644
--- a/webview/src/experiments/index.tsx
+++ b/webview/src/experiments/index.tsx
@@ -1,7 +1,13 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
+import { Provider } from 'react-redux'
import '../shared/style.scss'
import { App } from './components/App'
+import { experimentsStore } from './store'
const root = ReactDOM.createRoot(document.querySelector('#root') as HTMLElement)
-root.render()
+root.render(
+
+
+
+)
diff --git a/webview/src/experiments/store.ts b/webview/src/experiments/store.ts
new file mode 100644
index 0000000000..933f4dacb2
--- /dev/null
+++ b/webview/src/experiments/store.ts
@@ -0,0 +1,13 @@
+import { configureStore } from '@reduxjs/toolkit'
+import dragAndDropReducer from '../shared/components/dragDrop/dragDropSlice'
+
+export const experimentsReducers = {
+ dragAndDrop: dragAndDropReducer
+}
+
+export const experimentsStore = configureStore({
+ reducer: experimentsReducers
+})
+
+export type ExperimentsState = ReturnType
+export type ExperimentsDispatch = typeof experimentsStore.dispatch
diff --git a/webview/src/shared/components/dragDrop/DragDropContext.tsx b/webview/src/shared/components/dragDrop/DragDropContext.tsx
deleted file mode 100644
index bde6b08555..0000000000
--- a/webview/src/shared/components/dragDrop/DragDropContext.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import React, { createContext, useState } from 'react'
-
-export type DraggedInfo =
- | {
- itemIndex: string
- itemId: string
- group?: string
- }
- | undefined
-
-export interface DragDropGroupState {
- draggedId?: string
- draggedOverId?: string
-}
-
-export type GroupStates = {
- [group: string]: DragDropGroupState | undefined
-}
-
-export type DragDropContextValue = {
- draggedRef: DraggedInfo
- setDraggedRef: ((draggedRef: DraggedInfo) => void) | undefined
- groupStates?: GroupStates
- setGroupState?: (group: string, handlers: DragDropGroupState) => void
- removeGroupState?: (group: string) => void
-}
-
-export const DragDropContext = createContext({
- draggedRef: undefined,
- groupStates: undefined,
- removeGroupState: undefined,
- setDraggedRef: undefined,
- setGroupState: undefined
-})
-
-type DragDropProviderProps = {
- children: React.ReactNode
-}
-
-export const DragDropProvider: React.FC = ({
- children
-}) => {
- const [draggedRef, setDraggedRef] = useState(undefined)
-
- const [groupStates, setGroupStates] = useState({})
-
- const changeDraggedRef = (d: DraggedInfo) => setDraggedRef(d)
-
- const setGroupState = (group: string, handlers: DragDropGroupState) => {
- setGroupStates({
- ...groupStates,
- [group]: handlers
- })
- }
-
- const removeGroupState = (group: string) => {
- setGroupStates({
- ...groupStates,
- [group]: undefined
- })
- }
-
- return (
-
- {children}
-
- )
-}
diff --git a/webview/src/shared/components/dragDrop/DragDropWorkbench.tsx b/webview/src/shared/components/dragDrop/DragDropWorkbench.tsx
index fef1886459..5a7b976e50 100644
--- a/webview/src/shared/components/dragDrop/DragDropWorkbench.tsx
+++ b/webview/src/shared/components/dragDrop/DragDropWorkbench.tsx
@@ -1,6 +1,8 @@
-import React, { DragEvent, useContext } from 'react'
+import React, { DragEvent } from 'react'
+import { useDispatch, useSelector } from 'react-redux'
import { makeTarget } from './DragDropContainer'
-import { DragDropContext, DragDropContextValue } from './DragDropContext'
+import { setGroup } from './dragDropSlice'
+import { ExperimentsState } from '../../../experiments/store'
export type OnDrop = (draggedId: string, draggedOverId: string) => void
export type OnDragStart = (draggedId: string) => void
@@ -26,21 +28,33 @@ export const Draggable: React.FC = ({
onDrop,
onDragOver,
onDragStart
+ // eslint-disable-next-line sonarjs/cognitive-complexity
}) => {
- const { groupStates, setGroupState } =
- useContext(DragDropContext)
+ const groupStates = useSelector(
+ (state: ExperimentsState) => state.dragAndDrop.groups
+ )
+ const dispatch = useDispatch()
- const groupState = groupStates?.[group] || {}
+ const groupState = groupStates[group] || {}
const { draggedOverId, draggedId } = groupState
+ const modifyGroup = (id: string) => {
+ dispatch(
+ setGroup({
+ group: {
+ ...groupState,
+ draggedId: id
+ },
+ id: group
+ })
+ )
+ }
+
const handleDragStart = (e: DragEvent) => {
const { id } = e.currentTarget
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.dropEffect = 'move'
- setGroupState?.(group, {
- ...groupState,
- draggedId: id
- })
+ modifyGroup(id)
onDragStart?.(id)
}
@@ -52,16 +66,14 @@ export const Draggable: React.FC = ({
}
const handleDragEnter = (e: DragEvent) => {
- const { id } = e.currentTarget
- !disabled &&
- draggedId &&
- id !== draggedId &&
- id !== draggedOverId &&
- (setGroupState?.(group, {
- ...groupState,
- draggedOverId: id
- }) ||
- onDragOver?.(draggedId, id))
+ if (!disabled && draggedId) {
+ const { id } = e.currentTarget
+
+ if (id !== draggedId && id !== draggedOverId) {
+ modifyGroup(id)
+ onDragOver?.(draggedId, id)
+ }
+ }
}
const handleDragOver = (e: DragEvent) => {
@@ -69,11 +81,16 @@ export const Draggable: React.FC = ({
}
const handleDragEnd = () => {
- setGroupState?.(group, {
- ...groupState,
- draggedId: undefined,
- draggedOverId: undefined
- })
+ dispatch(
+ setGroup({
+ group: {
+ ...groupState,
+ draggedId: undefined,
+ draggedOverId: undefined
+ },
+ id: group
+ })
+ )
}
const item = (
diff --git a/webview/src/shared/components/dragDrop/dragDropSlice.ts b/webview/src/shared/components/dragDrop/dragDropSlice.ts
index b547e88cbb..95596f5874 100644
--- a/webview/src/shared/components/dragDrop/dragDropSlice.ts
+++ b/webview/src/shared/components/dragDrop/dragDropSlice.ts
@@ -7,12 +7,22 @@ export type DraggedInfo =
group?: string
}
| undefined
+export interface DragDropGroupState {
+ draggedId?: string
+ draggedOverId?: string
+}
+
+export type GroupStates = {
+ [group: string]: DragDropGroupState | undefined
+}
export interface DragDropState {
draggedRef: DraggedInfo
+ groups: GroupStates
}
export const dragDropInitialState: DragDropState = {
- draggedRef: undefined
+ draggedRef: undefined,
+ groups: {}
}
export const dragDropSlice = createSlice({
@@ -24,10 +34,19 @@ export const dragDropSlice = createSlice({
...state,
draggedRef: action.payload
}
+ },
+ setGroup: (
+ state,
+ action: PayloadAction<{ id: string; group: DragDropGroupState }>
+ ) => {
+ return {
+ ...state,
+ groups: { ...state.groups, [action.payload.id]: action.payload.group }
+ }
}
}
})
-export const { changeRef } = dragDropSlice.actions
+export const { changeRef, setGroup } = dragDropSlice.actions
export default dragDropSlice.reducer
diff --git a/webview/src/stories/Table.stories.tsx b/webview/src/stories/Table.stories.tsx
index 61aee5aa03..1a6fcdd718 100644
--- a/webview/src/stories/Table.stories.tsx
+++ b/webview/src/stories/Table.stories.tsx
@@ -1,4 +1,6 @@
+import { configureStore } from '@reduxjs/toolkit'
import React from 'react'
+import { Provider } from 'react-redux'
import { ComponentStory } from '@storybook/react'
import { Meta } from '@storybook/react/types-6-0'
import rowsFixture from 'dvc/src/test/fixtures/expShow/rows'
@@ -22,6 +24,7 @@ import {
setExperimentsAsSelected,
setExperimentsAsStarred
} from '../test/tableDataFixture'
+import { experimentsReducers } from '../experiments/store'
const tableData: TableData = {
changes: workspaceChangesFixture,
@@ -100,7 +103,11 @@ export default {
} as Meta
const Template: ComponentStory = ({ tableData }) => {
- return
+ return (
+
+
+
+ )
}
export const WithData = Template.bind({})
@@ -196,9 +203,11 @@ WithNoSortsOrFilters.args = {
export const Scrolled: ComponentStory = ({ tableData }) => {
return (
-
-
-
+
+
+
+
+
)
}
Scrolled.play = async ({ canvasElement }) => {
diff --git a/webview/src/test/experimentsTable.tsx b/webview/src/test/experimentsTable.tsx
index be8e6730ad..e97a90bd52 100644
--- a/webview/src/test/experimentsTable.tsx
+++ b/webview/src/test/experimentsTable.tsx
@@ -1,3 +1,4 @@
+import { configureStore } from '@reduxjs/toolkit'
import {
fireEvent,
render,
@@ -6,12 +7,14 @@ import {
queries
} from '@testing-library/react'
import React from 'react'
+import { Provider } from 'react-redux'
import deeplyNestedTableDataFixture from 'dvc/src/test/fixtures/expShow/deeplyNested'
import tableDataFixture from 'dvc/src/test/fixtures/expShow/tableData'
import { MessageToWebviewType } from 'dvc/src/webview/contract'
import { tableData as sortingTableDataFixture } from './sort'
import { customQueries, getRow } from './queries'
import { App } from '../experiments/components/App'
+import { experimentsReducers } from '../experiments/store'
export const setTableData = (data = tableDataFixture) => {
fireEvent(
@@ -26,9 +29,14 @@ export const setTableData = (data = tableDataFixture) => {
}
export const renderTable = (data = tableDataFixture) => {
- const renderedTable = render(, {
- queries: { ...queries, ...customQueries }
- })
+ const renderedTable = render(
+
+
+ ,
+ {
+ queries: { ...queries, ...customQueries }
+ }
+ )
setTableData(data)
return renderedTable
}