diff --git a/optuna_dashboard/ts/components/GraphHistory.tsx b/optuna_dashboard/ts/components/GraphHistory.tsx
index 6d90191b..42536e29 100644
--- a/optuna_dashboard/ts/components/GraphHistory.tsx
+++ b/optuna_dashboard/ts/components/GraphHistory.tsx
@@ -1,7 +1,9 @@
import { useTheme } from "@mui/material"
import { PlotHistory } from "@optuna/react"
import React, { FC } from "react"
+import { useNavigate } from "react-router-dom"
import { StudyDetail } from "ts/types/optuna"
+import { useConstants } from "../constantsProvider"
import { usePlotlyColorTheme } from "../state"
export const GraphHistory: FC<{
@@ -9,14 +11,22 @@ export const GraphHistory: FC<{
logScale: boolean
includePruned: boolean
}> = ({ studies, logScale, includePruned }) => {
+ const { url_prefix } = useConstants()
const theme = useTheme()
const colorTheme = usePlotlyColorTheme(theme.palette.mode)
+ const linkURL = (studyId: number, trialNumber: number) => {
+ return url_prefix + `/studies/${studyId}/trials?numbers=${trialNumber}`
+ }
+ const navigate = useNavigate()
+
return (
)
}
diff --git a/tslib/react/src/components/PlotHistory.tsx b/tslib/react/src/components/PlotHistory.tsx
index 968651af..294dc7e0 100644
--- a/tslib/react/src/components/PlotHistory.tsx
+++ b/tslib/react/src/components/PlotHistory.tsx
@@ -39,7 +39,10 @@ export const PlotHistory: FC<{
logScale?: boolean
includePruned?: boolean
colorTheme?: Partial
-}> = ({ studies, logScale, includePruned, colorTheme }) => {
+ linkURL?: (studyId: number, trialNumber: number) => string
+ // biome-ignore lint/suspicious/noExplicitAny: It will accept any routers of each library.
+ router?: any
+}> = ({ studies, logScale, includePruned, colorTheme, linkURL, router }) => {
const { graphComponentState, notifyGraphDidRender } = useGraphComponentState()
const theme = useTheme()
@@ -108,6 +111,45 @@ export const PlotHistory: FC<{
colorThemeUsed,
markerSize
)?.then(notifyGraphDidRender)
+
+ const element = document.getElementById(plotDomId)
+ if (
+ element !== null &&
+ studies.length >= 1 &&
+ linkURL !== undefined &&
+ router !== undefined
+ ) {
+ // @ts-ignore
+ element.on("plotly_click", (data) => {
+ if (data.points[0].data.mode !== "lines") {
+ let studyId = 1
+ if (data.points[0].data.name.includes("Infeasible Trial of")) {
+ const studyInfo: { id: number; name: string }[] = []
+ for (const study of studies) {
+ studyInfo.push({ id: study.id, name: study.name })
+ }
+ const dataPointStudyName = data.points[0].data.name.replace(
+ "Infeasible Trial of ",
+ ""
+ )
+ const targetId = studyInfo.find(
+ (s) => s.name === dataPointStudyName
+ )?.id
+ if (targetId !== undefined) {
+ studyId = targetId
+ }
+ } else {
+ studyId = studies[Math.floor(data.points[0].curveNumber / 2)].id
+ }
+ const trialNumber = data.points[0].x
+ router(linkURL(studyId, trialNumber))
+ }
+ })
+ return () => {
+ // @ts-ignore
+ element.removeAllListeners("plotly_click")
+ }
+ }
}
}, [
historyPlotInfos,