From 6063faa0b0fcc7067215f42cd8ae5c60cd0bd235 Mon Sep 17 00:00:00 2001 From: George Gritsouk <989898+gggritso@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:24:53 -0400 Subject: [PATCH] feat(dashboards): Allow disabling `WidgetFrame` actions (#79592) Used in a few places in the UI, like in Dashboard Preview mode. --- .../widgets/common/widgetFrame.spec.tsx | 60 ++++++++++++ .../widgets/common/widgetFrame.stories.tsx | 49 +++++++++- .../dashboards/widgets/common/widgetFrame.tsx | 93 +++++++++++++------ 3 files changed, 172 insertions(+), 30 deletions(-) diff --git a/static/app/views/dashboards/widgets/common/widgetFrame.spec.tsx b/static/app/views/dashboards/widgets/common/widgetFrame.spec.tsx index d3255405af00ca..077609547462ca 100644 --- a/static/app/views/dashboards/widgets/common/widgetFrame.spec.tsx +++ b/static/app/views/dashboards/widgets/common/widgetFrame.spec.tsx @@ -70,6 +70,36 @@ describe('WidgetFrame', () => { expect(onAction).toHaveBeenCalledTimes(1); }); + it('Allows disabling a single action', async () => { + const onAction = jest.fn(); + + render( + + ); + + const $button = screen.getByRole('button', {name: 'Make Go'}); + expect($button).toBeInTheDocument(); + expect($button).toBeDisabled(); + + await userEvent.click($button); + expect(onAction).not.toHaveBeenCalled(); + + await userEvent.hover($button); + expect(await screen.findByText('Actions are not supported')).toBeInTheDocument(); + }); + it('Renders multiple actions in a dropdown menu', async () => { const onAction1 = jest.fn(); const onAction2 = jest.fn(); @@ -101,6 +131,36 @@ describe('WidgetFrame', () => { await userEvent.click(screen.getByRole('menuitemradio', {name: 'Two'})); expect(onAction2).toHaveBeenCalledTimes(1); }); + + it('Allows disabling multiple actions', async () => { + render( + + ); + + const $trigger = screen.getByRole('button', {name: 'Actions'}); + await userEvent.click($trigger); + + expect(screen.queryByRole('menuitemradio', {name: 'One'})).not.toBeInTheDocument(); + expect(screen.queryByRole('menuitemradio', {name: 'Two'})).not.toBeInTheDocument(); + + await userEvent.hover($trigger); + expect(await screen.findByText('Actions are not supported')).toBeInTheDocument(); + }); }); describe('Full Screen View Button', () => { diff --git a/static/app/views/dashboards/widgets/common/widgetFrame.stories.tsx b/static/app/views/dashboards/widgets/common/widgetFrame.stories.tsx index 6cd5f3ecb63b97..fa054d1671eef7 100644 --- a/static/app/views/dashboards/widgets/common/widgetFrame.stories.tsx +++ b/static/app/views/dashboards/widgets/common/widgetFrame.stories.tsx @@ -106,7 +106,9 @@ export default storyBook(WidgetFrame, story => { supports an action menu. If only one action is passed, the single action is rendered as a small button. If multiple actions are passed, they are grouped into a dropdown menu. Menu actions appear on hover or - keyboard focus. + keyboard focus. They can be disabled with the actionsDisabled prop, + and supplemented with an optional actionsMessage prop that adds a + tooltip.

@@ -127,6 +129,25 @@ export default storyBook(WidgetFrame, story => { /> + + { + // eslint-disable-next-line no-console + console.log('See more!'); + }, + }, + ]} + /> + + { ]} /> + + + { + // eslint-disable-next-line no-console + console.log('See more!'); + }, + }, + { + key: 'see-less', + label: t('See Less'), + onAction: () => { + // eslint-disable-next-line no-console + console.log('See less!'); + }, + }, + ]} + /> + ); diff --git a/static/app/views/dashboards/widgets/common/widgetFrame.tsx b/static/app/views/dashboards/widgets/common/widgetFrame.tsx index 9dd7a318d2c4d7..0385532811c581 100644 --- a/static/app/views/dashboards/widgets/common/widgetFrame.tsx +++ b/static/app/views/dashboards/widgets/common/widgetFrame.tsx @@ -18,6 +18,8 @@ import {WarningsList} from './warningsList'; export interface WidgetFrameProps extends StateProps { actions?: MenuItemProps[]; + actionsDisabled?: boolean; + actionsMessage?: string; badgeProps?: BadgeProps; children?: React.ReactNode; description?: string; @@ -63,36 +65,51 @@ export function WidgetFrame(props: WidgetFrameProps) { {(props.description || props.onFullScreenViewClick || (actions && actions.length > 0)) && ( - + {props.description && ( )} - {actions.length === 1 ? ( - actions[0].to ? ( - - {actions[0].label} - - ) : ( - - ) - ) : null} - - {actions.length > 1 ? ( - , - }} - position="bottom-end" - /> - ) : null} + + {actions.length === 1 ? ( + actions[0].to ? ( + + {actions[0].label} + + ) : ( + + ) + ) : null} + + {actions.length > 1 ? ( + , + }} + position="bottom-end" + /> + ) : null} + {props.onFullScreenViewClick && (