Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
86d4ea8
Prepare release v24.4.0
haschek Jul 29, 2025
84e5a65
CodeEditor component should switch to/from read-only mode without los…
andreas-schultz Jul 9, 2025
978b733
Support updating most config parameters of CodeMirror component after…
andreas-schultz Jul 9, 2025
e4b6afe
Fix compile errors
andreas-schultz Jul 18, 2025
2705c54
Allow to set parameters of the outer div container of the CodeAutocom…
andreas-schultz Jul 7, 2025
a2a556c
Fix CodeEditor Enter key handling using wrong offset; Fix first drop …
haschek Jul 29, 2025
56e4c5d
Fix compile issue
andreas-schultz Jul 29, 2025
8e19597
improve changelog messages
haschek Jul 29, 2025
71ccfd7
Remove errors in explore (CMEM-6724)
fiorsaoirse Aug 5, 2025
3580f3f
Revert "Remove errors in explore (CMEM-6724)"
fiorsaoirse Aug 5, 2025
9e7f048
Added additional actions to ActivityControlWidget
fiorsaoirse Aug 5, 2025
0911071
add explicit property to set delay before swapping the placeholder el…
haschek Aug 5, 2025
88f68e1
use minimal swap time as default
haschek Aug 5, 2025
d2f7033
Added a story, added tests
fiorsaoirse Aug 5, 2025
cc3e52d
prevent that flow drag action removes a click action on a menu element
haschek Aug 5, 2025
48bf477
prevent infering event processes, only use latest one
haschek Aug 5, 2025
86ea778
Check data-test-id before usage
fiorsaoirse Aug 6, 2025
409475a
update changelog
haschek Aug 6, 2025
1c518f5
Merge pull request #311 from eccenca/bugfix/remove-errors-CMEM-6724
haschek Aug 6, 2025
137ba76
Merge remote-tracking branch 'origin/develop' into fix/improveTooltip…
haschek Aug 6, 2025
ccf1f47
upgrade jest to v30, fix critical security issue in one of the used s…
haschek Aug 6, 2025
3ca5705
fix typo
haschek Aug 7, 2025
2b0cf16
decide to use a longer opening delay for the first time and use a tra…
haschek Aug 7, 2025
a5b45a7
Merge pull request #312 from eccenca/fix/improveTooltipPerformanceRep…
andreas-schultz Aug 7, 2025
09c52e4
Merge remote-tracking branch 'origin/develop' into release/v24.4.0
haschek Aug 7, 2025
7b25d6b
swich back to any type to prevent a breaking change
haschek Aug 7, 2025
56be6df
Merge pull request #307 from eccenca/release/v24.4.0
haschek Aug 7, 2025
a3c5346
Update changelog section to 24.4.0
haschek Aug 7, 2025
d2b885b
Merge pull request #315 from eccenca/maintain/mergeReleaseTag-v24.4.0
haschek Aug 11, 2025
009ca81
Merge remote-tracking branch 'origin/develop' into feature/forwardUpg…
haschek Aug 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,43 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

This is a major release, and it might be not compatible with your current usage of our library. Please read about the necessary changes in the section about how to migrate.

### Fixed

- `<Modal />`:
- Add 'nopan', 'nowheel' and 'nodrag' classes to Modal's overlay classes in order to always prevent react-flow to react to drag and pan actions in modals.

## [24.4.0] - 2025-08-07

### Added

- Extended existing height and readOnly props from `CodeEditorProps` to `AutoSuggestionProps` & `ExtendedCodeEditorProps` to be configurable from `<CodeAutocompleteField />`
- `<ExtendedCodeEditor />`
- `height` and `readOnly` properties to forward them to `<CodeEditor/>`
- `<CodeAutocompleteField />`:
- outerDivAttributes parameter: Allows to set parameter of the container div element of the code complete field.
- `outerDivAttributes` property: allows to set parameter of the container element
- `height` and `readOnly` properties to forward them to `<ExtendedCodeEditor/>`
- `<ActivityControlWidget />`
- `additionalActions` property to include other more complex components between the action buttons and the context menu of the widget
- `<Tooltip />`
- `swapPlaceholderDelay` property to allow configuration of the delay time before the placeholder element is replaced by the actual tooltip component

### Fixed

- <CodeMirror />:
- Editor is re-created after certain property changes and is reset, i.e. loses it current state.
- <CodeAutocompleteField />:
- Read-only mode does not work correctly. It is still possible to change the value via pressing Enter (in multiline mode) or clicking the clear button.
- First auto-completion item not marked as active when drop down first shown.
- `<CodeEditor />`:
- Enter key handling (adding new line) broken when `onKeyDown` is defined.
- `<Modal />`:
- Add 'nopan', 'nowheel' and 'nodrag' classes to Modal's overlay classes in order to always prevent react-flow to react to drag and pan actions in modals.
- `<CodeEditor />`
- Editor is re-created after certain property changes and is reset, i.e. loses it current state.
- Enter key handling (adding new line) was broken when `onKeyDown` is defined.
- `<CodeAutocompleteField />`
- First auto-completion item not marked as active when drop down first shown.
- Read-only mode does not work correctly. It is still possible to change the value via pressing Enter (in multiline mode) or clicking the clear button.

### Changed

- `<NodeContent />`
- prevent start of a react flow drag action of a node when user clicks in the node menu section

### Deprecated

- `<CodeEditor />`
- `onChange` property: support for `(v: any) => void` type will be exchanged to more specific `(v: string) => void`

## [24.3.0] - 2025-06-05

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@eccenca/gui-elements",
"description": "GUI elements based on other libraries, usable in React application, written in Typescript.",
"version": "24.3.0",
"version": "24.4.0",
"license": "Apache-2.0",
"homepage": "https://github.com/eccenca/gui-elements",
"bugs": "https://github.com/eccenca/gui-elements/issues",
Expand Down Expand Up @@ -149,8 +149,8 @@
"eslint-plugin-simple-import-sort": "^12.1.1",
"husky": "4",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest": "^30.0.5",
"jest-environment-jsdom": "^30.0.5",
"jest-pnp-resolver": "^1.2.3",
"lint-staged": "^15.5.1",
"node-sass-package-importer": "^5.3.3",
Expand Down
50 changes: 45 additions & 5 deletions src/cmem/ActivityControl/ActivityControlWidget.stories.tsx
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -19,7 +27,7 @@ const Template: StoryFn<typeof ActivityControlWidget> = (args) => <ActivityContr

export const FullExample = Template.bind({});

const actions = [
const actions: ActivityControlWidgetAction[] = [
{
"data-test-id": "activity-reload-activity",
icon: "item-reload",
Expand All @@ -30,13 +38,15 @@ const actions = [
{
"data-test-id": "activity-start-activity",
icon: "item-start",
// eslint-disable-next-line no-console
action: () => console.log("start"),
tooltip: "Start Activity",
disabled: false,
},
{
"data-test-id": "activity-stop-activity",
icon: "item-stop",
// eslint-disable-next-line no-console
action: () => console.log("cancel"),
tooltip: "Stop Activity",
disabled: false,
Expand All @@ -57,8 +67,8 @@ const commonWidgetArgs = {
progressSpinner: {
intent: "none",
value: 0.5,
},
};
} as const,
} as const;

FullExample.args = {
...commonWidgetArgs,
Expand All @@ -80,3 +90,33 @@ WidgetWithTags.args = {
...commonWidgetArgs,
tags: widgetTags,
};

export const WidgetWithAdditionalActions: StoryFn<typeof ActivityControlWidget> = (args) => {
const [isOpen, setIsOpen] = useState(false);

const params = useMemo(
() => ({
...commonWidgetArgs,
...args,
additionalActions: args.additionalActions ?? [
<IconButton name="application-explore" onClick={() => setIsOpen(true)} />,
],
}),
[]
);

return (
<OverlaysProvider>
<ActivityControlWidget {...params} />
<SimpleDialog
title="Additional actions dialog"
isOpen={isOpen}
onClose={() => setIsOpen(false)}
canOutsideClickClose
canEscapeKeyClose
>
Modal content
</SimpleDialog>
</OverlaysProvider>
);
};
56 changes: 47 additions & 9 deletions src/cmem/ActivityControl/ActivityControlWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<unknown>[];
}

interface IActivityContextMenu extends TestableComponent {
Expand Down Expand Up @@ -110,11 +114,13 @@ 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,
activityContextMenu,
additionalActions,
small,
border,
hasSpacing,
Expand All @@ -126,10 +132,19 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
} = props;
const spinnerClassNames = (progressSpinner?.className ?? "") + ` ${eccgui}-spinner--permanent`;
const widget = (
<OverviewItem data-test-id={dataTestId} hasSpacing={border || hasSpacing} densityHigh={small}>
<OverviewItem
data-test-id={dataTestIdLegacy}
data-testid={dataTestId}
hasSpacing={border || hasSpacing}
densityHigh={small}
>
{progressBar && <ProgressBar {...progressBar} />}
{(progressSpinner || progressSpinnerFinishedIcon) && (
<OverviewItemDepiction keepColors>
<OverviewItemDepiction
data-testid={dataTestId ? `${dataTestId}-progress-spinner` : undefined}
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-progress-spinner` : undefined}
keepColors
>
{progressSpinnerFinishedIcon ? (
React.cloneElement(progressSpinnerFinishedIcon as JSX.Element, { small, large: !small })
) : (
Expand All @@ -145,13 +160,21 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
)}
<OverviewItemDescription>
{props.label && (
<OverviewItemLine small={small}>
<OverviewItemLine
data-testid={dataTestId ? `${dataTestId}-label` : undefined}
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-label` : undefined}
small={small}
>
{React.cloneElement(labelWrapper, {}, props.label)}
{timerExecutionMsg && (props.statusMessage || tags) && <>&nbsp;({timerExecutionMsg})</>}
</OverviewItemLine>
)}
{(props.statusMessage || tags) && (
<OverviewItemLine small>
<OverviewItemLine
data-testid={dataTestId ? `${dataTestId}-status-message` : undefined}
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-status-message` : undefined}
small
>
{tags}
{props.statusMessage && (
<OverflowText passDown>
Expand All @@ -172,28 +195,43 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
</OverviewItemLine>
)}
{timerExecutionMsg && !(props.statusMessage || tags) && (
<OverviewItemLine small>{timerExecutionMsg}</OverviewItemLine>
<OverviewItemLine
data-testid={dataTestId ? `${dataTestId}-status-message` : undefined}
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-status-message` : undefined}
small
>
{timerExecutionMsg}
</OverviewItemLine>
)}
</OverviewItemDescription>
<OverviewItemActions>
<OverviewItemActions
data-testid={dataTestId ? `${dataTestId}-actions` : undefined}
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-actions` : undefined}
>
{activityActions &&
activityActions.map((action, idx) => {
return (
<IconButton
key={typeof action.icon === "string" ? action.icon : action["data-test-id"] ?? idx}
key={
typeof action.icon === "string"
? action.icon
: action["data-test-id"] ?? action["data-testid"] ?? idx
}
data-test-id={action["data-test-id"]}
data-testid={action["data-testid"]}
name={action.icon}
text={action.tooltip}
onClick={action.action}
disabled={action.disabled}
hasStateWarning={action.hasStateWarning}
intent={action.hasStateWarning ? "warning" : undefined}
tooltipProps={{
hoverOpenDelay: 200,
placement: "bottom",
}}
/>
);
})}
{additionalActions}
{activityContextMenu && activityContextMenu.menuItems.length > 0 && (
<ContextMenu
data-test-id={activityContextMenu["data-test-id"]}
Expand Down
99 changes: 99 additions & 0 deletions src/cmem/ActivityControl/tests/ActivityControlWidget.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";

import "@testing-library/jest-dom";

import { IconButton, Tag, TagList } from "../../../index";
import { ActivityControlWidget, ActivityControlWidgetAction } from "../ActivityControlWidget";

describe("ActivityControlWidget", () => {
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(
<ActivityControlWidget
label="Basic widget"
data-testid="basic-widget"
activityActions={actions}
statusMessage="Status message"
/>
);

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 = (
<TagList>
<Tag>Tag one</Tag>
<Tag>Other tag</Tag>
</TagList>
);
render(<ActivityControlWidget label="Widget with tags" tags={tags} data-testid="widget-with-tags" />);

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 = [
<IconButton
key="add-btn"
name="application-explore"
onClick={mockAction}
data-testid="additional-action"
/>,
];
render(<ActivityControlWidget additionalActions={additionalActions} />);

const customButton = screen.getByTestId("additional-action");
expect(customButton).toBeInTheDocument();

fireEvent.click(customButton);
expect(mockAction).toHaveBeenCalledTimes(1);
});
});
1 change: 1 addition & 0 deletions src/components/Icon/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const IconButton = ({
const defaultIconTooltipProps = {
hoverOpenDelay: 1000,
openOnTargetFocus: restProps.disabled || (restProps.tabIndex ?? 0) < 0 ? false : undefined,
swapPlaceholderDelay: 1,
};
const iconProps = {
small: restProps.small,
Expand Down
Loading