Skip to content

Commit 819c814

Browse files
authored
Merge pull request #361 from eccenca/feature/extendActivityControl-CMEM-7150
Additions and changes for Mapping Creator (CMEM-7150)
2 parents 41b2b36 + d24afd1 commit 819c814

17 files changed

Lines changed: 345 additions & 123 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,24 @@ 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
- CSS custom properties
1825
- beside the color palette we now mirror the most important layout configuration variables as CSS custom properties
26+
- new icons:
27+
- `state-confirmed-all`
28+
- `state-declined-all`
1929

2030
### Fixed
2131

@@ -24,6 +34,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
2434
- reduce visual impact of border
2535
- `<StringPreviewContentBlobToggler />`
2636
- take Markdown rendering into account before testing the maximum preview length
37+
- `<CodeEditor />`
38+
- fix `disabled` property update
39+
- `<VisualTour />`
40+
- fix color of buttons to move to previous/next step
41+
- take Markdown rendering into account before testing the maximum preview length
2742
- `<NodeContent />`
2843
- header-menu items are vertically centered now
2944
- `<Link />`
@@ -34,6 +49,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
3449

3550
### Changed
3651

52+
- `<MultiSelect />`:
53+
- Change default filter predicate to match multi-word queries.
54+
- `<EdgeDefault />`
55+
- reduce stroke width to only 1px
3756
- automatically hide user interaction elements in print view
3857
- all application header components except `<WorkspaceHeader />`
3958
- `<CardActions />` and `<CardOptions />`

src/cmem/ActivityControl/ActivityControlWidget.tsx

Lines changed: 44 additions & 21 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 {
@@ -210,13 +223,9 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
210223
>
211224
{activityActions &&
212225
activityActions.map((action, idx) => {
213-
return (
226+
const actionButtonRef = React.useRef(null);
227+
const ActionButton = () => (
214228
<IconButton
215-
key={
216-
typeof action.icon === "string"
217-
? action.icon
218-
: action["data-test-id"] ?? action["data-testid"] ?? idx
219-
}
220229
data-test-id={action["data-test-id"]}
221230
data-testid={action["data-testid"]}
222231
name={action.icon}
@@ -226,10 +235,28 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
226235
intent={action.hasStateWarning ? "warning" : undefined}
227236
tooltipProps={{
228237
hoverOpenDelay: 200,
229-
placement: "bottom",
238+
placement: "bottom"
230239
}}
240+
active={action.active}
231241
/>
232-
);
242+
)
243+
return action.notification ?
244+
<>
245+
<span key={idx} ref={actionButtonRef}>
246+
<ActionButton />
247+
</span>
248+
{actionButtonRef.current && (
249+
<DecoupledOverlay targetSelectorOrElement={actionButtonRef.current} paddingSize={"small"}>
250+
<Notification
251+
message={action.notification.message}
252+
intent={action.notification.intent ?? "neutral"}
253+
onDismiss={action.notification.onClose}
254+
timeout={action.notification.timeout}
255+
/>
256+
</DecoupledOverlay>
257+
)}
258+
</> :
259+
<ActionButton key={idx} />
233260
})}
234261
{additionalActions}
235262
{activityContextMenu && activityContextMenu.menuItems.length > 0 && (
@@ -241,11 +268,7 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
241268
return (
242269
<MenuItem
243270
icon={menuAction.icon}
244-
key={
245-
typeof menuAction.icon === "string"
246-
? menuAction.icon
247-
: menuAction["data-test-id"] ?? idx
248-
}
271+
key={idx}
249272
onClick={menuAction.action}
250273
text={menuAction.tooltip}
251274
/>

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;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.#{$eccgui}-decoupled-overlay__arrow {
2+
&::before {
3+
background: $card-background-color;
4+
}
5+
6+
.#{$eccgui}-decoupled-overlay[data-popper-placement="top"] & {
7+
bottom: -0.5 * $eccgui-size-block-whitespace;
8+
}
9+
.#{$eccgui}-decoupled-overlay[data-popper-placement="right"] & {
10+
left: -0.5 * $eccgui-size-block-whitespace;
11+
}
12+
.#{$eccgui}-decoupled-overlay[data-popper-placement="bottom"] & {
13+
top: -0.5 * $eccgui-size-block-whitespace;
14+
}
15+
.#{$eccgui}-decoupled-overlay[data-popper-placement="left"] & {
16+
right: -0.5 * $eccgui-size-block-whitespace;
17+
}
18+
}
19+
20+
.#{$eccgui}-decoupled-overlay {
21+
&.#{$prefix-blueprintjs}-popover {
22+
z-index: 8001;
23+
}
24+
25+
&--small {
26+
@extend .#{$eccgui}-tooltip--small;
27+
}
28+
29+
&--medium {
30+
@extend .#{$eccgui}-tooltip--medium;
31+
}
32+
33+
&--large {
34+
@extend .#{$eccgui}-tooltip--large;
35+
}
36+
37+
&:has(.#{$eccgui}-decoupled-overlay__arrow) {
38+
.#{$eccgui}-decoupled-overlay__content {
39+
min-height: 30px; // height of blueprint arrow
40+
}
41+
}
42+
}
43+
44+
.#{$eccgui}-decoupled-overlay__content {
45+
padding: 0.1px; // force margins of children to stay inside
46+
}

0 commit comments

Comments
 (0)