From 5207df81425c5071215d36651f097a17330f2eaa Mon Sep 17 00:00:00 2001 From: Grace Date: Fri, 10 Apr 2026 18:15:32 +0100 Subject: [PATCH 1/8] Make data samples grid heading scrollable but with the right items fixed. Additionally make the close icons in front of the data samples using portals. --- src/components/ActionDataSamplesCard.tsx | 156 +++++++++++++---------- src/components/DataSamplesTable.tsx | 26 ++-- src/components/HeadingGrid.tsx | 49 +++++-- 3 files changed, 135 insertions(+), 96 deletions(-) diff --git a/src/components/ActionDataSamplesCard.tsx b/src/components/ActionDataSamplesCard.tsx index fb6d74e65..d92ec41a2 100644 --- a/src/components/ActionDataSamplesCard.tsx +++ b/src/components/ActionDataSamplesCard.tsx @@ -21,7 +21,7 @@ import { Text, VStack, } from "@chakra-ui/react"; -import { ReactNode, useCallback } from "react"; +import { forwardRef, ReactNode, useCallback, useRef } from "react"; import { RiHashtag, RiTimerLine } from "react-icons/ri"; import { FormattedMessage, useIntl } from "react-intl"; import { DataSamplesView, ActionData, RecordingData } from "../model"; @@ -81,32 +81,33 @@ const ActionDataSamplesCard = ({ )} {value.recordings.map((recording, idx) => ( - deleteActionRecording(value.id, recording.id)} + /> + } > - deleteActionRecording(value.id, recording.id)} - /> - + ))} ); @@ -163,37 +164,58 @@ const ActionDataSamplesCard = ({ ); }; -interface DataSamplesRowCardProps extends CardProps { - selected: boolean; - onSelectRow?: () => void; +interface GraphAndDataFeaturesDataSampleCardProps + extends DataSamplesRowCardProps { children: ReactNode; + closeButton: ReactNode; } -const DataSamplesRowCard = ({ - selected, - onSelectRow, +const GraphAndDataFeaturesDataSampleCard = ({ children, - ...rest -}: DataSamplesRowCardProps) => { + closeButton, + ...props +}: GraphAndDataFeaturesDataSampleCardProps) => { + const ref = useRef(null); return ( - - - {children} - - + + {closeButton} + {children} + ); }; +interface DataSamplesRowCardProps extends CardProps { + selected: boolean; + onSelectRow?: () => void; + children: ReactNode; +} + +const DataSamplesRowCard = forwardRef( + function DataSamplesRowCard( + { selected, onSelectRow, children, ...rest }: DataSamplesRowCardProps, + ref + ) { + return ( + + + {children} + + + ); + } +); + interface RecordingAreaProps extends BoxProps { action: ActionData; selected: boolean; @@ -328,6 +350,7 @@ const DataSample = ({ view: DataSamplesView; hasClose?: boolean; }) => { + const ref = useRef(null); const hasGraph = view === DataSamplesView.Graph || view === DataSamplesView.GraphAndDataFeatures; @@ -339,26 +362,27 @@ const DataSample = ({ onDelete(actionId, recording.id); }, [actionId, onDelete, recording.id]); return ( - + {hasClose && ( - + + + )} {hasGraph && ( = { }; const headings: GridColumnHeadingItemProps[] = [ - { - titleId: "action-label", - descriptionId: "action-tooltip", - }, - { - titleId: "data-samples-label", - descriptionId: "data-samples-tooltip", - itemsRight: ( - - - - - ), - }, + { titleId: "action-label", descriptionId: "action-tooltip" }, + { titleId: "data-samples-label", descriptionId: "data-samples-tooltip" }, ]; interface DataSamplesTableProps { @@ -249,6 +237,13 @@ const DataSamplesTable = ({ top={0} {...gridCommonProps} headings={headings} + w={gridRef.current?.clientWidth} + rightItems={ + <> + + + + } /> 0 ? `${gridPadding}px` : 2} alignItems="start" autoRows="max-content" - overflow="auto" flexGrow={1} h={0} ref={(node) => { diff --git a/src/components/HeadingGrid.tsx b/src/components/HeadingGrid.tsx index 8e5d6d092..4e7125d08 100644 --- a/src/components/HeadingGrid.tsx +++ b/src/components/HeadingGrid.tsx @@ -10,22 +10,43 @@ import { ReactNode } from "react"; interface HeadingGridProps extends GridProps { headings: GridColumnHeadingItemProps[]; + rightItems: ReactNode; } - -const HeadingGrid = ({ headings, ...props }: HeadingGridProps) => { +export const headingGridCommonProps: GridProps = { + height: "3.25rem", + borderBottomWidth: 3, + borderColor: "gray.200", + zIndex: 1, +}; +const HeadingGrid = ({ headings, rightItems, ...props }: HeadingGridProps) => { return ( - - {headings.map((props, idx) => ( - - ))} - + <> + + + {headings.map((props, idx) => ( + + ))} + + + {rightItems} + + ); }; From c70772a0aba11df00a038121d1a7460c664321d4 Mon Sep 17 00:00:00 2001 From: Grace Date: Fri, 10 Apr 2026 18:21:19 +0100 Subject: [PATCH 2/8] WIP Testing model page - make the headings scrollable - ensure code preview has min width --- src/components/CodeViewCard.tsx | 1 - src/components/CodeViewDefaultBlockCard.tsx | 7 +- src/components/TestingModelTable.tsx | 138 ++++++++++---------- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/src/components/CodeViewCard.tsx b/src/components/CodeViewCard.tsx index c1b529b97..b8e58a6d4 100644 --- a/src/components/CodeViewCard.tsx +++ b/src/components/CodeViewCard.tsx @@ -54,7 +54,6 @@ const CodeViewCard = ({ project, parentRef }: CodeViewCardProps) => { alignSelf="start" display="flex" flexDirection="column" - py={2} h="full" w="full" borderColor="brand.500" diff --git a/src/components/CodeViewDefaultBlockCard.tsx b/src/components/CodeViewDefaultBlockCard.tsx index 41a971769..cb13a8525 100644 --- a/src/components/CodeViewDefaultBlockCard.tsx +++ b/src/components/CodeViewDefaultBlockCard.tsx @@ -3,18 +3,19 @@ * * SPDX-License-Identifier: MIT */ -import { Card } from "@chakra-ui/react"; +import { Card, CardProps } from "@chakra-ui/react"; import { memo } from "react"; import { ActionData } from "../model"; import { tourElClassname } from "../tours"; import CodeViewDefaultBlock from "./CodeViewDefaultBlock"; -interface CodeViewDefaultBlockCardProps { +interface CodeViewDefaultBlockCardProps extends CardProps { action: ActionData; } const CodeViewDefaultBlockCard = ({ action, + ...props }: CodeViewDefaultBlockCardProps) => { return ( diff --git a/src/components/TestingModelTable.tsx b/src/components/TestingModelTable.tsx index e8bc39838..41b6cfd4f 100644 --- a/src/components/TestingModelTable.tsx +++ b/src/components/TestingModelTable.tsx @@ -4,15 +4,7 @@ * * SPDX-License-Identifier: MIT */ -import { - Box, - Grid, - GridItem, - GridProps, - HStack, - Icon, - VStack, -} from "@chakra-ui/react"; +import { Box, Grid, GridItem, GridProps, Icon, VStack } from "@chakra-ui/react"; import { MakeCodeRenderBlocksProvider } from "@microbit/makecode-embed/react"; import { useRef } from "react"; import { RiArrowRightLine } from "react-icons/ri"; @@ -28,8 +20,10 @@ import CodeViewCard from "./CodeViewCard"; import CodeViewDefaultBlockCard from "./CodeViewDefaultBlockCard"; import HeadingGrid from "./HeadingGrid"; +const blockCardMinWidth = "400px"; + const gridCommonProps: Partial = { - gridTemplateColumns: "290px 360px 40px auto", + gridTemplateColumns: "290px 360px 40px minmax(400px, 1fr)", gap: 3, w: "100%", }; @@ -70,69 +64,81 @@ const TestingModelTable = () => { justifyContent="start" flexGrow={1} alignItems="start" - overflow="auto" flexShrink={1} ref={scrollableAreaRef} > - - - {actions.map((action) => { - const { requiredConfidence: threshold } = action; - return ( - - - + {actions.map((action, actionIdx) => { + const { requiredConfidence: threshold } = action; + return ( + + + + + + + setRequiredConfidence(action.id, val) + } + requiredConfidence={ + threshold ?? mlSettings.defaultRequiredConfidence + } + disabled={!isConnected} + /> + + + + + {!projectEdited && ( + + - - - setRequiredConfidence(action.id, val) - } - requiredConfidence={ - threshold ?? mlSettings.defaultRequiredConfidence - } - disabled={!isConnected} + )} + + {projectEdited && actionIdx === 0 && ( + + - - - - - {!projectEdited && ( - - )} - - - ); - })} - - {projectEdited && ( - - )} - + )} + + ); + })} + ); From 34a9e4b3e3547f80fe6b5ed91583a4862a7affe6 Mon Sep 17 00:00:00 2001 From: Grace Date: Fri, 10 Apr 2026 18:24:35 +0100 Subject: [PATCH 3/8] Fix linting --- src/components/HeadingGrid.tsx | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/HeadingGrid.tsx b/src/components/HeadingGrid.tsx index 4e7125d08..a9ffc34eb 100644 --- a/src/components/HeadingGrid.tsx +++ b/src/components/HeadingGrid.tsx @@ -10,7 +10,7 @@ import { ReactNode } from "react"; interface HeadingGridProps extends GridProps { headings: GridColumnHeadingItemProps[]; - rightItems: ReactNode; + rightItems?: ReactNode; } export const headingGridCommonProps: GridProps = { height: "3.25rem", @@ -37,15 +37,17 @@ const HeadingGrid = ({ headings, rightItems, ...props }: HeadingGridProps) => { ))} - - {rightItems} - + {rightItems && ( + + {rightItems} + + )} ); }; From da06b83aa43d3894315f397a174b7ba1db9d0965 Mon Sep 17 00:00:00 2001 From: Grace Date: Mon, 13 Apr 2026 13:55:46 +0100 Subject: [PATCH 4/8] Testing model heading stick to the top --- src/components/DataSamplesTable.tsx | 2 -- src/components/HeadingGrid.tsx | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DataSamplesTable.tsx b/src/components/DataSamplesTable.tsx index fbddb1471..b0f61d57c 100644 --- a/src/components/DataSamplesTable.tsx +++ b/src/components/DataSamplesTable.tsx @@ -233,8 +233,6 @@ const DataSamplesTable = ({ )} { From 8dcaecf2a86e59cc6e128c048efb4e43046e409c Mon Sep 17 00:00:00 2001 From: Grace Date: Mon, 13 Apr 2026 14:27:18 +0100 Subject: [PATCH 5/8] Ensure rows in testing model page don't spread out when the code is big --- src/components/CodeViewCard.tsx | 7 ++++--- src/components/TestingModelTable.tsx | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/CodeViewCard.tsx b/src/components/CodeViewCard.tsx index b8e58a6d4..1ae40ae09 100644 --- a/src/components/CodeViewCard.tsx +++ b/src/components/CodeViewCard.tsx @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: MIT */ -import { Box, Card, SkeletonText, VStack } from "@chakra-ui/react"; +import { Box, Card, CardProps, SkeletonText, VStack } from "@chakra-ui/react"; import { BlockLayout, MakeCodeBlocksRendering, @@ -12,12 +12,12 @@ import { import { memo, useLayoutEffect, useRef, useState } from "react"; import { tourElClassname } from "../tours"; -interface CodeViewCardProps { +interface CodeViewCardProps extends CardProps { project: MakeCodeProject; parentRef: React.RefObject; } -const CodeViewCard = ({ project, parentRef }: CodeViewCardProps) => { +const CodeViewCard = ({ project, parentRef, ...props }: CodeViewCardProps) => { // This is used to set the tour cutout as the card can be taller than // the parent in a scrollable area. const [observableHeight, setObservableHeight] = useState( @@ -66,6 +66,7 @@ const CodeViewCard = ({ project, parentRef }: CodeViewCardProps) => { p={5} objectFit="contain" position="relative" + {...props} > = { gridTemplateColumns: "290px 360px 40px minmax(400px, 1fr)", - gap: 3, + gap, w: "100%", }; @@ -123,7 +124,8 @@ const TestingModelTable = () => { {projectEdited && actionIdx === 0 && ( { )} From c3951b85b4a4961e3692d51ea4ad32d019255379 Mon Sep 17 00:00:00 2001 From: Grace Date: Tue, 14 Apr 2026 11:00:14 +0100 Subject: [PATCH 6/8] Fix scrollbar being overlapped - Use sticky position to keep right items of heading grid on the top right despite horizontal scrolling - Replace hacky heading grid so that it fills up the width depending on content instead of putting an absolute position line and background --- src/components/DataSamplesTable.tsx | 19 ++++++---- src/components/HeadingGrid.tsx | 56 +++++++++------------------- src/components/TestingModelTable.tsx | 13 ++++--- src/pages/DataSamplesPage.tsx | 8 +++- src/pages/TestingModelPage.tsx | 2 +- 5 files changed, 43 insertions(+), 55 deletions(-) diff --git a/src/components/DataSamplesTable.tsx b/src/components/DataSamplesTable.tsx index b0f61d57c..8eee04edb 100644 --- a/src/components/DataSamplesTable.tsx +++ b/src/components/DataSamplesTable.tsx @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -import { Grid, GridProps, Text } from "@chakra-ui/react"; +import { Grid, GridProps, HStack, Text } from "@chakra-ui/react"; import { ButtonData } from "@microbit/microbit-connection"; import { useCallback, useEffect, useRef, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -37,7 +37,16 @@ const gridCommonProps: Partial = { const headings: GridColumnHeadingItemProps[] = [ { titleId: "action-label", descriptionId: "action-tooltip" }, - { titleId: "data-samples-label", descriptionId: "data-samples-tooltip" }, + { + titleId: "data-samples-label", + descriptionId: "data-samples-tooltip", + itemsRight: ( + + + + + ), + }, ]; interface DataSamplesTableProps { @@ -236,12 +245,6 @@ const DataSamplesTable = ({ {...gridCommonProps} headings={headings} w={gridRef.current?.clientWidth} - rightItems={ - <> - - - - } /> { +const HeadingGrid = ({ headings, ...props }: HeadingGridProps) => { return ( - <> - - - {headings.map((props, idx) => ( - - ))} - - {rightItems && ( - - {rightItems} - - )} - + + {headings.map((props, idx) => ( + + ))} + ); }; diff --git a/src/components/TestingModelTable.tsx b/src/components/TestingModelTable.tsx index b53bf2d63..32476255e 100644 --- a/src/components/TestingModelTable.tsx +++ b/src/components/TestingModelTable.tsx @@ -24,7 +24,7 @@ const blockCardMinWidth = "400px"; const gap = 3; const gridCommonProps: Partial = { - gridTemplateColumns: "290px 360px 40px minmax(400px, 1fr)", + gridTemplateColumns: `290px 360px 40px minmax(${blockCardMinWidth}, 1fr)`, gap, w: "100%", }; @@ -53,24 +53,25 @@ const TestingModelTable = () => { const isConnected = useDataConnected(); const [{ languageId }] = useSettings(); const makeCodeLang = getMakeCodeLang(languageId); - const scrollableAreaRef = useRef(null); + const gridAreaRef = useRef(null); const intl = useIntl(); + return ( { position="relative" > { } backLabelId="home-action" > - + { stage="openEditor" onNextLoading={editorLoading} /> - + From c9f8fde996386498dd4194b034adca222e2ef60e Mon Sep 17 00:00:00 2001 From: Grace Date: Tue, 14 Apr 2026 11:36:01 +0100 Subject: [PATCH 7/8] Fix introducing of horizontal scrollbar when vertical scrollbar is present in testing model page --- src/components/TestingModelTable.tsx | 211 ++++++++++++++++----------- src/pages/TestingModelPage.tsx | 4 +- 2 files changed, 127 insertions(+), 88 deletions(-) diff --git a/src/components/TestingModelTable.tsx b/src/components/TestingModelTable.tsx index 32476255e..a96981df4 100644 --- a/src/components/TestingModelTable.tsx +++ b/src/components/TestingModelTable.tsx @@ -4,9 +4,17 @@ * * SPDX-License-Identifier: MIT */ -import { Box, Grid, GridItem, GridProps, Icon, VStack } from "@chakra-ui/react"; +import { + Box, + Flex, + Grid, + GridItem, + GridProps, + Icon, + VStack, +} from "@chakra-ui/react"; import { MakeCodeRenderBlocksProvider } from "@microbit/makecode-embed/react"; -import { useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { RiArrowRightLine } from "react-icons/ri"; import { useIntl } from "react-intl"; import { useDataConnected } from "../data-connection-flow"; @@ -56,95 +64,126 @@ const TestingModelTable = () => { const gridAreaRef = useRef(null); const intl = useIntl(); + const scrollableAreaRef = useRef(null); + const [scrollbarWidth, setScrollbarWidth] = useState(0); + useEffect(() => { + const scrollEl = scrollableAreaRef.current; + if (!scrollEl) { + return; + } + const measure = () => { + const hasScrollbar = scrollEl.scrollHeight > scrollEl.clientHeight; + const scrollbarWidth = scrollEl.offsetWidth - scrollEl.clientWidth; + setScrollbarWidth(hasScrollbar ? scrollbarWidth : 0); + }; + + measure(); // run on mount. + window.addEventListener("resize", measure); + return () => window.removeEventListener("resize", measure); + }, []); + return ( - - - - {actions.map((action, actionIdx) => { - const { requiredConfidence: threshold } = action; - return ( - - - - - - - setRequiredConfidence(action.id, val) - } - requiredConfidence={ - threshold ?? mlSettings.defaultRequiredConfidence - } - disabled={!isConnected} - /> - - - - - {!projectEdited && ( - - - - )} - - {projectEdited && actionIdx === 0 && ( - + + + + {actions.map((action, actionIdx) => { + const { requiredConfidence: threshold } = action; + return ( + - - - )} - - ); - })} - - + + + + + + setRequiredConfidence(action.id, val) + } + requiredConfidence={ + threshold ?? mlSettings.defaultRequiredConfidence + } + disabled={!isConnected} + /> + + + + + {!projectEdited && ( + + + + )} + + {projectEdited && actionIdx === 0 && ( + + + + )} + + ); + })} + + + + ); }; diff --git a/src/pages/TestingModelPage.tsx b/src/pages/TestingModelPage.tsx index 2095aafcb..608725fec 100644 --- a/src/pages/TestingModelPage.tsx +++ b/src/pages/TestingModelPage.tsx @@ -199,8 +199,8 @@ const TestingModelPage = () => { stage="openEditor" onNextLoading={editorLoading} /> - - + + ) : ( From 225f9d35480d2dcc72d839d6029e9e1849085781 Mon Sep 17 00:00:00 2001 From: Grace Date: Tue, 14 Apr 2026 11:42:04 +0100 Subject: [PATCH 8/8] Prettier --- src/pages/TestingModelPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/TestingModelPage.tsx b/src/pages/TestingModelPage.tsx index 608725fec..f120a6166 100644 --- a/src/pages/TestingModelPage.tsx +++ b/src/pages/TestingModelPage.tsx @@ -200,7 +200,7 @@ const TestingModelPage = () => { onNextLoading={editorLoading} /> - + ) : (