@eccenca/gui-elements 25.0.0-rc.2 → 25.1.0-rc.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.
- package/CHANGELOG.md +64 -18
- package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js +1 -3
- package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
- package/dist/cjs/cmem/ContentBlobToggler/ContentBlobToggler.js +1 -0
- package/dist/cjs/cmem/ContentBlobToggler/ContentBlobToggler.js.map +1 -1
- package/dist/cjs/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js +34 -11
- package/dist/cjs/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js.map +1 -1
- package/dist/cjs/cmem/react-flow/ReactFlow/ReactFlowV12.js.map +1 -1
- package/dist/cjs/common/Intent/index.js +1 -1
- package/dist/cjs/common/Intent/index.js.map +1 -1
- package/dist/cjs/common/index.js +3 -1
- package/dist/cjs/common/index.js.map +1 -1
- package/dist/cjs/common/utils/reduceToText.js +26 -1
- package/dist/cjs/common/utils/reduceToText.js.map +1 -1
- package/dist/cjs/components/Application/ApplicationViewability.js +33 -0
- package/dist/cjs/components/Application/ApplicationViewability.js.map +1 -0
- package/dist/cjs/components/Application/index.js +1 -0
- package/dist/cjs/components/Application/index.js.map +1 -1
- package/dist/cjs/components/ContextOverlay/ContextMenu.js +2 -2
- package/dist/cjs/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js +62 -24
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/cjs/components/Icon/IconButton.js.map +1 -1
- package/dist/cjs/components/TextReducer/TextReducer.js +15 -3
- package/dist/cjs/components/TextReducer/TextReducer.js.map +1 -1
- package/dist/cjs/components/Typography/InlineText.js +29 -0
- package/dist/cjs/components/Typography/InlineText.js.map +1 -0
- package/dist/cjs/components/Typography/index.js +1 -0
- package/dist/cjs/components/Typography/index.js.map +1 -1
- package/dist/cjs/extensions/codemirror/CodeMirror.js +2 -2
- package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +1 -1
- package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
- package/dist/cjs/extensions/codemirror/tests/codemirrorTestHelper.js +2 -2
- package/dist/cjs/extensions/codemirror/tests/codemirrorTestHelper.js.map +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeNew.js +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeNew.js.map +1 -1
- package/dist/cjs/extensions/react-flow/handles/HandleDefault.js +1 -1
- package/dist/cjs/extensions/react-flow/handles/HandleDefault.js.map +1 -1
- package/dist/cjs/extensions/react-flow/minimap/MiniMap.js +1 -1
- package/dist/cjs/extensions/react-flow/minimap/MiniMap.js.map +1 -1
- package/dist/cjs/extensions/react-flow/minimap/MiniMapV12.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/nodeUtils.js.map +1 -1
- package/dist/esm/cmem/ActivityControl/SilkActivityControl.js +1 -3
- package/dist/esm/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
- package/dist/esm/cmem/ContentBlobToggler/ContentBlobToggler.js +1 -0
- package/dist/esm/cmem/ContentBlobToggler/ContentBlobToggler.js.map +1 -1
- package/dist/esm/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js +32 -9
- package/dist/esm/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js.map +1 -1
- package/dist/esm/cmem/react-flow/ReactFlow/ReactFlowV12.js.map +1 -1
- package/dist/esm/common/Intent/index.js +1 -1
- package/dist/esm/common/Intent/index.js.map +1 -1
- package/dist/esm/common/index.js +3 -1
- package/dist/esm/common/index.js.map +1 -1
- package/dist/esm/common/utils/reduceToText.js +37 -1
- package/dist/esm/common/utils/reduceToText.js.map +1 -1
- package/dist/esm/components/Application/ApplicationViewability.js +28 -0
- package/dist/esm/components/Application/ApplicationViewability.js.map +1 -0
- package/dist/esm/components/Application/index.js +1 -0
- package/dist/esm/components/Application/index.js.map +1 -1
- package/dist/esm/components/ContextOverlay/ContextMenu.js +2 -2
- package/dist/esm/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/esm/components/ContextOverlay/ContextOverlay.js +66 -28
- package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/esm/components/Icon/IconButton.js.map +1 -1
- package/dist/esm/components/TextReducer/TextReducer.js +14 -3
- package/dist/esm/components/TextReducer/TextReducer.js.map +1 -1
- package/dist/esm/components/Typography/InlineText.js +33 -0
- package/dist/esm/components/Typography/InlineText.js.map +1 -0
- package/dist/esm/components/Typography/index.js +1 -0
- package/dist/esm/components/Typography/index.js.map +1 -1
- package/dist/esm/extensions/codemirror/CodeMirror.js +3 -3
- package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +1 -1
- package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
- package/dist/esm/extensions/codemirror/tests/codemirrorTestHelper.js +3 -3
- package/dist/esm/extensions/codemirror/tests/codemirrorTestHelper.js.map +1 -1
- package/dist/esm/extensions/react-flow/edges/EdgeNew.js +1 -1
- package/dist/esm/extensions/react-flow/edges/EdgeNew.js.map +1 -1
- package/dist/esm/extensions/react-flow/handles/HandleDefault.js +1 -1
- package/dist/esm/extensions/react-flow/handles/HandleDefault.js.map +1 -1
- package/dist/esm/extensions/react-flow/minimap/MiniMap.js +1 -1
- package/dist/esm/extensions/react-flow/minimap/MiniMap.js.map +1 -1
- package/dist/esm/extensions/react-flow/minimap/MiniMapV12.js.map +1 -1
- package/dist/esm/extensions/react-flow/nodes/nodeUtils.js.map +1 -1
- package/dist/types/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.d.ts +26 -10
- package/dist/types/common/index.d.ts +2 -0
- package/dist/types/common/utils/reduceToText.d.ts +1 -1
- package/dist/types/components/Application/ApplicationViewability.d.ts +36 -0
- package/dist/types/components/Application/index.d.ts +1 -0
- package/dist/types/components/ContextOverlay/ContextMenu.d.ts +1 -1
- package/dist/types/components/Icon/IconButton.d.ts +1 -1
- package/dist/types/components/Structure/TitleSubsection.d.ts +1 -1
- package/dist/types/components/Tabs/Tab.d.ts +2 -2
- package/dist/types/components/TextReducer/TextReducer.d.ts +13 -1
- package/dist/types/components/Typography/InlineText.d.ts +13 -0
- package/dist/types/components/Typography/index.d.ts +1 -0
- package/dist/types/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.d.ts +1 -1
- package/dist/types/extensions/codemirror/tests/codemirrorTestHelper.d.ts +1 -1
- package/package.json +54 -53
- package/src/cmem/ActivityControl/SilkActivityControl.tsx +1 -1
- package/src/cmem/ContentBlobToggler/ContentBlobToggler.tsx +1 -1
- package/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx +66 -18
- package/src/cmem/ContentBlobToggler/stories/StringPreviewContentBlobToggler.stories.tsx +27 -0
- package/src/cmem/ContentBlobToggler/tests/StringPreviewContentBlobToggler.test.tsx +98 -0
- package/src/cmem/react-flow/ReactFlow/ReactFlowV12.tsx +1 -0
- package/src/common/Intent/index.ts +2 -1
- package/src/common/index.ts +6 -2
- package/src/common/utils/reduceToText.tsx +30 -2
- package/src/components/Application/ApplicationViewability.tsx +61 -0
- package/src/components/Application/_content.scss +7 -0
- package/src/components/Application/_header.scss +12 -3
- package/src/components/Application/_viewability.scss +13 -0
- package/src/components/Application/application.scss +1 -0
- package/src/components/Application/index.ts +1 -0
- package/src/components/Application/stories/ApplicationViewability.stories.tsx +37 -0
- package/src/components/Application/tests/ApplicationViewability.test.tsx +43 -0
- package/src/components/AutoSuggestion/tests/ExtendedCodeEditor.test.tsx +1 -1
- package/src/components/Card/card.scss +6 -0
- package/src/components/Checkbox/checkbox.scss +14 -2
- package/src/components/ContentGroup/_contentgroup.scss +9 -0
- package/src/components/ContextOverlay/ContextMenu.tsx +4 -1
- package/src/components/ContextOverlay/ContextOverlay.tsx +74 -24
- package/src/components/ContextOverlay/tests/ContextMenu.test.tsx +43 -0
- package/src/components/ContextOverlay/tests/ContextOverlay.test.tsx +71 -0
- package/src/components/Depiction/depiction.scss +6 -0
- package/src/components/Dialog/stories/Modal.stories.tsx +12 -150
- package/src/components/Dialog/stories/ModalContext.stories.tsx +153 -0
- package/src/components/FlexibleLayout/flexiblelayout.scss +16 -0
- package/src/components/Grid/grid.scss +17 -0
- package/src/components/Grid/stories/Grid.stories.tsx +10 -7
- package/src/components/Grid/stories/GridRow.stories.tsx +13 -7
- package/src/components/Icon/IconButton.tsx +1 -1
- package/src/components/Notification/notification.scss +6 -0
- package/src/components/OverviewItem/overviewitem.scss +9 -0
- package/src/components/OverviewItem/stories/OverviewItem.stories.tsx +28 -0
- package/src/components/OverviewItem/stories/OverviewItemActions.stories.tsx +2 -2
- package/src/components/OverviewItem/stories/OverviewItemDescription.stories.tsx +1 -1
- package/src/components/OverviewItem/stories/OverviewItemLine.stories.tsx +1 -1
- package/src/components/PropertyValuePair/propertyvalue.scss +23 -1
- package/src/components/Separation/separation.scss +6 -0
- package/src/components/Table/table.scss +22 -0
- package/src/components/Tabs/stories/TabTitle.stories.tsx +7 -2
- package/src/components/Tag/stories/TagList.stories.tsx +2 -2
- package/src/components/Tag/tag.scss +19 -9
- package/src/components/TextReducer/TextReducer.stories.tsx +2 -1
- package/src/components/TextReducer/TextReducer.test.tsx +44 -0
- package/src/components/TextReducer/TextReducer.tsx +14 -4
- package/src/components/Typography/InlineText.tsx +24 -0
- package/src/components/Typography/_reset.scss +1 -0
- package/src/components/Typography/index.ts +1 -0
- package/src/components/Typography/stories/InlineText.stories.tsx +27 -0
- package/src/components/Typography/typography.scss +28 -2
- package/src/extensions/codemirror/CodeMirror.tsx +4 -4
- package/src/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.ts +1 -2
- package/src/extensions/codemirror/tests/codemirrorTestHelper.ts +3 -3
- package/src/extensions/react-flow/edges/EdgeNew.tsx +2 -1
- package/src/extensions/react-flow/handles/HandleDefault.tsx +2 -2
- package/src/extensions/react-flow/minimap/MiniMap.tsx +2 -1
- package/src/extensions/react-flow/minimap/MiniMapV12.tsx +1 -1
- package/src/extensions/react-flow/nodes/_nodes.scss +4 -3
- package/src/extensions/react-flow/nodes/nodeUtils.tsx +1 -0
- package/src/extensions/react-flow/nodes/stories/NodeContent.stories.tsx +2 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Meta, StoryFn } from "@storybook/react";
|
|
3
|
+
|
|
4
|
+
import { Markdown, StringPreviewContentBlobToggler } from "../../../index";
|
|
5
|
+
|
|
6
|
+
const config = {
|
|
7
|
+
title: "CMEM/ContentBlobToggler/StringPreview",
|
|
8
|
+
component: StringPreviewContentBlobToggler,
|
|
9
|
+
} as Meta<typeof StringPreviewContentBlobToggler>;
|
|
10
|
+
export default config;
|
|
11
|
+
|
|
12
|
+
const Template: StoryFn<typeof StringPreviewContentBlobToggler> = (args) => (
|
|
13
|
+
<StringPreviewContentBlobToggler {...args} />
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const initialTeststring =
|
|
17
|
+
"A library for GUI elements.\nIn order to create graphical user interfaces, please have look at the documentation at [Github](https://github.com/eccenca/gui-elements).";
|
|
18
|
+
|
|
19
|
+
export const Default = Template.bind({});
|
|
20
|
+
Default.args = {
|
|
21
|
+
content: initialTeststring,
|
|
22
|
+
fullviewContent: <Markdown htmlContentBlockProps={{ large: true }}>{initialTeststring}</Markdown>,
|
|
23
|
+
previewMaxLength: 64,
|
|
24
|
+
renderPreviewAsMarkdown: true,
|
|
25
|
+
toggleExtendText: "show more",
|
|
26
|
+
toggleReduceText: "show less",
|
|
27
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, RenderResult } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import "@testing-library/jest-dom";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
StringPreviewContentBlobToggler,
|
|
8
|
+
StringPreviewContentBlobTogglerProps,
|
|
9
|
+
} from "../StringPreviewContentBlobToggler";
|
|
10
|
+
|
|
11
|
+
import { Default as StringPreviewContentBlobTogglerStory } from "./../stories/StringPreviewContentBlobToggler.stories";
|
|
12
|
+
|
|
13
|
+
describe("StringPreviewContentBlobToggler", () => {
|
|
14
|
+
const textMustExist = (queryByText: RenderResult["queryByText"], text: string) => {
|
|
15
|
+
expect(queryByText(text, { exact: false })).not.toBeNull();
|
|
16
|
+
};
|
|
17
|
+
const textMustNotExist = (queryByText: RenderResult["queryByText"], text: string) => {
|
|
18
|
+
expect(queryByText(text, { exact: false })).toBeNull();
|
|
19
|
+
};
|
|
20
|
+
it("should cut preview and show toggler to extend", () => {
|
|
21
|
+
const { queryByText } = render(
|
|
22
|
+
<StringPreviewContentBlobToggler
|
|
23
|
+
{...(StringPreviewContentBlobTogglerStory.args as StringPreviewContentBlobTogglerProps)}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
textMustExist(queryByText, "A library for GUI elements.");
|
|
27
|
+
textMustNotExist(
|
|
28
|
+
queryByText,
|
|
29
|
+
"In order to create graphical user interfaces, please have look at the documentation at"
|
|
30
|
+
);
|
|
31
|
+
textMustExist(queryByText, "show more");
|
|
32
|
+
});
|
|
33
|
+
it("should display full view if `startExtended` is enabled, and show toggler to reduce", () => {
|
|
34
|
+
const { queryByText } = render(
|
|
35
|
+
<StringPreviewContentBlobToggler
|
|
36
|
+
{...(StringPreviewContentBlobTogglerStory.args as StringPreviewContentBlobTogglerProps)}
|
|
37
|
+
startExtended
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
textMustExist(
|
|
41
|
+
queryByText,
|
|
42
|
+
"In order to create graphical user interfaces, please have look at the documentation at"
|
|
43
|
+
);
|
|
44
|
+
textMustExist(queryByText, "show less");
|
|
45
|
+
});
|
|
46
|
+
it('should display only first content line on `useOnly={"firstNonEmptyLine"}`', () => {
|
|
47
|
+
const { queryByText } = render(
|
|
48
|
+
<StringPreviewContentBlobToggler
|
|
49
|
+
{...(StringPreviewContentBlobTogglerStory.args as StringPreviewContentBlobTogglerProps)}
|
|
50
|
+
useOnly={"firstNonEmptyLine"}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
textMustExist(queryByText, "A library for GUI elements.");
|
|
54
|
+
textMustNotExist(queryByText, "In order to create");
|
|
55
|
+
});
|
|
56
|
+
it('should use first Markdown paragraph as preview content on `useOnly={"firstMarkdownSection"}` but shorten it', () => {
|
|
57
|
+
const { queryByText } = render(
|
|
58
|
+
<StringPreviewContentBlobToggler
|
|
59
|
+
{...(StringPreviewContentBlobTogglerStory.args as StringPreviewContentBlobTogglerProps)}
|
|
60
|
+
useOnly={"firstMarkdownSection"}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
textMustExist(queryByText, "A library for GUI elements.");
|
|
64
|
+
textMustExist(queryByText, "In order to create");
|
|
65
|
+
textMustNotExist(queryByText, "please have look at the documentation at");
|
|
66
|
+
});
|
|
67
|
+
it("should display full preview and no toggler if content is short enough", () => {
|
|
68
|
+
const { queryByText } = render(
|
|
69
|
+
<StringPreviewContentBlobToggler
|
|
70
|
+
{...(StringPreviewContentBlobTogglerStory.args as StringPreviewContentBlobTogglerProps)}
|
|
71
|
+
previewMaxLength={144}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
textMustExist(queryByText, "A library for GUI elements.");
|
|
75
|
+
textMustExist(
|
|
76
|
+
queryByText,
|
|
77
|
+
"In order to create graphical user interfaces, please have look at the documentation at"
|
|
78
|
+
);
|
|
79
|
+
textMustNotExist(queryByText, "https://github.com/"); // test if Markdown was rendered
|
|
80
|
+
textMustNotExist(queryByText, "show more");
|
|
81
|
+
});
|
|
82
|
+
it("should not use Markdown rendering on `renderPreviewAsMarkdown={false}`", () => {
|
|
83
|
+
const { queryByText } = render(
|
|
84
|
+
<StringPreviewContentBlobToggler
|
|
85
|
+
{...(StringPreviewContentBlobTogglerStory.args as StringPreviewContentBlobTogglerProps)}
|
|
86
|
+
previewMaxLength={144}
|
|
87
|
+
renderPreviewAsMarkdown={false}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
90
|
+
textMustExist(queryByText, "A library for GUI elements.");
|
|
91
|
+
textMustExist(
|
|
92
|
+
queryByText,
|
|
93
|
+
"In order to create graphical user interfaces, please have look at the documentation at"
|
|
94
|
+
);
|
|
95
|
+
textMustExist(queryByText, "https://github.com/"); // test if Markdown was rendered
|
|
96
|
+
textMustExist(queryByText, "show more");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
2
1
|
import { Intent as BlueprintIntent } from "@blueprintjs/core";
|
|
3
2
|
|
|
3
|
+
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
4
|
+
|
|
4
5
|
export type IntentBlueprint = BlueprintIntent;
|
|
5
6
|
export const DefinitionsBlueprint = BlueprintIntent;
|
|
6
7
|
|
package/src/common/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { decode } from "he";
|
|
2
|
+
|
|
1
3
|
import { invisibleZeroWidthCharacters } from "./utils/characters";
|
|
2
4
|
import { colorCalculateDistance } from "./utils/colorCalculateDistance";
|
|
3
5
|
import decideContrastColorValue from "./utils/colorDecideContrastvalue";
|
|
@@ -6,7 +8,8 @@ import getColorConfiguration from "./utils/getColorConfiguration";
|
|
|
6
8
|
import { getScrollParent } from "./utils/getScrollParent";
|
|
7
9
|
import { getGlobalVar, setGlobalVar } from "./utils/globalVars";
|
|
8
10
|
import { openInNewTab } from "./utils/openInNewTab";
|
|
9
|
-
import { reduceToText } from "./utils/reduceToText"
|
|
11
|
+
import { reduceToText } from "./utils/reduceToText";
|
|
12
|
+
export type { DecodeOptions as DecodeHtmlEntitiesOptions } from "he";
|
|
10
13
|
export type { IntentTypes as IntentBaseTypes } from "./Intent";
|
|
11
14
|
|
|
12
15
|
export const utils = {
|
|
@@ -20,5 +23,6 @@ export const utils = {
|
|
|
20
23
|
getScrollParent,
|
|
21
24
|
getEnabledColorsFromPalette,
|
|
22
25
|
textToColorHash,
|
|
23
|
-
reduceToText
|
|
26
|
+
reduceToText,
|
|
27
|
+
decodeHtmlEntities: decode,
|
|
24
28
|
};
|
|
@@ -3,6 +3,7 @@ import { renderToString } from "react-dom/server";
|
|
|
3
3
|
import * as ReactIs from "react-is";
|
|
4
4
|
|
|
5
5
|
import { TextReducerProps } from "./../../components/TextReducer/TextReducer";
|
|
6
|
+
import { DecodeHtmlEntitiesOptions, utils } from "./../";
|
|
6
7
|
|
|
7
8
|
export interface ReduceToTextFuncType {
|
|
8
9
|
(
|
|
@@ -10,12 +11,12 @@ export interface ReduceToTextFuncType {
|
|
|
10
11
|
* Component or text to reduce HTML markup content to plain text.
|
|
11
12
|
*/
|
|
12
13
|
input: React.ReactNode | React.ReactNode[] | string,
|
|
13
|
-
options?: Pick<TextReducerProps, "maxNodes" | "maxLength">
|
|
14
|
+
options?: Pick<TextReducerProps, "maxNodes" | "maxLength" | "decodeHtmlEntities" | "decodeHtmlEntitiesOptions">
|
|
14
15
|
): string;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const reduceToText: ReduceToTextFuncType = (input, options) => {
|
|
18
|
-
const { maxNodes, maxLength } = options || {};
|
|
19
|
+
const { maxNodes, maxLength, decodeHtmlEntities } = options || {};
|
|
19
20
|
const content: React.ReactNode | React.ReactNode[] = input;
|
|
20
21
|
let nodeCount = 0;
|
|
21
22
|
|
|
@@ -46,6 +47,33 @@ export const reduceToText: ReduceToTextFuncType = (input, options) => {
|
|
|
46
47
|
// Basic HTML cleanup
|
|
47
48
|
text = text.replace(/<[^\s][^>]*>/g, "").replace(/\n/g, " ");
|
|
48
49
|
|
|
50
|
+
if (decodeHtmlEntities) {
|
|
51
|
+
const decodeDefaultOptions = {
|
|
52
|
+
isAttributeValue: true,
|
|
53
|
+
strict: true,
|
|
54
|
+
} as DecodeHtmlEntitiesOptions;
|
|
55
|
+
let decodeErrors = 0;
|
|
56
|
+
// we decode in pieces to apply some error tolerance even in strict mode
|
|
57
|
+
text = text
|
|
58
|
+
.split(" ")
|
|
59
|
+
.map((value) => {
|
|
60
|
+
try {
|
|
61
|
+
return utils.decodeHtmlEntities(value, {
|
|
62
|
+
...decodeDefaultOptions,
|
|
63
|
+
...options?.decodeHtmlEntitiesOptions,
|
|
64
|
+
});
|
|
65
|
+
} catch {
|
|
66
|
+
decodeErrors++;
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
.join(" ");
|
|
71
|
+
if (decodeErrors > 0) {
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.warn(`${decodeErrors} parse error(s) for decodeHtmlEntities, return un-decoded text`, text);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
49
77
|
if (typeof maxLength === "number") {
|
|
50
78
|
text = text.slice(0, maxLength);
|
|
51
79
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
|
|
4
|
+
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
5
|
+
|
|
6
|
+
type media = "print" | "screen";
|
|
7
|
+
|
|
8
|
+
interface ApplicationViewabilityShow {
|
|
9
|
+
/**
|
|
10
|
+
* Show on media type.
|
|
11
|
+
* If used, `hide` cannot be set.
|
|
12
|
+
*/
|
|
13
|
+
show: media;
|
|
14
|
+
hide?: never;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ApplicationViewabilityHide {
|
|
18
|
+
/**
|
|
19
|
+
* Hide on media type.
|
|
20
|
+
* If used, `show` cannot be set.
|
|
21
|
+
*/
|
|
22
|
+
hide: media;
|
|
23
|
+
show?: never;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ApplicationViewabilityUndecided {
|
|
27
|
+
/**
|
|
28
|
+
* Only one child allowed.
|
|
29
|
+
* Need to process the `className` property.
|
|
30
|
+
*/
|
|
31
|
+
children: React.ReactElement<{ className?: string }>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type ApplicationViewabilityProps = ApplicationViewabilityUndecided &
|
|
35
|
+
(ApplicationViewabilityShow | ApplicationViewabilityHide);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sets the viewability of the the contained element regarding media.
|
|
39
|
+
* Can be used to hide elements, e.g. when the page is printed.
|
|
40
|
+
*/
|
|
41
|
+
export const ApplicationViewability = ({ children, show, hide }: ApplicationViewabilityProps) => {
|
|
42
|
+
if (!show && !hide) {
|
|
43
|
+
return children;
|
|
44
|
+
}
|
|
45
|
+
if (show === hide) {
|
|
46
|
+
// eslint-disable-next-line no-console
|
|
47
|
+
console.warn("`<ApplicationViewability/>` used with same media type for `hide` and `show`.");
|
|
48
|
+
return children;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const enhancedClone = React.cloneElement(children, {
|
|
52
|
+
className: classNames(children.props.className, {
|
|
53
|
+
[`${eccgui}-application__hide--${hide}`]: hide,
|
|
54
|
+
[`${eccgui}-application__show--${show}`]: show,
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return enhancedClone;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default ApplicationViewability;
|
|
@@ -25,3 +25,10 @@ $ui-02: $eccgui-color-workspace-background !default;
|
|
|
25
25
|
.#{$eccgui}-application__content--railsidebar {
|
|
26
26
|
margin-left: mini-units(8);
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
@media print {
|
|
30
|
+
.#{$eccgui}-application__content {
|
|
31
|
+
padding: $eccgui-size-block-whitespace 0 0 0 !important;
|
|
32
|
+
margin: 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -98,10 +98,10 @@ span.#{$prefix}--header__name {
|
|
|
98
98
|
.#{$eccgui}-application__title--content {
|
|
99
99
|
display: inline-block;
|
|
100
100
|
overflow: hidden;
|
|
101
|
+
text-overflow: ellipsis;
|
|
101
102
|
font-size: $eccgui-size-typo-caption;
|
|
102
103
|
font-weight: $eccgui-font-weight-bold;
|
|
103
104
|
line-height: $eccgui-size-typo-caption-lineheight;
|
|
104
|
-
text-overflow: ellipsis;
|
|
105
105
|
letter-spacing: $eccgui-font-spacing-wide;
|
|
106
106
|
white-space: nowrap;
|
|
107
107
|
}
|
|
@@ -122,7 +122,7 @@ span.#{$prefix}--header__name {
|
|
|
122
122
|
height: auto;
|
|
123
123
|
max-height: mini-units(5);
|
|
124
124
|
padding: 0;
|
|
125
|
-
margin: mini-units(1.4) 0 mini-units(1.6)
|
|
125
|
+
margin: mini-units(1.4) 0 mini-units(1.6);
|
|
126
126
|
vertical-align: middle;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -195,9 +195,9 @@ a.#{$prefix}--header__menu-item:active {
|
|
|
195
195
|
.#{$prefix}--header__action.#{$prefix}--btn--primary:focus,
|
|
196
196
|
a.#{$prefix}--header__name:focus,
|
|
197
197
|
a.#{$prefix}--header__menu-item:focus {
|
|
198
|
-
border: none;
|
|
199
198
|
outline: 1px dotted $shell-header-focus;
|
|
200
199
|
outline-offset: -1px;
|
|
200
|
+
border: none;
|
|
201
201
|
box-shadow: none;
|
|
202
202
|
}
|
|
203
203
|
.#{$prefix}--header__menu-title[aria-expanded="true"] {
|
|
@@ -267,3 +267,12 @@ a.#{$prefix}--header__menu-item:focus > svg {
|
|
|
267
267
|
margin: 0;
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
|
+
|
|
271
|
+
@media print {
|
|
272
|
+
.#{$eccgui}-application__header {
|
|
273
|
+
position: relative;
|
|
274
|
+
& > :not(.#{$eccgui}-workspace__header) {
|
|
275
|
+
display: none;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
@media print {
|
|
2
|
+
.#{eccgui}-application__hide--print,
|
|
3
|
+
.#{eccgui}-application__show--screen {
|
|
4
|
+
display: none !important;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@media screen {
|
|
9
|
+
.#{eccgui}-application__hide--screen,
|
|
10
|
+
.#{eccgui}-application__show--print {
|
|
11
|
+
display: none !important;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { LoremIpsum } from "react-lorem-ipsum";
|
|
3
|
+
import { Meta, StoryFn } from "@storybook/react";
|
|
4
|
+
|
|
5
|
+
import { ApplicationViewability } from "../../../index";
|
|
6
|
+
export default {
|
|
7
|
+
title: "Components/Application/Viewability",
|
|
8
|
+
component: ApplicationViewability,
|
|
9
|
+
argTypes: {
|
|
10
|
+
children: {
|
|
11
|
+
control: false,
|
|
12
|
+
},
|
|
13
|
+
hide: {
|
|
14
|
+
control: {
|
|
15
|
+
type: "radio",
|
|
16
|
+
},
|
|
17
|
+
options: ["print", "screen"],
|
|
18
|
+
},
|
|
19
|
+
show: {
|
|
20
|
+
control: {
|
|
21
|
+
type: "radio",
|
|
22
|
+
},
|
|
23
|
+
options: ["print", "screen"],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
} as Meta<typeof ApplicationViewability>;
|
|
27
|
+
|
|
28
|
+
const TemplateBasicExample: StoryFn<typeof ApplicationViewability> = (args) => <ApplicationViewability {...args} />;
|
|
29
|
+
|
|
30
|
+
export const Default = TemplateBasicExample.bind({});
|
|
31
|
+
Default.args = {
|
|
32
|
+
children: (
|
|
33
|
+
<div>
|
|
34
|
+
<LoremIpsum random={false} />
|
|
35
|
+
</div>
|
|
36
|
+
),
|
|
37
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { expect } from "@storybook/test";
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
import "@testing-library/jest-dom";
|
|
6
|
+
|
|
7
|
+
import { ApplicationViewability, ApplicationViewabilityProps, CLASSPREFIX as eccgui } from "../../../index";
|
|
8
|
+
|
|
9
|
+
import { Default as ApplicationViewabilityStory } from "./../stories/ApplicationViewability.stories";
|
|
10
|
+
|
|
11
|
+
const applyViewabilityAndCheckClass = (props: Omit<ApplicationViewabilityProps, "children">) => {
|
|
12
|
+
const { container } = render(<ApplicationViewability {...ApplicationViewabilityStory.args} {...props} />);
|
|
13
|
+
const element = container.getElementsByClassName(
|
|
14
|
+
props.hide ? `${eccgui}-application__hide--${props.hide}` : `${eccgui}-application__show--${props.show}`
|
|
15
|
+
);
|
|
16
|
+
expect(element.length).toBe(1);
|
|
17
|
+
return element;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe("ApplicationViewability", () => {
|
|
21
|
+
it("should be visible on `show=screen`", () => {
|
|
22
|
+
applyViewabilityAndCheckClass({ show: "screen" });
|
|
23
|
+
/**
|
|
24
|
+
* Currently we cannot really test visibility via jest if it is defined by S/CSS rules because those styles are not known.
|
|
25
|
+
* Looks like it is not too easy to include and test them.
|
|
26
|
+
* So we only test for the correct CSS class.
|
|
27
|
+
*/
|
|
28
|
+
// console.log(window.getComputedStyle(element.item(0)??new Element).getPropertyValue("display"));
|
|
29
|
+
// waitFor(() => expect(element).toBeVisible());
|
|
30
|
+
});
|
|
31
|
+
it("should not be visible on `hide=screen`", () => {
|
|
32
|
+
applyViewabilityAndCheckClass({ hide: "screen" });
|
|
33
|
+
// waitFor(() => expect(element).not.toBeVisible());
|
|
34
|
+
});
|
|
35
|
+
it("should be visible on `hide=print`", () => {
|
|
36
|
+
applyViewabilityAndCheckClass({ hide: "print" });
|
|
37
|
+
// waitFor(() => expect(element).toBeVisible());
|
|
38
|
+
});
|
|
39
|
+
it("should not be visible on `show=print`", () => {
|
|
40
|
+
applyViewabilityAndCheckClass({ show: "print" });
|
|
41
|
+
// waitFor(() => expect(element).not.toBeVisible());
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -24,9 +24,15 @@ $control-indicator-spacing: $eccgui-size-inline-whitespace !default;
|
|
|
24
24
|
// $switch-background-color-active: rgba($gray1, 0.5) !default;
|
|
25
25
|
// $switch-background-color-disabled: $button-background-color-disabled !default;
|
|
26
26
|
$switch-checked-background-color: $eccgui-color-accent !default;
|
|
27
|
-
$switch-checked-background-color-hover: eccgui-color-rgba(
|
|
27
|
+
$switch-checked-background-color-hover: eccgui-color-rgba(
|
|
28
|
+
$switch-checked-background-color,
|
|
29
|
+
$eccgui-opacity-narrow
|
|
30
|
+
) !default;
|
|
28
31
|
$switch-checked-background-color-active: $switch-checked-background-color-hover !default;
|
|
29
|
-
$switch-checked-background-color-disabled: eccgui-color-rgba(
|
|
32
|
+
$switch-checked-background-color-disabled: eccgui-color-rgba(
|
|
33
|
+
$switch-checked-background-color,
|
|
34
|
+
$eccgui-opacity-disabled
|
|
35
|
+
) !default;
|
|
30
36
|
|
|
31
37
|
@import "~@blueprintjs/core/src/components/forms/controls"; // Checkbox, Radio, Switch
|
|
32
38
|
|
|
@@ -73,3 +79,9 @@ $switch-checked-background-color-disabled: eccgui-color-rgba($switch-checked-bac
|
|
|
73
79
|
display: inline-block;
|
|
74
80
|
vertical-align: text-top;
|
|
75
81
|
}
|
|
82
|
+
|
|
83
|
+
@media print {
|
|
84
|
+
.#{$ns}-control {
|
|
85
|
+
print-color-adjust: exact;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -60,3 +60,12 @@ $eccgui-color-scontentgroup-border-sub: eccgui-color-rgba(
|
|
|
60
60
|
flex-shrink: 1;
|
|
61
61
|
width: 100%;
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
@media print {
|
|
65
|
+
.#{$eccgui}-contentgroup__header__options {
|
|
66
|
+
display: none;
|
|
67
|
+
}
|
|
68
|
+
.#{$eccgui}-contentgroup--border-sub::after {
|
|
69
|
+
print-color-adjust: exact;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -66,6 +66,8 @@ export const ContextMenu = ({
|
|
|
66
66
|
so by default we use the title attribute instead of Tooltip. */
|
|
67
67
|
tooltipAsTitle = true,
|
|
68
68
|
preventPlaceholder = false,
|
|
69
|
+
"data-test-id": dataTestId,
|
|
70
|
+
"data-testid": dataTestid,
|
|
69
71
|
...restProps
|
|
70
72
|
}: ContextMenuProps) => {
|
|
71
73
|
const toggleButton =
|
|
@@ -76,7 +78,8 @@ export const ContextMenu = ({
|
|
|
76
78
|
text={togglerText}
|
|
77
79
|
large={togglerLarge}
|
|
78
80
|
disabled={!!disabled}
|
|
79
|
-
data-test-id={
|
|
81
|
+
data-test-id={dataTestId ?? undefined}
|
|
82
|
+
data-testid={dataTestid ?? undefined}
|
|
80
83
|
/>
|
|
81
84
|
) : (
|
|
82
85
|
(togglerElement as ReactElement)
|
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
2
|
import {
|
|
3
3
|
Classes as BlueprintClasses,
|
|
4
4
|
Popover as BlueprintPopover,
|
|
5
|
+
PopoverInteractionKind as InteractionKind,
|
|
5
6
|
PopoverProps as BlueprintPopoverProps,
|
|
6
7
|
Utils as BlueprintUtils,
|
|
7
8
|
} from "@blueprintjs/core";
|
|
@@ -37,9 +38,10 @@ export const ContextOverlay = ({
|
|
|
37
38
|
usePlaceholder = false,
|
|
38
39
|
...otherPopoverProps
|
|
39
40
|
}: ContextOverlayProps) => {
|
|
40
|
-
const placeholderRef = React.useRef(null);
|
|
41
|
-
const eventMemory = React.useRef<undefined | "
|
|
41
|
+
const placeholderRef = React.useRef<HTMLElement>(null);
|
|
42
|
+
const eventMemory = React.useRef<undefined | "mouseenter" | "focusin" | "click">(undefined);
|
|
42
43
|
const swapDelay = React.useRef<null | NodeJS.Timeout>(null);
|
|
44
|
+
const interactionKind = React.useRef<InteractionKind>(otherPopoverProps.interactionKind ?? InteractionKind.CLICK);
|
|
43
45
|
const swapDelayTime = 15;
|
|
44
46
|
const [placeholder, setPlaceholder] = React.useState<boolean>(
|
|
45
47
|
// use placeholder only for "simple" overlays without special states
|
|
@@ -50,37 +52,85 @@ export const ContextOverlay = ({
|
|
|
50
52
|
usePlaceholder
|
|
51
53
|
);
|
|
52
54
|
|
|
55
|
+
const swap = (ev: MouseEvent | globalThis.FocusEvent) => {
|
|
56
|
+
const waitForClick =
|
|
57
|
+
interactionKind.current === InteractionKind.CLICK ||
|
|
58
|
+
interactionKind.current === InteractionKind.CLICK_TARGET_ONLY;
|
|
59
|
+
|
|
60
|
+
if (swapDelay.current) {
|
|
61
|
+
clearTimeout(swapDelay.current);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const replacePlaceholder = () => {
|
|
65
|
+
eventMemory.current = ev.type as "mouseenter" | "focusin" | "click";
|
|
66
|
+
setPlaceholder(false);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (waitForClick) {
|
|
70
|
+
ev.stopImmediatePropagation();
|
|
71
|
+
replacePlaceholder();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
swapDelay.current = setTimeout(
|
|
76
|
+
replacePlaceholder,
|
|
77
|
+
// we delay the swap for hover/focus to prevent unwanted effects
|
|
78
|
+
// (e.g. event hickup after replacing elements when it is not really necessary)
|
|
79
|
+
swapDelayTime
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
53
83
|
React.useEffect(() => {
|
|
84
|
+
interactionKind.current = otherPopoverProps.interactionKind ?? InteractionKind.CLICK;
|
|
85
|
+
const waitForClick =
|
|
86
|
+
interactionKind.current === InteractionKind.CLICK ||
|
|
87
|
+
interactionKind.current === InteractionKind.CLICK_TARGET_ONLY;
|
|
88
|
+
const removeEvents = () => {
|
|
89
|
+
if (placeholderRef.current) {
|
|
90
|
+
placeholderRef.current.removeEventListener("click", swap);
|
|
91
|
+
placeholderRef.current.removeEventListener("mouseenter", swap);
|
|
92
|
+
placeholderRef.current.removeEventListener("focusin", swap);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
};
|
|
54
96
|
if (placeholderRef.current) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
eventMemory.current = ev.type === "focusin" ? "afterfocus" : "afterhover";
|
|
63
|
-
setPlaceholder(false);
|
|
64
|
-
}, swapDelayTime);
|
|
65
|
-
};
|
|
66
|
-
(placeholderRef.current as HTMLElement).addEventListener("mouseenter", swap);
|
|
67
|
-
(placeholderRef.current as HTMLElement).addEventListener("focusin", swap);
|
|
97
|
+
removeEvents(); // remove events in case of interaction kind changed during existence
|
|
98
|
+
if (waitForClick) {
|
|
99
|
+
placeholderRef.current.addEventListener("click", swap);
|
|
100
|
+
} else {
|
|
101
|
+
placeholderRef.current.addEventListener("mouseenter", swap);
|
|
102
|
+
placeholderRef.current.addEventListener("focusin", swap);
|
|
103
|
+
}
|
|
68
104
|
return () => {
|
|
69
|
-
|
|
70
|
-
(placeholderRef.current as HTMLElement).removeEventListener("mouseenter", swap);
|
|
71
|
-
(placeholderRef.current as HTMLElement).removeEventListener("focusin", swap);
|
|
72
|
-
}
|
|
105
|
+
removeEvents();
|
|
73
106
|
};
|
|
74
107
|
}
|
|
75
108
|
return () => {};
|
|
76
|
-
}, [!!placeholderRef.current]);
|
|
109
|
+
}, [!!placeholderRef.current, otherPopoverProps.interactionKind]);
|
|
77
110
|
|
|
78
111
|
const refocus = React.useCallback((node) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
112
|
+
const target = node?.targetRef.current.children[0];
|
|
113
|
+
if (!eventMemory.current || !target) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
switch (eventMemory.current) {
|
|
117
|
+
case "focusin":
|
|
82
118
|
target.focus();
|
|
83
|
-
|
|
119
|
+
break;
|
|
120
|
+
case "click":
|
|
121
|
+
target.click();
|
|
122
|
+
break;
|
|
123
|
+
case "mouseenter":
|
|
124
|
+
// re-check if the cursor is still over the element after swapping the placeholder before triggering the event to bubble up
|
|
125
|
+
(target as HTMLElement).addEventListener(
|
|
126
|
+
"mouseover",
|
|
127
|
+
() => (target as HTMLElement).dispatchEvent(new MouseEvent("mouseover", { bubbles: true })),
|
|
128
|
+
{
|
|
129
|
+
capture: true,
|
|
130
|
+
once: true,
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
break;
|
|
84
134
|
}
|
|
85
135
|
}, []);
|
|
86
136
|
|