Skip to content

Commit e99fa43

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feature/colorWheelInput-CMEM-7327
2 parents 190e1fa + 023f031 commit e99fa43

17 files changed

Lines changed: 370 additions & 138 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,28 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
88

99
### Added
1010

11+
- `<ActivityControlWidget />`
12+
- Add parameter `active` to activity control action to set the `active` state of its button.
13+
- action now can have a `active` and `notification` property
1114
- `<ApplicationViewability />`
1215
- component for hiding elements in specific media
1316
- `<InlineText />`
1417
- force children to get displayed as inline content
18+
- `<DecoupledOverlay />`
19+
- similar to `ContextOverlay` component but not directly linked to a React element, it specifies the target in the DOM to get connected lazy
1520
- `<StringPreviewContentBlobToggler />`
1621
- `useOnly` property: specify if only parts of the content should be used for the shortened preview, this property replaces `firstNonEmptyLineOnly`
22+
- `<ContextOverlay />`
23+
- `paddingSize` property to add easily some white space
1724
- `<RadioButton />`
1825
- `hideIndicator` property: hide the radio inout indicator but click on children can be processed via `onChange` event
1926
- `<ColorField />`
2027
- input component for colors, uses the configured palette by default but it also allows to enter custom colors
2128
- CSS custom properties
2229
- beside the color palette we now mirror the most important layout configuration variables as CSS custom properties
30+
- new icons:
31+
- `state-confirmed-all`
32+
- `state-declined-all`
2333

2434
### Fixed
2535

@@ -28,6 +38,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
2838
- reduce visual impact of border
2939
- `<StringPreviewContentBlobToggler />`
3040
- take Markdown rendering into account before testing the maximum preview length
41+
- `<CodeEditor />`
42+
- fix `disabled` property update
43+
- `<VisualTour />`
44+
- fix color of buttons to move to previous/next step
45+
- take Markdown rendering into account before testing the maximum preview length
3146
- `<NodeContent />`
3247
- header-menu items are vertically centered now
3348
- `<Link />`
@@ -38,6 +53,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
3853

3954
### Changed
4055

56+
- `<MultiSelect />`:
57+
- Change default filter predicate to match multi-word queries.
58+
- `<EdgeDefault />`
59+
- reduce stroke width to only 1px
4160
- automatically hide user interaction elements in print view
4261
- all application header components except `<WorkspaceHeader />`
4362
- `<CardActions />` and `<CardOptions />`
@@ -52,7 +71,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
5271
### Deprecated
5372

5473
- `<StringPreviewContentBlobToggler />`
55-
- `firstNonEmptyLineOnly` will be removed, is replaced by `useOnly="firstNonEmptyLine"`
74+
- `firstNonEmptyLineOnly` will be removed, is replaced by `useOnly="firstNonEmptyLine"`
5675

5776
## [25.0.0] - 2025-12-01
5877

src/cmem/ActivityControl/ActivityControlWidget.tsx

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import React from "react";
22

3-
import { ValidIconName } from "../../components/Icon/canonicalIconNames";
4-
import { IconProps } from "../../components/Icon/Icon";
5-
import { TestIconProps } from "../../components/Icon/TestIcon";
6-
import { TestableComponent } from "../../components/interfaces";
7-
import { ProgressBarProps } from "../../components/ProgressBar/ProgressBar";
8-
import { SpinnerProps } from "../../components/Spinner/Spinner";
9-
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
3+
import {ValidIconName} from "../../components/Icon/canonicalIconNames";
4+
import {IconProps} from "../../components/Icon/Icon";
5+
import {TestIconProps} from "../../components/Icon/TestIcon";
6+
import {TestableComponent} from "../../components/interfaces";
7+
import {ProgressBarProps} from "../../components/ProgressBar/ProgressBar";
8+
import {SpinnerProps} from "../../components/Spinner/Spinner";
9+
import {CLASSPREFIX as eccgui} from "../../configuration/constants";
1010
import {
1111
Card,
1212
ContextMenu,
13+
DecoupledOverlay,
1314
IconButton,
1415
MenuItem,
16+
Notification,
17+
NotificationProps,
1518
OverflowText,
1619
OverviewItem,
1720
OverviewItemActions,
@@ -97,14 +100,24 @@ interface IActivityContextMenu extends TestableComponent {
97100
export interface ActivityControlWidgetAction extends TestableComponent {
98101
// The action that should be triggered
99102
action: () => void;
100-
// The tooltip that should be shown over the action icon
103+
// The tooltip that should be shown over the action icon on hover
101104
tooltip?: string;
102105
// The icon of the action button
103106
icon: ValidIconName | React.ReactElement<TestIconProps>;
104107
// Action is currently disabled (but shown)
105108
disabled?: boolean;
106109
// Warning state
107110
hasStateWarning?: boolean;
111+
// Active state
112+
active?: boolean
113+
/** A notification that is shown in an overlay pointing at the activity action button. */
114+
notification?: {
115+
message: string
116+
onClose: () => void
117+
intent?: NotificationProps["intent"]
118+
// Timeout in ms before notification is closed. Default: none
119+
timeout?: number
120+
}
108121
}
109122

110123
interface IActivityMenuAction extends ActivityControlWidgetAction {
@@ -209,28 +222,11 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
209222
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-actions` : undefined}
210223
>
211224
{activityActions &&
212-
activityActions.map((action, idx) => {
213-
return (
214-
<IconButton
215-
key={
216-
typeof action.icon === "string"
217-
? action.icon
218-
: action["data-test-id"] ?? action["data-testid"] ?? idx
219-
}
220-
data-test-id={action["data-test-id"]}
221-
data-testid={action["data-testid"]}
222-
name={action.icon}
223-
text={action.tooltip}
224-
onClick={action.action}
225-
disabled={action.disabled}
226-
intent={action.hasStateWarning ? "warning" : undefined}
227-
tooltipProps={{
228-
hoverOpenDelay: 200,
229-
placement: "bottom",
230-
}}
231-
/>
232-
);
233-
})}
225+
activityActions.map((action, idx) => <ActivityActionButton
226+
key={idx}
227+
action={action}
228+
/>
229+
)}
234230
{additionalActions}
235231
{activityContextMenu && activityContextMenu.menuItems.length > 0 && (
236232
<ContextMenu
@@ -241,11 +237,7 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
241237
return (
242238
<MenuItem
243239
icon={menuAction.icon}
244-
key={
245-
typeof menuAction.icon === "string"
246-
? menuAction.icon
247-
: menuAction["data-test-id"] ?? idx
248-
}
240+
key={idx}
249241
onClick={menuAction.action}
250242
text={menuAction.tooltip}
251243
/>
@@ -267,3 +259,44 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
267259
<div className={classname}>{widget}</div>
268260
);
269261
}
262+
263+
interface ActivityActionButtonProps {
264+
action: ActivityControlWidgetAction
265+
}
266+
267+
const ActivityActionButton = ({action}: ActivityActionButtonProps) => {
268+
const actionButtonRef = React.useRef(null);
269+
const ActionButton = () => (
270+
<IconButton
271+
data-test-id={action["data-test-id"]}
272+
data-testid={action["data-testid"]}
273+
name={action.icon}
274+
text={action.tooltip}
275+
onClick={action.action}
276+
disabled={action.disabled}
277+
intent={action.hasStateWarning ? "warning" : undefined}
278+
tooltipProps={{
279+
hoverOpenDelay: 200,
280+
placement: "bottom"
281+
}}
282+
active={action.active}
283+
/>
284+
)
285+
return action.notification ?
286+
<>
287+
<span ref={actionButtonRef}>
288+
<ActionButton/>
289+
</span>
290+
{actionButtonRef.current && (
291+
<DecoupledOverlay targetSelectorOrElement={actionButtonRef.current} paddingSize={"small"}>
292+
<Notification
293+
message={action.notification.message}
294+
intent={action.notification.intent ?? "neutral"}
295+
onDismiss={action.notification.onClose}
296+
timeout={action.notification.timeout}
297+
/>
298+
</DecoupledOverlay>
299+
)}
300+
</> :
301+
<ActionButton/>
302+
}

src/components/ContextOverlay/ContextOverlay.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
Utils as BlueprintUtils,
88
} from "@blueprintjs/core";
99

10-
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
10+
import { CLASSPREFIX as eccgui, WhiteSpaceContainer, WhiteSpaceContainerProps } from "../../index";
1111

1212
export interface ContextOverlayProps extends Omit<BlueprintPopoverProps, "position"> {
1313
/**
@@ -24,6 +24,11 @@ export interface ContextOverlayProps extends Omit<BlueprintPopoverProps, "positi
2424
* Currently experimental.
2525
*/
2626
usePlaceholder?: boolean;
27+
/**
28+
* Adds white space to each side of the overlay content.
29+
* For more control use `WhiteSpaceContainer` directly as wrapper for the content children.
30+
*/
31+
paddingSize?: WhiteSpaceContainerProps["paddingTop"];
2732
}
2833

2934
/**
@@ -36,6 +41,8 @@ export const ContextOverlay = ({
3641
preventTopPosition,
3742
className = "",
3843
usePlaceholder = false,
44+
paddingSize,
45+
content,
3946
...otherPopoverProps
4047
}: ContextOverlayProps) => {
4148
const placeholderRef = React.useRef<HTMLElement>(null);
@@ -169,6 +176,18 @@ export const ContextOverlay = ({
169176
) : (
170177
<BlueprintPopover
171178
placement="bottom"
179+
content={content ? (
180+
paddingSize ? (
181+
<WhiteSpaceContainer
182+
paddingTop={paddingSize}
183+
paddingRight={paddingSize}
184+
paddingBottom={paddingSize}
185+
paddingLeft={paddingSize}
186+
>
187+
{content}
188+
</WhiteSpaceContainer>
189+
) : content
190+
) : undefined}
172191
{...otherPopoverProps}
173192
className={targetClassName}
174193
portalClassName={portalClassNameFinal.trim() ?? undefined}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from "react";
2+
import { Meta, StoryFn } from "@storybook/react";
3+
4+
import { DecoupledOverlay, DecoupledOverlayProps, Tag, WhiteSpaceContainer } from "../../../index";
5+
6+
export default {
7+
title: "Components/DecoupledOverlay",
8+
component: DecoupledOverlay,
9+
argTypes: {},
10+
} as Meta<typeof DecoupledOverlay>;
11+
12+
const Template: StoryFn<typeof DecoupledOverlay> = (args: DecoupledOverlayProps) => {
13+
return (
14+
<>
15+
<Tag id={"decoupledTarget"}>Decoupled target</Tag>
16+
<DecoupledOverlay {...args} />
17+
</>
18+
);
19+
};
20+
21+
export const Default = Template.bind({});
22+
23+
Default.args = {
24+
children: (
25+
<WhiteSpaceContainer marginTop={"small"} marginRight={"small"} marginBottom={"small"} marginLeft={"small"}>
26+
Decoupled overlay
27+
</WhiteSpaceContainer>
28+
),
29+
targetSelectorOrElement: "#decoupledTarget",
30+
};
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from "react";
2+
import { createPortal } from "react-dom";
3+
import { Classes as BlueprintClasses } from "@blueprintjs/core";
4+
import { createPopper } from "@popperjs/core";
5+
6+
import { CLASSPREFIX as eccgui, ContextOverlayProps, TestableComponent, TooltipSize, WhiteSpaceContainer } from "../../index";
7+
8+
export interface DecoupledOverlayProps
9+
extends React.HTMLAttributes<HTMLDivElement>,
10+
TestableComponent,
11+
Pick<ContextOverlayProps, "usePortal" | "portalContainer" | "placement" | "minimal" | "paddingSize"> {
12+
/**
13+
* Element that should be used. The step content is displayed as a tooltip instead of a modal.
14+
* In case of an array, the first match is highlighted. */
15+
targetSelectorOrElement: string | Element;
16+
/**
17+
* The size of the overlay.
18+
* */
19+
size?: TooltipSize;
20+
}
21+
22+
/**
23+
* Use an overlay popover without the necessity to use a target that need to be rendered in place.
24+
* The target is referenced by a selector string or element object.
25+
* It can exist somewhere in the DOM, but it must exist when the overlay is rendered.
26+
* It is always displayed, close it by removement.
27+
*/
28+
export const DecoupledOverlay = ({
29+
targetSelectorOrElement,
30+
usePortal = true,
31+
portalContainer = document.body,
32+
minimal = false,
33+
placement = "auto",
34+
size = "large",
35+
paddingSize,
36+
children,
37+
}: DecoupledOverlayProps) => {
38+
const overlayRef = React.useCallback(
39+
(overlay: HTMLDivElement | null) => {
40+
const target =
41+
typeof targetSelectorOrElement === "string"
42+
? document.querySelector(targetSelectorOrElement)
43+
: targetSelectorOrElement;
44+
if (overlay && target) {
45+
createPopper(target, overlay, {
46+
placement: placement,
47+
modifiers: [
48+
{
49+
name: "offset",
50+
options: {
51+
offset: [0, 15],
52+
},
53+
},
54+
],
55+
});
56+
}
57+
},
58+
[targetSelectorOrElement]
59+
);
60+
61+
const overlay = (
62+
<div
63+
className={
64+
`${eccgui}-decoupled-overlay` +
65+
` ${eccgui}-decoupled-overlay--${size}` +
66+
` ${BlueprintClasses.POPOVER}` +
67+
(minimal ? ` ${BlueprintClasses.MINIMAL}` : "")
68+
}
69+
role="tooltip"
70+
ref={overlayRef}
71+
>
72+
{!minimal && (
73+
<div
74+
className={`${eccgui}-decoupled-overlay__arrow ${BlueprintClasses.POPOVER_ARROW}`}
75+
data-popper-arrow
76+
aria-hidden
77+
/>
78+
)}
79+
<div className={`${BlueprintClasses.POPOVER_CONTENT} ${eccgui}-decoupled-overlay__content`}>
80+
{paddingSize ? (
81+
<WhiteSpaceContainer
82+
paddingTop={paddingSize}
83+
paddingRight={paddingSize}
84+
paddingBottom={paddingSize}
85+
paddingLeft={paddingSize}
86+
>
87+
{children}
88+
</WhiteSpaceContainer>
89+
) : children}
90+
</div>
91+
</div>
92+
);
93+
94+
return usePortal ? createPortal(overlay, portalContainer) : overlay;
95+
};
96+
97+
export default DecoupledOverlay;

0 commit comments

Comments
 (0)