From 7d405ac2fdbeff74cf0ec485259cb7a12123bec8 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Wed, 20 Mar 2024 13:50:34 +0900 Subject: [PATCH 01/28] Add table --- optuna_dashboard/ts/components/TrialTable.tsx | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 9c171438..28b56c3b 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -7,6 +7,84 @@ import { Link } from "react-router-dom" import { StudyDetail, Trial } from "ts/types/optuna" import { DataGrid, DataGridColumn } from "./DataGrid" +import Box from "@mui/material/Box" +import Table from "@mui/material/Table" +import TableBody from "@mui/material/TableBody" +import TableCell from "@mui/material/TableCell" +import TableContainer from "@mui/material/TableContainer" +import TableHead from "@mui/material/TableHead" +import TableRow from "@mui/material/TableRow" +import Paper from "@mui/material/Paper" + +import { + createColumnHelper, + Table as ReactTable, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table" + +function BasicTable(props: { + columns: any + rows: Trial[] +}): React.ReactElement { + const { columns, rows } = props + const data = rows + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }) + + return ( + + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : ( +
+ {flexRender( + header.column.columnDef.header, + header.getContext() + )} +
+ )} +
+ ) + })} +
+ ))} +
+ + {table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ) + })} + + ) + })} + +
+
+
+ ) +} + export const TrialTable: FC<{ studyDetail: StudyDetail | null initialRowsPerPage?: number @@ -26,6 +104,18 @@ export const TrialTable: FC<{ toCellValue: (i) => trials[i].state.toString(), }, ] + + const columnHelper = createColumnHelper() + const tcolumns: any[] = [ + columnHelper.accessor("number", { + header: "Number", + footer: (info) => info.column.id, + }), + columnHelper.accessor("state", { + header: "State", + footer: (info) => info.column.id, + }), + ] const valueComparator = ( firstVal?: number, secondVal?: number, @@ -60,6 +150,12 @@ export const TrialTable: FC<{ return trials[i].values?.[0] }, }) + tcolumns.push( + columnHelper.accessor("values", { + header: "Value", + footer: (info) => info.column.id, + }) + ) } else { const objectiveColumns: DataGridColumn[] = studyDetail.directions.map((s, objectiveId) => ({ @@ -84,6 +180,14 @@ export const TrialTable: FC<{ }, })) columns.push(...objectiveColumns) + tcolumns.push( + ...studyDetail.directions.map((s, objectiveId) => + columnHelper.accessor("values", { + header: `Objective ${objectiveId}`, + footer: (info) => info.column.id, + }) + ) + ) } const isDynamicSpace = studyDetail?.union_search_space.length !== @@ -119,6 +223,15 @@ export const TrialTable: FC<{ return valueComparator(firstVal, secondVal) }, }) + tcolumns.push( + columnHelper.accessor("params", { + header: `Param ${s.name}`, + cell: (info) => + info.getValue().find((p) => p.name === s.name) + ?.param_external_value || null, + footer: (info) => info.column.id, + }) + ) }) studyDetail?.union_user_attrs.forEach((attr_spec) => { @@ -143,6 +256,14 @@ export const TrialTable: FC<{ ) }, }) + tcolumns.push( + columnHelper.accessor("user_attrs", { + header: `UserAttribute ${attr_spec.key}`, + cell: (info) => + info.getValue().find((a) => a.key === attr_spec.key)?.value || null, + footer: (info) => info.column.id, + }) + ) }) columns.push({ field: "trial_id", @@ -181,6 +302,7 @@ export const TrialTable: FC<{ > Download CSV File + ) } From 170ae58467cc834a55092e2e04a5cd17d2bdbb91 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Wed, 20 Mar 2024 22:00:02 +0900 Subject: [PATCH 02/28] Add all columns --- optuna_dashboard/ts/components/TrialTable.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 28b56c3b..6d92e182 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -225,6 +225,7 @@ export const TrialTable: FC<{ }) tcolumns.push( columnHelper.accessor("params", { + id: `params_${s.name}`, header: `Param ${s.name}`, cell: (info) => info.getValue().find((p) => p.name === s.name) @@ -258,6 +259,7 @@ export const TrialTable: FC<{ }) tcolumns.push( columnHelper.accessor("user_attrs", { + id: `user_attrs_${attr_spec.key}`, header: `UserAttribute ${attr_spec.key}`, cell: (info) => info.getValue().find((a) => a.key === attr_spec.key)?.value || null, @@ -283,6 +285,28 @@ export const TrialTable: FC<{ ), }) + tcolumns.push( + columnHelper.accessor((row) => row, { + header: "Detail", + cell: (info) => ( + + + + ), + footer: (info) => info.column.id, + }) + ) return ( <> From 9524091456fdc0f66ee49a611c30a5203e71ee53 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Wed, 20 Mar 2024 22:17:30 +0900 Subject: [PATCH 03/28] Add sort --- optuna_dashboard/ts/components/TrialTable.tsx | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 6d92e182..1de93ac8 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -17,24 +17,34 @@ import TableRow from "@mui/material/TableRow" import Paper from "@mui/material/Paper" import { + ColumnDef, createColumnHelper, - Table as ReactTable, flexRender, getCoreRowModel, + getSortedRowModel, + SortingState, useReactTable, } from "@tanstack/react-table" function BasicTable(props: { - columns: any + columns: ColumnDef[] rows: Trial[] }): React.ReactElement { const { columns, rows } = props + const [sorting, setSorting] = React.useState([]) + const data = rows const table = useReactTable({ data, columns, + state: { + sorting, + }, + onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + debugTable: true, }) return ( @@ -48,11 +58,31 @@ function BasicTable(props: { return ( {header.isPlaceholder ? null : ( -
+
{flexRender( header.column.columnDef.header, header.getContext() )} + {{ + asc: " 🔼", + desc: " 🔽", + }[header.column.getIsSorted() as string] ?? null}
)} @@ -106,7 +136,7 @@ export const TrialTable: FC<{ ] const columnHelper = createColumnHelper() - const tcolumns: any[] = [ + const tcolumns: ColumnDef[] = [ columnHelper.accessor("number", { header: "Number", footer: (info) => info.column.id, From c3fab28d0f492c70e8cc9fcbc9cdeeb4c7952461 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Thu, 21 Mar 2024 18:21:07 +0900 Subject: [PATCH 04/28] Add enableSorting --- optuna_dashboard/ts/components/TrialTable.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 1de93ac8..62887e24 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -144,6 +144,7 @@ export const TrialTable: FC<{ columnHelper.accessor("state", { header: "State", footer: (info) => info.column.id, + enableSorting: false, }), ] const valueComparator = ( @@ -213,6 +214,7 @@ export const TrialTable: FC<{ tcolumns.push( ...studyDetail.directions.map((s, objectiveId) => columnHelper.accessor("values", { + id: `values_${objectiveId}`, header: `Objective ${objectiveId}`, footer: (info) => info.column.id, }) @@ -335,6 +337,7 @@ export const TrialTable: FC<{ ), footer: (info) => info.column.id, + enableSorting: false, }) ) From 271c68147ff763d8cb1f915b8936f5ee8afe41c9 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Mon, 1 Apr 2024 22:06:36 +0900 Subject: [PATCH 05/28] Fix sort arrows --- optuna_dashboard/ts/components/TrialTable.tsx | 133 ++++++++++++++++-- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 62887e24..b066f0c3 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -14,7 +14,19 @@ import TableCell from "@mui/material/TableCell" import TableContainer from "@mui/material/TableContainer" import TableHead from "@mui/material/TableHead" import TableRow from "@mui/material/TableRow" +import TablePagination from "@mui/material/TablePagination" import Paper from "@mui/material/Paper" +import { TablePaginationActionsProps } from "@mui/material/TablePagination/TablePaginationActions" +import FirstPageIcon from "@mui/icons-material/FirstPage" +import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft" +import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight" +import LastPageIcon from "@mui/icons-material/LastPage" + +import { + TiArrowSortedDown, + TiArrowSortedUp, + TiArrowUnsorted, +} from "react-icons/ti" import { ColumnDef, @@ -22,18 +34,87 @@ import { flexRender, getCoreRowModel, getSortedRowModel, + getPaginationRowModel, SortingState, useReactTable, } from "@tanstack/react-table" -function BasicTable(props: { - columns: ColumnDef[] - rows: Trial[] -}): React.ReactElement { - const { columns, rows } = props - const [sorting, setSorting] = React.useState([]) +const TablePaginationActions = (props: TablePaginationActionsProps) => { + const theme = useTheme() + const { count, page, rowsPerPage, onPageChange } = props - const data = rows + const handleFirstPageButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, 0) + } + + const handleBackButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, page - 1) + } + + const handleNextButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, page + 1) + } + + const handleLastPageButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)) + } + + return ( + + + {theme.direction === "rtl" ? : } + + + {theme.direction === "rtl" ? ( + + ) : ( + + )} + + = Math.ceil(count / rowsPerPage) - 1} + aria-label="next page" + > + {theme.direction === "rtl" ? ( + + ) : ( + + )} + + = Math.ceil(count / rowsPerPage) - 1} + aria-label="last page" + > + {theme.direction === "rtl" ? : } + + + ) +} + +function BasicTable(props: { + data: Trial[] + columns: ColumnDef[] +}): React.ReactElement { + const { data, columns } = props + const [sorting, setSorting] = React.useState([]) const table = useReactTable({ data, @@ -44,9 +125,13 @@ function BasicTable(props: { onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + // debugTable: true, }) + const { pageSize, pageIndex } = table.getState().pagination + return ( @@ -79,10 +164,13 @@ function BasicTable(props: { header.column.columnDef.header, header.getContext() )} - {{ - asc: " 🔼", - desc: " 🔽", - }[header.column.getIsSorted() as string] ?? null} + {header.column.getCanSort() + ? { + asc: , + desc: , + false: , + }[header.column.getIsSorted() as string] + : null}
)}
@@ -111,6 +199,27 @@ function BasicTable(props: { + { + table.setPageIndex(page) + }} + onRowsPerPageChange={(e) => { + const size = e.target.value ? Number(e.target.value) : 10 + table.setPageSize(size) + }} + ActionsComponent={TablePaginationActions} + /> ) } @@ -359,7 +468,7 @@ export const TrialTable: FC<{ > Download CSV File - + ) } From a631d1dcf63cea9e6215030b9880445ff4f87c44 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Tue, 2 Apr 2024 00:53:32 +0900 Subject: [PATCH 06/28] Support filter --- optuna_dashboard/ts/components/TrialTable.tsx | 185 ++++++++++++++---- 1 file changed, 150 insertions(+), 35 deletions(-) diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index b066f0c3..54bb69db 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -1,3 +1,13 @@ +import React, { FC } from "react" +import { + IconButton, + Button, + useTheme, + TableSortLabel, + Menu, + MenuItem, +} from "@mui/material" +import LinkIcon from "@mui/icons-material/Link" import DownloadIcon from "@mui/icons-material/Download" import LinkIcon from "@mui/icons-material/Link" import { Button, IconButton, useTheme } from "@mui/material" @@ -21,12 +31,11 @@ import FirstPageIcon from "@mui/icons-material/FirstPage" import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft" import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight" import LastPageIcon from "@mui/icons-material/LastPage" - -import { - TiArrowSortedDown, - TiArrowSortedUp, - TiArrowUnsorted, -} from "react-icons/ti" +import { styled } from "@mui/system" +import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank" +import CheckBoxIcon from "@mui/icons-material/CheckBox" +import FilterListIcon from "@mui/icons-material/FilterList" +import ListItemIcon from "@mui/material/ListItemIcon" import { ColumnDef, @@ -35,10 +44,41 @@ import { getCoreRowModel, getSortedRowModel, getPaginationRowModel, + getFilteredRowModel, + getFacetedUniqueValues, SortingState, + ColumnFiltersState, useReactTable, + Row, + IdType, + FilterFn, } from "@tanstack/react-table" +const TableHeaderCellSpan = styled("span")({ + display: "inline-flex", +}) + +const HiddenSpan = styled("span")({ + border: 0, + clip: "rect(0 0 0 0)", + height: 1, + margin: -1, + overflow: "hidden", + padding: 0, + position: "absolute", + top: 20, + width: 1, +}) + +const multiValueFilter: FilterFn = ( + row: Row, + columnId: IdType, + filterValue: string[] +) => { + const rowValue = row.getValue(columnId) as string + return !filterValue.includes(rowValue) +} + const TablePaginationActions = (props: TablePaginationActionsProps) => { const theme = useTheme() const { count, page, rowsPerPage, onPageChange } = props @@ -115,19 +155,28 @@ function BasicTable(props: { }): React.ReactElement { const { data, columns } = props const [sorting, setSorting] = React.useState([]) + const [columnFilters, setColumnFilters] = React.useState( + [] + ) + const [filterMenuAnchorEl, setFilterMenuAnchorEl] = + React.useState(null) const table = useReactTable({ data, columns, state: { + columnFilters, sorting, }, + onColumnFiltersChange: setColumnFilters, onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), getSortedRowModel: getSortedRowModel(), getPaginationRowModel: getPaginationRowModel(), + getFilteredRowModel: getFilteredRowModel(), // - debugTable: true, + // debugTable: true, }) const { pageSize, pageIndex } = table.getState().pagination @@ -140,38 +189,97 @@ function BasicTable(props: { {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { + const order = header.column.getIsSorted() + const filterChoices = header.column.getCanFilter() + ? Array.from( + header.column.getFacetedUniqueValues().keys() + ).sort() + : [] + if ( + header.column.getCanFilter() && + !header.column.getIsFiltered() + ) { + header.column.setFilterValue([]) + } return ( {header.isPlaceholder ? null : ( -
- {flexRender( - header.column.columnDef.header, - header.getContext() + + {header.column.getCanSort() ? ( + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + {order !== null ? ( + + {order === "desc" + ? "sorted descending" + : "sorted ascending"} + + ) : null} + + ) : ( + flexRender( + header.column.columnDef.header, + header.getContext() + ) )} - {header.column.getCanSort() - ? { - asc: , - desc: , - false: , - }[header.column.getIsSorted() as string] - : null} -
+ {header.column.getCanFilter() ? ( + <> + { + setFilterMenuAnchorEl(e.currentTarget) + }} + > + + + { + setFilterMenuAnchorEl(null) + }} + > + {filterChoices.map((choice) => ( + { + const skippedValues = + header.column.getFilterValue() as string[] + const isSkipped = + skippedValues.includes(choice) + const newSkippedValues = isSkipped + ? skippedValues.filter( + (v) => v !== choice + ) + : skippedValues.concat(choice) + header.column.setFilterValue( + newSkippedValues + ) + }} + > + + {( + header.column.getFilterValue() as string[] + ).includes(choice) ? ( + + ) : ( + + )} + + {choice ?? "(missing value)"} + + ))} + + + ) : null} + )}
) @@ -249,11 +357,13 @@ export const TrialTable: FC<{ columnHelper.accessor("number", { header: "Number", footer: (info) => info.column.id, + enableColumnFilter: false, }), columnHelper.accessor("state", { header: "State", footer: (info) => info.column.id, enableSorting: false, + filterFn: multiValueFilter, }), ] const valueComparator = ( @@ -294,6 +404,7 @@ export const TrialTable: FC<{ columnHelper.accessor("values", { header: "Value", footer: (info) => info.column.id, + enableColumnFilter: false, }) ) } else { @@ -326,6 +437,7 @@ export const TrialTable: FC<{ id: `values_${objectiveId}`, header: `Objective ${objectiveId}`, footer: (info) => info.column.id, + enableColumnFilter: false, }) ) ) @@ -372,6 +484,7 @@ export const TrialTable: FC<{ info.getValue().find((p) => p.name === s.name) ?.param_external_value || null, footer: (info) => info.column.id, + enableColumnFilter: false, }) ) }) @@ -405,6 +518,7 @@ export const TrialTable: FC<{ cell: (info) => info.getValue().find((a) => a.key === attr_spec.key)?.value || null, footer: (info) => info.column.id, + enableColumnFilter: false, }) ) }) @@ -447,6 +561,7 @@ export const TrialTable: FC<{ ), footer: (info) => info.column.id, enableSorting: false, + enableColumnFilter: false, }) ) From c58150634daadaa4fad6292d7dcf9bbe94affd5e Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Tue, 2 Apr 2024 10:06:40 +0900 Subject: [PATCH 07/28] Fix sort --- optuna_dashboard/ts/components/TrialTable.tsx | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 54bb69db..9d575017 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -181,6 +181,12 @@ function BasicTable(props: { const { pageSize, pageIndex } = table.getState().pagination + React.useEffect(() => { + return () => { + table.resetColumnFilters() + } + }, []) + return ( @@ -200,6 +206,7 @@ function BasicTable(props: { !header.column.getIsFiltered() ) { header.column.setFilterValue([]) + console.log(header.column.getFilterValue()) } return ( @@ -363,6 +370,8 @@ export const TrialTable: FC<{ header: "State", footer: (info) => info.column.id, enableSorting: false, + // enableColumnFilter: true, + enableColumnFilter: false, filterFn: multiValueFilter, }), ] @@ -477,15 +486,20 @@ export const TrialTable: FC<{ }, }) tcolumns.push( - columnHelper.accessor("params", { - id: `params_${s.name}`, - header: `Param ${s.name}`, - cell: (info) => - info.getValue().find((p) => p.name === s.name) - ?.param_external_value || null, - footer: (info) => info.column.id, - enableColumnFilter: false, - }) + columnHelper.accessor( + (row) => + row["params"].find((p) => p.name === s.name)?.param_external_value || + null, + { + id: `params_${s.name}`, + header: `Param ${s.name}`, + footer: (info) => info.column.id, + enableSorting: sortable, + // enableColumnFilter: filterChoices !== undefined, + enableColumnFilter: false, + filterFn: multiValueFilter, + } + ) ) }) @@ -512,14 +526,17 @@ export const TrialTable: FC<{ }, }) tcolumns.push( - columnHelper.accessor("user_attrs", { - id: `user_attrs_${attr_spec.key}`, - header: `UserAttribute ${attr_spec.key}`, - cell: (info) => - info.getValue().find((a) => a.key === attr_spec.key)?.value || null, - footer: (info) => info.column.id, - enableColumnFilter: false, - }) + columnHelper.accessor( + (row) => + row["user_attrs"].find((a) => a.key === attr_spec.key)?.value || null, + { + id: `user_attrs_${attr_spec.key}`, + header: `UserAttribute ${attr_spec.key}`, + footer: (info) => info.column.id, + enableSorting: attr_spec.sortable, + enableColumnFilter: false, + } + ) ) }) columns.push({ From f89e028f23f8f8e060f611a6cc163ac4e3c456ee Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Tue, 2 Apr 2024 10:30:33 +0900 Subject: [PATCH 08/28] Fix filter --- optuna_dashboard/ts/components/DataGrid.tsx | 292 +++++++++++++++- optuna_dashboard/ts/components/TrialTable.tsx | 323 +----------------- 2 files changed, 296 insertions(+), 319 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 3df8b414..7f858ee8 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -18,10 +18,36 @@ import { TableRow, TableSortLabel, TextField, + Collapse, + IconButton, + Menu, + MenuItem, + Box, + useTheme, } from "@mui/material" import ListItemIcon from "@mui/material/ListItemIcon" import { styled } from "@mui/system" import React from "react" +import Paper from "@mui/material/Paper" +import { TablePaginationActionsProps } from "@mui/material/TablePagination/TablePaginationActions" +import FirstPageIcon from "@mui/icons-material/FirstPage" +import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft" +import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight" +import LastPageIcon from "@mui/icons-material/LastPage" + +import { + ColumnDef, + flexRender, + getCoreRowModel, + getSortedRowModel, + getPaginationRowModel, + getFilteredRowModel, + getFacetedUniqueValues, + getFacetedRowModel, + SortingState, + ColumnFiltersState, + useReactTable, +} from "@tanstack/react-table" type Order = "asc" | "desc" @@ -455,4 +481,268 @@ const isNumber = ( return typeof rowsPerPage === "number" } -export { DataGrid, DataGridColumn } +function DataGrid2(props: { + data: Trial[] + columns: ColumnDef[] +}): React.ReactElement { + const { data, columns } = props + const [sorting, setSorting] = React.useState([]) + const [columnFilters, setColumnFilters] = React.useState( + [] + ) + const [filterMenuAnchorEl, setFilterMenuAnchorEl] = + React.useState(null) + + const table = useReactTable({ + data, + columns, + state: { + columnFilters, + sorting, + }, + onColumnFiltersChange: setColumnFilters, + onSortingChange: setSorting, + getCoreRowModel: getCoreRowModel(), + getFacetedRowModel: getFacetedRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getFilteredRowModel: getFilteredRowModel(), + // + // debugTable: true, + }) + + const { pageSize, pageIndex } = table.getState().pagination + + React.useEffect(() => { + return () => { + table.resetColumnFilters() + } + }, []) + + return ( + + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + const order = header.column.getIsSorted() + const filterChoices = header.column.getCanFilter() + ? Array.from( + header.column.getFacetedUniqueValues().keys() + ).sort() + : [] + if ( + header.column.getCanFilter() && + !header.column.getIsFiltered() + ) { + header.column.setFilterValue([]) + console.log(header.column.getFilterValue()) + } + return ( + + {header.isPlaceholder ? null : ( + + {header.column.getCanSort() ? ( + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + {order !== null ? ( + + {order === "desc" + ? "sorted descending" + : "sorted ascending"} + + ) : null} + + ) : ( + flexRender( + header.column.columnDef.header, + header.getContext() + ) + )} + {header.column.getCanFilter() ? ( + <> + { + setFilterMenuAnchorEl(e.currentTarget) + }} + > + + + { + setFilterMenuAnchorEl(null) + }} + > + {filterChoices.map((choice) => ( + { + const skippedValues = + header.column.getFilterValue() as string[] + const isSkipped = + skippedValues.includes(choice) + const newSkippedValues = isSkipped + ? skippedValues.filter( + (v) => v !== choice + ) + : skippedValues.concat(choice) + header.column.setFilterValue( + newSkippedValues + ) + }} + > + + {header.column.getFilterValue() !== + undefined ? ( + ( + header.column.getFilterValue() as string[] + ).includes(choice) ? ( + + ) : ( + + ) + ) : null} + + {choice ?? "(missing value)"} + + ))} + + + ) : null} + + )} + + ) + })} + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ) + })} + + ) + })} + +
+
+ { + table.setPageIndex(page) + }} + onRowsPerPageChange={(e) => { + const size = e.target.value ? Number(e.target.value) : 10 + table.setPageSize(size) + }} + ActionsComponent={TablePaginationActions} + /> +
+ ) +} + +const TablePaginationActions = (props: TablePaginationActionsProps) => { + const theme = useTheme() + const { count, page, rowsPerPage, onPageChange } = props + + const handleFirstPageButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, 0) + } + + const handleBackButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, page - 1) + } + + const handleNextButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, page + 1) + } + + const handleLastPageButtonClick = ( + event: React.MouseEvent + ) => { + onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)) + } + + return ( + + + {theme.direction === "rtl" ? : } + + + {theme.direction === "rtl" ? ( + + ) : ( + + )} + + = Math.ceil(count / rowsPerPage) - 1} + aria-label="next page" + > + {theme.direction === "rtl" ? ( + + ) : ( + + )} + + = Math.ceil(count / rowsPerPage) - 1} + aria-label="last page" + > + {theme.direction === "rtl" ? : } + + + ) +} + +export { DataGrid, DataGrid2, DataGridColumn } diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 9d575017..7d78e967 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -1,75 +1,24 @@ import React, { FC } from "react" -import { - IconButton, - Button, - useTheme, - TableSortLabel, - Menu, - MenuItem, -} from "@mui/material" +import { IconButton, Button, useTheme } from "@mui/material" import LinkIcon from "@mui/icons-material/Link" import DownloadIcon from "@mui/icons-material/Download" import LinkIcon from "@mui/icons-material/Link" import { Button, IconButton, useTheme } from "@mui/material" import React, { FC } from "react" +import { DataGridColumn, DataGrid, DataGrid2 } from "./DataGrid" import { Link } from "react-router-dom" import { StudyDetail, Trial } from "ts/types/optuna" import { DataGrid, DataGridColumn } from "./DataGrid" -import Box from "@mui/material/Box" -import Table from "@mui/material/Table" -import TableBody from "@mui/material/TableBody" -import TableCell from "@mui/material/TableCell" -import TableContainer from "@mui/material/TableContainer" -import TableHead from "@mui/material/TableHead" -import TableRow from "@mui/material/TableRow" -import TablePagination from "@mui/material/TablePagination" -import Paper from "@mui/material/Paper" -import { TablePaginationActionsProps } from "@mui/material/TablePagination/TablePaginationActions" -import FirstPageIcon from "@mui/icons-material/FirstPage" -import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft" -import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight" -import LastPageIcon from "@mui/icons-material/LastPage" -import { styled } from "@mui/system" -import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank" -import CheckBoxIcon from "@mui/icons-material/CheckBox" -import FilterListIcon from "@mui/icons-material/FilterList" -import ListItemIcon from "@mui/material/ListItemIcon" - import { ColumnDef, createColumnHelper, - flexRender, - getCoreRowModel, - getSortedRowModel, - getPaginationRowModel, - getFilteredRowModel, - getFacetedUniqueValues, - SortingState, - ColumnFiltersState, - useReactTable, Row, IdType, FilterFn, } from "@tanstack/react-table" -const TableHeaderCellSpan = styled("span")({ - display: "inline-flex", -}) - -const HiddenSpan = styled("span")({ - border: 0, - clip: "rect(0 0 0 0)", - height: 1, - margin: -1, - overflow: "hidden", - padding: 0, - position: "absolute", - top: 20, - width: 1, -}) - const multiValueFilter: FilterFn = ( row: Row, columnId: IdType, @@ -79,266 +28,6 @@ const multiValueFilter: FilterFn = ( return !filterValue.includes(rowValue) } -const TablePaginationActions = (props: TablePaginationActionsProps) => { - const theme = useTheme() - const { count, page, rowsPerPage, onPageChange } = props - - const handleFirstPageButtonClick = ( - event: React.MouseEvent - ) => { - onPageChange(event, 0) - } - - const handleBackButtonClick = ( - event: React.MouseEvent - ) => { - onPageChange(event, page - 1) - } - - const handleNextButtonClick = ( - event: React.MouseEvent - ) => { - onPageChange(event, page + 1) - } - - const handleLastPageButtonClick = ( - event: React.MouseEvent - ) => { - onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)) - } - - return ( - - - {theme.direction === "rtl" ? : } - - - {theme.direction === "rtl" ? ( - - ) : ( - - )} - - = Math.ceil(count / rowsPerPage) - 1} - aria-label="next page" - > - {theme.direction === "rtl" ? ( - - ) : ( - - )} - - = Math.ceil(count / rowsPerPage) - 1} - aria-label="last page" - > - {theme.direction === "rtl" ? : } - - - ) -} - -function BasicTable(props: { - data: Trial[] - columns: ColumnDef[] -}): React.ReactElement { - const { data, columns } = props - const [sorting, setSorting] = React.useState([]) - const [columnFilters, setColumnFilters] = React.useState( - [] - ) - const [filterMenuAnchorEl, setFilterMenuAnchorEl] = - React.useState(null) - - const table = useReactTable({ - data, - columns, - state: { - columnFilters, - sorting, - }, - onColumnFiltersChange: setColumnFilters, - onSortingChange: setSorting, - getCoreRowModel: getCoreRowModel(), - getFacetedUniqueValues: getFacetedUniqueValues(), - getSortedRowModel: getSortedRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getFilteredRowModel: getFilteredRowModel(), - // - // debugTable: true, - }) - - const { pageSize, pageIndex } = table.getState().pagination - - React.useEffect(() => { - return () => { - table.resetColumnFilters() - } - }, []) - - return ( - - - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - const order = header.column.getIsSorted() - const filterChoices = header.column.getCanFilter() - ? Array.from( - header.column.getFacetedUniqueValues().keys() - ).sort() - : [] - if ( - header.column.getCanFilter() && - !header.column.getIsFiltered() - ) { - header.column.setFilterValue([]) - console.log(header.column.getFilterValue()) - } - return ( - - {header.isPlaceholder ? null : ( - - {header.column.getCanSort() ? ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - {order !== null ? ( - - {order === "desc" - ? "sorted descending" - : "sorted ascending"} - - ) : null} - - ) : ( - flexRender( - header.column.columnDef.header, - header.getContext() - ) - )} - {header.column.getCanFilter() ? ( - <> - { - setFilterMenuAnchorEl(e.currentTarget) - }} - > - - - { - setFilterMenuAnchorEl(null) - }} - > - {filterChoices.map((choice) => ( - { - const skippedValues = - header.column.getFilterValue() as string[] - const isSkipped = - skippedValues.includes(choice) - const newSkippedValues = isSkipped - ? skippedValues.filter( - (v) => v !== choice - ) - : skippedValues.concat(choice) - header.column.setFilterValue( - newSkippedValues - ) - }} - > - - {( - header.column.getFilterValue() as string[] - ).includes(choice) ? ( - - ) : ( - - )} - - {choice ?? "(missing value)"} - - ))} - - - ) : null} - - )} - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { - return ( - - {row.getVisibleCells().map((cell) => { - return ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ) - })} - - ) - })} - -
-
- { - table.setPageIndex(page) - }} - onRowsPerPageChange={(e) => { - const size = e.target.value ? Number(e.target.value) : 10 - table.setPageSize(size) - }} - ActionsComponent={TablePaginationActions} - /> -
- ) -} - export const TrialTable: FC<{ studyDetail: StudyDetail | null initialRowsPerPage?: number @@ -370,8 +59,7 @@ export const TrialTable: FC<{ header: "State", footer: (info) => info.column.id, enableSorting: false, - // enableColumnFilter: true, - enableColumnFilter: false, + enableColumnFilter: true, filterFn: multiValueFilter, }), ] @@ -495,8 +183,7 @@ export const TrialTable: FC<{ header: `Param ${s.name}`, footer: (info) => info.column.id, enableSorting: sortable, - // enableColumnFilter: filterChoices !== undefined, - enableColumnFilter: false, + enableColumnFilter: filterChoices !== undefined, filterFn: multiValueFilter, } ) @@ -600,7 +287,7 @@ export const TrialTable: FC<{ > Download CSV File - + ) } From 5c034f821d547ab96fe1cfeaf62c9e39d7e8837b Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Mon, 15 Apr 2024 11:10:01 +0900 Subject: [PATCH 09/28] Install tanstack table --- optuna_dashboard/package-lock.json | 32 ++++++++++++++++++++++++++++++ optuna_dashboard/package.json | 1 + 2 files changed, 33 insertions(+) diff --git a/optuna_dashboard/package-lock.json b/optuna_dashboard/package-lock.json index 7baac227..e4d44204 100644 --- a/optuna_dashboard/package-lock.json +++ b/optuna_dashboard/package-lock.json @@ -17,6 +17,7 @@ "@react-three/drei": "^9.96.4", "@react-three/fiber": "^8.15.15", "@tanstack/react-query": "^5.18.1", + "@tanstack/react-table": "^8.16.0", "@tanstack/react-virtual": "^3.1.2", "@types/three": "^0.160.0", "axios": "^1.6.7", @@ -3120,6 +3121,25 @@ "react": "^18.0.0" } }, + "node_modules/@tanstack/react-table": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.16.0.tgz", + "integrity": "sha512-rKRjnt8ostqN2fercRVOIH/dq7MAmOENCMvVlKx6P9Iokhh6woBGnIZEkqsY/vEJf1jN3TqLOb34xQGLVRuhAg==", + "dependencies": { + "@tanstack/table-core": "8.16.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@tanstack/react-virtual": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.4.0.tgz", @@ -3136,6 +3156,18 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@tanstack/table-core": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.16.0.tgz", + "integrity": "sha512-dCG8vQGk4js5v88/k83tTedWOwjGnIyONrKpHpfmSJB8jwFHl8GSu1sBBxbtACVAPtAQgwNxl0rw1d3RqRM1Tg==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/virtual-core": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.4.0.tgz", diff --git a/optuna_dashboard/package.json b/optuna_dashboard/package.json index c09e81bd..b81887c1 100644 --- a/optuna_dashboard/package.json +++ b/optuna_dashboard/package.json @@ -22,6 +22,7 @@ "@react-three/drei": "^9.96.4", "@react-three/fiber": "^8.15.15", "@tanstack/react-query": "^5.18.1", + "@tanstack/react-table": "^8.16.0", "@tanstack/react-virtual": "^3.1.2", "@types/three": "^0.160.0", "axios": "^1.6.7", From ab7257e9b025d510020732bff02e30469cab4eac Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Mon, 15 Apr 2024 14:59:20 +0900 Subject: [PATCH 10/28] Changed default row numbers --- optuna_dashboard/ts/components/DataGrid.tsx | 7 +++++++ optuna_dashboard/ts/components/TrialTable.tsx | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 7f858ee8..4efecfd4 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -45,6 +45,7 @@ import { getFacetedUniqueValues, getFacetedRowModel, SortingState, + PaginationState, ColumnFiltersState, useReactTable, } from "@tanstack/react-table" @@ -492,6 +493,10 @@ function DataGrid2(props: { ) const [filterMenuAnchorEl, setFilterMenuAnchorEl] = React.useState(null) + const [pagination, setPagination] = React.useState({ + pageIndex: 0, + pageSize: 50, + }) const table = useReactTable({ data, @@ -499,9 +504,11 @@ function DataGrid2(props: { state: { columnFilters, sorting, + pagination, }, onColumnFiltersChange: setColumnFilters, onSortingChange: setSorting, + onPaginationChange: setPagination, getCoreRowModel: getCoreRowModel(), getFacetedRowModel: getFacetedRowModel(), getFacetedUniqueValues: getFacetedUniqueValues(), diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 7d78e967..7ea88c6f 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -19,7 +19,7 @@ import { FilterFn, } from "@tanstack/react-table" -const multiValueFilter: FilterFn = ( +const multiValueFilter: FilterFn = ( row: Row, columnId: IdType, filterValue: string[] @@ -49,7 +49,7 @@ export const TrialTable: FC<{ ] const columnHelper = createColumnHelper() - const tcolumns: ColumnDef[] = [ + const tcolumns: ColumnDef[] = [ columnHelper.accessor("number", { header: "Number", footer: (info) => info.column.id, From c4aff133ede4a5428c72f5c5a2b38024ae337ae5 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Mon, 15 Apr 2024 15:39:21 +0900 Subject: [PATCH 11/28] Made autoResetPageIndex false --- optuna_dashboard/ts/components/DataGrid.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 4efecfd4..41f0d129 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -515,18 +515,11 @@ function DataGrid2(props: { getSortedRowModel: getSortedRowModel(), getPaginationRowModel: getPaginationRowModel(), getFilteredRowModel: getFilteredRowModel(), + autoResetPageIndex: false, // // debugTable: true, }) - const { pageSize, pageIndex } = table.getState().pagination - - React.useEffect(() => { - return () => { - table.resetColumnFilters() - } - }, []) - return ( @@ -661,8 +654,8 @@ function DataGrid2(props: { rowsPerPageOptions={[10, 50, 100, { label: "All", value: data.length }]} component="div" count={table.getFilteredRowModel().rows.length} - rowsPerPage={pageSize} - page={pageIndex} + rowsPerPage={table.getState().pagination.pageSize} + page={table.getState().pagination.pageIndex} slotProps={{ select: { inputProps: { "aria-label": "rows per page" }, From b74c9931b940f91ada40088b1a30aa33c2880d6a Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Mon, 15 Apr 2024 16:14:41 +0900 Subject: [PATCH 12/28] Fix sort order --- optuna_dashboard/ts/components/DataGrid.tsx | 88 ++++++++++++++----- optuna_dashboard/ts/components/TrialTable.tsx | 8 +- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 41f0d129..4b5bbe41 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -650,27 +650,40 @@ function DataGrid2(props: { - { - table.setPageIndex(page) - }} - onRowsPerPageChange={(e) => { - const size = e.target.value ? Number(e.target.value) : 10 - table.setPageSize(size) - }} - ActionsComponent={TablePaginationActions} - /> + + { + table.setPageIndex(page) + }} + onRowsPerPageChange={(e) => { + const size = e.target.value ? Number(e.target.value) : 10 + table.setPageSize(size) + }} + ActionsComponent={TablePaginationActions} + /> + {table.getPageCount() > 2 ? ( + table.setPageIndex(page)} + maxPageNumber={table.getPageCount()} + /> + ) : null} + ) } @@ -745,4 +758,37 @@ const TablePaginationActions = (props: TablePaginationActionsProps) => { ) } +const PaginationForm1: React.FC<{ + onPageNumberSubmit: (value: number) => void + maxPageNumber: number +}> = ({ onPageNumberSubmit, maxPageNumber }) => { + // This component is separated from DataGrid to prevent `DataGrid` from re-rendering the page, + // every time any letters are input. + const [specifiedPageText, setSpecifiedPageText] = React.useState("") + + const handleSubmitPageNumber = (event: React.FormEvent) => { + event.preventDefault() + const newPageNumber = parseInt(specifiedPageText, 10) + // Page is 0-indexed in `TablePagination`. + onPageNumberSubmit(newPageNumber - 1) + setSpecifiedPageText("") // reset the input field + } + + return ( +
+ { + setSpecifiedPageText(e.target.value) + }} + /> + + ) +} + export { DataGrid, DataGrid2, DataGridColumn } diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 7ea88c6f..47698ddc 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -101,7 +101,9 @@ export const TrialTable: FC<{ columnHelper.accessor("values", { header: "Value", footer: (info) => info.column.id, + enableSorting: true, enableColumnFilter: false, + sortUndefined: "last", }) ) } else { @@ -130,11 +132,13 @@ export const TrialTable: FC<{ columns.push(...objectiveColumns) tcolumns.push( ...studyDetail.directions.map((s, objectiveId) => - columnHelper.accessor("values", { + columnHelper.accessor((row) => row["values"]?.[objectiveId], { id: `values_${objectiveId}`, header: `Objective ${objectiveId}`, footer: (info) => info.column.id, + enableSorting: true, enableColumnFilter: false, + sortUndefined: "last", }) ) ) @@ -183,6 +187,7 @@ export const TrialTable: FC<{ header: `Param ${s.name}`, footer: (info) => info.column.id, enableSorting: sortable, + sortUndefined: "last", enableColumnFilter: filterChoices !== undefined, filterFn: multiValueFilter, } @@ -222,6 +227,7 @@ export const TrialTable: FC<{ footer: (info) => info.column.id, enableSorting: attr_spec.sortable, enableColumnFilter: false, + sortUndefined: "last", } ) ) From e4f92e85ead667044b7aaa11bb11775fad5e8f3a Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Wed, 17 Apr 2024 21:25:44 +0900 Subject: [PATCH 13/28] Small fix --- optuna_dashboard/ts/components/DataGrid.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 4b5bbe41..c3344503 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -528,19 +528,17 @@ function DataGrid2(props: { {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { - const order = header.column.getIsSorted() - const filterChoices = header.column.getCanFilter() - ? Array.from( - header.column.getFacetedUniqueValues().keys() - ).sort() - : [] if ( header.column.getCanFilter() && !header.column.getIsFiltered() ) { header.column.setFilterValue([]) - console.log(header.column.getFilterValue()) } + const order = header.column.getIsSorted() + const filterChoices = header.column.getCanFilter() + ? Array.from( + header.column.getFacetedUniqueValues().keys() + ).sort() : null return ( {header.isPlaceholder ? null : ( @@ -569,7 +567,7 @@ function DataGrid2(props: { header.getContext() ) )} - {header.column.getCanFilter() ? ( + {filterChoices !== null ? ( <> Date: Wed, 17 Apr 2024 21:56:34 +0900 Subject: [PATCH 14/28] Replace all DataGrid --- optuna_dashboard/ts/components/DataGrid.tsx | 9 +++--- .../Preferential/PreferentialAnalytics.tsx | 30 +++++++++++------- .../ts/components/StudyHistory.tsx | 31 ++++++++++++------- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index c3344503..8346a0ad 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -482,9 +482,9 @@ const isNumber = ( return typeof rowsPerPage === "number" } -function DataGrid2(props: { - data: Trial[] - columns: ColumnDef[] +function DataGrid2(props: { + data: T[] + columns: ColumnDef[] }): React.ReactElement { const { data, columns } = props const [sorting, setSorting] = React.useState([]) @@ -538,7 +538,8 @@ function DataGrid2(props: { const filterChoices = header.column.getCanFilter() ? Array.from( header.column.getFacetedUniqueValues().keys() - ).sort() : null + ).sort() + : null return ( {header.isPlaceholder ? null : ( diff --git a/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx b/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx index 63dba5e2..e701bd92 100644 --- a/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx +++ b/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx @@ -7,6 +7,10 @@ import { useTheme, } from "@mui/material" import Grid2 from "@mui/material/Unstable_Grid2" +import { DataGrid2 } from "../DataGrid" +import { BestTrialsCard } from "../BestTrialsCard" +import { useStudyDetailValue, useStudySummaryValue } from "../../state" +import { Contour } from "../GraphContour" import * as Optuna from "@optuna/types" import React, { FC } from "react" import { useStudyDetailValue, useStudySummaryValue } from "../../state" @@ -14,15 +18,26 @@ import { BestTrialsCard } from "../BestTrialsCard" import { DataGrid, DataGridColumn } from "../DataGrid" import { Contour } from "../GraphContour" +import { ColumnDef, createColumnHelper } from "@tanstack/react-table" + export const PreferentialAnalytics: FC<{ studyId: number }> = ({ studyId }) => { const theme = useTheme() const studySummary = useStudySummaryValue(studyId) const studyDetail = useStudyDetailValue(studyId) const userAttrs = studySummary?.user_attrs || studyDetail?.user_attrs || [] - const userAttrColumns: DataGridColumn[] = [ - { field: "key", label: "Key", sortable: true }, - { field: "value", label: "Value", sortable: true }, + const columnHelper = createColumnHelper() + const columns: ColumnDef[] = [ + columnHelper.accessor("key", { + header: "Key", + footer: (info) => info.column.id, + enableSorting: true, + }), + columnHelper.accessor("value", { + header: "Value", + footer: (info) => info.column.id, + enableSorting: true, + }), ] return ( = ({ studyId }) => { > Study User Attributes - - columns={userAttrColumns} - rows={userAttrs} - keyField={"key"} - dense={true} - initialRowsPerPage={5} - rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} - /> + diff --git a/optuna_dashboard/ts/components/StudyHistory.tsx b/optuna_dashboard/ts/components/StudyHistory.tsx index 12728c16..39eea698 100644 --- a/optuna_dashboard/ts/components/StudyHistory.tsx +++ b/optuna_dashboard/ts/components/StudyHistory.tsx @@ -11,6 +11,11 @@ import FormControlLabel from "@mui/material/FormControlLabel" import Grid2 from "@mui/material/Unstable_Grid2" import * as Optuna from "@optuna/types" import React, { FC, useState } from "react" +import { DataGrid2 } from "./DataGrid" +import { GraphHyperparameterImportance } from "./GraphHyperparameterImportances" +import { UserDefinedPlot } from "./UserDefinedPlot" +import { BestTrialsCard } from "./BestTrialsCard" +import { StudyArtifactCards } from "./Artifact/StudyArtifactCards" import { useRecoilValue } from "recoil" import { Trial } from "ts/types/optuna" import { @@ -29,6 +34,8 @@ import { GraphParetoFront } from "./GraphParetoFront" import { GraphTimeline } from "./GraphTimeline" import { UserDefinedPlot } from "./UserDefinedPlot" +import { ColumnDef, createColumnHelper } from "@tanstack/react-table" + export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { const theme = useTheme() const directions = useStudyDirections(studyId) @@ -47,9 +54,18 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { } const userAttrs = studySummary?.user_attrs || studyDetail?.user_attrs || [] - const userAttrColumns: DataGridColumn[] = [ - { field: "key", label: "Key", sortable: true }, - { field: "value", label: "Value", sortable: true }, + const columnHelper = createColumnHelper() + const columns: ColumnDef[] = [ + columnHelper.accessor("key", { + header: "Key", + footer: (info) => info.column.id, + enableSorting: true, + }), + columnHelper.accessor("value", { + header: "Value", + footer: (info) => info.column.id, + enableSorting: true, + }), ] const trials: Trial[] = studyDetail?.trials || [] return ( @@ -153,14 +169,7 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { > Study User Attributes - - columns={userAttrColumns} - rows={userAttrs} - keyField={"key"} - dense={true} - initialRowsPerPage={5} - rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} - /> + From dbc7264c2cd62bc15e7e92641c09a02f940af60d Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Wed, 17 Apr 2024 22:02:07 +0900 Subject: [PATCH 15/28] Delete unnecessary code --- optuna_dashboard/ts/components/DataGrid.tsx | 423 +----------------- optuna_dashboard/ts/components/TrialTable.tsx | 139 +----- 2 files changed, 8 insertions(+), 554 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 8346a0ad..d64cc1cb 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -18,13 +18,16 @@ import { TableRow, TableSortLabel, TextField, - Collapse, IconButton, Menu, MenuItem, Box, useTheme, } from "@mui/material" +import { styled } from "@mui/system" +import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank" +import CheckBoxIcon from "@mui/icons-material/CheckBox" +import FilterListIcon from "@mui/icons-material/FilterList" import ListItemIcon from "@mui/material/ListItemIcon" import { styled } from "@mui/system" import React from "react" @@ -50,224 +53,6 @@ import { useReactTable, } from "@tanstack/react-table" -type Order = "asc" | "desc" - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Value = any - -const defaultRowsPerPageOption = [10, 50, 100, { label: "All", value: -1 }] - -interface DataGridColumn { - field: keyof T - label: string - sortable?: boolean - less?: (a: T, b: T, ascending: boolean) => number - filterChoices?: (string | null)[] - toCellValue?: (rowIndex: number) => string | React.ReactNode - padding?: "normal" | "checkbox" | "none" -} - -interface RowFilter { - columnIdx: number - values: Value[] -} - -function DataGrid(props: { - columns: DataGridColumn[] - rows: T[] - keyField: keyof T - dense?: boolean - collapseBody?: (rowIndex: number) => React.ReactNode - initialRowsPerPage?: number - rowsPerPageOption?: Array - defaultFilter?: (row: T) => boolean -}): React.ReactElement { - const { columns, rows, keyField, dense, collapseBody, defaultFilter } = props - let { initialRowsPerPage, rowsPerPageOption } = props - const [order, setOrder] = React.useState("asc") - const [orderBy, setOrderBy] = React.useState(0) // index of columns - const [page, setPage] = React.useState(0) - const [filters, setFilters] = React.useState([]) - - const getRowIndex = (row: T): number => { - return rows.findIndex((row2) => row[keyField] === row2[keyField]) - } - - // Pagination - rowsPerPageOption = rowsPerPageOption || defaultRowsPerPageOption - initialRowsPerPage = initialRowsPerPage // use first element as default - ? initialRowsPerPage - : isNumber(rowsPerPageOption[0]) - ? rowsPerPageOption[0] - : rowsPerPageOption[0].value - const [rowsPerPage, setRowsPerPage] = React.useState(initialRowsPerPage) - - const handleChangePage = (event: unknown, newPage: number) => { - setPage(newPage) - } - - const handleChangeRowsPerPage = ( - event: React.ChangeEvent - ) => { - setRowsPerPage(parseInt(event.target.value, 10)) - setPage(0) - } - - const PaginationForm: React.FC<{ - onPageNumberSubmit: (value: number) => void - maxPageNumber: number - }> = ({ onPageNumberSubmit, maxPageNumber }) => { - // This component is separated from DataGrid to prevent `DataGrid` from re-rendering the page, - // every time any letters are input. - const [specifiedPageText, setSpecifiedPageText] = React.useState("") - - const handleSubmitPageNumber = ( - event: React.FormEvent - ) => { - event.preventDefault() - const newPageNumber = parseInt(specifiedPageText, 10) - // Page is 0-indexed in `TablePagination`. - onPageNumberSubmit(newPageNumber - 1) - setSpecifiedPageText("") // reset the input field - } - - return ( -
- { - setSpecifiedPageText(e.target.value) - }} - /> - - ) - } - - // Filtering - const filteredRows = rows.filter((row, rowIdx) => { - if (defaultFilter !== undefined && defaultFilter(row)) { - return false - } - return filters.length === 0 - ? true - : filters.every((f) => { - if (columns.length <= f.columnIdx) { - console.log( - `columnIdx=${f.columnIdx} must be smaller than columns.length=${columns.length}` - ) - return true - } - const toCellValue = columns[f.columnIdx].toCellValue - const cellValue = - toCellValue !== undefined - ? toCellValue(rowIdx) - : row[columns[f.columnIdx].field] - return f.values.some((v) => v === cellValue) - }) - }) - - // Sorting - const sortedRows = stableSort(filteredRows, order, orderBy, columns) - const currentPageRows = - rowsPerPage > 0 - ? sortedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - : sortedRows - const emptyRows = - rowsPerPage - Math.min(rowsPerPage, sortedRows.length - page * rowsPerPage) - - const RootDiv = styled("div")({ - width: "100%", - }) - const maxPageNumber = Math.ceil(filteredRows.length / rowsPerPage) - return ( - - - - - - {collapseBody ? : null} - {columns.map((column, columnIdx) => { - return ( - - key={columnIdx} - column={column} - order={orderBy === columnIdx ? order : null} - filter={ - filters.find((f) => f.columnIdx === columnIdx) || null - } - onOrderByChange={(direction: Order) => { - setOrder(direction) - setOrderBy(columnIdx) - }} - onFilterChange={(values: Value[]) => { - const newFilters = filters.filter( - (f) => f.columnIdx !== columnIdx - ) - newFilters.push({ - columnIdx: columnIdx, - values: values, - }) - setFilters(newFilters) - }} - /> - ) - })} - - - - {currentPageRows.map((row) => ( - - columns={columns} - rowIndex={getRowIndex(row)} - row={row} - keyField={keyField} - collapseBody={collapseBody} - key={`${row[keyField]}`} - /> - ))} - {emptyRows > 0 && ( - - - - )} - -
-
- {filteredRows.length > 0 ? ( - <> - {/* @ts-ignore */} - - - {maxPageNumber > 2 ? ( - setPage(page)} - maxPageNumber={maxPageNumber} - /> - ) : null} - - - ) : null} -
- ) -} - const TableHeaderCellSpan = styled("span")({ display: "inline-flex", }) @@ -284,204 +69,6 @@ const HiddenSpan = styled("span")({ width: 1, }) -function DataGridHeaderColumn(props: { - column: DataGridColumn - order: Order | null - onOrderByChange: (order: Order) => void - filter: RowFilter | null - onFilterChange: (values: Value[]) => void - dense?: boolean -}) { - const { column, order, onOrderByChange, filter, onFilterChange, dense } = - props - const [filterMenuAnchorEl, setFilterMenuAnchorEl] = - React.useState(null) - - const filterChoices = column.filterChoices - - return ( - - - {column.sortable ? ( - { - onOrderByChange(order === "asc" ? "desc" : "asc") - }} - > - {column.label} - {order !== null ? ( - - {order === "desc" ? "sorted descending" : "sorted ascending"} - - ) : null} - - ) : ( - column.label - )} - {filterChoices !== undefined ? ( - <> - { - setFilterMenuAnchorEl(e.currentTarget) - }} - > - - - { - setFilterMenuAnchorEl(null) - }} - > - {filterChoices.map((choice) => ( - { - const newTickedValues = - filter === null - ? filterChoices.filter((v) => v !== choice) // By default, every choice is ticked, so the chosen option will be unticked. - : filter.values.some((v) => v === choice) - ? filter.values.filter((v) => v !== choice) - : [...filter.values, choice] - onFilterChange(newTickedValues) - }} - > - - {!filter || filter.values.some((v) => v === choice) ? ( - - ) : ( - - )} - - {choice ?? "(missing value)"} - - ))} - - - ) : null} - - - ) -} - -function DataGridRow(props: { - columns: DataGridColumn[] - rowIndex: number - row: T - keyField: keyof T - collapseBody?: (rowIndex: number) => React.ReactNode -}) { - const { columns, rowIndex, row, keyField, collapseBody } = props - const [open, setOpen] = React.useState(false) - - return ( - - - {collapseBody ? ( - - setOpen(!open)} - > - {open ? : } - - - ) : null} - {columns.map((column, columnIndex) => { - const cellItem = column.toCellValue - ? column.toCellValue(rowIndex) - : // TODO(c-bata): Avoid this implicit type conversion. - (row[column.field] as number | string | null | undefined) - - return ( - - {cellItem} - - ) - })} - - {collapseBody ? ( - - - - {collapseBody(rowIndex)} - - - - ) : null} - - ) -} - -function getComparator( - order: Order, - columns: DataGridColumn[], - orderBy: number -): (a: T, b: T) => number { - return order === "desc" - ? (a, b) => descendingComparator(a, b, columns, orderBy) - : (a, b) => -descendingComparator(a, b, columns, orderBy) -} - -function descendingComparator( - a: T, - b: T, - columns: DataGridColumn[], - orderBy: number -): number { - const field = columns[orderBy].field - if (b[field] < a[field]) { - return -1 - } - if (b[field] > a[field]) { - return 1 - } - return 0 -} - -function stableSort( - array: T[], - order: Order, - orderBy: number, - columns: DataGridColumn[] -) { - // TODO(c-bata): Refactor here by implementing as the same comparator interface. - const less = columns[orderBy].less - const comparator = getComparator(order, columns, orderBy) - const stabilizedThis = array.map((el, index) => [el, index] as [T, number]) - stabilizedThis.sort((a, b) => { - if (less) { - const ascending = order === "asc" - const result = ascending - ? -less(a[0], b[0], ascending) - : less(a[0], b[0], ascending) - if (result !== 0) return result - } else { - const result = comparator(a[0], b[0]) - if (result !== 0) return result - } - return a[1] - b[1] - }) - return stabilizedThis.map((el) => el[0]) -} - -const isNumber = ( - rowsPerPage: number | { value: number; label: string } -): rowsPerPage is number => { - return typeof rowsPerPage === "number" -} - function DataGrid2(props: { data: T[] columns: ColumnDef[] @@ -790,4 +377,4 @@ const PaginationForm1: React.FC<{ ) } -export { DataGrid, DataGrid2, DataGridColumn } +export { DataGrid2 } diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 47698ddc..93177997 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -6,7 +6,7 @@ import LinkIcon from "@mui/icons-material/Link" import { Button, IconButton, useTheme } from "@mui/material" import React, { FC } from "react" -import { DataGridColumn, DataGrid, DataGrid2 } from "./DataGrid" +import { DataGrid2 } from "./DataGrid" import { Link } from "react-router-dom" import { StudyDetail, Trial } from "ts/types/optuna" import { DataGrid, DataGridColumn } from "./DataGrid" @@ -34,19 +34,7 @@ export const TrialTable: FC<{ }> = ({ studyDetail, initialRowsPerPage }) => { const theme = useTheme() const trials: Trial[] = studyDetail !== null ? studyDetail.trials : [] - const objectiveNames: string[] = studyDetail?.objective_names || [] - - const columns: DataGridColumn[] = [ - { field: "number", label: "Number", sortable: true, padding: "none" }, - { - field: "state", - label: "State", - sortable: true, - filterChoices: ["Complete", "Pruned", "Fail", "Running", "Waiting"], - padding: "none", - toCellValue: (i) => trials[i].state.toString(), - }, - ] + // TODO: const objectiveNames: string[] = studyDetail?.objective_names || [] const columnHelper = createColumnHelper() const tcolumns: ColumnDef[] = [ @@ -63,40 +51,7 @@ export const TrialTable: FC<{ filterFn: multiValueFilter, }), ] - const valueComparator = ( - firstVal?: number, - secondVal?: number, - ascending = true - ): number => { - if (firstVal === secondVal) { - return 0 - } - if (firstVal === undefined) { - return ascending ? -1 : 1 - } else if (secondVal === undefined) { - return ascending ? 1 : -1 - } - return firstVal < secondVal ? 1 : -1 - } if (studyDetail === null || studyDetail.directions.length === 1) { - columns.push({ - field: "values", - label: "Value", - sortable: true, - less: (firstEl, secondEl, ascending): number => { - return valueComparator( - firstEl.values?.[0], - secondEl.values?.[0], - ascending - ) - }, - toCellValue: (i) => { - if (trials[i].values === undefined) { - return null - } - return trials[i].values?.[0] - }, - }) tcolumns.push( columnHelper.accessor("values", { header: "Value", @@ -107,29 +62,6 @@ export const TrialTable: FC<{ }) ) } else { - const objectiveColumns: DataGridColumn[] = - studyDetail.directions.map((s, objectiveId) => ({ - field: "values", - label: - objectiveNames.length === studyDetail?.directions.length - ? objectiveNames[objectiveId] - : `Objective ${objectiveId}`, - sortable: true, - less: (firstEl, secondEl, ascending): number => { - return valueComparator( - firstEl.values?.[objectiveId], - secondEl.values?.[objectiveId], - ascending - ) - }, - toCellValue: (i) => { - if (trials[i].values === undefined) { - return null - } - return trials[i].values?.[objectiveId] - }, - })) - columns.push(...objectiveColumns) tcolumns.push( ...studyDetail.directions.map((s, objectiveId) => columnHelper.accessor((row) => row["values"]?.[objectiveId], { @@ -158,25 +90,6 @@ export const TrialTable: FC<{ if (filterChoices !== undefined && isDynamicSpace && hasMissingValue) { filterChoices.push(null) } - columns.push({ - field: "params", - label: `Param ${s.name}`, - toCellValue: (i) => - trials[i].params.find((p) => p.name === s.name)?.param_external_value || - null, - sortable: sortable, - filterChoices: filterChoices, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - less: (firstEl, secondEl, _): number => { - const firstVal = firstEl.params.find( - (p) => p.name === s.name - )?.param_internal_value - const secondVal = secondEl.params.find( - (p) => p.name === s.name - )?.param_internal_value - return valueComparator(firstVal, secondVal) - }, - }) tcolumns.push( columnHelper.accessor( (row) => @@ -196,27 +109,6 @@ export const TrialTable: FC<{ }) studyDetail?.union_user_attrs.forEach((attr_spec) => { - columns.push({ - field: "user_attrs", - label: `UserAttribute ${attr_spec.key}`, - toCellValue: (i) => - trials[i].user_attrs.find((attr) => attr.key === attr_spec.key) - ?.value || null, - sortable: attr_spec.sortable, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - less: (firstEl, secondEl, _): number => { - const firstValString = firstEl.user_attrs.find( - (attr) => attr.key === attr_spec.key - )?.value - const secondValString = secondEl.user_attrs.find( - (attr) => attr.key === attr_spec.key - )?.value - return valueComparator( - Number(firstValString) ?? firstValString, - Number(secondValString) ?? secondValString - ) - }, - }) tcolumns.push( columnHelper.accessor( (row) => @@ -232,24 +124,6 @@ export const TrialTable: FC<{ ) ) }) - columns.push({ - field: "trial_id", - label: "Detail", - toCellValue: (i) => ( - - - - ), - }) tcolumns.push( columnHelper.accessor((row) => row, { header: "Detail", @@ -277,13 +151,7 @@ export const TrialTable: FC<{ return ( <> - - columns={columns} - rows={trials} - keyField={"trial_id"} - dense={true} - initialRowsPerPage={initialRowsPerPage} - /> + - ) } From 0cc538ff36cfd40100e1bf4951db07ab115edeff Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Wed, 17 Apr 2024 22:54:48 +0900 Subject: [PATCH 16/28] Rename class --- optuna_dashboard/ts/components/DataGrid.tsx | 4 ++-- .../ts/components/Preferential/PreferentialAnalytics.tsx | 4 ++-- optuna_dashboard/ts/components/StudyHistory.tsx | 5 ++--- optuna_dashboard/ts/components/TrialTable.tsx | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index d64cc1cb..3652d915 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -69,7 +69,7 @@ const HiddenSpan = styled("span")({ width: 1, }) -function DataGrid2(props: { +function DataGrid(props: { data: T[] columns: ColumnDef[] }): React.ReactElement { @@ -377,4 +377,4 @@ const PaginationForm1: React.FC<{ ) } -export { DataGrid2 } +export { DataGrid } diff --git a/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx b/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx index e701bd92..79d2dd8c 100644 --- a/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx +++ b/optuna_dashboard/ts/components/Preferential/PreferentialAnalytics.tsx @@ -7,7 +7,7 @@ import { useTheme, } from "@mui/material" import Grid2 from "@mui/material/Unstable_Grid2" -import { DataGrid2 } from "../DataGrid" +import { DataGrid } from "../DataGrid" import { BestTrialsCard } from "../BestTrialsCard" import { useStudyDetailValue, useStudySummaryValue } from "../../state" import { Contour } from "../GraphContour" @@ -70,7 +70,7 @@ export const PreferentialAnalytics: FC<{ studyId: number }> = ({ studyId }) => { > Study User Attributes - + diff --git a/optuna_dashboard/ts/components/StudyHistory.tsx b/optuna_dashboard/ts/components/StudyHistory.tsx index 39eea698..8850b9db 100644 --- a/optuna_dashboard/ts/components/StudyHistory.tsx +++ b/optuna_dashboard/ts/components/StudyHistory.tsx @@ -10,8 +10,7 @@ import { import FormControlLabel from "@mui/material/FormControlLabel" import Grid2 from "@mui/material/Unstable_Grid2" import * as Optuna from "@optuna/types" -import React, { FC, useState } from "react" -import { DataGrid2 } from "./DataGrid" +import { DataGrid } from "./DataGrid" import { GraphHyperparameterImportance } from "./GraphHyperparameterImportances" import { UserDefinedPlot } from "./UserDefinedPlot" import { BestTrialsCard } from "./BestTrialsCard" @@ -169,7 +168,7 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { > Study User Attributes - + diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 93177997..8841b5e4 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -6,7 +6,7 @@ import LinkIcon from "@mui/icons-material/Link" import { Button, IconButton, useTheme } from "@mui/material" import React, { FC } from "react" -import { DataGrid2 } from "./DataGrid" +import { DataGrid } from "./DataGrid" import { Link } from "react-router-dom" import { StudyDetail, Trial } from "ts/types/optuna" import { DataGrid, DataGridColumn } from "./DataGrid" @@ -151,7 +151,7 @@ export const TrialTable: FC<{ return ( <> - +