diff --git a/frontend/src/metabase/actions/components/ActionViz/Action.tsx b/frontend/src/metabase/actions/components/ActionViz/Action.tsx index cffd6d9847a7754ac57eb92ef294f5d00953b9e0..bb689f38b3d64d3c2c352a2164b5d2c684fe1f69 100644 --- a/frontend/src/metabase/actions/components/ActionViz/Action.tsx +++ b/frontend/src/metabase/actions/components/ActionViz/Action.tsx @@ -20,6 +20,9 @@ import { } from "metabase/actions/utils"; import { getEditingDashcardId } from "metabase/dashboard/selectors"; +import { getMetadata } from "metabase/selectors/metadata"; +import type Metadata from "metabase-lib/metadata/Metadata"; + import { getDashcardParamValues, getNotProvidedActionParameters, @@ -34,6 +37,7 @@ export interface ActionProps extends VisualizationProps { dispatch: Dispatch; parameterValues: { [id: string]: ParameterValueOrArray }; isEditingDashcard: boolean; + metadata: Metadata; } export function ActionComponent({ @@ -116,16 +120,29 @@ const ConnectedActionComponent = connect()(ActionComponent); function mapStateToProps(state: State, props: ActionProps) { return { isEditingDashcard: getEditingDashcardId(state) === props.dashcard.id, + metadata: getMetadata(state), }; } export function ActionFn(props: ActionProps) { - if (!props.dashcard?.action) { + const { + metadata, + dashcard: { action }, + } = props; + const actionsEnabled = !!metadata + ?.database(action?.database_id) + ?.hasActionsEnabled?.(); + + if (!props.dashcard?.action || !actionsEnabled) { + const tooltip = !action + ? t`No action assigned` + : t`Actions are not enabled for this database`; + return ( <ActionButtonView disabled icon="bolt" - tooltip={t`No action assigned`} + tooltip={tooltip} settings={props.settings} focus={props.isEditingDashcard} /> diff --git a/frontend/src/metabase/actions/components/ActionViz/Action.unit.spec.tsx b/frontend/src/metabase/actions/components/ActionViz/Action.unit.spec.tsx index e53750935cf013a83894a9df7700b02291a02acf..45d86836f3ef0dab5a5902491bfb5b02367c8abe 100644 --- a/frontend/src/metabase/actions/components/ActionViz/Action.unit.spec.tsx +++ b/frontend/src/metabase/actions/components/ActionViz/Action.unit.spec.tsx @@ -11,6 +11,7 @@ import { createMockQueryAction, createMockImplicitQueryAction, createMockDashboard, + createMockDatabase, } from "metabase-types/api/mocks"; import Action, { ActionComponent, ActionProps } from "./Action"; @@ -21,6 +22,7 @@ const defaultProps = { card_id: 777, // action model id action: createMockQueryAction({ name: "My Awesome Action", + database_id: 2, parameters: [ createMockActionParameter({ id: "1", @@ -65,7 +67,20 @@ async function setup(options?: Partial<ActionProps>) { } async function setupActionWrapper(options?: Partial<ActionProps>) { - return renderWithProviders(<Action {...defaultProps} {...options} />); + return renderWithProviders(<Action {...defaultProps} {...options} />, { + withSampleDatabase: true, + storeInitialState: { + entities: { + databases: { + 1: createMockDatabase({ id: 1 }), + 2: createMockDatabase({ + id: 2, + settings: { "database-enable-actions": true }, + }), + }, + }, + }, + }); } function setupExecutionEndpoint(expectedBody: any) { @@ -91,6 +106,38 @@ describe("Actions > ActionViz > ActionComponent", () => { }, }); expect(screen.getByLabelText("bolt icon")).toBeInTheDocument(); + expect(screen.getByRole("button")).toBeDisabled(); + expect(screen.getByLabelText(/no action assigned/i)).toBeInTheDocument(); + }); + + it("should render a disabled state for a button with an action from a database where actions are disabled", async () => { + await setupActionWrapper({ + dashcard: { + ...defaultProps.dashcard, + action: createMockQueryAction({ + name: "My Awesome Action", + database_id: 1, + }), + }, + }); + expect(screen.getByLabelText("bolt icon")).toBeInTheDocument(); + expect(screen.getByRole("button")).toBeDisabled(); + expect( + screen.getByLabelText(/actions are not enabled/i), + ).toBeInTheDocument(); + }); + + it("should render an enabled state when the action is valid", async () => { + await setupActionWrapper({ + dashcard: { + ...defaultProps.dashcard, + action: createMockQueryAction({ + name: "My Awesome Action", + database_id: 2, + }), + }, + }); + expect(screen.getByRole("button")).toBeEnabled(); }); it("should render a button with default text", async () => { diff --git a/frontend/src/metabase/actions/components/ActionViz/ActionButtonView.tsx b/frontend/src/metabase/actions/components/ActionViz/ActionButtonView.tsx index 2532a33b9ff37570b8231b00a34a8fb2a2cce492..f33d30c8c056b2e65105afde207807e72d218095 100644 --- a/frontend/src/metabase/actions/components/ActionViz/ActionButtonView.tsx +++ b/frontend/src/metabase/actions/components/ActionViz/ActionButtonView.tsx @@ -39,6 +39,7 @@ function ActionButtonView({ onClick={onClick} isFullHeight={isFullHeight} focus={focus} + aria-label={tooltip} {...variantProps} > <StyledButtonContent>