@eccenca/gui-elements 24.2.0-rc.0 → 24.2.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +10 -7
  3. package/dist/cjs/cmem/markdown/Markdown.js +13 -11
  4. package/dist/cjs/cmem/markdown/Markdown.js.map +1 -1
  5. package/dist/cjs/cmem/markdown/highlightSearchWords.js +6 -1
  6. package/dist/cjs/cmem/markdown/highlightSearchWords.js.map +1 -1
  7. package/dist/cjs/common/Intent/index.js +4 -11
  8. package/dist/cjs/common/Intent/index.js.map +1 -1
  9. package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +27 -18
  10. package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  11. package/dist/cjs/components/Button/Button.js +2 -2
  12. package/dist/cjs/components/Button/Button.js.map +1 -1
  13. package/dist/cjs/components/ContextOverlay/ContextMenu.js +3 -2
  14. package/dist/cjs/components/ContextOverlay/ContextMenu.js.map +1 -1
  15. package/dist/cjs/components/ContextOverlay/ContextOverlay.js +46 -2
  16. package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
  17. package/dist/cjs/components/Form/FieldItem.js +3 -2
  18. package/dist/cjs/components/Form/FieldItem.js.map +1 -1
  19. package/dist/cjs/components/Form/FieldSet.js +3 -2
  20. package/dist/cjs/components/Form/FieldSet.js.map +1 -1
  21. package/dist/cjs/components/Icon/canonicalIconNames.js +1 -0
  22. package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
  23. package/dist/cjs/components/MultiSelect/MultiSelect.js +18 -14
  24. package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
  25. package/dist/cjs/components/Notification/Notification.js +7 -4
  26. package/dist/cjs/components/Notification/Notification.js.map +1 -1
  27. package/dist/cjs/components/OverviewItem/OverviewItemActions.js +9 -2
  28. package/dist/cjs/components/OverviewItem/OverviewItemActions.js.map +1 -1
  29. package/dist/cjs/components/Spinner/Spinner.js +7 -5
  30. package/dist/cjs/components/Spinner/Spinner.js.map +1 -1
  31. package/dist/cjs/components/Tooltip/Tooltip.js +75 -7
  32. package/dist/cjs/components/Tooltip/Tooltip.js.map +1 -1
  33. package/dist/esm/cmem/markdown/Markdown.js +13 -11
  34. package/dist/esm/cmem/markdown/Markdown.js.map +1 -1
  35. package/dist/esm/cmem/markdown/highlightSearchWords.js +6 -1
  36. package/dist/esm/cmem/markdown/highlightSearchWords.js.map +1 -1
  37. package/dist/esm/common/Intent/index.js +14 -10
  38. package/dist/esm/common/Intent/index.js.map +1 -1
  39. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +32 -25
  40. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  41. package/dist/esm/components/Button/Button.js +2 -2
  42. package/dist/esm/components/Button/Button.js.map +1 -1
  43. package/dist/esm/components/ContextOverlay/ContextMenu.js +3 -2
  44. package/dist/esm/components/ContextOverlay/ContextMenu.js.map +1 -1
  45. package/dist/esm/components/ContextOverlay/ContextOverlay.js +63 -3
  46. package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
  47. package/dist/esm/components/Form/FieldItem.js +3 -2
  48. package/dist/esm/components/Form/FieldItem.js.map +1 -1
  49. package/dist/esm/components/Form/FieldSet.js +3 -2
  50. package/dist/esm/components/Form/FieldSet.js.map +1 -1
  51. package/dist/esm/components/Icon/canonicalIconNames.js +1 -0
  52. package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
  53. package/dist/esm/components/MultiSelect/MultiSelect.js +18 -14
  54. package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
  55. package/dist/esm/components/Notification/Notification.js +7 -4
  56. package/dist/esm/components/Notification/Notification.js.map +1 -1
  57. package/dist/esm/components/OverviewItem/OverviewItemActions.js +25 -2
  58. package/dist/esm/components/OverviewItem/OverviewItemActions.js.map +1 -1
  59. package/dist/esm/components/Spinner/Spinner.js +9 -5
  60. package/dist/esm/components/Spinner/Spinner.js.map +1 -1
  61. package/dist/esm/components/Tooltip/Tooltip.js +92 -8
  62. package/dist/esm/components/Tooltip/Tooltip.js.map +1 -1
  63. package/dist/types/cmem/markdown/Markdown.d.ts +8 -1
  64. package/dist/types/common/Intent/index.d.ts +10 -1
  65. package/dist/types/components/Button/Button.d.ts +5 -1
  66. package/dist/types/components/ContextOverlay/ContextMenu.d.ts +9 -2
  67. package/dist/types/components/ContextOverlay/ContextOverlay.d.ts +6 -1
  68. package/dist/types/components/Form/FieldItem.d.ts +10 -1
  69. package/dist/types/components/Form/FieldSet.d.ts +10 -1
  70. package/dist/types/components/Icon/canonicalIconNames.d.ts +1 -0
  71. package/dist/types/components/MultiSelect/MultiSelect.d.ts +12 -4
  72. package/dist/types/components/Notification/Notification.d.ts +10 -1
  73. package/dist/types/components/OverviewItem/OverviewItemActions.d.ts +13 -1
  74. package/dist/types/components/Spinner/Spinner.d.ts +8 -3
  75. package/dist/types/components/Table/TableContainer.d.ts +2 -2
  76. package/dist/types/components/Table/TableExpandRow.d.ts +1 -1
  77. package/dist/types/components/Table/index.d.ts +1 -0
  78. package/dist/types/components/Tooltip/Tooltip.d.ts +9 -1
  79. package/package.json +10 -7
  80. package/src/cmem/markdown/Markdown.tsx +25 -14
  81. package/src/cmem/markdown/highlightSearchWords.test.ts +8 -2
  82. package/src/cmem/markdown/highlightSearchWords.ts +6 -1
  83. package/src/common/Intent/index.ts +6 -6
  84. package/src/components/AutoSuggestion/AutoSuggestion.tsx +35 -27
  85. package/src/components/Button/Button.stories.tsx +10 -6
  86. package/src/components/Button/Button.tsx +7 -2
  87. package/src/components/ContextOverlay/ContextMenu.stories.tsx +1 -1
  88. package/src/components/ContextOverlay/ContextMenu.tsx +26 -13
  89. package/src/components/ContextOverlay/ContextOverlay.tsx +83 -5
  90. package/src/components/Form/FieldItem.tsx +14 -3
  91. package/src/components/Form/FieldSet.tsx +13 -2
  92. package/src/components/Form/Stories/FieldItem.stories.tsx +4 -0
  93. package/src/components/Form/Stories/FieldSet.stories.tsx +4 -0
  94. package/src/components/Icon/canonicalIconNames.tsx +1 -0
  95. package/src/components/MultiSelect/MultiSelect.tsx +27 -15
  96. package/src/components/MultiSuggestField/MultiSuggestField.stories.tsx +6 -0
  97. package/src/components/Notification/Notification.stories.tsx +4 -0
  98. package/src/components/Notification/Notification.tsx +17 -4
  99. package/src/components/OverviewItem/OverviewItemActions.tsx +24 -1
  100. package/src/components/OverviewItem/stories/OverviewItemList.stories.tsx +2 -7
  101. package/src/components/OverviewItem/stories/OverviewItemListPerformance.tsx +174 -0
  102. package/src/components/OverviewItem/stories/OverviewItemPerformance.stories.tsx +19 -0
  103. package/src/components/Spinner/Spinner.tsx +13 -5
  104. package/src/components/Spinner/Stories/spinner.stories.tsx +6 -1
  105. package/src/components/Table/TableContainer.tsx +2 -2
  106. package/src/components/Table/TableExpandRow.tsx +1 -1
  107. package/src/components/Table/index.tsx +1 -0
  108. package/src/components/Tooltip/Tooltip.stories.tsx +3 -2
  109. package/src/components/Tooltip/Tooltip.tsx +121 -10
@@ -32,18 +32,22 @@ interface AdditionalButtonProps {
32
32
  elevated?: boolean;
33
33
  /**
34
34
  * The button is displayed with primary color scheme.
35
+ * @deprecated (v25) use `intent="primary"` instead.
35
36
  */
36
37
  hasStatePrimary?: boolean;
37
38
  /**
38
39
  * The button is displayed with success (some type of green) color scheme.
40
+ * @deprecated (v25) use `intent="success"` instead.
39
41
  */
40
42
  hasStateSuccess?: boolean;
41
43
  /**
42
44
  * The button is displayed with warning (some type of orange) color scheme.
45
+ * @deprecated (v25) use `intent="warning"` instead.
43
46
  */
44
47
  hasStateWarning?: boolean;
45
48
  /**
46
49
  * The button is displayed with danger (some type of red) color scheme.
50
+ * @deprecated (v25) use `intent="danger"` instead.
47
51
  */
48
52
  hasStateDanger?: boolean;
49
53
  /**
@@ -100,6 +104,7 @@ export const Button = ({
100
104
  tooltipProps,
101
105
  badge,
102
106
  badgeProps = { size: "small", position: "top-right", maxLength: 2 },
107
+ intent,
103
108
  ...restProps
104
109
  }: ButtonProps) => {
105
110
  let intention;
@@ -120,13 +125,13 @@ export const Button = ({
120
125
  break;
121
126
  }
122
127
 
123
- const ButtonType: any = restProps.href ? BlueprintAnchorButton : BlueprintButton;
128
+ const ButtonType = restProps.href ? BlueprintAnchorButton : BlueprintButton;
124
129
 
125
130
  const button = (
126
131
  <ButtonType
127
132
  {...restProps}
128
133
  className={`${eccgui}-button ` + className}
129
- intent={intention}
134
+ intent={(intent || intention) as BlueprintIntent}
130
135
  icon={typeof icon === "string" ? <Icon name={icon} /> : icon}
131
136
  rightIcon={typeof rightIcon === "string" ? <Icon name={rightIcon} /> : rightIcon}
132
137
  >
@@ -10,7 +10,7 @@ export default {
10
10
  subcomponents: { MenuItem },
11
11
  argTypes: {
12
12
  children: {
13
- control: "none",
13
+ control: false,
14
14
  },
15
15
  },
16
16
  } as Meta<typeof ContextMenu>;
@@ -40,8 +40,15 @@ export interface ContextMenuProps extends TestableComponent {
40
40
  * Props to spread to `ContextOverlay` that is used to display the dropdown.
41
41
  */
42
42
  contextOverlayProps?: Partial<Omit<ContextOverlayProps, "content" | "children" | "className">>;
43
- /** Disables the button to open the menu. */
43
+ /**
44
+ * Disables the button to open the menu.
45
+ */
44
46
  disabled?: boolean;
47
+ /**
48
+ * We use the target as placeholder before the real `<ContextMenu /` is rendered on first hover or focus event.
49
+ * In case of problems set this property to `true`.
50
+ */
51
+ preventPlaceholder?: boolean;
45
52
  }
46
53
 
47
54
  /**
@@ -58,27 +65,33 @@ export const ContextMenu = ({
58
65
  /* FIXME: The Tooltip component can interfere with the opened menu, since it is implemented via portal and may cover the menu,
59
66
  so by default we use the title attribute instead of Tooltip. */
60
67
  tooltipAsTitle = true,
68
+ preventPlaceholder = false,
61
69
  ...restProps
62
70
  }: ContextMenuProps) => {
71
+ const toggleButton =
72
+ typeof togglerElement === "string" ? (
73
+ <IconButton
74
+ tooltipAsTitle={tooltipAsTitle}
75
+ name={[togglerElement]}
76
+ text={togglerText}
77
+ large={togglerLarge}
78
+ disabled={!!disabled}
79
+ data-test-id={restProps["data-test-id"]}
80
+ />
81
+ ) : (
82
+ (togglerElement as ReactElement)
83
+ );
84
+
63
85
  return (
64
86
  <ContextOverlay
65
87
  {...restProps}
66
88
  {...contextOverlayProps}
67
89
  className={`${eccgui}-contextmenu ` + className}
68
90
  content={<Menu>{children}</Menu>}
91
+ disabled={!!disabled}
92
+ usePlaceholder={!preventPlaceholder}
69
93
  >
70
- {typeof togglerElement === "string" ? (
71
- <IconButton
72
- tooltipAsTitle={tooltipAsTitle}
73
- name={[togglerElement]}
74
- text={togglerText}
75
- large={togglerLarge}
76
- disabled={!!disabled}
77
- data-test-id={restProps["data-test-id"]}
78
- />
79
- ) : (
80
- (togglerElement as ReactElement)
81
- )}
94
+ {toggleButton}
82
95
  </ContextOverlay>
83
96
  );
84
97
  };
@@ -1,5 +1,10 @@
1
1
  import React from "react";
2
- import { Popover as BlueprintPopover, PopoverProps as BlueprintPopoverProps } from "@blueprintjs/core";
2
+ import {
3
+ Classes as BlueprintClasses,
4
+ Popover as BlueprintPopover,
5
+ PopoverProps as BlueprintPopoverProps,
6
+ Utils as BlueprintUtils,
7
+ } from "@blueprintjs/core";
3
8
 
4
9
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
5
10
 
@@ -13,6 +18,11 @@ export interface ContextOverlayProps extends Omit<BlueprintPopoverProps, "positi
13
18
  * Use it when you need to display modal dialogs out of the context overlay.
14
19
  */
15
20
  preventTopPosition?: boolean;
21
+ /**
22
+ * Use the overlay target as placeholder before the real `<ContextOverlay /` is rendered on first hover or focus event.
23
+ * Currently experimental.
24
+ */
25
+ usePlaceholder?: boolean;
16
26
  }
17
27
 
18
28
  /**
@@ -24,18 +34,86 @@ export const ContextOverlay = ({
24
34
  portalClassName,
25
35
  preventTopPosition,
26
36
  className = "",
27
- ...restProps
37
+ usePlaceholder = false,
38
+ ...otherPopoverProps
28
39
  }: ContextOverlayProps) => {
40
+ const placeholderRef = React.useRef(null);
41
+ const eventMemory = React.useRef<undefined | "afterhover" | "afterfocus">(undefined);
42
+ const [placeholder, setPlaceholder] = React.useState<boolean>(
43
+ // use placeholder only for "simple" overlays without special states
44
+ !otherPopoverProps.disabled &&
45
+ !otherPopoverProps.defaultIsOpen &&
46
+ !otherPopoverProps.isOpen &&
47
+ otherPopoverProps.renderTarget === undefined &&
48
+ usePlaceholder
49
+ );
50
+
51
+ React.useEffect(() => {
52
+ if (placeholderRef.current) {
53
+ const swap = (ev: MouseEvent | globalThis.FocusEvent) => {
54
+ eventMemory.current = ev.type === "focusin" ? "afterfocus" : "afterhover";
55
+ setPlaceholder(false);
56
+ };
57
+ (placeholderRef.current as HTMLElement).addEventListener("mouseenter", swap);
58
+ (placeholderRef.current as HTMLElement).addEventListener("focusin", swap);
59
+ return () => {
60
+ if (placeholderRef.current) {
61
+ (placeholderRef.current as HTMLElement).removeEventListener("mouseenter", swap);
62
+ (placeholderRef.current as HTMLElement).removeEventListener("focusin", swap);
63
+ }
64
+ };
65
+ }
66
+ return () => {};
67
+ }, [!!placeholderRef.current]);
68
+
69
+ const refocus = React.useCallback((node) => {
70
+ if (eventMemory.current === "afterfocus" && node) {
71
+ const target = node.targetRef.current.children[0];
72
+ if (target) {
73
+ target.focus();
74
+ }
75
+ }
76
+ }, []);
77
+
78
+ const targetClassName = `${eccgui}-contextoverlay` + (className ? ` ${className}` : "");
79
+
80
+ const displayPlaceholder = () => {
81
+ const PlaceholderElement = otherPopoverProps?.targetTagName ?? (otherPopoverProps?.fill ? "div" : "span");
82
+ const childTarget = BlueprintUtils.ensureElement(React.Children.toArray(children)[0]);
83
+ if (!childTarget) {
84
+ return null;
85
+ }
86
+ return React.createElement(
87
+ PlaceholderElement,
88
+ {
89
+ ...otherPopoverProps?.targetProps,
90
+ className: `${BlueprintClasses.POPOVER_TARGET} ${targetClassName}`,
91
+ ref: placeholderRef,
92
+ },
93
+ React.cloneElement(childTarget, {
94
+ ...childTarget.props,
95
+ className:
96
+ childTarget.props.className ?? "" + (otherPopoverProps.fill ? ` ${BlueprintClasses.FILL}` : ""),
97
+ tabIndex:
98
+ childTarget.props.tabIndex ??
99
+ (!otherPopoverProps?.disabled && otherPopoverProps?.openOnTargetFocus ? 0 : undefined),
100
+ })
101
+ );
102
+ };
103
+
29
104
  const portalClassNameFinal =
30
105
  (preventTopPosition ? `${eccgui}-contextoverlay__portal--lowertop` : "") +
31
106
  (portalClassName ? ` ${portalClassName}` : "");
32
107
 
33
- return (
108
+ return placeholder ? (
109
+ displayPlaceholder()
110
+ ) : (
34
111
  <BlueprintPopover
35
112
  placement="bottom"
36
- {...restProps}
37
- className={`${eccgui}-contextoverlay` + (className ? ` ${className}` : "")}
113
+ {...otherPopoverProps}
114
+ className={targetClassName}
38
115
  portalClassName={portalClassNameFinal.trim() ?? undefined}
116
+ ref={refocus}
39
117
  >
40
118
  {children}
41
119
  </BlueprintPopover>
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { ClassNames as IntentClassNames } from "../../common/Intent";
3
+ import { ClassNames as IntentClassNames, IntentTypes } from "../../common/Intent";
4
4
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
5
5
  import { TestableComponent } from "../interfaces";
6
6
  import Label, { LabelProps } from "../Label/Label";
@@ -18,23 +18,31 @@ export interface FieldItemProps extends React.HTMLAttributes<HTMLDivElement>, Te
18
18
  /**
19
19
  * Set primary state.
20
20
  * This is not routed through automatically.
21
+ * @deprecated (v25) use `intent="primary"` instead.
21
22
  */
22
23
  hasStatePrimary?: boolean;
23
24
  /**
24
25
  * Set success state.
25
26
  * This is not routed through automatically.
27
+ * @deprecated (v25) use `intent="success"` instead.
26
28
  */
27
29
  hasStateSuccess?: boolean;
28
30
  /**
29
31
  * Set warning state.
30
32
  * This is not routed through automatically.
33
+ * @deprecated (v25) use `intent="warning"` instead.
31
34
  */
32
35
  hasStateWarning?: boolean;
33
36
  /**
34
37
  * Set danger state.
35
38
  * This is not routed through automatically.
39
+ * @deprecated (v25) use `intent="danger"` instead.
36
40
  */
37
41
  hasStateDanger?: boolean;
42
+ /**
43
+ * Intent state of the field item.
44
+ */
45
+ intent?: IntentTypes;
38
46
  /**
39
47
  * Is disabled.
40
48
  * The included inout element nedd to set disabled directly itself.
@@ -71,6 +79,7 @@ export const FieldItem = ({
71
79
  labelProps,
72
80
  helperText,
73
81
  messageText,
82
+ intent,
74
83
  ...otherProps
75
84
  }: FieldItemProps) => {
76
85
  let classIntent = "";
@@ -91,6 +100,8 @@ export const FieldItem = ({
91
100
  break;
92
101
  }
93
102
 
103
+ const intentClass = intent ? " " + IntentClassNames[intent.toUpperCase()] : "";
104
+
94
105
  const label = <Label {...labelProps} disabled={disabled} />;
95
106
 
96
107
  const userhelp =
@@ -106,9 +117,9 @@ export const FieldItem = ({
106
117
  const notification =
107
118
  messageText &&
108
119
  (typeof messageText === "string" ? (
109
- <p className={`${eccgui}-fielditem__message` + classIntent}>{messageText}</p>
120
+ <p className={`${eccgui}-fielditem__message` + (intentClass || classIntent)}>{messageText}</p>
110
121
  ) : (
111
- <div className={`${eccgui}-fielditem__message` + classIntent}>{messageText}</div>
122
+ <div className={`${eccgui}-fielditem__message` + (intentClass || classIntent)}>{messageText}</div>
112
123
  ));
113
124
 
114
125
  return (
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { ClassNames as IntentClassNames } from "../../common/Intent";
3
+ import { ClassNames as IntentClassNames, IntentTypes } from "../../common/Intent";
4
4
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
5
5
 
6
6
  export interface FieldSetProps extends Omit<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, "title"> {
@@ -11,20 +11,28 @@ export interface FieldSetProps extends Omit<React.FieldsetHTMLAttributes<HTMLFie
11
11
  boxed?: boolean;
12
12
  /**
13
13
  * The fieldsetsection is displayed with primary color scheme.
14
+ * @deprecated (v25) use `intent="primary"` instead.
14
15
  */
15
16
  hasStatePrimary?: boolean;
16
17
  /**
17
18
  * The fieldset section is displayed with success (some type of green) color scheme.
19
+ * @deprecated (v25) use `intent="success"` instead.
18
20
  */
19
21
  hasStateSuccess?: boolean;
20
22
  /**
21
23
  * The fieldset section is displayed with warning (some type of orange) color scheme.
24
+ * @deprecated (v25) use `intent="warning"` instead.
22
25
  */
23
26
  hasStateWarning?: boolean;
24
27
  /**
25
28
  * The fieldsetsection is displayed with danger (some type of red) color scheme.
29
+ * @deprecated (v25) use `intent="danger"` instead.
26
30
  */
27
31
  hasStateDanger?: boolean;
32
+ /**
33
+ * Intent state of the field item.
34
+ */
35
+ intent?: IntentTypes;
28
36
  /**
29
37
  * Optional helper text. If given then it is displayed after the title.
30
38
  */
@@ -52,6 +60,7 @@ export const FieldSet = ({
52
60
  hasStateSuccess = false,
53
61
  hasStateWarning = false,
54
62
  hasStateDanger = false,
63
+ intent,
55
64
  helperText,
56
65
  messageText,
57
66
  title,
@@ -75,6 +84,8 @@ export const FieldSet = ({
75
84
  break;
76
85
  }
77
86
 
87
+ const intentClass = intent ? " " + IntentClassNames[intent.toUpperCase()] : "";
88
+
78
89
  const userhelp =
79
90
  helperText &&
80
91
  (typeof helperText === "string" ? (
@@ -98,7 +109,7 @@ export const FieldSet = ({
98
109
  className={
99
110
  `${eccgui}-fieldset` +
100
111
  (className ? " " + className : "") +
101
- classIntent +
112
+ (intentClass || classIntent) +
102
113
  (boxed ? ` ${eccgui}-fieldset--boxed` : "")
103
114
  }
104
115
  {...otherProps}
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { LoremIpsum } from "react-lorem-ipsum";
3
3
  import { Meta, StoryFn } from "@storybook/react";
4
4
 
5
+ import { helpersArgTypes } from "../../../../.storybook/helpers";
5
6
  import { FieldItem, TextField } from "../../../../index";
6
7
 
7
8
  export default {
@@ -11,6 +12,9 @@ export default {
11
12
  children: {
12
13
  control: "none",
13
14
  },
15
+ intent: {
16
+ ...helpersArgTypes.exampleIntent,
17
+ },
14
18
  },
15
19
  } as Meta<typeof FieldItem>;
16
20
 
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { LoremIpsum } from "react-lorem-ipsum";
3
3
  import { Meta, StoryFn } from "@storybook/react";
4
4
 
5
+ import { helpersArgTypes } from "../../../../.storybook/helpers";
5
6
  import { FieldItem, FieldItemRow, FieldSet, TitleSubsection } from "../../../../index";
6
7
 
7
8
  import { Default as SimpleFieldItemExample } from "./FieldItem.stories";
@@ -14,6 +15,9 @@ export default {
14
15
  children: {
15
16
  control: "none",
16
17
  },
18
+ intent: {
19
+ ...helpersArgTypes.exampleIntent,
20
+ },
17
21
  },
18
22
  } as Meta<typeof FieldSet>;
19
23
 
@@ -129,6 +129,7 @@ const canonicalIcons = {
129
129
  "operation-filteredit": icons.FilterEdit,
130
130
  "operation-filterremove": icons.FilterRemove,
131
131
  "operation-filter": icons.Filter,
132
+ "operation-focus": icons.CenterSquare,
132
133
  "operation-format-codeblock": icons.CodeBlock,
133
134
  "operation-format-text-code": icons.Code,
134
135
  "operation-format-text-bold": icons.TextBold,
@@ -78,20 +78,28 @@ interface MultiSelectCommonProps<T>
78
78
  * Items that were newly created and not taken from the list will be post-fixed with this string.
79
79
  */
80
80
  newItemPostfix?: string;
81
+ /**
82
+ * Intent state of the multi select.
83
+ */
84
+ intent?: BlueprintIntent;
81
85
  /**
82
86
  * The input element is displayed with primary color scheme.
87
+ * @deprecated (v25) use `intent="primary"` instead.
83
88
  */
84
89
  hasStatePrimary?: boolean;
85
90
  /**
86
91
  * The input element is displayed with success (some type of green) color scheme.
92
+ * @deprecated (v25) use `intent="success"` instead.
87
93
  */
88
94
  hasStateSuccess?: boolean;
89
95
  /**
90
- * The input element is displayed with success (some type of orange) color scheme.
96
+ * The input element is displayed with warning (some type of orange) color scheme.
97
+ * @deprecated (v25) use `intent="warning"` instead.
91
98
  */
92
99
  hasStateWarning?: boolean;
93
100
  /**
94
- * The input element is displayed with success (some type of red) color scheme.
101
+ * The input element is displayed with danger (some type of red) color scheme.
102
+ * @deprecated (v25) use `intent="danger"` instead.
95
103
  */
96
104
  hasStateDanger?: boolean;
97
105
  /**
@@ -179,6 +187,7 @@ function MultiSelect<T>({
179
187
  wrapperProps,
180
188
  searchPredicate,
181
189
  limitHeightOpened,
190
+ intent,
182
191
  ...otherMultiSelectProps
183
192
  }: MultiSelectProps<T>) {
184
193
  // Options created by a user
@@ -203,19 +212,19 @@ function MultiSelect<T>({
203
212
  timeoutId?: number;
204
213
  }>({});
205
214
 
206
- let intent;
215
+ let stateIntent;
207
216
  switch (true) {
208
217
  case hasStatePrimary:
209
- intent = BlueprintIntent.PRIMARY;
218
+ stateIntent = BlueprintIntent.PRIMARY;
210
219
  break;
211
220
  case hasStateSuccess:
212
- intent = BlueprintIntent.SUCCESS;
221
+ stateIntent = BlueprintIntent.SUCCESS;
213
222
  break;
214
223
  case hasStateWarning:
215
- intent = BlueprintIntent.WARNING;
224
+ stateIntent = BlueprintIntent.WARNING;
216
225
  break;
217
226
  case hasStateDanger:
218
- intent = BlueprintIntent.DANGER;
227
+ stateIntent = BlueprintIntent.DANGER;
219
228
  break;
220
229
  default:
221
230
  break;
@@ -230,12 +239,11 @@ function MultiSelect<T>({
230
239
  }, [items.map((item) => itemId(item)).join("|")]);
231
240
 
232
241
  React.useEffect(() => {
233
- onSelection &&
234
- onSelection({
235
- newlySelected: selectedItems.slice(-1)[0],
236
- createdItems: createdItems.current,
237
- selectedItems,
238
- });
242
+ onSelection?.({
243
+ newlySelected: selectedItems.slice(-1)[0],
244
+ createdItems: createdItems.current,
245
+ selectedItems,
246
+ });
239
247
  }, [
240
248
  onSelection,
241
249
  selectedItems.map((item) => itemId(item)).join("|"),
@@ -430,7 +438,11 @@ function MultiSelect<T>({
430
438
  const handleOnKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
431
439
  if (event.key === "Tab" && !!requestState.current.query) {
432
440
  event.preventDefault();
433
- focusedItem ? onItemSelect(focusedItem) : onItemSelect(createNewItem(requestState.current.query));
441
+ if (focusedItem) {
442
+ onItemSelect(focusedItem);
443
+ } else {
444
+ onItemSelect(createNewItem(requestState.current.query));
445
+ }
434
446
  requestState.current.query = "";
435
447
  setTimeout(() => inputRef.current?.focus());
436
448
  }
@@ -511,7 +523,7 @@ function MultiSelect<T>({
511
523
  className: `${eccgui}-multiselect` + (className ? ` ${className}` : ""),
512
524
  fill: fullWidth,
513
525
  inputRef: inputRef,
514
- intent,
526
+ intent: intent || stateIntent,
515
527
  addOnBlur: true,
516
528
  onKeyDown: handleOnKeyDown,
517
529
  onKeyUp: handleOnKeyUp,
@@ -4,6 +4,8 @@ import { OverlaysProvider } from "@blueprintjs/core";
4
4
  import { Meta, StoryFn } from "@storybook/react";
5
5
  import { fn } from "@storybook/test";
6
6
 
7
+ import { helpersArgTypes } from "../../../.storybook/helpers";
8
+
7
9
  import { MultiSuggestField, MultiSuggestFieldSelectionProps, SimpleDialog } from "./../../../index";
8
10
 
9
11
  const testLabels = loremIpsum({
@@ -29,6 +31,10 @@ export default {
29
31
  items: {
30
32
  control: "none",
31
33
  },
34
+ intent: {
35
+ ...helpersArgTypes.exampleIntent,
36
+ options: ["UNDEFINED", "primary", "success", "warning", "danger"],
37
+ },
32
38
  },
33
39
  args: {
34
40
  onSelection: fn(),
@@ -15,6 +15,10 @@ export default {
15
15
  icon: {
16
16
  ...helpersArgTypes.exampleIcon,
17
17
  },
18
+ intent: {
19
+ ...helpersArgTypes.exampleIntent,
20
+ options: ["UNDEFINED", "success", "warning", "danger", "neutral", "info"],
21
+ },
18
22
  },
19
23
  } as Meta<typeof Notification>;
20
24
 
@@ -5,7 +5,7 @@ import {
5
5
  ToastProps as BlueprintToastProps,
6
6
  } from "@blueprintjs/core";
7
7
 
8
- import { ClassNames as IntentClassNames } from "../../common/Intent";
8
+ import { ClassNames as IntentClassNames, IntentTypes } from "../../common/Intent";
9
9
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
10
10
  import { TestableComponent } from "../interfaces";
11
11
 
@@ -25,23 +25,31 @@ export interface NotificationProps
25
25
  * Notification message that can be used as alternative to children elements.
26
26
  */
27
27
  message?: JSX.Element | string;
28
+ /**
29
+ * Intent state of the notification.
30
+ */
31
+ intent?: Extract<IntentTypes, "neutral" | "success" | "warning" | "danger" | "info">;
28
32
  /**
29
33
  * Notification has a neutral color scheme.
34
+ * @deprecated (v25) use `intent="neutral"` instead.
30
35
  */
31
36
  neutral?: boolean;
32
37
  /**
33
38
  * Notification is a success info.
34
39
  * This defines the colorization and the icon symbol.
40
+ * @deprecated (v25) use `intent="success"` instead.
35
41
  */
36
42
  success?: boolean;
37
43
  /**
38
44
  * Notification is a warning alert.
39
45
  * This defines the colorization and the icon symbol.
46
+ * @deprecated (v25) use `intent="warning"` instead.
40
47
  */
41
48
  warning?: boolean;
42
49
  /**
43
50
  * Notification is a danger alert.
44
51
  * This defines the colorization and the icon symbol.
52
+ * @deprecated (v25) use `intent="danger"` instead.
45
53
  */
46
54
  danger?: boolean;
47
55
  /**
@@ -81,6 +89,7 @@ export const Notification = ({
81
89
  wrapperProps,
82
90
  "data-test-id": dataTestId,
83
91
  "data-testid": dataTestid,
92
+ intent,
84
93
  ...otherProps
85
94
  }: NotificationProps) => {
86
95
  let intentLevel: string = IntentClassNames.INFO;
@@ -103,9 +112,13 @@ export const Notification = ({
103
112
  break;
104
113
  }
105
114
 
115
+ const intents: Array<NotificationProps["intent"]> = ["info", "success", "warning", "danger"];
116
+ const intentClass = intent ? " " + IntentClassNames[intent.toUpperCase()] : "";
117
+ const intentIconSymbol = intents.includes(intent) ? `state-${intent}` : iconSymbol;
118
+
106
119
  let notificationIcon = icon !== false ? icon : undefined;
107
- if (icon !== false && !notificationIcon && !!iconSymbol) {
108
- notificationIcon = <Icon name={iconSymbol as ValidIconName} />;
120
+ if (icon !== false && !notificationIcon && !!intentIconSymbol) {
121
+ notificationIcon = <Icon name={intentIconSymbol as ValidIconName} />;
109
122
  }
110
123
 
111
124
  const content = actions ? (
@@ -123,7 +136,7 @@ export const Notification = ({
123
136
  <BlueprintToast
124
137
  className={
125
138
  `${eccgui}-notification ` +
126
- intentLevel +
139
+ (intentClass || intentLevel) +
127
140
  (className ? ` ${className}` : "") +
128
141
  (flexWidth ? ` ${eccgui}-notification--flexwidth` : "") +
129
142
  (otherProps.onDismiss ? "" : ` ${eccgui}-notification--static`)
@@ -7,6 +7,18 @@ export interface OverviewItemActionsProps extends React.HTMLAttributes<HTMLDivEl
7
7
  * Display it only when the parent `OverviewItem` is hovered or focused.
8
8
  */
9
9
  hiddenInteractions?: boolean;
10
+ /**
11
+ * Delay the rendering of the children by a time in milliseconds.
12
+ * Could be used to prevent browser freezes for the initial `OverviewItem` rendering.
13
+ * In general, it is better to fix the cause, i.e. action elements that are expensive to initialize/render should be
14
+ * optimized or replaced etc. This workaround only prevents the browser from getting blocked completely and does NOT
15
+ * solve the actual performance issue.
16
+ */
17
+ delayDisplayChildren?: number;
18
+ /**
19
+ * Display element while the rendering of the actual children is delayed.
20
+ */
21
+ delaySkeleton?: JSX.Element;
10
22
  }
11
23
 
12
24
  /**
@@ -17,8 +29,19 @@ export const OverviewItemActions = ({
17
29
  children,
18
30
  className = "",
19
31
  hiddenInteractions = false,
32
+ delayDisplayChildren = 0,
33
+ delaySkeleton = <></>,
20
34
  ...restProps
21
35
  }: OverviewItemActionsProps) => {
36
+ const [showActions, setShowActions] = React.useState(!(delayDisplayChildren > 0));
37
+
38
+ React.useEffect(() => {
39
+ // Delay rendering of item actions when they are hidden anyways, because rendering interaction elements like context menus currently has a large performance impact.
40
+ if (!showActions && delayDisplayChildren > 0) {
41
+ setTimeout(() => setShowActions(true), delayDisplayChildren);
42
+ }
43
+ }, []);
44
+
22
45
  return (
23
46
  <div
24
47
  {...restProps}
@@ -28,7 +51,7 @@ export const OverviewItemActions = ({
28
51
  (className ? ` ${className}` : "")
29
52
  }
30
53
  >
31
- {children}
54
+ {showActions ? children : delaySkeleton}
32
55
  </div>
33
56
  );
34
57
  };