From 9ad75bd93759815fe01c00fabe09b3d1cad28136 Mon Sep 17 00:00:00 2001 From: Robert Knight <95928279+microbit-robert@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:16:03 +0000 Subject: [PATCH] Pull up dialogs to prevent odd behaviours (#551) --- src/components/{ => ActionBar}/ActionBar.tsx | 0 .../ActionBar/ActionBarItemsRight.tsx | 119 +++++++++++++++++ src/components/DefaultPageLayout.tsx | 50 ++------ src/components/HelpMenu.tsx | 38 +++--- src/components/HelpMenuItems.tsx | 120 +++++++----------- src/components/LanguageMenuItem.tsx | 27 +--- src/components/SettingsMenu.tsx | 13 +- src/components/SettingsMenuItem.tsx | 30 ++--- 8 files changed, 224 insertions(+), 173 deletions(-) rename src/components/{ => ActionBar}/ActionBar.tsx (100%) create mode 100644 src/components/ActionBar/ActionBarItemsRight.tsx diff --git a/src/components/ActionBar.tsx b/src/components/ActionBar/ActionBar.tsx similarity index 100% rename from src/components/ActionBar.tsx rename to src/components/ActionBar/ActionBar.tsx diff --git a/src/components/ActionBar/ActionBarItemsRight.tsx b/src/components/ActionBar/ActionBarItemsRight.tsx new file mode 100644 index 000000000..53b66e0a6 --- /dev/null +++ b/src/components/ActionBar/ActionBarItemsRight.tsx @@ -0,0 +1,119 @@ +import { HStack, MenuDivider, useDisclosure } from "@chakra-ui/react"; +import { ReactNode, useMemo } from "react"; +import { useIntl } from "react-intl"; +import { useLocation } from "react-router"; +import { useStore } from "../../store"; +import AboutDialog from "../AboutDialog"; +import ConnectFirstDialog from "../ConnectFirstDialog"; +import FeedbackForm from "../FeedbackForm"; +import HelpMenu from "../HelpMenu"; +import HelpMenuItems, { tourMap } from "../HelpMenuItems"; +import { LanguageDialog } from "../LanguageDialog"; +import LanguageMenuItem from "../LanguageMenuItem"; +import { SettingsDialog } from "../SettingsDialog"; +import SettingsMenu from "../SettingsMenu"; +import SettingsMenuItem from "../SettingsMenuItem"; +import ToolbarMenu from "../ToolbarMenu"; + +interface ItemsRightProps { + menuItems?: ReactNode; + toolbarItems?: ReactNode; +} + +const ItemsRight = ({ menuItems, toolbarItems }: ItemsRightProps) => { + const intl = useIntl(); + const languageDisclosure = useDisclosure(); + const settingsDisclosure = useDisclosure(); + const aboutDialogDisclosure = useDisclosure(); + const feedbackDisclosure = useDisclosure(); + const connectFirstDisclosure = useDisclosure(); + const setPostConnectTourTrigger = useStore( + (s) => s.setPostConnectTourTrigger + ); + const tourTriggerName = tourMap[useLocation().pathname]; + const tourTrigger = useMemo(() => { + switch (tourTriggerName) { + case "TrainModel": { + return { + name: tourTriggerName, + delayedUntilConnection: true, + }; + } + case "Connect": { + return { name: tourTriggerName }; + } + default: { + return undefined; + } + } + }, [tourTriggerName]); + return ( + <> + + + setPostConnectTourTrigger(tourTrigger)} + explanationTextId="connect-to-tour-body" + options={{ postConnectTourTrigger: tourTrigger }} + /> + + + + {toolbarItems} + + + + + {menuItems} + + + + {/* Toolbar items when sm window size. */} + + {menuItems} + + + + + + + ); +}; + +export default ItemsRight; diff --git a/src/components/DefaultPageLayout.tsx b/src/components/DefaultPageLayout.tsx index 5553d0d09..b0db0208a 100644 --- a/src/components/DefaultPageLayout.tsx +++ b/src/components/DefaultPageLayout.tsx @@ -2,7 +2,6 @@ import { Button, Flex, Heading, - HStack, Icon, IconButton, MenuDivider, @@ -28,21 +27,16 @@ import { import Tour from "../pages/Tour"; import { useStore } from "../store"; import { createHomePageUrl } from "../urls"; -import ActionBar from "./ActionBar"; +import ActionBar from "./ActionBar/ActionBar"; import AppLogo from "./AppLogo"; import ConnectionDialogs from "./ConnectionFlowDialogs"; -import HelpMenu from "./HelpMenu"; -import LanguageMenuItem from "./LanguageMenuItem"; +import ImportErrorDialog from "./ImportErrorDialog"; +import MakeCodeLoadErrorDialog from "./MakeCodeLoadErrorDialog"; +import NotCreateAiHexImportDialog from "./NotCreateAiHexImportDialog"; import PreReleaseNotice from "./PreReleaseNotice"; import ProjectDropTarget from "./ProjectDropTarget"; import SaveDialogs from "./SaveDialogs"; -import SettingsMenu from "./SettingsMenu"; -import ToolbarMenu from "./ToolbarMenu"; -import HelpMenuItems from "./HelpMenuItems"; -import ImportErrorDialog from "./ImportErrorDialog"; -import NotCreateAiHexImportDialog from "./NotCreateAiHexImportDialog"; -import MakeCodeLoadErrorDialog from "./MakeCodeLoadErrorDialog"; -import SettingsMenuItem from "./SettingsMenuItem"; +import ItemsRight from "./ActionBar/ActionBarItemsRight"; interface DefaultPageLayoutProps { titleId?: string; @@ -151,36 +145,10 @@ const DefaultPageLayout = ({ } itemsLeftProps={{ width: 0 }} itemsRight={ - <> - - {toolbarItemsRight} - - - - - {menuItems} - - - - {/* Toolbar items when sm window size. */} - - {menuItems} - - - - - - + } /> {flags.preReleaseNotice && } diff --git a/src/components/HelpMenu.tsx b/src/components/HelpMenu.tsx index f135f4901..8ea16e813 100644 --- a/src/components/HelpMenu.tsx +++ b/src/components/HelpMenu.tsx @@ -6,38 +6,35 @@ import { MenuButton, MenuList, Portal, - useDisclosure, } from "@chakra-ui/react"; import { useRef } from "react"; import { RiQuestionLine } from "react-icons/ri"; import { useIntl } from "react-intl"; -import AboutDialog from "./AboutDialog"; -import FeedbackForm from "./FeedbackForm"; import HelpMenuItems from "./HelpMenuItems"; +import { TourTrigger } from "../model"; -interface HelpMenuProps extends BoxProps {} +interface HelpMenuProps extends BoxProps { + onAboutDialogOpen: () => void; + onConnectFirstDialogOpen: () => void; + onFeedbackOpen: () => void; + tourTrigger: TourTrigger | undefined; +} /** * A help button that triggers a drop-down menu with actions. */ -const HelpMenu = ({ ...rest }: HelpMenuProps) => { - const aboutDialogDisclosure = useDisclosure(); - const feedbackDisclosure = useDisclosure(); +const HelpMenu = ({ + onAboutDialogOpen, + onConnectFirstDialogOpen, + onFeedbackOpen, + tourTrigger, + ...rest +}: HelpMenuProps) => { const intl = useIntl(); const menuButtonRef = useRef(null); const containerRef = useRef(null); return ( - - { /> - + diff --git a/src/components/HelpMenuItems.tsx b/src/components/HelpMenuItems.tsx index 086a7aeb7..ec4d4f65c 100644 --- a/src/components/HelpMenuItems.tsx +++ b/src/components/HelpMenuItems.tsx @@ -1,5 +1,4 @@ -import { MenuDivider, MenuItem, useDisclosure } from "@chakra-ui/react"; -import { useRef } from "react"; +import { MenuDivider, MenuItem } from "@chakra-ui/react"; import { MdOutlineCookie } from "react-icons/md"; import { RiExternalLinkLine, @@ -8,34 +7,29 @@ import { RiInformationLine, } from "react-icons/ri"; import { FormattedMessage } from "react-intl"; -import { useLocation } from "react-router"; import { useConnectionStage } from "../connection-stage-hooks"; import { useDeployment } from "../deployment"; +import { flags } from "../flags"; +import { TourTrigger } from "../model"; import { useStore } from "../store"; -import { createDataSamplesPageUrl, createTestingModelPageUrl } from "../urls"; import { userGuideUrl } from "../utils/external-links"; -import AboutDialog from "./AboutDialog"; -import ConnectFirstDialog from "./ConnectFirstDialog"; -import FeedbackForm from "./FeedbackForm"; -import { flags } from "../flags"; +import { createDataSamplesPageUrl, createTestingModelPageUrl } from "../urls"; -const HelpMenuItems = () => { - const aboutDialogDisclosure = useDisclosure(); - const feedbackDisclosure = useDisclosure(); - const menuButtonRef = useRef(null); +interface HelpMenuItemsProps { + onAboutDialogOpen: () => void; + onConnectFirstDialogOpen: () => void; + onFeedbackOpen: () => void; + tourTrigger: TourTrigger | undefined; +} +const HelpMenuItems = ({ + onAboutDialogOpen, + onConnectFirstDialogOpen, + onFeedbackOpen, + tourTrigger, +}: HelpMenuItemsProps) => { const deployment = useDeployment(); return ( <> - - {flags.websiteContent && ( { )} - + {deployment.supportLinks.main && ( <> { > - } - onClick={feedbackDisclosure.onOpen} - > + } onClick={onFeedbackOpen}> @@ -102,63 +96,47 @@ const HelpMenuItems = () => { {(deployment.privacyPolicyLink || deployment.compliance.manageCookies || deployment.termsOfUseLink) && } - } - onClick={aboutDialogDisclosure.onOpen} - > + } onClick={onAboutDialogOpen}> ); }; -const tourMap = { - [createDataSamplesPageUrl()]: "Connect" as const, - [createTestingModelPageUrl()]: "TrainModel" as const, - // No UI to retrigger MakeCode tour -}; +interface TourMenuItemProps { + onConnectFirstDialogOpen: () => void; + tourTrigger: TourTrigger | undefined; +} -const TourMenuItem = () => { - const tourTriggerName = tourMap[useLocation().pathname]; - const setPostConnectTourTrigger = useStore( - (s) => s.setPostConnectTourTrigger - ); +const TourMenuItem = ({ + onConnectFirstDialogOpen, + tourTrigger, +}: TourMenuItemProps) => { const tourStart = useStore((s) => s.tourStart); - const disclosure = useDisclosure(); const { isConnected } = useConnectionStage(); - if (tourTriggerName) { - const tourTrigger = - tourTriggerName === "TrainModel" - ? { - name: tourTriggerName, - delayedUntilConnection: true, - } - : { name: tourTriggerName }; + if (tourTrigger) { return ( - <> - setPostConnectTourTrigger(tourTrigger)} - explanationTextId="connect-to-tour-body" - options={{ postConnectTourTrigger: tourTrigger }} - /> - { - if (!isConnected) { - disclosure.onOpen(); - } else { - tourStart(tourTrigger, true); - } - }} - icon={} - > - - - + { + if (!isConnected) { + onConnectFirstDialogOpen(); + } else { + tourStart(tourTrigger, true); + } + }} + icon={} + > + + ); } return null; }; +export const tourMap = { + [createDataSamplesPageUrl()]: "Connect" as const, + [createTestingModelPageUrl()]: "TrainModel" as const, + // No UI to retrigger MakeCode tour +}; + export default HelpMenuItems; diff --git a/src/components/LanguageMenuItem.tsx b/src/components/LanguageMenuItem.tsx index e56c3f58e..793be22a3 100644 --- a/src/components/LanguageMenuItem.tsx +++ b/src/components/LanguageMenuItem.tsx @@ -1,29 +1,16 @@ -import { Icon, MenuItem, useDisclosure } from "@chakra-ui/react"; -import { FormattedMessage } from "react-intl"; +import { Icon, MenuItem } from "@chakra-ui/react"; import { IoMdGlobe } from "react-icons/io"; -import { LanguageDialog } from "./LanguageDialog"; +import { FormattedMessage } from "react-intl"; interface LanguageMenuItemProps { - finalFocusRef?: React.RefObject; + onOpen: () => void; } -const LanguageMenuItem = ({ finalFocusRef }: LanguageMenuItemProps) => { - const languageDisclosure = useDisclosure(); - +const LanguageMenuItem = ({ onOpen }: LanguageMenuItemProps) => { return ( - <> - - } - onClick={languageDisclosure.onOpen} - > - - - + } onClick={onOpen}> + + ); }; diff --git a/src/components/SettingsMenu.tsx b/src/components/SettingsMenu.tsx index f7bf51ff2..dd1e221ce 100644 --- a/src/components/SettingsMenu.tsx +++ b/src/components/SettingsMenu.tsx @@ -12,10 +12,17 @@ import { useIntl } from "react-intl"; import LanguageMenuItem from "./LanguageMenuItem"; import SettingsMenuItem from "./SettingsMenuItem"; +interface SettingsMenuProps { + onLanguageDialogOpen: () => void; + onSettingsDialogOpen: () => void; +} /** * A settings button that triggers a drop-down menu with actions. */ -const SettingsMenu = () => { +const SettingsMenu = ({ + onLanguageDialogOpen, + onSettingsDialogOpen, +}: SettingsMenuProps) => { const intl = useIntl(); const settingsMenuRef = useRef(null); const containerRef = useRef(null); @@ -40,8 +47,8 @@ const SettingsMenu = () => { /> - - + + diff --git a/src/components/SettingsMenuItem.tsx b/src/components/SettingsMenuItem.tsx index eb3b8cb43..e96a0f23d 100644 --- a/src/components/SettingsMenuItem.tsx +++ b/src/components/SettingsMenuItem.tsx @@ -1,29 +1,19 @@ -import { Icon, MenuItem, useDisclosure } from "@chakra-ui/react"; -import { FormattedMessage } from "react-intl"; +import { Icon, MenuItem } from "@chakra-ui/react"; import { RiListSettingsLine } from "react-icons/ri"; -import { SettingsDialog } from "./SettingsDialog"; +import { FormattedMessage } from "react-intl"; interface SettingsMenuItemProps { - finalFocusRef?: React.RefObject; + onOpen: () => void; } -const SettingsMenuItem = ({ finalFocusRef }: SettingsMenuItemProps) => { - const { isOpen, onClose, onOpen } = useDisclosure(); - +const SettingsMenuItem = ({ onOpen }: SettingsMenuItemProps) => { return ( - <> - - } - onClick={onOpen} - > - - - + } + onClick={onOpen} + > + + ); };