diff --git a/frontend/src/metabase/dashboard/components/DashboardActions.styled.tsx b/frontend/src/metabase/dashboard/components/DashboardActions.styled.tsx deleted file mode 100644 index 971c075fe54f6ef1a2b97c1f1e6776a060b0543a..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/dashboard/components/DashboardActions.styled.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import styled from "@emotion/styled"; - -import FullscreenIcon from "metabase/components/icons/FullscreenIcon"; -import NightModeIcon from "metabase/components/icons/NightModeIcon"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; -import { RefreshWidget } from "metabase/dashboard/components/RefreshWidget"; -import { color } from "metabase/lib/colors"; - -interface ShareButtonProps { - canShareDashboard?: boolean; -} - -export const ShareButton = styled(DashboardHeaderButton)<ShareButtonProps>` - color: ${props => !props.canShareDashboard && color("text-light")}; - - &:hover { - color: ${props => props.canShareDashboard && color("brand")}; - } -`; - -export const FullScreenButtonIcon = styled(FullscreenIcon)` - &:hover { - color: var(--mb-color-brand); - } -`; - -export const NightModeButtonIcon = styled(NightModeIcon)` - cursor: pointer; - - &:hover { - color: var(--mb-color-brand); - } -`; - -export const RefreshWidgetButton = styled(RefreshWidget)` - &:hover { - color: var(--mb-color-brand); - } -`; diff --git a/frontend/src/metabase/dashboard/components/DashboardEmbedAction/DashboardEmbedAction.unit.spec.tsx b/frontend/src/metabase/dashboard/components/DashboardEmbedAction/DashboardEmbedAction.unit.spec.tsx index bbc79f4bd7ec19a7b9e3be56e762e1905ac793d6..ba8c7177b7afd14a3ae65ad062f61a81c9f6efb8 100644 --- a/frontend/src/metabase/dashboard/components/DashboardEmbedAction/DashboardEmbedAction.unit.spec.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardEmbedAction/DashboardEmbedAction.unit.spec.tsx @@ -1,4 +1,6 @@ -import userEvent from "@testing-library/user-event"; +import userEvent, { + PointerEventsCheckLevel, +} from "@testing-library/user-event"; import { indexBy } from "underscore"; import { renderWithProviders, screen } from "__support__/ui"; @@ -115,7 +117,10 @@ describe("DashboardEmbedAction", () => { isAdmin: false, }); - await userEvent.hover(screen.getByLabelText("share icon")); + await userEvent.hover(screen.getByLabelText("share icon"), { + // The button is disabled so pointer events should be disabled + pointerEventsCheck: PointerEventsCheckLevel.Never, + }); expect( await screen.findByText("Public links are disabled"), ).toBeInTheDocument(); @@ -128,7 +133,10 @@ describe("DashboardEmbedAction", () => { publicLinksEnabled: true, }); - await userEvent.hover(screen.getByLabelText("share icon")); + await userEvent.hover(screen.getByLabelText("share icon"), { + // The button is disabled so pointer events should be disabled + pointerEventsCheck: PointerEventsCheckLevel.Never, + }); expect( await screen.findByText("Ask your admin to create a public link"), ).toBeInTheDocument(); diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/DashboardHeader.styled.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/DashboardHeader.styled.tsx deleted file mode 100644 index 3bcb8a609bcf088166cb589005847e957b92011d..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/DashboardHeader.styled.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { css } from "@emotion/react"; -import styled from "@emotion/styled"; - -import Button from "metabase/core/components/Button"; -import { color, darken } from "metabase/lib/colors"; -import { breakpointMaxSmall } from "metabase/styled-components/theme"; -import { Menu } from "metabase/ui"; - -export const DashboardHeaderActionDivider = styled.div` - height: 1.25rem; - padding-left: 0.5rem; - margin-left: 0.5rem; - width: 0; - border-left: 1px solid var(--mb-color-border); -`; - -export const DashboardHeaderButton = styled(Button)<{ - isActive?: boolean; - visibleOnSmallScreen?: boolean; - hasBackground?: boolean; -}>` - ${({ hasBackground }) => - hasBackground - ? css` - padding: 0.25rem 0.5rem; - height: 2rem; - min-width: 2rem; - ` - : css` - padding: 0; - `} - - color: ${props => (props.isActive ? color("brand") : color("text-dark"))}; - font-size: 1rem; - - &:hover { - color: var(--mb-color-brand); - background: ${({ hasBackground }) => - hasBackground ? "var(--mb-color-bg-medium)" : "transparent"}; - } - - svg { - vertical-align: middle; - } - - ${breakpointMaxSmall} { - ${props => - !props.visibleOnSmallScreen && - css` - display: none; - `} - } -`; - -DashboardHeaderButton.defaultProps = { - onlyIcon: true, - iconSize: 16, - visibleOnSmallScreen: true, - hasBackground: true, -}; - -export const SectionMenuItem = styled(Menu.Item)` - background-color: ${() => darken(color("bg-medium"), 0.1)}; - - &:hover { - background-color: var(--mb-color-brand); - } -`; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/DashboardHeaderButtonRow/action-buttons.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/DashboardHeaderButtonRow/action-buttons.tsx index 9ad4ea87cd3de6b935194ec834a93491606ae1f9..06087fd503c4f24d4eef361a961f67397a24f341 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/DashboardHeaderButtonRow/action-buttons.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/DashboardHeaderButtonRow/action-buttons.tsx @@ -1,10 +1,11 @@ import { withRouter } from "react-router"; -import { RefreshWidgetButton } from "../../DashboardActions.styled"; +import { RefreshWidget } from "metabase/dashboard/components/RefreshWidget"; +import { Center, Divider } from "metabase/ui"; + import { DashboardBookmark } from "../../DashboardBookmark"; import { DashboardEmbedAction } from "../../DashboardEmbedAction"; import { ExtraEditButtonsMenu } from "../../ExtraEditButtonsMenu"; -import { DashboardHeaderActionDivider } from "../DashboardHeader.styled"; import { AddActionElementButton, AddFilterParameterButton, @@ -133,7 +134,7 @@ export const dashboardActionButtons: Record< setRefreshElapsedHook, onRefreshPeriodChange, }) => ( - <RefreshWidgetButton + <RefreshWidget period={refreshPeriod} setRefreshElapsedHook={setRefreshElapsedHook} onChangePeriod={onRefreshPeriodChange} @@ -236,7 +237,11 @@ export const dashboardActionButtons: Record< // UTILITY [DASHBOARD_ACTION.DASHBOARD_HEADER_ACTION_DIVIDER]: { - component: () => <DashboardHeaderActionDivider />, + component: () => ( + <Center h="1.25rem" px="sm"> + <Divider orientation="vertical" /> + </Center> + ), enabled: () => true, }, }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddActionElementButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddActionElementButton.tsx index 66c0e8b62a29f778ed933358eb5721e301a0ea87..4618afd3d9871ac025a7db59a879b946040c695b 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddActionElementButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddActionElementButton.tsx @@ -1,10 +1,9 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { addActionToDashboard } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { getDashboard, getSelectedTabId } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Icon, Tooltip } from "metabase/ui"; export const AddActionElementButton = () => { const dispatch = useDispatch(); @@ -25,10 +24,11 @@ export const AddActionElementButton = () => { }; return ( - <Tooltip label={t`Add action button`}> - <DashboardHeaderButton onClick={onAddAction} aria-label={t`Add action`}> - <Icon name="click" size={18} /> - </DashboardHeaderButton> - </Tooltip> + <ToolbarButton + aria-label={t`Add action`} + onClick={onAddAction} + icon="click" + tooltipLabel={t`Add action button`} + /> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddFilterParameterButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddFilterParameterButton.tsx index 686c28ebb6dcf2e7cfc1fd6cdf68109f55b9bf6e..e6fe6d1458e773736afb3d0af540d853436e9130 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddFilterParameterButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddFilterParameterButton.tsx @@ -1,44 +1,40 @@ import { t } from "ttag"; -import TippyPopover from "metabase/components/Popover/TippyPopover"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { addParameter, hideAddParameterPopover, showAddParameterPopover, } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { ParametersPopover } from "metabase/dashboard/components/ParametersPopover"; import { getIsAddParameterPopoverOpen } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Tooltip } from "metabase/ui"; +import { Popover } from "metabase/ui"; export const AddFilterParameterButton = () => { const dispatch = useDispatch(); const isAddParameterPopoverOpen = useSelector(getIsAddParameterPopoverOpen); return ( - <span> - <TippyPopover - placement="bottom-start" - onClose={() => dispatch(hideAddParameterPopover())} - visible={isAddParameterPopoverOpen} - content={ - <ParametersPopover - onAddParameter={parameter => dispatch(addParameter(parameter))} - onClose={() => dispatch(hideAddParameterPopover())} - /> - } - > - <div> - <Tooltip label={t`Add a filter`}> - <DashboardHeaderButton - icon="filter" - onClick={() => dispatch(showAddParameterPopover())} - aria-label={t`Add a filter`} - /> - </Tooltip> - </div> - </TippyPopover> - </span> + <Popover opened={isAddParameterPopoverOpen} position="bottom-end"> + <Popover.Target> + <ToolbarButton + icon="filter" + onClick={() => + isAddParameterPopoverOpen + ? dispatch(hideAddParameterPopover()) + : dispatch(showAddParameterPopover()) + } + aria-label={t`Add a filter`} + tooltipLabel={t`Add a filter`} + /> + </Popover.Target> + <Popover.Dropdown> + <ParametersPopover + onAddParameter={parameter => dispatch(addParameter(parameter))} + onClose={() => dispatch(hideAddParameterPopover())} + /> + </Popover.Dropdown> + </Popover> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddHeadingOrTextButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddHeadingOrTextButton.tsx index 90140df04ace97f111e3e188ed9fa381c55dc147..4bd7500f3d29d52742dcd6a1cf2fad3098973b0f 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddHeadingOrTextButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddHeadingOrTextButton.tsx @@ -1,13 +1,13 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { addHeadingDashCardToDashboard, addMarkdownDashCardToDashboard, } from "metabase/dashboard/actions"; -import { TextOptionsButton } from "metabase/dashboard/components/TextOptions/TextOptionsButton"; import { getDashboard, getSelectedTabId } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Tooltip } from "metabase/ui"; +import { Group, Icon, Menu, Text } from "metabase/ui"; export const AddHeadingOrTextButton = () => { const dispatch = useDispatch(); @@ -36,14 +36,41 @@ export const AddHeadingOrTextButton = () => { } }; + const TEXT_OPTIONS = [ + { + title: t`Heading`, + action: onAddHeading, + }, + { + title: t`Text`, + action: onAddMarkdownBox, + }, + ]; + return ( - <Tooltip label={t`Add a heading or text`}> - <span> - <TextOptionsButton - onAddMarkdown={onAddMarkdownBox} - onAddHeading={onAddHeading} - /> - </span> - </Tooltip> + <Menu position="bottom-end"> + <Menu.Target> + <ToolbarButton + tooltipLabel={t`Add a heading or text`} + w="3rem" + data-element-id={t`Add a heading or text`} + aria-label={t`Add a heading or text box`} + > + <Group spacing="xs" noWrap> + <Icon name="string" size={18} /> + <Icon name="chevrondown" size={10} /> + </Group> + </ToolbarButton> + </Menu.Target> + <Menu.Dropdown miw="auto"> + {TEXT_OPTIONS.map(({ title, action }) => ( + <Menu.Item key={title} onClick={action}> + <Text pr="xl" fw="bold"> + {title} + </Text> + </Menu.Item> + ))} + </Menu.Dropdown> + </Menu> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddLinkCardButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddLinkCardButton.tsx index e901300a42997efd63c573dc6714cd0475f65c69..6c6b9f9696a4fac9d9f50b59e9fb68f14025b95f 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddLinkCardButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddLinkCardButton.tsx @@ -1,16 +1,14 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { addLinkDashCardToDashboard } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { getDashboard, getSelectedTabId } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Icon, Tooltip } from "metabase/ui"; export const AddLinkCardButton = () => { const dispatch = useDispatch(); const dashboard = useSelector(getDashboard); const selectedTabId = useSelector(getSelectedTabId); - const onAddLinkCard = () => { if (dashboard) { dispatch( @@ -23,11 +21,13 @@ export const AddLinkCardButton = () => { }; const addLinkLabel = t`Add link card`; + return ( - <Tooltip label={addLinkLabel}> - <DashboardHeaderButton aria-label={addLinkLabel} onClick={onAddLinkCard}> - <Icon name="link" size={18} /> - </DashboardHeaderButton> - </Tooltip> + <ToolbarButton + tooltipLabel={addLinkLabel} + icon="link" + aria-label={addLinkLabel} + onClick={onAddLinkCard} + /> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddQuestionButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddQuestionButton.tsx index 750ec5ba503ad911119bc9bb56481a5800633639..e18a812fef793c3de3618e6feb714de2a1892400 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddQuestionButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddQuestionButton.tsx @@ -1,30 +1,26 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { toggleSidebar } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { SIDEBAR_NAME } from "metabase/dashboard/constants"; import { getSidebar } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Tooltip } from "metabase/ui"; export const AddQuestionButton = () => { const dispatch = useDispatch(); - const sidebar = useSelector(getSidebar); - const addQuestionButtonHint = sidebar.name === SIDEBAR_NAME.addQuestion ? t`Close sidebar` : t`Add questions`; return ( - <Tooltip label={addQuestionButtonHint}> - <DashboardHeaderButton - icon="add" - isActive={sidebar.name === SIDEBAR_NAME.addQuestion} - onClick={() => dispatch(toggleSidebar(SIDEBAR_NAME.addQuestion))} - aria-label={addQuestionButtonHint} - /> - </Tooltip> + <ToolbarButton + tooltipLabel={addQuestionButtonHint} + icon="add" + isActive={sidebar.name === SIDEBAR_NAME.addQuestion} + onClick={() => dispatch(toggleSidebar(SIDEBAR_NAME.addQuestion))} + aria-label={addQuestionButtonHint} + /> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/AddSectionButton.module.css b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/AddSectionButton.module.css new file mode 100644 index 0000000000000000000000000000000000000000..6050cd2e954b101b0e481328b04ca28500d52462 --- /dev/null +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/AddSectionButton.module.css @@ -0,0 +1,4 @@ +/* We have to explicitly hit the data-hovered tag to override its styles on the default Mantine menu item */ +.AddSectionButton > button[data-hovered="true"] { + background-color: var(--mb-color-brand); +} diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/AddSectionButton.tsx similarity index 67% rename from frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton.tsx rename to frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/AddSectionButton.tsx index cf75958849c55dcf49e707dd48cc18d29e80928b..acac93b2434525bec42e1c83203cce209a996ab6 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/AddSectionButton.tsx @@ -1,21 +1,20 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { addSectionToDashboard } from "metabase/dashboard/actions"; -import { - DashboardHeaderButton, - SectionMenuItem, -} from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { SectionLayoutPreview } from "metabase/dashboard/components/DashboardHeader/SectionLayoutPreview"; import { layoutOptions, type SectionLayout } from "metabase/dashboard/sections"; import { getDashboard, getSelectedTabId } from "metabase/dashboard/selectors"; +import { darken } from "metabase/lib/colors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Flex, Icon, Menu, Tooltip } from "metabase/ui"; +import { Flex, Menu } from "metabase/ui"; + +import AddSectionButtonS from "./AddSectionButton.module.css"; export const AddSectionButton = () => { const dispatch = useDispatch(); const dashboard = useSelector(getDashboard); const selectedTabId = useSelector(getSelectedTabId); - const onAddSection = (sectionLayout: SectionLayout) => { if (dashboard) { dispatch( @@ -27,29 +26,33 @@ export const AddSectionButton = () => { ); } }; - return ( <Menu position="bottom-end"> <Menu.Target> - <span> - <Tooltip label={t`Add section`}> - <DashboardHeaderButton aria-label={t`Add section`}> - <Icon name="section" size={18} /> - </DashboardHeaderButton> - </Tooltip> - </span> + <ToolbarButton + tooltipLabel={t`Add section`} + aria-label={t`Add section`} + icon="section" + /> </Menu.Target> <Menu.Dropdown miw="100px"> - <Flex direction="column" align="center" gap="md" p="12px"> + <Flex + direction="column" + align="center" + gap="md" + p="12px" + className={AddSectionButtonS.AddSectionButton} + > {layoutOptions.map(layout => ( - <SectionMenuItem + <Menu.Item key={layout.id} + bg={darken("bg-medium", 0.1)} onClick={() => onAddSection(layout)} aria-label={layout.label} p="14px" > <SectionLayoutPreview layout={layout} /> - </SectionMenuItem> + </Menu.Item> ))} </Flex> </Menu.Dropdown> diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/index.ts b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2737c6929cfd6168d3c1439abe6c3f8204d2d59 --- /dev/null +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddSectionButton/index.ts @@ -0,0 +1 @@ +export * from "./AddSectionButton"; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddTemporalUnitButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddTemporalUnitButton.tsx index c766a095df5b4610b646481e261f34692280fd19..ee45bcdd5ed6e46bb81334481bd0bf67b7272685 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddTemporalUnitButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/AddTemporalUnitButton.tsx @@ -1,19 +1,17 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { addTemporalUnitParameter } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { useDispatch } from "metabase/lib/redux"; -import { Tooltip } from "metabase/ui"; export const AddTemporalUnitButton = () => { const dispatch = useDispatch(); return ( - <Tooltip label={t`Add a Unit of Time widget`}> - <DashboardHeaderButton - icon="clock" - aria-label={t`Add a Unit of Time widget`} - onClick={() => dispatch(addTemporalUnitParameter())} - /> - </Tooltip> + <ToolbarButton + tooltipLabel={t`Add a Unit of Time widget`} + icon="clock" + aria-label={t`Add a Unit of Time widget`} + onClick={() => dispatch(addTemporalUnitParameter())} + /> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardInfoButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardInfoButton.tsx index dd9e9357ab14c1f15800322b6ac093cd8bcda90e..cca873725755e05c8236658b35d6a5227bba03a9 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardInfoButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardInfoButton.tsx @@ -1,30 +1,28 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { closeSidebar, setSidebar } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { SIDEBAR_NAME } from "metabase/dashboard/constants"; import { getIsShowDashboardInfoSidebar } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Tooltip } from "metabase/ui"; export const DashboardInfoButton = () => { const dispatch = useDispatch(); - const isShowingDashboardInfoSidebar = useSelector( getIsShowDashboardInfoSidebar, ); return ( - <Tooltip label={t`More info`}> - <DashboardHeaderButton - icon="info" - isActive={isShowingDashboardInfoSidebar} - onClick={() => - isShowingDashboardInfoSidebar - ? dispatch(closeSidebar()) - : dispatch(setSidebar({ name: SIDEBAR_NAME.info })) - } - /> - </Tooltip> + <ToolbarButton + aria-label={t`More info`} + tooltipLabel={t`More info`} + icon="info" + isActive={isShowingDashboardInfoSidebar} + onClick={() => + isShowingDashboardInfoSidebar + ? dispatch(closeSidebar()) + : dispatch(setSidebar({ name: SIDEBAR_NAME.info })) + } + /> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardSubscriptionButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardSubscriptionButton.tsx index b004470c54c4afc90b4098491d27cb3d1c27d2b0..4dec12011e6d3268770ed2c90bce313e5adddbee 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardSubscriptionButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/DashboardSubscriptionButton.tsx @@ -1,8 +1,7 @@ import { t } from "ttag"; -import Tooltip from "metabase/core/components/Tooltip"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { setSharing } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { getIsSharing } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; import { canManageSubscriptions as canManageSubscriptionsSelector } from "metabase/selectors/user"; @@ -16,17 +15,25 @@ export const DashboardSubscriptionButton = () => { dispatch(setSharing(!isSharing)); }; return ( - <Tooltip tooltip={t`Subscriptions`} key="dashboard-subscriptions"> - <DashboardHeaderButton - icon="subscription" - disabled={!isSubscriptionsEnabled} - onClick={toggleSharing} - aria-label="subscriptions" - /> - </Tooltip> + <ToolbarButton + tooltipLabel={t`Subscriptions`} + icon="subscription" + disabled={!isSubscriptionsEnabled} + onClick={toggleSharing} + aria-label="subscriptions" + /> ); }; +type ShouldRenderSubscriptionButtonProps = { + dashboard: Dashboard; + canManageSubscriptions: boolean; + formInput: any; + isAdmin: boolean; + isEditing: boolean; + isFullscreen: boolean; +}; + export function shouldRenderSubscriptionButton({ dashboard, canManageSubscriptions, @@ -34,14 +41,7 @@ export function shouldRenderSubscriptionButton({ isAdmin, isEditing, isFullscreen, -}: { - dashboard: Dashboard; - canManageSubscriptions: boolean; - formInput: any; - isAdmin: boolean; - isEditing: boolean; - isFullscreen: boolean; -}) { +}: ShouldRenderSubscriptionButtonProps) { const isLoaded = !!dashboard; const hasCards = isLoaded && dashboard.dashcards.length > 0; @@ -52,15 +52,11 @@ export function shouldRenderSubscriptionButton({ (dashCard: DashboardCard) => !["text", "heading"].includes(dashCard.card.display), ); - const canCreateSubscription = hasDataCards && canManageSubscriptions; - const emailConfigured = formInput?.channels?.email?.configured || false; const slackConfigured = formInput?.channels?.slack?.configured || false; - const shouldShowSubscriptionsButton = emailConfigured || slackConfigured || isAdmin; - return ( !isEditing && !dashboard?.archived && diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/EditDashboardButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/EditDashboardButton.tsx index 49f0d5c78ad412f4fafb7acb66004e5c57e75805..937a7cd999338996abe7f191d81505da9289ccdd 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/EditDashboardButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/EditDashboardButton.tsx @@ -1,18 +1,16 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { setEditingDashboard } from "metabase/dashboard/actions"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { getDashboardComplete } from "metabase/dashboard/selectors"; import type { DashboardRefreshPeriodControls } from "metabase/dashboard/types"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Tooltip } from "metabase/ui"; export const EditDashboardButton = ({ onRefreshPeriodChange, }: Pick<DashboardRefreshPeriodControls, "onRefreshPeriodChange">) => { const dispatch = useDispatch(); const dashboard = useSelector(getDashboardComplete); - const onBeginEditing = () => { if (dashboard) { onRefreshPeriodChange(null); @@ -21,14 +19,13 @@ export const EditDashboardButton = ({ }; return ( - <Tooltip key="edit-dashboard" label={t`Edit dashboard`}> - <DashboardHeaderButton - visibleOnSmallScreen={false} - key="edit" - aria-label={t`Edit dashboard`} - icon="pencil" - onClick={onBeginEditing} - /> - </Tooltip> + <ToolbarButton + tooltipLabel={t`Edit dashboard`} + visibleOnSmallScreen={false} + key="edit" + aria-label={t`Edit dashboard`} + icon="pencil" + onClick={onBeginEditing} + /> ); }; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenAnalyticsDashboard.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenAnalyticsDashboard.tsx index 35e00acfa8d3616d74282311902843352610d9cb..ce3b0a1f7b281ef5327283c7c5c89765808aab11 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenAnalyticsDashboard.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenAnalyticsDashboard.tsx @@ -1,14 +1,14 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import CS from "metabase/css/core/index.css"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import type { DashboardFullscreenControls } from "metabase/dashboard/types"; export const FullscreenAnalyticsDashboard = ({ isFullscreen, onFullscreenChange, }: DashboardFullscreenControls) => ( - <DashboardHeaderButton + <ToolbarButton key="expand" aria-label={t`Enter Fullscreen`} icon="expand" diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenToggle.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenToggle.tsx index ecd8f7e770dbb7b648a9719f6c414672d4e3e837..b37a190f9b7f3697f7114ac0cf2eaedd32b6a5d1 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenToggle.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/FullscreenToggle.tsx @@ -1,21 +1,19 @@ import { t } from "ttag"; -import Tooltip from "metabase/core/components/Tooltip"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import type { DashboardFullscreenControls } from "metabase/dashboard/types"; -import { FullScreenButtonIcon } from "../../DashboardActions.styled"; -import { DashboardHeaderButton } from "../DashboardHeader.styled"; - export const FullscreenToggle = ({ isFullscreen, onFullscreenChange, -}: DashboardFullscreenControls) => ( - <Tooltip tooltip={isFullscreen ? t`Exit fullscreen` : t`Enter fullscreen`}> - <span> - <DashboardHeaderButton - icon={<FullScreenButtonIcon isFullscreen={isFullscreen} />} - onClick={e => onFullscreenChange(!isFullscreen, !e.altKey)} - /> - </span> - </Tooltip> -); +}: DashboardFullscreenControls) => { + const label = isFullscreen ? t`Exit fullscreen` : t`Enter fullscreen`; + return ( + <ToolbarButton + tooltipLabel={label} + icon={isFullscreen ? "contract" : "expand"} + onClick={e => onFullscreenChange(!isFullscreen, !e.altKey)} + aria-label={label} + /> + ); +}; diff --git a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/NightModeToggleButton.tsx b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/NightModeToggleButton.tsx index 9afd9d8d2f81611b58dab9f8c66f8046f3750fbb..b802cdb44bb919d74e099e15f281de770ac15b40 100644 --- a/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/NightModeToggleButton.tsx +++ b/frontend/src/metabase/dashboard/components/DashboardHeader/buttons/NightModeToggleButton.tsx @@ -1,24 +1,19 @@ import { t } from "ttag"; -import Tooltip from "metabase/core/components/Tooltip"; -import { NightModeButtonIcon } from "metabase/dashboard/components/DashboardActions.styled"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import type { DashboardNightModeControls } from "metabase/dashboard/types"; export const NightModeToggleButton = ({ isNightMode, onNightModeChange, -}: Pick<DashboardNightModeControls, "isNightMode" | "onNightModeChange">) => ( - <Tooltip tooltip={isNightMode ? t`Daytime mode` : t`Nighttime mode`}> - <span> - <DashboardHeaderButton - icon={ - <NightModeButtonIcon - isNightMode={isNightMode} - onClick={() => onNightModeChange(!isNightMode)} - /> - } - /> - </span> - </Tooltip> -); +}: Pick<DashboardNightModeControls, "isNightMode" | "onNightModeChange">) => { + const label = isNightMode ? t`Daytime mode` : t`Nighttime mode`; + return ( + <ToolbarButton + icon={isNightMode ? "sun" : "moon"} + onClick={() => onNightModeChange?.(!isNightMode)} + tooltipLabel={label} + aria-label={label} + /> + ); +}; diff --git a/frontend/src/metabase/dashboard/components/EmbedMenu/NonAdminEmbedMenu.unit.spec.tsx b/frontend/src/metabase/dashboard/components/EmbedMenu/NonAdminEmbedMenu.unit.spec.tsx index ec5633dbc99b56c612e4a36c13f16bd1b44e76c7..ddb9e04e6f8f2bb09bb128629968969f4a7d87f5 100644 --- a/frontend/src/metabase/dashboard/components/EmbedMenu/NonAdminEmbedMenu.unit.spec.tsx +++ b/frontend/src/metabase/dashboard/components/EmbedMenu/NonAdminEmbedMenu.unit.spec.tsx @@ -1,4 +1,6 @@ -import userEvent from "@testing-library/user-event"; +import userEvent, { + PointerEventsCheckLevel, +} from "@testing-library/user-event"; import { setupDashboardPublicLinkEndpoints } from "__support__/server-mocks"; import { mockSettings } from "__support__/settings"; @@ -80,7 +82,10 @@ describe("NonAdminEmbedMenu", () => { }); it("should render a disabled button with a `Ask your admin to create a public link` tooltip", async () => { - await userEvent.hover(screen.getByLabelText("share icon")); + await userEvent.hover(screen.getByLabelText("share icon"), { + // The button is disabled so pointer events should be disabled + pointerEventsCheck: PointerEventsCheckLevel.Never, + }); expect( screen.getByText("Ask your admin to create a public link"), ).toBeInTheDocument(); @@ -93,7 +98,10 @@ describe("NonAdminEmbedMenu", () => { }); it("should render a disabled button with a `Public links are disabled` tooltip", async () => { - await userEvent.hover(screen.getByLabelText("share icon")); + await userEvent.hover(screen.getByLabelText("share icon"), { + // The button is disabled so pointer events should be disabled + pointerEventsCheck: PointerEventsCheckLevel.Never, + }); expect(screen.getByText("Public links are disabled")).toBeInTheDocument(); }); }); diff --git a/frontend/src/metabase/dashboard/components/ExtraEditButtonsMenu/ExtraEditButtonsMenu.tsx b/frontend/src/metabase/dashboard/components/ExtraEditButtonsMenu/ExtraEditButtonsMenu.tsx index f4b25e598d9ea955caf755ba3e5191c7cc5e43cc..090c8ad5ad8948ed02e5c37fb5e0cc5e776f475a 100644 --- a/frontend/src/metabase/dashboard/components/ExtraEditButtonsMenu/ExtraEditButtonsMenu.tsx +++ b/frontend/src/metabase/dashboard/components/ExtraEditButtonsMenu/ExtraEditButtonsMenu.tsx @@ -1,11 +1,11 @@ import { t } from "ttag"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { setDashboardAttributes } from "metabase/dashboard/actions"; import { trackDashboardWidthChange } from "metabase/dashboard/analytics"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { getDashboard, getDashboardId } from "metabase/dashboard/selectors"; import { useDispatch, useSelector } from "metabase/lib/redux"; -import { Box, Icon, Popover, Stack, Switch, Tooltip } from "metabase/ui"; +import { Box, Popover, Stack, Switch } from "metabase/ui"; const EXTRA_BUTTONS_DESCRIPTION = t`Toggle width`; @@ -13,10 +13,8 @@ export function ExtraEditButtonsMenu() { const dispatch = useDispatch(); const id = useSelector(getDashboardId); const dashboard = useSelector(getDashboard); - const handleToggleWidth = (event: React.ChangeEvent<HTMLInputElement>) => { const nextWidth = event.currentTarget.checked ? "full" : "fixed"; - if (id) { dispatch( setDashboardAttributes({ id, attributes: { width: nextWidth } }), @@ -24,16 +22,15 @@ export function ExtraEditButtonsMenu() { trackDashboardWidthChange(id, nextWidth); } }; - return ( <Popover shadow="sm" position="bottom-end" offset={5}> <Popover.Target> <Box> - <Tooltip label={EXTRA_BUTTONS_DESCRIPTION}> - <DashboardHeaderButton aria-label={EXTRA_BUTTONS_DESCRIPTION}> - <Icon name="ellipsis" size={18} /> - </DashboardHeaderButton> - </Tooltip> + <ToolbarButton + tooltipLabel={EXTRA_BUTTONS_DESCRIPTION} + aria-label={EXTRA_BUTTONS_DESCRIPTION} + icon="ellipsis" + /> </Box> </Popover.Target> <Popover.Dropdown> diff --git a/frontend/src/metabase/dashboard/components/RefreshWidget/RefreshWidgetTarget.tsx b/frontend/src/metabase/dashboard/components/RefreshWidget/RefreshWidgetTarget.tsx index c81a6c946cbfcb8a01f71c7ddff5d30174d80c83..87e86da652633deb3afbb5949a44de9b6657fd05 100644 --- a/frontend/src/metabase/dashboard/components/RefreshWidget/RefreshWidgetTarget.tsx +++ b/frontend/src/metabase/dashboard/components/RefreshWidget/RefreshWidgetTarget.tsx @@ -1,32 +1,36 @@ import { t } from "ttag"; import { CountdownIcon } from "metabase/components/icons/CountdownIcon"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; import { isNotNull } from "metabase/lib/types"; -import { Tooltip } from "metabase/ui"; + +import { ToolbarButton } from "../../../components/ToolbarButton/ToolbarButton"; + +type RefreshWidgetTargetProps = { + period: number | null; + elapsed: number | null; +}; export const RefreshWidgetTarget = ({ period, elapsed, -}: { - elapsed: number | null; - period: number | null; -}) => { +}: RefreshWidgetTargetProps) => { const showRemaining = isNotNull(elapsed) && isNotNull(period); if (!showRemaining) { return ( - <Tooltip label={t`Auto-refresh`}> - <DashboardHeaderButton icon="clock" aria-label={t`Auto Refresh`} /> - </Tooltip> + <ToolbarButton + tooltipLabel={t`Auto-refresh`} + icon="clock" + aria-label={t`Auto Refresh`} + /> ); } const remaining = period - elapsed; return ( - <Tooltip - label={ + <ToolbarButton + tooltipLabel={ t`Refreshing in` + " " + Math.floor(remaining / 60) + @@ -34,17 +38,14 @@ export const RefreshWidgetTarget = ({ (remaining % 60 < 10 ? "0" : "") + Math.round(remaining % 60) } + name="clock" + aria-label={t`Auto Refresh`} > - <DashboardHeaderButton - icon={ - <CountdownIcon - width={16} - height={16} - percent={Math.min(0.95, remaining / period)} - /> - } - aria-label={t`Auto Refresh`} + <CountdownIcon + width={16} + height={16} + percent={Math.min(0.95, remaining / period)} /> - </Tooltip> + </ToolbarButton> ); }; diff --git a/frontend/src/metabase/dashboard/components/TextOptions/TextOptionsButton.styled.tsx b/frontend/src/metabase/dashboard/components/TextOptions/TextOptionsButton.styled.tsx deleted file mode 100644 index 26343e11d1363f74c489726958e5465fe9a0f6a5..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/dashboard/components/TextOptions/TextOptionsButton.styled.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import styled from "@emotion/styled"; - -export const IconContainer = styled.div` - display: flex; - column-gap: 0.25rem; - align-items: center; -`; diff --git a/frontend/src/metabase/dashboard/components/TextOptions/TextOptionsButton.tsx b/frontend/src/metabase/dashboard/components/TextOptions/TextOptionsButton.tsx deleted file mode 100644 index 96597658358aab179c81b9d4c89b9f3259b418f0..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/dashboard/components/TextOptions/TextOptionsButton.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { t } from "ttag"; - -import EntityMenu from "metabase/components/EntityMenu"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; -import { Icon } from "metabase/ui"; - -import { IconContainer } from "./TextOptionsButton.styled"; - -interface TextOptionsButtonProps { - onAddMarkdown: () => void; - onAddHeading: () => void; -} - -export function TextOptionsButton({ - onAddMarkdown, - onAddHeading, -}: TextOptionsButtonProps) { - const TEXT_OPTIONS = [ - { - title: t`Heading`, - action: onAddHeading, - event: "Dashboard; Add Heading", - }, - { - title: t`Text`, - action: onAddMarkdown, - event: "Dashboard; Add Markdown Box", - }, - ]; - - return ( - <EntityMenu - items={TEXT_OPTIONS} - trigger={ - <DashboardHeaderButton aria-label={t`Add a heading or text box`}> - <IconContainer> - <Icon name="string" size={18} /> - <Icon name="chevrondown" size={10} /> - </IconContainer> - </DashboardHeaderButton> - } - minWidth={90} - /> - ); -} diff --git a/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.tsx b/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.tsx index ae48db1b9af7fab4dc96a36d4ccf00ed48e40290..72067cb49ce06bb5f803582a7779ea2a2075578f 100644 --- a/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.tsx +++ b/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.tsx @@ -2,10 +2,9 @@ import type { MouseEvent, Ref } from "react"; import { forwardRef } from "react"; import { t } from "ttag"; -import { DashboardHeaderButton } from "metabase/dashboard/components/DashboardHeader/DashboardHeader.styled"; +import { ToolbarButton } from "metabase/components/ToolbarButton"; import { useSelector } from "metabase/lib/redux"; import { getSetting } from "metabase/selectors/settings"; -import { Flex, Tooltip } from "metabase/ui"; export type ResourceEmbedButtonProps = { onClick?: () => void; @@ -36,18 +35,16 @@ export const ResourceEmbedButton = forwardRef(function ResourceEmbedButton( }; return ( - <Tooltip label={tooltipLabel}> - <Flex> - <DashboardHeaderButton - data-disabled={disabled} - data-testid="resource-embed-button" - icon="share" - disabled={disabled} - onClick={onHeaderButtonClick} - ref={ref} - hasBackground={hasBackground} - /> - </Flex> - </Tooltip> + <ToolbarButton + data-disabled={disabled || undefined} + data-testid="resource-embed-button" + icon="share" + disabled={disabled} + onClick={onHeaderButtonClick} + ref={ref} + hasBackground={hasBackground} + aria-label={tooltipLabel} + tooltipLabel={tooltipLabel} + /> ); }); diff --git a/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.unit.spec.tsx b/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.unit.spec.tsx index 4696757a82f1c238c480fdaf5346054d6724da51..a514ad481a1e95c7ffcc3195ac34301b9390fce8 100644 --- a/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.unit.spec.tsx +++ b/frontend/src/metabase/public/components/ResourceEmbedButton/ResourceEmbedButton.unit.spec.tsx @@ -1,4 +1,6 @@ -import userEvent from "@testing-library/user-event"; +import userEvent, { + PointerEventsCheckLevel, +} from "@testing-library/user-event"; import { renderWithProviders, screen } from "__support__/ui"; import { @@ -56,7 +58,10 @@ describe("ResourceEmbedButton", () => { it("should be disabled when disabled=true", async () => { const { onClick } = setup({ disabled: true }); - await userEvent.click(screen.getByTestId("resource-embed-button")); + await userEvent.click(screen.getByTestId("resource-embed-button"), { + // The button is disabled so pointer events should be disabled + pointerEventsCheck: PointerEventsCheckLevel.Never, + }); expect(screen.getByTestId("resource-embed-button")).toBeDisabled(); expect(onClick).not.toHaveBeenCalled();