@eccenca/gui-elements 25.0.0-rc.1 → 25.0.0

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 (42) hide show
  1. package/CHANGELOG.md +26 -18
  2. package/dist/cjs/common/index.js +3 -1
  3. package/dist/cjs/common/index.js.map +1 -1
  4. package/dist/cjs/common/utils/reduceToText.js +26 -1
  5. package/dist/cjs/common/utils/reduceToText.js.map +1 -1
  6. package/dist/cjs/components/ContextOverlay/ContextMenu.js +2 -2
  7. package/dist/cjs/components/ContextOverlay/ContextMenu.js.map +1 -1
  8. package/dist/cjs/components/ContextOverlay/ContextOverlay.js +62 -24
  9. package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
  10. package/dist/cjs/components/Icon/canonicalIconNames.js +2 -2
  11. package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
  12. package/dist/cjs/components/TextReducer/TextReducer.js +15 -3
  13. package/dist/cjs/components/TextReducer/TextReducer.js.map +1 -1
  14. package/dist/esm/common/index.js +3 -1
  15. package/dist/esm/common/index.js.map +1 -1
  16. package/dist/esm/common/utils/reduceToText.js +37 -1
  17. package/dist/esm/common/utils/reduceToText.js.map +1 -1
  18. package/dist/esm/components/ContextOverlay/ContextMenu.js +2 -2
  19. package/dist/esm/components/ContextOverlay/ContextMenu.js.map +1 -1
  20. package/dist/esm/components/ContextOverlay/ContextOverlay.js +66 -28
  21. package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
  22. package/dist/esm/components/Icon/canonicalIconNames.js +2 -2
  23. package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
  24. package/dist/esm/components/TextReducer/TextReducer.js +14 -3
  25. package/dist/esm/components/TextReducer/TextReducer.js.map +1 -1
  26. package/dist/types/common/index.d.ts +2 -0
  27. package/dist/types/common/utils/reduceToText.d.ts +1 -1
  28. package/dist/types/components/ContextOverlay/ContextMenu.d.ts +1 -1
  29. package/dist/types/components/TextReducer/TextReducer.d.ts +13 -1
  30. package/package.json +5 -3
  31. package/src/common/index.ts +6 -2
  32. package/src/common/utils/reduceToText.tsx +30 -2
  33. package/src/components/ContextOverlay/ContextMenu.tsx +4 -1
  34. package/src/components/ContextOverlay/ContextOverlay.tsx +74 -24
  35. package/src/components/ContextOverlay/tests/ContextMenu.test.tsx +43 -0
  36. package/src/components/ContextOverlay/tests/ContextOverlay.test.tsx +71 -0
  37. package/src/components/Dialog/stories/Modal.stories.tsx +12 -150
  38. package/src/components/Dialog/stories/ModalContext.stories.tsx +153 -0
  39. package/src/components/Icon/canonicalIconNames.tsx +3 -3
  40. package/src/components/TextReducer/TextReducer.stories.tsx +2 -1
  41. package/src/components/TextReducer/TextReducer.test.tsx +44 -0
  42. package/src/components/TextReducer/TextReducer.tsx +14 -4
@@ -0,0 +1,71 @@
1
+ import React from "react";
2
+ import { PopoverInteractionKind } from "@blueprintjs/core";
3
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
4
+
5
+ import "@testing-library/jest-dom";
6
+
7
+ import { CLASSPREFIX as eccgui } from "../../../configuration/constants";
8
+
9
+ import ContextOverlay from "./../ContextOverlay";
10
+ import { Default as ContextOverlayStory } from "./../ContextOverlay.stories";
11
+
12
+ const overlayWrapper = `${eccgui}-contextoverlay`;
13
+ const placeholderClass = `${overlayWrapper}__wrapper--placeholder`;
14
+
15
+ const checkForPlaceholderClass = (container: HTMLElement, tobe: number) => {
16
+ expect(container.getElementsByClassName(placeholderClass).length).toBe(tobe);
17
+ };
18
+
19
+ describe("ContextOverlay", () => {
20
+ it("should not render placeholder automatically", () => {
21
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} />);
22
+ checkForPlaceholderClass(container, 0);
23
+ });
24
+ it("should render placeholder when `usePlaceholder===true`", () => {
25
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={true} />);
26
+ checkForPlaceholderClass(container, 1);
27
+ });
28
+ it("should render no placeholder when `usePlaceholder===false`", () => {
29
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={false} />);
30
+ checkForPlaceholderClass(container, 0);
31
+ });
32
+ it("if no placeholder is used the overlay should be displayed on click", async () => {
33
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={false} />);
34
+ fireEvent.click(container.getElementsByClassName(overlayWrapper)[0]);
35
+ expect(await screen.findByText("Overlay:")).toBeVisible();
36
+ });
37
+ it("if no placeholder is used the overlay should be displayed on hover (hover interactionKind)", async () => {
38
+ const { container } = render(
39
+ <ContextOverlay
40
+ {...ContextOverlayStory.args}
41
+ usePlaceholder={false}
42
+ interactionKind={PopoverInteractionKind.HOVER}
43
+ />
44
+ );
45
+ fireEvent.mouseEnter(container.getElementsByClassName(overlayWrapper)[0]);
46
+ expect(await screen.findByText("Overlay:")).toBeVisible();
47
+ });
48
+ it("if placeholder is used the overlay should be displayed on click", async () => {
49
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={true} />);
50
+ fireEvent.click(container.getElementsByClassName(overlayWrapper)[0]);
51
+ expect(await screen.findByText("Overlay:")).toBeVisible();
52
+ });
53
+ it("if placeholder is used the overlay should be displayed on hover (hover interactionKind)", async () => {
54
+ const { container } = render(
55
+ <ContextOverlay
56
+ {...ContextOverlayStory.args}
57
+ usePlaceholder={true}
58
+ interactionKind={PopoverInteractionKind.HOVER}
59
+ />
60
+ );
61
+ checkForPlaceholderClass(container, 1);
62
+ fireEvent.mouseEnter(container.getElementsByClassName(overlayWrapper)[0]);
63
+ await waitFor(async () => {
64
+ expect(screen.queryByDisplayValue("Overlay:")).toBeNull();
65
+ checkForPlaceholderClass(container, 0);
66
+ // we need to emulate another mouseover to simulate real user behaviour
67
+ fireEvent.mouseOver(container.getElementsByClassName(overlayWrapper)[0]);
68
+ expect(await screen.findByText("Overlay:")).toBeVisible();
69
+ });
70
+ });
71
+ });
@@ -1,20 +1,11 @@
1
1
  import React from "react";
2
- import { Classes, OverlaysProvider } from "@blueprintjs/core";
2
+ import { OverlaysProvider } from "@blueprintjs/core";
3
3
  import { Meta, StoryFn } from "@storybook/react";
4
4
  import { fn } from "@storybook/test";
5
5
 
6
6
  import { SimpleCard } from "../../Card/stories/Card.stories";
7
7
 
8
- import {
9
- Button,
10
- Card,
11
- CardContent,
12
- Modal,
13
- ModalContext,
14
- ModalSize,
15
- Spacing,
16
- useModalContext,
17
- } from "./../../../../index";
8
+ import { Card, Modal } from "./../../../../index";
18
9
 
19
10
  export default {
20
11
  title: "Components/Dialog/Modal",
@@ -24,15 +15,18 @@ export default {
24
15
  control: false,
25
16
  },
26
17
  },
18
+ decorators: [
19
+ (Story) => (
20
+ <OverlaysProvider>
21
+ <div style={{ height: "400px" }}>
22
+ <Story />
23
+ </div>
24
+ </OverlaysProvider>
25
+ ),
26
+ ],
27
27
  } as Meta<typeof Modal>;
28
28
 
29
- const Template: StoryFn<typeof Modal> = (args) => (
30
- <OverlaysProvider>
31
- <div style={{ height: "400px" }}>
32
- <Modal {...args} />
33
- </div>
34
- </OverlaysProvider>
35
- );
29
+ const Template: StoryFn<typeof Modal> = (args) => <Modal {...args} />;
36
30
 
37
31
  export const Default = Template.bind({});
38
32
  Default.args = {
@@ -42,135 +36,3 @@ Default.args = {
42
36
  onOpening: fn(),
43
37
  onClosing: fn(),
44
38
  };
45
-
46
- const ContextTemplate = ({ children }: React.HTMLAttributes<HTMLDivElement>) => {
47
- const { setModalOpen, openModalStack } = useModalContext();
48
-
49
- return (
50
- <OverlaysProvider>
51
- <div style={{ height: "70vh", position: "relative" }} id={"modalPortal"}>
52
- <ModalContext.Provider value={{ setModalOpen, openModalStack }}>{children}</ModalContext.Provider>
53
- </div>
54
- </OverlaysProvider>
55
- );
56
- };
57
-
58
- const ModalContent = ({ children }: React.HTMLAttributes<HTMLDivElement>) => {
59
- return (
60
- <Card style={{ height: "100%" }}>
61
- <CardContent>{children}</CardContent>
62
- </Card>
63
- );
64
- };
65
-
66
- /** Component for nested modals. */
67
- const ExampleModal = ({
68
- id,
69
- size,
70
- children,
71
- }: {
72
- id?: string;
73
- size: ModalSize;
74
- children?: React.HTMLAttributes<HTMLDivElement>["children"];
75
- }) => {
76
- const [isOpen, setIsOpen] = React.useState(true);
77
- const [portalElement, setPortalElement] = React.useState<HTMLElement | undefined>();
78
-
79
- React.useEffect(() => {
80
- setPortalElement(document.getElementById("modalPortal")!);
81
- }, []);
82
-
83
- return (
84
- <Modal
85
- modalId={id}
86
- size={size}
87
- isOpen={isOpen}
88
- usePortal={true}
89
- portalContainer={portalElement}
90
- hasBackdrop={true}
91
- onOpened={() => {
92
- // workaround, Blueprint attach a class to body tht prevents scrolling, probably it is attached to the wrong portal
93
- document.body.classList.remove(Classes.OVERLAY_OPEN);
94
- }}
95
- >
96
- <ModalContent>
97
- Modal with constant modal ID "{id}".
98
- <Spacing />
99
- <TrackingContent />
100
- <Spacing />
101
- {children}
102
- <Spacing />
103
- <Button key={"close"} onClick={() => setIsOpen(false)}>
104
- Close
105
- </Button>
106
- </ModalContent>
107
- </Modal>
108
- );
109
- };
110
-
111
- const InnerModal = () => {
112
- return <ExampleModal id="innerModal" size="small" />;
113
- };
114
-
115
- const MiddleModal = () => {
116
- return (
117
- <ExampleModal id="middleModal" size="regular">
118
- <InnerModal />
119
- </ExampleModal>
120
- );
121
- };
122
-
123
- /** Shows the current stack of open modals. */
124
- const TrackingContent = () => {
125
- const modalContext = React.useContext(ModalContext);
126
-
127
- return (
128
- <ul>
129
- {(modalContext.openModalStack() ?? []).map((modalId, idx) => (
130
- <li key={modalId}>
131
- {idx + 1}. {modalId}
132
- </li>
133
- ))}
134
- </ul>
135
- );
136
- };
137
-
138
- /**
139
- * `ModalContext` can be used as provider to track a stack of modals.
140
- *
141
- * ```(Javascript)
142
- * const ContextTemplate = () => {
143
- * const { setModalOpen, openModalStack } = useModalContext();
144
- * return (
145
- * <ModalContext.Provider value={{ setModalOpen, openModalStack }}>
146
- * <SimpleDialog size="large" isOpen>
147
- * <OtherModal />
148
- * </SimpleDialog>
149
- * </ModalContext.Provider>
150
- * );
151
- * };
152
- *
153
- * const OtherModal = () => {
154
- * const modalContext = React.useContext(ModalContext);
155
- * return (
156
- * <SimpleDialog size="small">
157
- * <ul>
158
- * {(modalContext.openModalStack ?? []).map((modalId, idx) => (
159
- * <li key={modalId}>
160
- * {idx + 1}. {modalId}
161
- * </li>
162
- * ))}
163
- * </ul>
164
- * </SimpleDialog>
165
- * );
166
- * };
167
- * ```
168
- */
169
- export const NestedModalWithContext = ContextTemplate.bind({});
170
- NestedModalWithContext.args = {
171
- children: [
172
- <ExampleModal id="rootModal" size="large">
173
- <MiddleModal />
174
- </ExampleModal>,
175
- ],
176
- };
@@ -0,0 +1,153 @@
1
+ import React from "react";
2
+ import { Classes, OverlaysProvider } from "@blueprintjs/core";
3
+ import { Meta } from "@storybook/react";
4
+
5
+ import {
6
+ Button,
7
+ Card,
8
+ CardContent,
9
+ Modal,
10
+ ModalContext,
11
+ ModalContextProps,
12
+ ModalSize,
13
+ Spacing,
14
+ useModalContext,
15
+ } from "./../../../../index";
16
+
17
+ /**
18
+ * `ModalContext` can be used as provider to track a stack of modals.
19
+ *
20
+ * ```(Javascript)
21
+ * import { ModalContext, SimpleDialog } from "@eccenca/gui-elements";
22
+ *
23
+ * const ContextTemplate = () => {
24
+ * const { setModalOpen, openModalStack } = useModalContext();
25
+ * return (
26
+ * <ModalContext.Provider value={{ setModalOpen, openModalStack }}>
27
+ * <SimpleDialog size="large" isOpen>
28
+ * <OtherModal />
29
+ * </SimpleDialog>
30
+ * </ModalContext.Provider>
31
+ * );
32
+ * };
33
+ *
34
+ * const OtherModal = () => {
35
+ * const modalContext = React.useContext(ModalContext);
36
+ * return (
37
+ * <SimpleDialog size="small">
38
+ * <ul>
39
+ * {(modalContext.openModalStack ?? []).map((modalId, idx) => (
40
+ * <li key={modalId}>
41
+ * {idx + 1}. {modalId}
42
+ * </li>
43
+ * ))}
44
+ * </ul>
45
+ * </SimpleDialog>
46
+ * );
47
+ * };
48
+ * ```
49
+ */
50
+ export default {
51
+ title: "Components/Dialog/ModalContext",
52
+ decorators: [
53
+ (Story) => (
54
+ <OverlaysProvider>
55
+ <div style={{ height: "70vh", position: "relative" }} id={"modalPortal"}>
56
+ <Story />
57
+ </div>
58
+ </OverlaysProvider>
59
+ ),
60
+ ],
61
+ } as Meta<ModalContextProps>;
62
+
63
+ export const Usage = () => {
64
+ const { setModalOpen, openModalStack } = useModalContext();
65
+
66
+ return (
67
+ <ModalContext.Provider value={{ setModalOpen, openModalStack }}>
68
+ <ExampleModal id="rootModal" size="large">
69
+ <MiddleModal />
70
+ </ExampleModal>
71
+ </ModalContext.Provider>
72
+ );
73
+ };
74
+
75
+ const ModalContent = ({ children }: React.HTMLAttributes<HTMLDivElement>) => {
76
+ return (
77
+ <Card style={{ height: "100%" }}>
78
+ <CardContent>{children}</CardContent>
79
+ </Card>
80
+ );
81
+ };
82
+
83
+ /** Component for nested modals. */
84
+ const ExampleModal = ({
85
+ id,
86
+ size,
87
+ children,
88
+ }: {
89
+ id?: string;
90
+ size: ModalSize;
91
+ children?: React.HTMLAttributes<HTMLDivElement>["children"];
92
+ }) => {
93
+ const [isOpen, setIsOpen] = React.useState(true);
94
+ const [portalElement, setPortalElement] = React.useState<HTMLElement | undefined>();
95
+
96
+ React.useEffect(() => {
97
+ setPortalElement(document.getElementById("modalPortal")!);
98
+ }, []);
99
+
100
+ return (
101
+ <Modal
102
+ modalId={id}
103
+ size={size}
104
+ isOpen={isOpen}
105
+ usePortal={true}
106
+ portalContainer={portalElement}
107
+ hasBackdrop={true}
108
+ onOpened={() => {
109
+ // workaround, Blueprint attach a class to body tht prevents scrolling, probably it is attached to the wrong portal
110
+ document.body.classList.remove(Classes.OVERLAY_OPEN);
111
+ }}
112
+ >
113
+ <ModalContent>
114
+ Modal with constant modal ID "{id}".
115
+ <Spacing />
116
+ <TrackingContent />
117
+ <Spacing />
118
+ {children}
119
+ <Spacing />
120
+ <Button key={"close"} onClick={() => setIsOpen(false)}>
121
+ Close
122
+ </Button>
123
+ </ModalContent>
124
+ </Modal>
125
+ );
126
+ };
127
+
128
+ const InnerModal = () => {
129
+ return <ExampleModal id="innerModal" size="small" />;
130
+ };
131
+
132
+ const MiddleModal = () => {
133
+ return (
134
+ <ExampleModal id="middleModal" size="regular">
135
+ <InnerModal />
136
+ </ExampleModal>
137
+ );
138
+ };
139
+
140
+ /** Shows the current stack of open modals. */
141
+ const TrackingContent = () => {
142
+ const modalContext = React.useContext(ModalContext);
143
+
144
+ return (
145
+ <ul>
146
+ {(modalContext.openModalStack() ?? []).map((modalId, idx) => (
147
+ <li key={modalId}>
148
+ {idx + 1}. {modalId}
149
+ </li>
150
+ ))}
151
+ </ul>
152
+ );
153
+ };
@@ -28,7 +28,7 @@ const canonicalIcons = {
28
28
  "artefact-task-multitablemerge": icons.JoinFull,
29
29
  "artefact-task-jsonparseroperator": icons.JsonReference,
30
30
  "artefact-task-searchaddresses": icons.SearchLocate,
31
- "artefact-task-sparkfunction": icons.Flash,
31
+ "artefact-task-sparkfunction": icons.Flash,
32
32
  "artefact-task-eccencadataplatformgraphstorefileuploadoperator": icons.Upload,
33
33
  "artefact-task-xsltoperator": icons.TransformCode,
34
34
  "artefact-task-distinctby": icons.Filter,
@@ -106,8 +106,8 @@ const canonicalIcons = {
106
106
  "item-hidedetails": icons.ViewOff,
107
107
  "item-image": icons.Image,
108
108
 
109
- "list-sortasc": icons.ArrowDown,
110
- "list-sortdesc": icons.ArrowUp,
109
+ "list-sortasc": icons.ArrowUp,
110
+ "list-sortdesc": icons.ArrowDown,
111
111
  "list-sort": icons.ArrowsVertical,
112
112
 
113
113
  "module-accesscontrol": icons.UserAdmin,
@@ -18,8 +18,9 @@ Default.args = {
18
18
  <LoremIpsum p={1} avgSentencesPerParagraph={1} random={false} />,
19
19
  "Simple text with URL http://example.com/ that should not get parsed.",
20
20
  "a < b to test equations in text like b > a.",
21
+ `Something with a "quote" in it.`,
21
22
  <>
22
- <Markdown>{`* This\n* is\n* a\n* list\n\nwritten in Markdown.`}</Markdown>
23
+ <Markdown>{`* This\n* is\n* a\n* list\n\nwritten in Markdown\n* containing a few HTML 'entities' & "quotes".`}</Markdown>
23
24
  <HtmlContentBlock>
24
25
  <h1>Block with sub elements</h1>
25
26
  <LoremIpsum p={3} avgSentencesPerParagraph={3} random={false} />
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import {render, RenderResult} from "@testing-library/react";
3
+
4
+ import "@testing-library/jest-dom";
5
+
6
+ import { Markdown, TextReducer } from "./../../";
7
+ import { Default as TextReducerStory } from "./TextReducer.stories";
8
+
9
+ describe("TextReducer", () => {
10
+ const textMustExist = (queryByText: RenderResult["queryByText"], text: string) => {
11
+ expect(queryByText(text, { exact: false })).not.toBeNull();
12
+ }
13
+ const textMustNotExist = (queryByText: RenderResult["queryByText"], text: string) => {
14
+ expect(queryByText(text, { exact: false })).toBeNull();
15
+ }
16
+ it("should display encoded HTML entities by default if they are used in the transformed markup", () => {
17
+ const { queryByText } = render(<TextReducer {...TextReducerStory.args} />);
18
+ textMustExist(queryByText, "&#x27;entities&#x27; &amp; &quot;quotes&quot;");
19
+ textMustNotExist(queryByText, `'entities' & "quotes"`);
20
+ });
21
+ it("should not display encoded HTML entities if `decodeHtmlEntities` is enabled", () => {
22
+ const { queryByText } = render(<TextReducer {...TextReducerStory.args} decodeHtmlEntities />);
23
+ textMustNotExist(queryByText, "&#x27;entities&#x27; &amp; &quot;quotes&quot;");
24
+ textMustExist(queryByText, `'entities' & "quotes"`);
25
+ });
26
+ it("should only decode if correct encoded HTML entities are found (strict mode)", () => {
27
+ const { queryByText } = render(
28
+ <TextReducer decodeHtmlEntities>
29
+ <Markdown>&</Markdown>&amp foo&ampbar
30
+ </TextReducer>
31
+ );
32
+ textMustExist(queryByText, "& &amp foo&ampbar");
33
+ textMustNotExist(queryByText, "& & foo&ampbar");
34
+ });
35
+ it("should allow decoding non-strict encoded HTML entities", () => {
36
+ const { queryByText } = render(
37
+ <TextReducer decodeHtmlEntities decodeHtmlEntitiesOptions={{ strict: false }}>
38
+ <Markdown>&</Markdown>&amp foo&ampbar
39
+ </TextReducer>
40
+ );
41
+ textMustNotExist(queryByText, "& &amp foo&ampbar");
42
+ textMustExist(queryByText, "& & foo&ampbar");
43
+ });
44
+ });
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { reduceToText } from "../../common/utils/reduceToText";
3
+ import { DecodeHtmlEntitiesOptions, utils } from "../../common";
4
4
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
5
5
 
6
6
  import { OverflowText, OverflowTextProps } from "./../Typography";
@@ -24,6 +24,17 @@ export interface TextReducerProps extends Pick<React.HTMLAttributes<HTMLElement>
24
24
  * Specify more `OverflowText` properties used when `useOverflowTextWrapper` is set to `true`.
25
25
  */
26
26
  overflowTextProps?: Omit<OverflowTextProps, "passDown">;
27
+ /**
28
+ * If you transform HTML markup to text then the result could contain HTML entity encoded strings.
29
+ * By enabling this option they are decoded back to it's original char.
30
+ */
31
+ decodeHtmlEntities?: boolean;
32
+ /**
33
+ * Set the options used to decode the HTML entities, if `decodeHtmlEntities` is enabled.
34
+ * Internally we use `he` library, see their [documentation on decode options](https://www.npmjs.com/package/he#hedecodehtml-options).
35
+ * If not set we use `{ isAttributeValue: true, strict: true }` as default value.
36
+ */
37
+ decodeHtmlEntitiesOptions?: DecodeHtmlEntitiesOptions;
27
38
  }
28
39
 
29
40
  /**
@@ -32,16 +43,15 @@ export interface TextReducerProps extends Pick<React.HTMLAttributes<HTMLElement>
32
43
  */
33
44
  export const TextReducer = ({
34
45
  children,
35
- maxNodes,
36
- maxLength,
37
46
  useOverflowTextWrapper,
38
47
  overflowTextProps,
48
+ ...reduceToTextOptions
39
49
  }: TextReducerProps) => {
40
50
  if (typeof children === "undefined") {
41
51
  return <></>;
42
52
  }
43
53
 
44
- const shrinkedContent = reduceToText(children, { maxLength, maxNodes });
54
+ const shrinkedContent = utils.reduceToText(children, reduceToTextOptions);
45
55
 
46
56
  return useOverflowTextWrapper ? (
47
57
  <OverflowText