@eccenca/gui-elements 23.7.0-rc.0 → 23.7.0-rc.1

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 (49) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/common/index.js +2 -0
  3. package/dist/cjs/common/index.js.map +1 -1
  4. package/dist/cjs/common/utils/getScrollParent.js +24 -0
  5. package/dist/cjs/common/utils/getScrollParent.js.map +1 -0
  6. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +19 -2
  7. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  8. package/dist/cjs/components/MultiSelect/MultiSelect.js +3 -4
  9. package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
  10. package/dist/cjs/components/Sticky/StickyTarget.js +85 -0
  11. package/dist/cjs/components/Sticky/StickyTarget.js.map +1 -0
  12. package/dist/cjs/components/Sticky/index.js +14 -0
  13. package/dist/cjs/components/Sticky/index.js.map +1 -0
  14. package/dist/cjs/components/index.js +1 -0
  15. package/dist/cjs/components/index.js.map +1 -1
  16. package/dist/esm/common/index.js +2 -0
  17. package/dist/esm/common/index.js.map +1 -1
  18. package/dist/esm/common/utils/getScrollParent.js +20 -0
  19. package/dist/esm/common/utils/getScrollParent.js.map +1 -0
  20. package/dist/esm/components/AutocompleteField/AutoCompleteField.js +27 -2
  21. package/dist/esm/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  22. package/dist/esm/components/MultiSelect/MultiSelect.js +3 -4
  23. package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
  24. package/dist/esm/components/Sticky/StickyTarget.js +89 -0
  25. package/dist/esm/components/Sticky/StickyTarget.js.map +1 -0
  26. package/dist/esm/components/Sticky/index.js +2 -0
  27. package/dist/esm/components/Sticky/index.js.map +1 -0
  28. package/dist/esm/components/index.js +1 -0
  29. package/dist/esm/components/index.js.map +1 -1
  30. package/dist/types/common/index.d.ts +2 -0
  31. package/dist/types/common/utils/getScrollParent.d.ts +9 -0
  32. package/dist/types/components/AutocompleteField/AutoCompleteField.d.ts +2 -0
  33. package/dist/types/components/Sticky/StickyTarget.d.ts +32 -0
  34. package/dist/types/components/Sticky/index.d.ts +1 -0
  35. package/dist/types/components/index.d.ts +1 -0
  36. package/package.json +1 -1
  37. package/src/common/index.ts +3 -0
  38. package/src/common/utils/getScrollParent.ts +20 -0
  39. package/src/components/AutocompleteField/AutoCompleteField.tsx +28 -0
  40. package/src/components/AutocompleteField/autocompletefield.scss +1 -1
  41. package/src/components/MultiSelect/MultiSelect.tsx +3 -4
  42. package/src/components/MultiSuggestField/MultiSuggestField.stories.tsx +76 -1
  43. package/src/components/MultiSuggestField/tests/MultiSuggestField.test.tsx +297 -109
  44. package/src/components/Sticky/StickyTarget.tsx +119 -0
  45. package/src/components/Sticky/index.ts +1 -0
  46. package/src/components/Sticky/sticky.scss +69 -0
  47. package/src/components/Sticky/stories/StickyTarget.stories.tsx +63 -0
  48. package/src/components/index.scss +1 -0
  49. package/src/components/index.ts +1 -0
@@ -0,0 +1,89 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __rest = (this && this.__rest) || function (s, e) {
13
+ var t = {};
14
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
15
+ t[p] = s[p];
16
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
17
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
18
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
19
+ t[p[i]] = s[p[i]];
20
+ }
21
+ return t;
22
+ };
23
+ import React from "react";
24
+ import { utils } from "../../common/index.js";
25
+ import { CLASSPREFIX as eccgui } from "../../configuration/constants.js";
26
+ /**
27
+ * Element wraps the content that need to be displayed sticky.
28
+ * The content then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor).
29
+ */
30
+ export var StickyTarget = function (_a) {
31
+ var className = _a.className, _b = _a.to, to = _b === void 0 ? "top" : _b, _c = _a.local, local = _c === void 0 ? false : _c, _d = _a.background, background = _d === void 0 ? "transparent" : _d, offset = _a.offset, style = _a.style, getConnectedElement = _a.getConnectedElement, otherDivProps = __rest(_a, ["className", "to", "local", "background", "offset", "style", "getConnectedElement"]);
32
+ var stickyTargetRef = React.useRef(null);
33
+ var offsetStyle = {};
34
+ if (typeof offset !== "undefined") {
35
+ offsetStyle = __assign(__assign({}, style), { "--eccgui-sticky-target-localoffset": offset });
36
+ }
37
+ var connectedOffset = 0;
38
+ React.useEffect(function () {
39
+ /**
40
+ * If the target should be sticky to a defined element then:
41
+ * * check for the element and its scroll parent
42
+ * * listen to scroll events and use the elements position as offset
43
+ */
44
+ if (getConnectedElement && stickyTargetRef) {
45
+ var stickyConnection_1 = getConnectedElement(stickyTargetRef);
46
+ if (stickyConnection_1) {
47
+ var scrollParent_1 = utils.getScrollParent(stickyConnection_1);
48
+ var scrollParentFallback_1 = !scrollParent_1 ? document.documentElement : false;
49
+ if (scrollParent_1 || scrollParentFallback_1) {
50
+ var updateTargetOffset_1 = function () {
51
+ var _a;
52
+ var scrollParentPosition = (scrollParent_1 || scrollParentFallback_1).getBoundingClientRect();
53
+ var stickyConnectionPosition = stickyConnection_1.getBoundingClientRect();
54
+ if (to === "top") {
55
+ connectedOffset =
56
+ stickyConnectionPosition.top -
57
+ Math.max(0, scrollParentPosition.top) +
58
+ stickyConnectionPosition.height;
59
+ }
60
+ if (to === "bottom") {
61
+ connectedOffset =
62
+ Math.max(scrollParentPosition.height, scrollParentPosition.bottom) -
63
+ stickyConnectionPosition.bottom +
64
+ stickyConnectionPosition.height;
65
+ }
66
+ (_a = stickyTargetRef.current) === null || _a === void 0 ? void 0 : _a.style.setProperty("--eccgui-sticky-target-applicationoffset", "".concat(connectedOffset, "px"));
67
+ };
68
+ updateTargetOffset_1();
69
+ var eventListeningTarget_1 = scrollParent_1 || window;
70
+ var eventListeningMethod_1 = function (_event) {
71
+ updateTargetOffset_1();
72
+ };
73
+ eventListeningTarget_1.addEventListener("scroll", eventListeningMethod_1);
74
+ return function () {
75
+ eventListeningTarget_1.removeEventListener("scroll", eventListeningMethod_1);
76
+ };
77
+ }
78
+ }
79
+ }
80
+ return;
81
+ }, [getConnectedElement, stickyTargetRef, to]);
82
+ return (React.createElement("div", __assign({ ref: stickyTargetRef, className: "".concat(eccgui, "-sticky__target") +
83
+ (to ? " ".concat(eccgui, "-sticky__target--").concat(to) : "") +
84
+ (local ? " ".concat(eccgui, "-sticky__target--localscrollarea") : "") +
85
+ (background ? " ".concat(eccgui, "-sticky__target--bg-").concat(background) : "") +
86
+ (className ? " ".concat(className) : ""), style: offset ? offsetStyle : style }, otherDivProps)));
87
+ };
88
+ export default StickyTarget;
89
+ //# sourceMappingURL=StickyTarget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StickyTarget.js","sourceRoot":"","sources":["../../../../src/components/Sticky/StickyTarget.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./StickyTarget.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/Sticky/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
@@ -36,6 +36,7 @@ export * from "./Separation/Divider.js";
36
36
  export * from "./Separation/Spacing.js";
37
37
  export * from "./Skeleton/Skeleton.js";
38
38
  export * from "./Spinner/Spinner.js";
39
+ export * from "./Sticky/index.js";
39
40
  export * from "./Structure/index.js";
40
41
  export * from "./Switch/Switch.js";
41
42
  export * from "./Table/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAE5B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAE5B,cAAc,cAAc,CAAC"}
@@ -21,6 +21,7 @@ export declare const utils: {
21
21
  };
22
22
  getGlobalVar: (varname: string) => Window;
23
23
  setGlobalVar: (varname: string, value: any) => any;
24
+ getScrollParent: (element: Element) => false | HTMLElement;
24
25
  };
25
26
  export declare const Utilities: {
26
27
  openInNewTab: (event: import("react").MouseEvent<HTMLAnchorElement, MouseEvent>, handler?: ((e: import("react").MouseEvent<HTMLAnchorElement, MouseEvent>) => void) | undefined, url?: string | undefined) => void;
@@ -44,4 +45,5 @@ export declare const Utilities: {
44
45
  };
45
46
  getGlobalVar: (varname: string) => Window;
46
47
  setGlobalVar: (varname: string, value: any) => any;
48
+ getScrollParent: (element: Element) => false | HTMLElement;
47
49
  };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Find the scroll parent of an element, returns `false` if it cannot be found.
3
+ * In case of a `false` return the very probably `document.documentElement` is the parent.
4
+ * In this case `window` object should be used for scroll event listeners.
5
+ * See `src/components/Sticky/StickyTarget.tsx` for an usage example.
6
+ * @param element
7
+ * @returns HTMLElement | false
8
+ */
9
+ export declare const getScrollParent: (element: Element) => HTMLElement | false;
@@ -109,6 +109,8 @@ export interface AutoCompleteFieldProps<T, UPDATE_VALUE> {
109
109
  * Use the full available width of the parent container.
110
110
  */
111
111
  fill?: boolean;
112
+ /** Utility that fetches more options when clicked*/
113
+ loadMoreResults?: () => Promise<T[]>;
112
114
  }
113
115
  export declare type IAutoCompleteFieldProps<T, UPDATE_VALUE> = AutoCompleteFieldProps<T, UPDATE_VALUE>;
114
116
  /**
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ export interface StickyTargetProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ /**
4
+ * Set the side the element need to be sticky on.
5
+ */
6
+ to?: "top" | "bottom";
7
+ /**
8
+ * The sticky area is positioned relatively to a local scroll area.
9
+ * The application header is not taken into offset calculation
10
+ */
11
+ local?: boolean;
12
+ /**
13
+ * Set the background color used for the sticky area.
14
+ * As it can overlay other content readability could be harmed if the overlayed content is shining through.
15
+ */
16
+ background?: "card" | "application" | "transparent";
17
+ /**
18
+ * Set additional distance to original sticky position.
19
+ */
20
+ offset?: `${number}${string}`;
21
+ /**
22
+ * Callback that returns an DOM element.
23
+ * The position of `StickyTarget` is then calculated relative to that element.
24
+ */
25
+ getConnectedElement?: (ref: React.MutableRefObject<HTMLDivElement | null>) => Element | false;
26
+ }
27
+ /**
28
+ * Element wraps the content that need to be displayed sticky.
29
+ * The content then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor).
30
+ */
31
+ export declare const StickyTarget: ({ className, to, local, background, offset, style, getConnectedElement, ...otherDivProps }: StickyTargetProps) => React.JSX.Element;
32
+ export default StickyTarget;
@@ -0,0 +1 @@
1
+ export * from "./StickyTarget";
@@ -36,6 +36,7 @@ export * from "./Separation/Divider";
36
36
  export * from "./Separation/Spacing";
37
37
  export * from "./Skeleton/Skeleton";
38
38
  export * from "./Spinner/Spinner";
39
+ export * from "./Sticky";
39
40
  export * from "./Structure";
40
41
  export * from "./Switch/Switch";
41
42
  export * from "./Table";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eccenca/gui-elements",
3
3
  "description": "GUI elements based on other libraries, usable in React application, written in Typescript.",
4
- "version": "23.7.0-rc.0",
4
+ "version": "23.7.0-rc.1",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/eccenca/gui-elements",
7
7
  "bugs": "https://github.com/eccenca/gui-elements/issues",
@@ -1,6 +1,7 @@
1
1
  import { invisibleZeroWidthCharacters } from "./utils/characters";
2
2
  import decideContrastColorValue from "./utils/colorDecideContrastvalue";
3
3
  import getColorConfiguration from "./utils/getColorConfiguration";
4
+ import { getScrollParent } from "./utils/getScrollParent";
4
5
  import { getGlobalVar, setGlobalVar } from "./utils/globalVars";
5
6
  import { openInNewTab } from "./utils/openInNewTab";
6
7
 
@@ -11,6 +12,8 @@ export const utils = {
11
12
  invisibleZeroWidthCharacters,
12
13
  getGlobalVar,
13
14
  setGlobalVar,
15
+ getScrollParent,
14
16
  };
17
+
15
18
  // @deprecated use `utils`
16
19
  export const Utilities = utils;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Find the scroll parent of an element, returns `false` if it cannot be found.
3
+ * In case of a `false` return the very probably `document.documentElement` is the parent.
4
+ * In this case `window` object should be used for scroll event listeners.
5
+ * See `src/components/Sticky/StickyTarget.tsx` for an usage example.
6
+ * @param element
7
+ * @returns HTMLElement | false
8
+ */
9
+ export const getScrollParent = (element: Element): HTMLElement | false => {
10
+ let scrollParent = element.parentElement;
11
+ while (scrollParent) {
12
+ const { overflow } = window.getComputedStyle(scrollParent);
13
+ if (overflow.split(" ").every((value) => value === "auto" || value === "scroll")) {
14
+ return scrollParent;
15
+ }
16
+ scrollParent = scrollParent.parentElement;
17
+ }
18
+
19
+ return false;
20
+ };
@@ -156,6 +156,8 @@ export interface AutoCompleteFieldProps<T, UPDATE_VALUE> {
156
156
  * Use the full available width of the parent container.
157
157
  */
158
158
  fill?: boolean;
159
+ /** Utility that fetches more options when clicked*/
160
+ loadMoreResults?: () => Promise<T[]>;
159
161
  }
160
162
 
161
163
  export type IAutoCompleteFieldProps<T, UPDATE_VALUE> = AutoCompleteFieldProps<T, UPDATE_VALUE>;
@@ -195,6 +197,7 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
195
197
  requestErrorPrefix,
196
198
  hasBackDrop,
197
199
  fill,
200
+ loadMoreResults,
198
201
  ...otherProps
199
202
  } = props;
200
203
  const [selectedItem, setSelectedItem] = useState<T | undefined>(initialValue);
@@ -334,6 +337,7 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
334
337
  disabled: modifiers.disabled,
335
338
  highlightingEnabled: highlightingEnabled,
336
339
  };
340
+
337
341
  const renderedItem = itemRenderer(item, query, relevantModifiers, handleClick);
338
342
  if (typeof renderedItem === "string") {
339
343
  return (
@@ -442,6 +446,26 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
442
446
  createNewItemPosition,
443
447
  }
444
448
  : {};
449
+
450
+ const handleMenuScroll = React.useCallback(
451
+ async (event: any) => {
452
+ const menu = event.target;
453
+ const { scrollTop, scrollHeight, clientHeight } = menu;
454
+ // Check if scrolled to the bottom of the list
455
+ if (scrollTop + clientHeight >= scrollHeight && loadMoreResults) {
456
+ const results = await loadMoreResults();
457
+ if (results) {
458
+ setFiltered((prev) => [...prev, ...results]);
459
+ setTimeout(() => {
460
+ menu.scrollTop = scrollHeight; //safari adaptation
461
+ menu.scrollTo({ left: 0, top: scrollHeight, behavior: "auto" });
462
+ });
463
+ }
464
+ }
465
+ },
466
+ [loadMoreResults]
467
+ );
468
+
445
469
  return (
446
470
  <BlueprintSuggest<T>
447
471
  className={`${eccgui}-autocompletefield__input` + (className ? ` ${className}` : "")}
@@ -455,7 +479,11 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
455
479
  noResults={<MenuItem disabled={true} text={noResultText} />}
456
480
  onItemSelect={onSelectionChange}
457
481
  onQueryChange={(q) => setQuery(q)}
482
+ resetOnQuery={false}
458
483
  closeOnSelect={true}
484
+ menuProps={{
485
+ onScroll: handleMenuScroll,
486
+ }}
459
487
  query={query}
460
488
  // This leads to odd compile errors without "as any"
461
489
  popoverProps={updatedContextOverlayProps as any}
@@ -1,5 +1,5 @@
1
1
  .#{$eccgui}-autocompletefield__options {
2
- .#{$ns}-popover2-content {
2
+ .#{$ns}-popover-content {
3
3
  .#{$ns}-menu {
4
4
  max-height: 45vh;
5
5
  overflow: auto;
@@ -229,16 +229,15 @@ export function MultiSelect<T>({
229
229
  ]);
230
230
 
231
231
  /**
232
- * Update selected items if the component is controlled and we get
233
- * new selected items from outside
232
+ * Update selected items if we get new selected items from outside
234
233
  */
235
234
  React.useEffect(() => {
236
- if (!isControlled) {
235
+ if (!externalSelectedItems) {
237
236
  return;
238
237
  }
239
238
 
240
239
  setSelectedItems(externalSelectedItems);
241
- }, [isControlled, externalSelectedItems]);
240
+ }, [externalSelectedItems]);
242
241
 
243
242
  /**
244
243
  * using the equality prop specified checks if an item has already been selected
@@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from "react";
2
2
  import { loremIpsum } from "react-lorem-ipsum";
3
3
  import { Meta, StoryFn } from "@storybook/react";
4
4
 
5
- import { MultiSelectSelectionProps, MultiSuggestField } from "./../../../index";
5
+ import { MultiSelectSelectionProps, MultiSuggestField, SimpleDialog } from "./../../../index";
6
6
 
7
7
  const testLabels = loremIpsum({
8
8
  p: 1,
@@ -78,6 +78,40 @@ predefinedNotControlledValues.args = {
78
78
  itemLabel: (item) => item.testLabel,
79
79
  };
80
80
 
81
+ const DeferredSelectionTemplate: StoryFn = () => {
82
+ const initialSelected: Array<{ testId: string; testLabel: string }> = [];
83
+ const [loaded, setLoaded] = useState(false);
84
+
85
+ const selected = loaded ? selectedItems : initialSelected;
86
+
87
+ const identity = useCallback((item: string): string => item, []);
88
+
89
+ return (
90
+ <>
91
+ <div>Selected items loaded: {loaded.toString()}</div>
92
+
93
+ <br />
94
+
95
+ <MultiSuggestField<string>
96
+ items={items.map(({ testId }) => testId)}
97
+ selectedItems={selected.map(({ testId }) => testId)}
98
+ itemId={identity}
99
+ itemLabel={(itemId) => items.find(({ testId }) => testId === itemId)?.testLabel ?? itemId}
100
+ createNewItemFromQuery={(query) => query}
101
+ />
102
+
103
+ <br />
104
+
105
+ <button onClick={() => setLoaded((prev) => !prev)}>Toggle selected</button>
106
+ </>
107
+ );
108
+ };
109
+
110
+ /**
111
+ *
112
+ */
113
+ export const deferredSelection = DeferredSelectionTemplate.bind({});
114
+
81
115
  /**
82
116
  * New item creation, add to a existing list
83
117
  */
@@ -160,3 +194,44 @@ const WithResetButton: StoryFn = () => {
160
194
  * Reset values
161
195
  */
162
196
  export const withResetItemAndCreation = WithResetButton.bind({});
197
+
198
+ const WithinModal = (): JSX.Element => {
199
+ const [isOpen, setIsOpen] = useState(false);
200
+
201
+ const copy: Array<{ testLabel: string; testId: string }> = [items[2]];
202
+
203
+ const [selected, setSelected] = useState(copy);
204
+
205
+ const handleOnSelect = useCallback((params) => {
206
+ const items = params.selectedItems;
207
+ setSelected(items);
208
+ }, []);
209
+
210
+ const handleReset = (): void => {
211
+ setSelected(copy);
212
+ };
213
+
214
+ return (
215
+ <>
216
+ <button onClick={() => setIsOpen(true)}>open modal</button>
217
+
218
+ <SimpleDialog isOpen={isOpen} onClose={() => setIsOpen(false)} canOutsideClickClose>
219
+ <div>
220
+ <button onClick={handleReset}>Reset</button>
221
+ <br />
222
+ <br />
223
+ <MultiSuggestField<{ testLabel: string; testId: string }>
224
+ items={items}
225
+ selectedItems={selected}
226
+ onSelection={handleOnSelect}
227
+ itemId={({ testId }) => testId}
228
+ itemLabel={({ testLabel }) => testLabel}
229
+ createNewItemFromQuery={(query) => ({ testId: `${query}-id`, testLabel: query })}
230
+ />
231
+ </div>
232
+ </SimpleDialog>
233
+ </>
234
+ );
235
+ };
236
+
237
+ export const withinModal = WithinModal.bind({});