From 71ccfd7f15378f54a84f1ef4a32b393c917344b3 Mon Sep 17 00:00:00 2001 From: Mariia Kovsharova Date: Tue, 5 Aug 2025 09:41:28 +0200 Subject: [PATCH 1/5] Remove errors in explore (CMEM-6724) --- .../ActivityControl/ActivityControlWidget.tsx | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/cmem/ActivityControl/ActivityControlWidget.tsx b/src/cmem/ActivityControl/ActivityControlWidget.tsx index e59ef64ca..dc66d4888 100644 --- a/src/cmem/ActivityControl/ActivityControlWidget.tsx +++ b/src/cmem/ActivityControl/ActivityControlWidget.tsx @@ -52,7 +52,7 @@ export interface ActivityControlWidgetProps extends TestableComponent { /** * The action buttons */ - activityActions?: ActivityControlWidgetAction[]; + activityActions?: (ActivityControlWidgetAction | React.ReactElement)[]; /** * Context menu items */ @@ -90,6 +90,12 @@ interface IActivityContextMenu extends TestableComponent { menuItems: IActivityMenuAction[]; } +const isActivityControlWidgetAction = ( + value: ActivityControlWidgetAction | React.ReactElement +): value is ActivityControlWidgetAction => { + return !!((value as ActivityControlWidgetAction).action && (value as ActivityControlWidgetAction).icon); +}; + export interface ActivityControlWidgetAction extends TestableComponent { // The action that should be triggered action: () => void; @@ -178,21 +184,25 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { {activityActions && activityActions.map((action, idx) => { - return ( - - ); + if (isActivityControlWidgetAction(action)) { + return ( + + ); + } + + return action; })} {activityContextMenu && activityContextMenu.menuItems.length > 0 && ( Date: Tue, 5 Aug 2025 10:56:24 +0200 Subject: [PATCH 2/5] Revert "Remove errors in explore (CMEM-6724)" This reverts commit 71ccfd7f15378f54a84f1ef4a32b393c917344b3. --- .../ActivityControl/ActivityControlWidget.tsx | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/cmem/ActivityControl/ActivityControlWidget.tsx b/src/cmem/ActivityControl/ActivityControlWidget.tsx index dc66d4888..e59ef64ca 100644 --- a/src/cmem/ActivityControl/ActivityControlWidget.tsx +++ b/src/cmem/ActivityControl/ActivityControlWidget.tsx @@ -52,7 +52,7 @@ export interface ActivityControlWidgetProps extends TestableComponent { /** * The action buttons */ - activityActions?: (ActivityControlWidgetAction | React.ReactElement)[]; + activityActions?: ActivityControlWidgetAction[]; /** * Context menu items */ @@ -90,12 +90,6 @@ interface IActivityContextMenu extends TestableComponent { menuItems: IActivityMenuAction[]; } -const isActivityControlWidgetAction = ( - value: ActivityControlWidgetAction | React.ReactElement -): value is ActivityControlWidgetAction => { - return !!((value as ActivityControlWidgetAction).action && (value as ActivityControlWidgetAction).icon); -}; - export interface ActivityControlWidgetAction extends TestableComponent { // The action that should be triggered action: () => void; @@ -184,25 +178,21 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { {activityActions && activityActions.map((action, idx) => { - if (isActivityControlWidgetAction(action)) { - return ( - - ); - } - - return action; + return ( + + ); })} {activityContextMenu && activityContextMenu.menuItems.length > 0 && ( Date: Tue, 5 Aug 2025 11:52:29 +0200 Subject: [PATCH 3/5] Added additional actions to ActivityControlWidget --- CHANGELOG.md | 1 + src/cmem/ActivityControl/ActivityControlWidget.tsx | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f0d096ae..942330234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Added - Extended existing height and readOnly props from `CodeEditorProps` to `AutoSuggestionProps` & `ExtendedCodeEditorProps` to be configurable from `` +- Added additional actions to `` ## [24.3.0] - 2025-06-05 diff --git a/src/cmem/ActivityControl/ActivityControlWidget.tsx b/src/cmem/ActivityControl/ActivityControlWidget.tsx index e59ef64ca..47f89529d 100644 --- a/src/cmem/ActivityControl/ActivityControlWidget.tsx +++ b/src/cmem/ActivityControl/ActivityControlWidget.tsx @@ -81,6 +81,10 @@ export interface ActivityControlWidgetProps extends TestableComponent { * execution timer messages for waiting and running times. */ timerExecutionMsg?: JSX.Element | null; + /** + * additional actions that can serve as a complex component, positioned between the default actions and the context menu + */ + additionalActions?: React.ReactElement[]; } interface IActivityContextMenu extends TestableComponent { @@ -115,6 +119,7 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { progressSpinner, activityActions, activityContextMenu, + additionalActions, small, border, hasSpacing, @@ -194,6 +199,7 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { /> ); })} + {additionalActions} {activityContextMenu && activityContextMenu.menuItems.length > 0 && ( Date: Tue, 5 Aug 2025 15:04:08 +0200 Subject: [PATCH 4/5] Added a story, added tests --- .../ActivityControlWidget.stories.tsx | 48 ++++++++- .../ActivityControl/ActivityControlWidget.tsx | 47 +++++++-- .../tests/ActivityControlWidget.test.tsx | 99 +++++++++++++++++++ 3 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 src/cmem/ActivityControl/tests/ActivityControlWidget.test.tsx diff --git a/src/cmem/ActivityControl/ActivityControlWidget.stories.tsx b/src/cmem/ActivityControl/ActivityControlWidget.stories.tsx index 21c1bd13c..952955b33 100644 --- a/src/cmem/ActivityControl/ActivityControlWidget.stories.tsx +++ b/src/cmem/ActivityControl/ActivityControlWidget.stories.tsx @@ -1,9 +1,17 @@ -import React from "react"; +import React, { useMemo, useState } from "react"; import { loremIpsum } from "react-lorem-ipsum"; +import { OverlaysProvider } from "@blueprintjs/core"; import { Meta, StoryFn } from "@storybook/react"; import { helpersArgTypes } from "../../../.storybook/helpers"; -import { ActivityControlWidget, Tag, TagList } from "../../../index"; +import { + ActivityControlWidget, + ActivityControlWidgetAction, + IconButton, + SimpleDialog, + Tag, + TagList, +} from "../../../index"; export default { title: "Cmem/ActivityControlWidget", @@ -19,7 +27,7 @@ const Template: StoryFn = (args) => = (args) => { + const [isOpen, setIsOpen] = useState(false); + + const params = useMemo( + () => ({ + ...commonWidgetArgs, + ...args, + additionalActions: args.additionalActions ?? [ + setIsOpen(true)} />, + ], + }), + [] + ); + + return ( + + + setIsOpen(false)} + canOutsideClickClose + canEscapeKeyClose + > + Modal content + + + ); +}; diff --git a/src/cmem/ActivityControl/ActivityControlWidget.tsx b/src/cmem/ActivityControl/ActivityControlWidget.tsx index 47f89529d..8fec8194c 100644 --- a/src/cmem/ActivityControl/ActivityControlWidget.tsx +++ b/src/cmem/ActivityControl/ActivityControlWidget.tsx @@ -114,7 +114,8 @@ interface IActivityMenuAction extends ActivityControlWidgetAction { /** Shows the status of activities and supports actions on these activities. */ export function ActivityControlWidget(props: ActivityControlWidgetProps) { const { - "data-test-id": dataTestId, + "data-test-id": dataTestIdLegacy, + "data-testid": dataTestId, progressBar, progressSpinner, activityActions, @@ -131,10 +132,19 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { } = props; const spinnerClassNames = (progressSpinner?.className ?? "") + ` ${eccgui}-spinner--permanent`; const widget = ( - + {progressBar && } {(progressSpinner || progressSpinnerFinishedIcon) && ( - + {progressSpinnerFinishedIcon ? ( React.cloneElement(progressSpinnerFinishedIcon as JSX.Element, { small, large: !small }) ) : ( @@ -150,13 +160,21 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { )} {props.label && ( - + {React.cloneElement(labelWrapper, {}, props.label)} {timerExecutionMsg && (props.statusMessage || tags) && <> ({timerExecutionMsg})} )} {(props.statusMessage || tags) && ( - + {tags} {props.statusMessage && ( @@ -177,21 +195,32 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { )} {timerExecutionMsg && !(props.statusMessage || tags) && ( - {timerExecutionMsg} + + {timerExecutionMsg} + )} - + {activityActions && activityActions.map((action, idx) => { return ( { + it("Renders basic widget with actions and handles clicks", () => { + const mockAction1 = jest.fn(); + const mockAction2 = jest.fn(); + const actions: ActivityControlWidgetAction[] = [ + { + "data-testid": "action-1", + icon: "item-reload", + action: mockAction1, + tooltip: "Action 1", + }, + { + "data-testid": "action-2", + icon: "item-start", + action: mockAction2, + tooltip: "Action 2", + }, + ]; + + render( + + ); + + const button1 = screen.getByTestId("action-1"); + const button2 = screen.getByTestId("action-2"); + + const label = screen.getByTestId("basic-widget-label"); + const statusMessage = screen.getByTestId("basic-widget-status-message"); + const actionsContainer = screen.getByTestId("basic-widget-actions"); + + expect(label).toBeInTheDocument(); + expect(statusMessage).toBeInTheDocument(); + expect(actionsContainer).toBeInTheDocument(); + + expect(label).toHaveTextContent("Basic widget"); + expect(statusMessage).toHaveTextContent("Status message"); + + expect(button1).toBeInTheDocument(); + expect(button2).toBeInTheDocument(); + + fireEvent.click(button1); + expect(mockAction1).toHaveBeenCalledTimes(1); + + fireEvent.click(button2); + expect(mockAction2).toHaveBeenCalledTimes(1); + }); + + it("Renders widget with tags", () => { + const tags = ( + + Tag one + Other tag + + ); + render(); + + const label = screen.getByTestId("widget-with-tags-label"); + const statusMessage = screen.getByTestId("widget-with-tags-status-message"); + + expect(label).toBeInTheDocument(); + expect(statusMessage).toBeInTheDocument(); + + expect(label).toHaveTextContent("Widget with tags"); + expect(statusMessage).toHaveTextContent("Tag one"); + expect(statusMessage).toHaveTextContent("Other tag"); + }); + + it("Renders widget with additional actions and handles click", () => { + const mockAction = jest.fn(); + const additionalActions = [ + , + ]; + render(); + + const customButton = screen.getByTestId("additional-action"); + expect(customButton).toBeInTheDocument(); + + fireEvent.click(customButton); + expect(mockAction).toHaveBeenCalledTimes(1); + }); +}); From 86ea778b26fa468d9cd88ba4bfc3ca6b1dc8cf62 Mon Sep 17 00:00:00 2001 From: Mariia Kovsharova Date: Wed, 6 Aug 2025 14:33:27 +0200 Subject: [PATCH 5/5] Check data-test-id before usage --- .../ActivityControl/ActivityControlWidget.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/cmem/ActivityControl/ActivityControlWidget.tsx b/src/cmem/ActivityControl/ActivityControlWidget.tsx index 8fec8194c..efda5060f 100644 --- a/src/cmem/ActivityControl/ActivityControlWidget.tsx +++ b/src/cmem/ActivityControl/ActivityControlWidget.tsx @@ -141,8 +141,8 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { {progressBar && } {(progressSpinner || progressSpinnerFinishedIcon) && ( {progressSpinnerFinishedIcon ? ( @@ -161,8 +161,8 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { {props.label && ( {React.cloneElement(labelWrapper, {}, props.label)} @@ -171,8 +171,8 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { )} {(props.statusMessage || tags) && ( {tags} @@ -196,15 +196,18 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) { )} {timerExecutionMsg && !(props.statusMessage || tags) && ( {timerExecutionMsg} )} - + {activityActions && activityActions.map((action, idx) => { return (