@eccenca/gui-elements 22.0.1 → 22.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 +20 -4
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +18 -14
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js +58 -30
- package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
- package/dist/cjs/cmem/ActivityControl/index.js +11 -0
- package/dist/cjs/cmem/ActivityControl/index.js.map +1 -0
- package/dist/cjs/cmem/ContentBlobToggler/index.js +8 -0
- package/dist/cjs/cmem/ContentBlobToggler/index.js.map +1 -0
- package/dist/cjs/cmem/index.js +14 -13
- package/dist/cjs/cmem/index.js.map +1 -1
- package/dist/cjs/cmem/react-flow/ReactFlow/ReactFlow.js +57 -0
- package/dist/cjs/cmem/react-flow/ReactFlow/ReactFlow.js.map +1 -0
- package/dist/cjs/cmem/react-flow/configuration/_colors-graph.module.scss +35 -0
- package/dist/cjs/cmem/react-flow/configuration/_colors-linking.module.scss +29 -0
- package/dist/cjs/cmem/react-flow/configuration/_colors-workflow.module.scss +22 -0
- package/dist/cjs/cmem/react-flow/configuration/graph.js +31 -0
- package/dist/cjs/cmem/react-flow/configuration/graph.js.map +1 -0
- package/dist/cjs/cmem/react-flow/configuration/linking.js +29 -0
- package/dist/cjs/cmem/react-flow/configuration/linking.js.map +1 -0
- package/dist/cjs/cmem/react-flow/configuration/unspecified.js +15 -0
- package/dist/cjs/cmem/react-flow/configuration/unspecified.js.map +1 -0
- package/dist/cjs/cmem/react-flow/configuration/workflow.js +27 -0
- package/dist/cjs/cmem/react-flow/configuration/workflow.js.map +1 -0
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +285 -0
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -0
- package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js +81 -0
- package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js.map +1 -0
- package/dist/cjs/components/AutoSuggestion/SingleLineCodeEditor.js +46 -0
- package/dist/cjs/components/AutoSuggestion/SingleLineCodeEditor.js.map +1 -0
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +8 -12
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +1 -1
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/ContextOverlay/ContextMenu.js +1 -1
- package/dist/cjs/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/cjs/components/Dialog/Modal.js +2 -2
- package/dist/cjs/components/Dialog/Modal.js.map +1 -1
- package/dist/cjs/components/Dialog/SimpleDialog.js.map +1 -1
- package/dist/cjs/components/Icon/Icon.js.map +1 -1
- package/dist/cjs/components/Icon/canonicalIconNames.js +30 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/cjs/components/MultiSelect/MultiSelect.js +219 -0
- package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -0
- package/dist/cjs/components/Tabs/Tab.js +95 -0
- package/dist/cjs/components/Tabs/Tab.js.map +1 -0
- package/dist/cjs/components/Tabs/TabTitle.js +20 -0
- package/dist/cjs/components/Tabs/TabTitle.js.map +1 -0
- package/dist/cjs/components/Tabs/Tabs.js +29 -19
- package/dist/cjs/components/Tabs/Tabs.js.map +1 -1
- package/dist/cjs/components/Tag/Tag.js +7 -1
- package/dist/cjs/components/Tag/Tag.js.map +1 -1
- package/dist/cjs/extensions/react-flow/edges/utils.js +96 -4
- package/dist/cjs/extensions/react-flow/edges/utils.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js +5 -4
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeContentExtension.js +2 -2
- package/dist/cjs/extensions/react-flow/nodes/NodeContentExtension.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeTools.js.map +1 -1
- package/dist/cjs/index.js +17 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/legacy-replacements/Button/AffirmativeButton.js +25 -0
- package/dist/cjs/legacy-replacements/Button/AffirmativeButton.js.map +1 -0
- package/dist/cjs/legacy-replacements/Button/Button.js +61 -0
- package/dist/cjs/legacy-replacements/Button/Button.js.map +1 -0
- package/dist/cjs/legacy-replacements/Button/DismissiveButton.js +25 -0
- package/dist/cjs/legacy-replacements/Button/DismissiveButton.js.map +1 -0
- package/dist/cjs/legacy-replacements/Button/DisruptiveButton.js +25 -0
- package/dist/cjs/legacy-replacements/Button/DisruptiveButton.js.map +1 -0
- package/dist/cjs/legacy-replacements/Checkbox/Checkbox.js +55 -0
- package/dist/cjs/legacy-replacements/Checkbox/Checkbox.js.map +1 -0
- package/dist/cjs/legacy-replacements/Radio/RadioButton.js +48 -0
- package/dist/cjs/legacy-replacements/Radio/RadioButton.js.map +1 -0
- package/dist/cjs/legacy-replacements/Tabs/Tabs.js +36 -0
- package/dist/cjs/legacy-replacements/Tabs/Tabs.js.map +1 -0
- package/dist/cjs/legacy-replacements/TextField/TextField.js +75 -0
- package/dist/cjs/legacy-replacements/TextField/TextField.js.map +1 -0
- package/dist/cjs/legacy-replacements/index.js +22 -0
- package/dist/cjs/legacy-replacements/index.js.map +1 -0
- package/dist/es5/cmem/ActivityControl/ActivityControlWidget.js +19 -15
- package/dist/es5/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/es5/cmem/ActivityControl/SilkActivityControl.js +56 -30
- package/dist/es5/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
- package/dist/es5/cmem/ActivityControl/index.js +4 -0
- package/dist/es5/cmem/ActivityControl/index.js.map +1 -0
- package/dist/es5/cmem/ContentBlobToggler/index.js +3 -0
- package/dist/es5/cmem/ContentBlobToggler/index.js.map +1 -0
- package/dist/es5/cmem/index.js +6 -8
- package/dist/es5/cmem/index.js.map +1 -1
- package/dist/es5/cmem/react-flow/ReactFlow/ReactFlow.js +43 -0
- package/dist/es5/cmem/react-flow/ReactFlow/ReactFlow.js.map +1 -0
- package/dist/es5/cmem/react-flow/configuration/_colors-graph.module.scss +35 -0
- package/dist/es5/cmem/react-flow/configuration/_colors-linking.module.scss +29 -0
- package/dist/es5/cmem/react-flow/configuration/_colors-workflow.module.scss +22 -0
- package/dist/es5/cmem/react-flow/configuration/graph.js +23 -0
- package/dist/es5/cmem/react-flow/configuration/graph.js.map +1 -0
- package/dist/es5/cmem/react-flow/configuration/linking.js +21 -0
- package/dist/es5/cmem/react-flow/configuration/linking.js.map +1 -0
- package/dist/es5/cmem/react-flow/configuration/unspecified.js +12 -0
- package/dist/es5/cmem/react-flow/configuration/unspecified.js.map +1 -0
- package/dist/es5/cmem/react-flow/configuration/workflow.js +19 -0
- package/dist/es5/cmem/react-flow/configuration/workflow.js.map +1 -0
- package/dist/es5/components/AutoSuggestion/AutoSuggestion.js +347 -0
- package/dist/es5/components/AutoSuggestion/AutoSuggestion.js.map +1 -0
- package/dist/es5/components/AutoSuggestion/AutoSuggestionList.js +93 -0
- package/dist/es5/components/AutoSuggestion/AutoSuggestionList.js.map +1 -0
- package/dist/es5/components/AutoSuggestion/SingleLineCodeEditor.js +42 -0
- package/dist/es5/components/AutoSuggestion/SingleLineCodeEditor.js.map +1 -0
- package/dist/es5/components/AutocompleteField/AutoCompleteField.js +8 -12
- package/dist/es5/components/AutocompleteField/AutoCompleteField.js.map +1 -1
- package/dist/es5/components/Button/Button.js.map +1 -1
- package/dist/es5/components/ContextOverlay/ContextMenu.js +1 -1
- package/dist/es5/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/es5/components/Dialog/Modal.js +2 -2
- package/dist/es5/components/Dialog/Modal.js.map +1 -1
- package/dist/es5/components/Dialog/SimpleDialog.js.map +1 -1
- package/dist/es5/components/Icon/Icon.js.map +1 -1
- package/dist/es5/components/Icon/canonicalIconNames.js +30 -0
- package/dist/es5/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/es5/components/MultiSelect/MultiSelect.js +301 -0
- package/dist/es5/components/MultiSelect/MultiSelect.js.map +1 -0
- package/dist/es5/components/Tabs/Tab.js +98 -0
- package/dist/es5/components/Tabs/Tab.js.map +1 -0
- package/dist/es5/components/Tabs/TabTitle.js +16 -0
- package/dist/es5/components/Tabs/TabTitle.js.map +1 -0
- package/dist/es5/components/Tabs/Tabs.js +11 -21
- package/dist/es5/components/Tabs/Tabs.js.map +1 -1
- package/dist/es5/components/Tag/Tag.js +7 -1
- package/dist/es5/components/Tag/Tag.js.map +1 -1
- package/dist/es5/extensions/react-flow/edges/utils.js +99 -5
- package/dist/es5/extensions/react-flow/edges/utils.js.map +1 -1
- package/dist/es5/extensions/react-flow/nodes/NodeContent.js +21 -4
- package/dist/es5/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/es5/extensions/react-flow/nodes/NodeContentExtension.js +2 -2
- package/dist/es5/extensions/react-flow/nodes/NodeContentExtension.js.map +1 -1
- package/dist/es5/extensions/react-flow/nodes/NodeTools.js.map +1 -1
- package/dist/es5/index.js +9 -2
- package/dist/es5/index.js.map +1 -1
- package/dist/es5/legacy-replacements/Button/AffirmativeButton.js +29 -0
- package/dist/es5/legacy-replacements/Button/AffirmativeButton.js.map +1 -0
- package/dist/es5/legacy-replacements/Button/Button.js +65 -0
- package/dist/es5/legacy-replacements/Button/Button.js.map +1 -0
- package/dist/es5/legacy-replacements/Button/DismissiveButton.js +29 -0
- package/dist/es5/legacy-replacements/Button/DismissiveButton.js.map +1 -0
- package/dist/es5/legacy-replacements/Button/DisruptiveButton.js +29 -0
- package/dist/es5/legacy-replacements/Button/DisruptiveButton.js.map +1 -0
- package/dist/es5/legacy-replacements/Checkbox/Checkbox.js +59 -0
- package/dist/es5/legacy-replacements/Checkbox/Checkbox.js.map +1 -0
- package/dist/es5/legacy-replacements/Radio/RadioButton.js +52 -0
- package/dist/es5/legacy-replacements/Radio/RadioButton.js.map +1 -0
- package/dist/es5/legacy-replacements/Tabs/Tabs.js +40 -0
- package/dist/es5/legacy-replacements/Tabs/Tabs.js.map +1 -0
- package/dist/es5/legacy-replacements/TextField/TextField.js +81 -0
- package/dist/es5/legacy-replacements/TextField/TextField.js.map +1 -0
- package/dist/es5/legacy-replacements/index.js +10 -0
- package/dist/es5/legacy-replacements/index.js.map +1 -0
- package/dist/types/cmem/ActivityControl/ActivityControlTypes.d.ts +2 -1
- package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +39 -2
- package/dist/types/cmem/ActivityControl/SilkActivityControl.d.ts +16 -3
- package/dist/types/cmem/ActivityControl/index.d.ts +3 -0
- package/dist/types/cmem/ContentBlobToggler/index.d.ts +2 -0
- package/dist/types/cmem/index.d.ts +8 -8
- package/dist/types/cmem/react-flow/ReactFlow/ReactFlow.d.ts +14 -0
- package/dist/types/cmem/react-flow/configuration/graph.d.ts +21 -0
- package/dist/types/cmem/react-flow/configuration/linking.d.ts +19 -0
- package/dist/types/cmem/react-flow/configuration/unspecified.d.ts +9 -0
- package/dist/types/cmem/react-flow/configuration/workflow.d.ts +17 -0
- package/dist/types/components/AutoSuggestion/AutoSuggestion.d.ts +67 -0
- package/dist/types/components/AutoSuggestion/AutoSuggestionList.d.ts +13 -0
- package/dist/types/components/AutoSuggestion/SingleLineCodeEditor.d.ts +25 -0
- package/dist/types/components/AutocompleteField/AutoCompleteField.d.ts +8 -7
- package/dist/types/components/Button/Button.d.ts +3 -2
- package/dist/types/components/ContextOverlay/ContextMenu.d.ts +6 -1
- package/dist/types/components/Dialog/Modal.d.ts +4 -3
- package/dist/types/components/Dialog/SimpleDialog.d.ts +2 -0
- package/dist/types/components/Icon/Icon.d.ts +2 -2
- package/dist/types/components/Icon/IconButton.d.ts +1 -1
- package/dist/types/components/Icon/canonicalIconNames.d.ts +1 -1
- package/dist/types/components/MultiSelect/MultiSelect.d.ts +79 -0
- package/dist/types/components/Tabs/Tab.d.ts +549 -0
- package/dist/types/components/Tabs/TabTitle.d.ts +38 -0
- package/dist/types/components/Tabs/Tabs.d.ts +14 -20
- package/dist/types/components/Tag/Tag.d.ts +1 -1
- package/dist/types/extensions/react-flow/edges/utils.d.ts +1 -1
- package/dist/types/extensions/react-flow/nodes/NodeContent.d.ts +8 -8
- package/dist/types/extensions/react-flow/nodes/NodeDefault.d.ts +2 -2
- package/dist/types/extensions/react-flow/nodes/NodeTools.d.ts +2 -1
- package/dist/types/extensions/react-flow/nodes/nodeTypes.d.ts +10 -10
- package/dist/types/index.d.ts +9 -2
- package/dist/types/legacy-replacements/Button/AffirmativeButton.d.ts +2 -0
- package/dist/types/legacy-replacements/Button/Button.d.ts +5 -0
- package/dist/types/legacy-replacements/Button/DismissiveButton.d.ts +2 -0
- package/dist/types/legacy-replacements/Button/DisruptiveButton.d.ts +2 -0
- package/dist/types/legacy-replacements/Checkbox/Checkbox.d.ts +2 -0
- package/dist/types/legacy-replacements/Radio/RadioButton.d.ts +2 -0
- package/dist/types/legacy-replacements/Tabs/Tabs.d.ts +22 -0
- package/dist/types/legacy-replacements/TextField/TextField.d.ts +2 -0
- package/dist/types/legacy-replacements/index.d.ts +10 -0
- package/package.json +16 -7
- package/src/cmem/ActivityControl/ActivityControlTypes.ts +3 -1
- package/src/cmem/ActivityControl/ActivityControlWidget.stories.tsx +98 -0
- package/src/cmem/ActivityControl/ActivityControlWidget.tsx +131 -89
- package/src/cmem/ActivityControl/SilkActivityControl.tsx +178 -120
- package/src/cmem/ActivityControl/index.ts +3 -0
- package/src/cmem/ContentBlobToggler/index.ts +2 -0
- package/src/cmem/_index.scss +6 -0
- package/src/cmem/index.ts +10 -23
- package/src/cmem/react-flow/ReactFlow/ReactFlow.stories.tsx +222 -0
- package/src/cmem/react-flow/ReactFlow/ReactFlow.tsx +40 -0
- package/src/cmem/react-flow/_edges.scss +23 -0
- package/src/cmem/react-flow/_handles.scss +34 -0
- package/src/cmem/react-flow/_index.scss +5 -0
- package/src/cmem/react-flow/_minimap.scss +33 -0
- package/src/cmem/react-flow/_nodes.scss +60 -0
- package/src/cmem/react-flow/configuration/_colors-graph.module.scss +35 -0
- package/src/cmem/react-flow/configuration/_colors-linking.module.scss +29 -0
- package/src/cmem/react-flow/configuration/_colors-workflow.module.scss +22 -0
- package/src/cmem/react-flow/configuration/_colors.scss +3 -0
- package/src/cmem/react-flow/configuration/graph.ts +29 -0
- package/src/cmem/react-flow/configuration/linking.ts +28 -0
- package/src/cmem/react-flow/configuration/unspecified.ts +13 -0
- package/src/cmem/react-flow/configuration/workflow.ts +25 -0
- package/src/components/AutoSuggestion/AutoSuggestion.scss +100 -0
- package/src/components/AutoSuggestion/AutoSuggestion.tsx +447 -0
- package/src/components/AutoSuggestion/AutoSuggestionList.tsx +170 -0
- package/src/components/AutoSuggestion/SingleLineCodeEditor.tsx +97 -0
- package/src/components/AutoSuggestion/tests/AutoSuggestion.test.tsx +37 -0
- package/src/components/AutoSuggestion/tests/AutoSuggestionList.test.tsx +124 -0
- package/src/components/AutoSuggestion/tests/SingleLineCodeEditor.test.tsx +55 -0
- package/src/components/AutocompleteField/AutoCompleteField.tsx +19 -19
- package/src/components/AutocompleteField/autoCompleteFieldUtils.tsx +2 -1
- package/src/components/Button/Button.tsx +3 -2
- package/src/components/ContextOverlay/ContextMenu.tsx +8 -2
- package/src/components/Dialog/Modal.tsx +9 -2
- package/src/components/Dialog/SimpleDialog.tsx +2 -0
- package/src/components/Icon/Icon.tsx +4 -4
- package/src/components/Icon/IconButton.tsx +1 -1
- package/src/components/Icon/canonicalIconNames.tsx +39 -2
- package/src/components/MultiSelect/MultiSelect.stories.tsx +63 -0
- package/src/components/MultiSelect/MultiSelect.tsx +370 -0
- package/src/components/Tabs/Tab.tsx +112 -0
- package/src/components/Tabs/TabTitle.tsx +71 -0
- package/src/components/Tabs/Tabs.tsx +26 -90
- package/src/components/Tabs/stories/Tab.stories.tsx +65 -0
- package/src/components/Tabs/stories/TabTitle.stories.tsx +35 -0
- package/src/components/Tabs/stories/Tabs.stories.tsx +98 -0
- package/src/components/Tabs/tabs.scss +11 -4
- package/src/components/Tag/Tag.tsx +10 -3
- package/src/components/Tag/tag.scss +1 -1
- package/src/components/TextField/TextField.tsx +1 -1
- package/src/configuration/_variables.scss +3 -3
- package/src/extensions/react-flow/_config.scss +2 -2
- package/src/extensions/react-flow/edges/_edges.scss +3 -28
- package/src/extensions/react-flow/edges/utils.ts +139 -4
- package/src/extensions/react-flow/handles/_handles.scss +4 -58
- package/src/extensions/react-flow/minimap/_minimap.scss +0 -45
- package/src/extensions/react-flow/nodes/NodeContent.tsx +134 -133
- package/src/extensions/react-flow/nodes/NodeContentExtension.tsx +11 -10
- package/src/extensions/react-flow/nodes/NodeDefault.tsx +2 -2
- package/src/extensions/react-flow/nodes/NodeTools.tsx +2 -1
- package/src/extensions/react-flow/nodes/_nodes.scss +31 -185
- package/src/extensions/react-flow/nodes/{NodeContent.stories.tsx → stories/NodeContent.stories.tsx} +4 -4
- package/src/extensions/react-flow/nodes/{NodeContentExtension.stories.tsx → stories/NodeContentExtension.stories.tsx} +4 -4
- package/src/extensions/react-flow/nodes/{NodeDefault.stories.tsx → stories/NodeDefault.stories.tsx} +5 -4
- package/src/index.scss +1 -0
- package/src/index.ts +16 -1
- package/src/legacy-replacements/Tabs/Tabs.stories.tsx +46 -0
- package/src/legacy-replacements/Tabs/Tabs.tsx +84 -0
- package/src/legacy-replacements/index.ts +11 -0
- package/src/test/setupTests.js +19 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import computeScrollIntoView from "compute-scroll-into-view";
|
|
3
|
+
import {
|
|
4
|
+
Menu,
|
|
5
|
+
MenuItem,
|
|
6
|
+
Highlighter,
|
|
7
|
+
OverviewItem,
|
|
8
|
+
OverviewItemDescription,
|
|
9
|
+
OverviewItemLine,
|
|
10
|
+
OverflowText,
|
|
11
|
+
Spinner,
|
|
12
|
+
Spacing,
|
|
13
|
+
Tooltip,
|
|
14
|
+
} from "./../../";
|
|
15
|
+
import { ISuggestionWithReplacementInfo } from "./AutoSuggestion";
|
|
16
|
+
|
|
17
|
+
export interface IDropdownProps {
|
|
18
|
+
// The options of the drop down
|
|
19
|
+
options: Array<ISuggestionWithReplacementInfo>;
|
|
20
|
+
// Called when an item has been selected from the drop down
|
|
21
|
+
onItemSelectionChange: (item: ISuggestionWithReplacementInfo) => any;
|
|
22
|
+
// If the drop down is visible
|
|
23
|
+
isOpen: boolean;
|
|
24
|
+
// If the drop down should show a loading state
|
|
25
|
+
loading?: boolean;
|
|
26
|
+
left?: number;
|
|
27
|
+
// The item from the drop down that is active
|
|
28
|
+
currentlyFocusedIndex: number;
|
|
29
|
+
// Callback indicating what item should currently being highlighted, i.e. is either active or is hovered over
|
|
30
|
+
itemToHighlight: (item: ISuggestionWithReplacementInfo | undefined) => any;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ListItem = ({ item }: any, ref: any) => {
|
|
34
|
+
const listItem = (
|
|
35
|
+
<OverviewItem densityHigh={true}>
|
|
36
|
+
<OverviewItemDescription>
|
|
37
|
+
<OverviewItemLine>
|
|
38
|
+
<OverflowText ellipsis="reverse">
|
|
39
|
+
<Highlighter
|
|
40
|
+
label={item.value}
|
|
41
|
+
searchValue={item.query}
|
|
42
|
+
/>
|
|
43
|
+
</OverflowText>
|
|
44
|
+
</OverviewItemLine>
|
|
45
|
+
{item.description ? (
|
|
46
|
+
<OverviewItemLine small={true}>
|
|
47
|
+
<OverflowText>
|
|
48
|
+
<Highlighter
|
|
49
|
+
label={item.description}
|
|
50
|
+
searchValue={item.query}
|
|
51
|
+
/>
|
|
52
|
+
</OverflowText>
|
|
53
|
+
</OverviewItemLine>
|
|
54
|
+
) : null}
|
|
55
|
+
</OverviewItemDescription>
|
|
56
|
+
</OverviewItem>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div ref={ref}>
|
|
61
|
+
{!!item.description && item.description.length > 50 ? (
|
|
62
|
+
<Tooltip content={item.description}>{listItem}</Tooltip>
|
|
63
|
+
) : (
|
|
64
|
+
<>{listItem}</>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const Item = React.forwardRef(ListItem);
|
|
71
|
+
|
|
72
|
+
/** A drop-down-like list that can be used in combination with other components to show and select items. */
|
|
73
|
+
export const AutoSuggestionList = ({
|
|
74
|
+
isOpen,
|
|
75
|
+
options,
|
|
76
|
+
loading,
|
|
77
|
+
onItemSelectionChange,
|
|
78
|
+
left,
|
|
79
|
+
currentlyFocusedIndex,
|
|
80
|
+
itemToHighlight,
|
|
81
|
+
}: IDropdownProps) => {
|
|
82
|
+
const [hoveredItem, setHoveredItem] = React.useState<
|
|
83
|
+
ISuggestionWithReplacementInfo | undefined
|
|
84
|
+
>(undefined);
|
|
85
|
+
// Refs of list items
|
|
86
|
+
const [refs] = React.useState<React.RefObject<Element>[]>([])
|
|
87
|
+
const dropdownRef = React.useRef<HTMLDivElement>(null);
|
|
88
|
+
const generateRef = (index: number) => {
|
|
89
|
+
if (!refs[index]) {
|
|
90
|
+
refs[index] = React.createRef();
|
|
91
|
+
}
|
|
92
|
+
return refs[index];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
React.useEffect(() => {
|
|
96
|
+
const listIndexNode = refs[currentlyFocusedIndex];
|
|
97
|
+
if (dropdownRef?.current && listIndexNode?.current) {
|
|
98
|
+
const actions = computeScrollIntoView(listIndexNode.current, {
|
|
99
|
+
boundary: dropdownRef.current,
|
|
100
|
+
block: "nearest",
|
|
101
|
+
scrollMode: "if-needed",
|
|
102
|
+
});
|
|
103
|
+
actions.forEach(({ el, top, left }) => {
|
|
104
|
+
el.scrollTop = top;
|
|
105
|
+
el.scrollLeft = left;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}, [currentlyFocusedIndex, refs]);
|
|
109
|
+
|
|
110
|
+
const focusedItem = options[currentlyFocusedIndex]
|
|
111
|
+
|
|
112
|
+
// Decide which item to highlight
|
|
113
|
+
React.useEffect(() => {
|
|
114
|
+
itemToHighlight(!isOpen ? undefined : hoveredItem || focusedItem);
|
|
115
|
+
}, [
|
|
116
|
+
currentlyFocusedIndex,
|
|
117
|
+
itemToHighlight,
|
|
118
|
+
focusedItem,
|
|
119
|
+
isOpen,
|
|
120
|
+
hoveredItem,
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
const Loader = (
|
|
124
|
+
<OverviewItem hasSpacing>
|
|
125
|
+
<OverviewItemLine>Fetching suggestions</OverviewItemLine>
|
|
126
|
+
<Spacing size="tiny" vertical={true} />
|
|
127
|
+
<Spinner position="inline" description="" />
|
|
128
|
+
</OverviewItem>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const loadingOrHasSuggestions = loading || options.length;
|
|
132
|
+
if (!loadingOrHasSuggestions || !isOpen) return null;
|
|
133
|
+
return (
|
|
134
|
+
<div
|
|
135
|
+
className="ecc-auto-suggestion-box__dropdown"
|
|
136
|
+
style={{ left }}
|
|
137
|
+
ref={dropdownRef}
|
|
138
|
+
>
|
|
139
|
+
{loading ? (
|
|
140
|
+
Loader
|
|
141
|
+
) : (
|
|
142
|
+
<Menu>
|
|
143
|
+
{options.map((item, index) => (
|
|
144
|
+
<MenuItem
|
|
145
|
+
key={index}
|
|
146
|
+
internalProps={{
|
|
147
|
+
active: currentlyFocusedIndex === index,
|
|
148
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
149
|
+
onClick: () => onItemSelectionChange(item),
|
|
150
|
+
text: (
|
|
151
|
+
<Item
|
|
152
|
+
ref={generateRef(index)}
|
|
153
|
+
item={item}
|
|
154
|
+
/>
|
|
155
|
+
),
|
|
156
|
+
onMouseEnter: () => setHoveredItem(item),
|
|
157
|
+
onMouseLeave: () => setHoveredItem(undefined),
|
|
158
|
+
onMouseOver: () => {
|
|
159
|
+
if (item.value !== hoveredItem?.value) {
|
|
160
|
+
setHoveredItem(item);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
165
|
+
))}
|
|
166
|
+
</Menu>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import "codemirror/addon/display/placeholder"
|
|
2
|
+
import "codemirror/mode/sparql/sparql.js";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Controlled as ControlledEditor } from "react-codemirror2";
|
|
5
|
+
import { Classes as BlueprintClassNames } from "@blueprintjs/core";
|
|
6
|
+
import {Editor as CodeMirrorEditor} from "codemirror";
|
|
7
|
+
|
|
8
|
+
export interface IEditorProps {
|
|
9
|
+
// Is called with the editor instance that allows access via the CodeMirror API
|
|
10
|
+
setEditorInstance: (editor: CodeMirrorEditor) => any
|
|
11
|
+
// Called whenever the editor content changes
|
|
12
|
+
onChange: (value: string) => any
|
|
13
|
+
// Called when the cursor position changes
|
|
14
|
+
onCursorChange: (pos: any, coords: any) => any
|
|
15
|
+
// The editor theme, e.g. "sparql"
|
|
16
|
+
mode?: string
|
|
17
|
+
// The initial value of the editor
|
|
18
|
+
initialValue: string
|
|
19
|
+
// Called when the focus status changes
|
|
20
|
+
onFocusChange: (focused: boolean) => any
|
|
21
|
+
// Called when the user presses a key
|
|
22
|
+
onKeyDown: (event: KeyboardEvent) => any
|
|
23
|
+
// Called when the user selects text
|
|
24
|
+
onSelection: (ranges: IRange[]) => any
|
|
25
|
+
// If the <Tab> key is enabled as normal input, i.e. it won't have the behavior of changing to the next input element, expected in a web app.
|
|
26
|
+
enableTab?: boolean
|
|
27
|
+
/** Placeholder tobe shown when no text has been entered, yet. */
|
|
28
|
+
placeholder?: string
|
|
29
|
+
showScrollBar?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IRange {
|
|
33
|
+
from: number
|
|
34
|
+
to: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A single-line code editor. */
|
|
38
|
+
const SingleLineCodeEditor = ({
|
|
39
|
+
setEditorInstance,
|
|
40
|
+
onChange,
|
|
41
|
+
onCursorChange,
|
|
42
|
+
mode,
|
|
43
|
+
initialValue,
|
|
44
|
+
onFocusChange,
|
|
45
|
+
onKeyDown,
|
|
46
|
+
onSelection,
|
|
47
|
+
enableTab = false,
|
|
48
|
+
placeholder,
|
|
49
|
+
showScrollBar = true
|
|
50
|
+
}: IEditorProps) => {
|
|
51
|
+
return (
|
|
52
|
+
<div className={"ecc-input-editor " + BlueprintClassNames.INPUT}>
|
|
53
|
+
<ControlledEditor
|
|
54
|
+
editorDidMount={(editor) => {
|
|
55
|
+
editor.on("beforeChange", (_, change) => {
|
|
56
|
+
// Prevent the user from entering new-line characters, since this is supposed to be a one-line editor.
|
|
57
|
+
const newText = change.text.join("").replace(/\n/g, "");
|
|
58
|
+
//failing unexpectedly during undo and redo
|
|
59
|
+
if (change.update && typeof change.update === "function") {
|
|
60
|
+
change.update(change.from, change.to, [newText]);
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
setEditorInstance(editor);
|
|
65
|
+
}}
|
|
66
|
+
value={initialValue}
|
|
67
|
+
onFocus={() => onFocusChange(true)}
|
|
68
|
+
onBlur={() => onFocusChange(false)}
|
|
69
|
+
options={{
|
|
70
|
+
mode: mode,
|
|
71
|
+
lineNumbers: false,
|
|
72
|
+
theme: "xq-light",
|
|
73
|
+
extraKeys: enableTab ? undefined : {Tab: false},
|
|
74
|
+
placeholder,
|
|
75
|
+
scrollbarStyle: showScrollBar ? "native" : "null"
|
|
76
|
+
}}
|
|
77
|
+
onSelection={(editor, data) => {
|
|
78
|
+
if(Array.isArray(data?.ranges)) {
|
|
79
|
+
onSelection(data.ranges
|
|
80
|
+
.map((r: any) => ({from: r.from().ch, to: r.to().ch}))
|
|
81
|
+
.filter((r: any) => r.from !== r.to))
|
|
82
|
+
}
|
|
83
|
+
}}
|
|
84
|
+
onCursor={(editor, data) => {
|
|
85
|
+
onCursorChange(data, editor.cursorCoords(true, "div"));
|
|
86
|
+
}}
|
|
87
|
+
onBeforeChange={(editor, data, value) => {
|
|
88
|
+
const trimmedValue = value.replace(/\n/g, "");
|
|
89
|
+
onChange(trimmedValue);
|
|
90
|
+
}}
|
|
91
|
+
onKeyDown={(_, event) => onKeyDown(event)}
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default SingleLineCodeEditor;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "@testing-library/jest-dom";
|
|
3
|
+
import {render} from "@testing-library/react";
|
|
4
|
+
import AutoSuggestion, {IProps} from "../AutoSuggestion"
|
|
5
|
+
|
|
6
|
+
describe("AutoSuggestion", () => {
|
|
7
|
+
let props: IProps
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
props = {
|
|
10
|
+
label: "test value path",
|
|
11
|
+
initialValue: "",
|
|
12
|
+
onChange: jest.fn((value) => {
|
|
13
|
+
}),
|
|
14
|
+
fetchSuggestions: jest.fn((inputString, cursorPosition) => undefined),
|
|
15
|
+
checkInput: jest.fn(inputString => ({
|
|
16
|
+
valid: true
|
|
17
|
+
})),
|
|
18
|
+
onInputChecked: jest.fn(validInput => {
|
|
19
|
+
}),
|
|
20
|
+
validationErrorText: "",
|
|
21
|
+
clearIconText: "",
|
|
22
|
+
onFocusChange: jest.fn(hasFocus => {
|
|
23
|
+
}),
|
|
24
|
+
id: "test-auto-suggestion"
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it("should render properly", () => {
|
|
29
|
+
const {container} = render(<AutoSuggestion {...props} />)
|
|
30
|
+
expect(container).not.toBeEmptyDOMElement()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("should set label prop properly", () => {
|
|
34
|
+
const {getByText} = render(<AutoSuggestion {...props}/>)
|
|
35
|
+
expect(getByText(props.label!!)).toBeTruthy()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '@testing-library/jest-dom'
|
|
3
|
+
import {fireEvent, render} from '@testing-library/react'
|
|
4
|
+
import {CLASSPREFIX as eccgui} from "../../../configuration/constants";
|
|
5
|
+
import {AutoSuggestionList, IDropdownProps} from '../AutoSuggestionList'
|
|
6
|
+
|
|
7
|
+
describe("Dropdown list", () => {
|
|
8
|
+
let props:IDropdownProps, mockOptions
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
props = {
|
|
11
|
+
currentlyFocusedIndex: 0,
|
|
12
|
+
loading: false,
|
|
13
|
+
left:0,
|
|
14
|
+
isOpen: false,
|
|
15
|
+
options: [],
|
|
16
|
+
itemToHighlight: () => {},
|
|
17
|
+
onItemSelectionChange: () => {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
mockOptions = [
|
|
21
|
+
{
|
|
22
|
+
query:"organizations",
|
|
23
|
+
from: 0,
|
|
24
|
+
length: 13,
|
|
25
|
+
value:"organizations/name"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
query:"",
|
|
29
|
+
from: 13,
|
|
30
|
+
length:0,
|
|
31
|
+
value:"/"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
it("doesn't render when not focused", () => {
|
|
38
|
+
const {container} = render(<AutoSuggestionList {...props} />)
|
|
39
|
+
expect(container).toBeEmptyDOMElement()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it("should be able to display loading state, when fetching suggestions", () => {
|
|
43
|
+
props = {
|
|
44
|
+
...props,
|
|
45
|
+
loading: true,
|
|
46
|
+
isOpen: true
|
|
47
|
+
}
|
|
48
|
+
const {getByText} = render(<AutoSuggestionList {...props} />)
|
|
49
|
+
expect(getByText("Fetching suggestions")).toBeTruthy()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("should render list when suggestions are fetched", () => {
|
|
53
|
+
props = {
|
|
54
|
+
...props,
|
|
55
|
+
loading: false,
|
|
56
|
+
isOpen:true,
|
|
57
|
+
options: mockOptions
|
|
58
|
+
}
|
|
59
|
+
const {container} = render(<AutoSuggestionList {...props} />)
|
|
60
|
+
const dropdownItems = container.getElementsByTagName("li")
|
|
61
|
+
expect(container).not.toBeEmptyDOMElement()
|
|
62
|
+
expect(dropdownItems.length).toBe(2)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it("should move horizontally for every cursor movement", () => {
|
|
66
|
+
props = {
|
|
67
|
+
...props,
|
|
68
|
+
options: mockOptions,
|
|
69
|
+
loading: false,
|
|
70
|
+
isOpen: true,
|
|
71
|
+
left: 10
|
|
72
|
+
}
|
|
73
|
+
const {container} = render(<AutoSuggestionList {...props} />)
|
|
74
|
+
const parentDiv:HTMLElement = container.querySelector(".ecc-auto-suggestion-box__dropdown")!!
|
|
75
|
+
const leftOffset = Number(parentDiv.style.left.replace(/px$/,""));
|
|
76
|
+
expect(leftOffset).toBe(props.left)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("should have one active item at a time", () => {
|
|
80
|
+
props = {
|
|
81
|
+
...props,
|
|
82
|
+
loading: false,
|
|
83
|
+
isOpen:true,
|
|
84
|
+
options: mockOptions
|
|
85
|
+
}
|
|
86
|
+
const {container} = render(<AutoSuggestionList {...props} />)
|
|
87
|
+
const activeListItems = Array.from(container.querySelectorAll("li .bp3-menu-item.bp3-active"))
|
|
88
|
+
expect(activeListItems.length).toBe(1)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it("should respond to click on each item and pass the clicked item to autosuggestion", () => {
|
|
92
|
+
const mockOnItemSelection = jest.fn((item) => {})
|
|
93
|
+
props = {
|
|
94
|
+
...props,
|
|
95
|
+
loading: false,
|
|
96
|
+
isOpen:true,
|
|
97
|
+
options: mockOptions,
|
|
98
|
+
onItemSelectionChange: mockOnItemSelection
|
|
99
|
+
}
|
|
100
|
+
const {getByText} = render(<AutoSuggestionList {...props}/>)
|
|
101
|
+
const dropdownListItem = getByText(props.options[0].query).closest(`.${eccgui}-menu__item`);
|
|
102
|
+
fireEvent.click(dropdownListItem!!)
|
|
103
|
+
expect(mockOnItemSelection).toHaveBeenCalledTimes(1)
|
|
104
|
+
expect(mockOnItemSelection).toHaveBeenCalledWith(props.options[0])
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it("should call highlight function when list item is mouse hovered",() => {
|
|
108
|
+
const mockItemToHighlight = jest.fn((item) => {})
|
|
109
|
+
props = {
|
|
110
|
+
...props,
|
|
111
|
+
loading: false,
|
|
112
|
+
isOpen:true,
|
|
113
|
+
options: mockOptions,
|
|
114
|
+
itemToHighlight: mockItemToHighlight
|
|
115
|
+
}
|
|
116
|
+
const {container} = render(<AutoSuggestionList {...props}/>)
|
|
117
|
+
const firstItem = container.querySelector("li");
|
|
118
|
+
fireEvent.mouseEnter(firstItem!!)
|
|
119
|
+
expect(mockItemToHighlight).toHaveBeenCalledWith(props.options[0])
|
|
120
|
+
expect(mockItemToHighlight).toHaveBeenCalledTimes(1)
|
|
121
|
+
fireEvent.mouseLeave(firstItem!!)
|
|
122
|
+
expect(mockItemToHighlight).toHaveBeenCalledTimes(1)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "@testing-library/jest-dom";
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
import SingleLineEditor, {
|
|
5
|
+
IEditorProps,
|
|
6
|
+
} from "../SingleLineCodeEditor";
|
|
7
|
+
import CodeMirror from "codemirror";
|
|
8
|
+
|
|
9
|
+
describe("SingleLineCodeEditor", () => {
|
|
10
|
+
let props: IEditorProps,
|
|
11
|
+
codeMirrorEditorInstance: CodeMirror.Editor = null as any;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
props = {
|
|
14
|
+
setEditorInstance: jest.fn((editor) => {
|
|
15
|
+
codeMirrorEditorInstance = editor;
|
|
16
|
+
}),
|
|
17
|
+
onChange: jest.fn((value) => {}),
|
|
18
|
+
onCursorChange: jest.fn((pos, coords) => {}),
|
|
19
|
+
mode: null,
|
|
20
|
+
initialValue: "",
|
|
21
|
+
onFocusChange: jest.fn((focused) => {}),
|
|
22
|
+
onKeyDown: jest.fn((event) => {}),
|
|
23
|
+
onSelection: jest.fn((ranges) => {}),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should render properly", () => {
|
|
28
|
+
const { container } = render(<SingleLineEditor {...props} />);
|
|
29
|
+
expect(container.querySelector(".ecc-input-editor")).not.toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should set the editorInstance immediately it's mounted", () => {
|
|
33
|
+
render(<SingleLineEditor {...props} />);
|
|
34
|
+
expect(props.setEditorInstance).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(codeMirrorEditorInstance).not.toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should set the default value on the editor input", () => {
|
|
39
|
+
props = {
|
|
40
|
+
...props,
|
|
41
|
+
initialValue: "This is the initial input",
|
|
42
|
+
};
|
|
43
|
+
const { getByText } = render(<SingleLineEditor {...props} />);
|
|
44
|
+
expect(codeMirrorEditorInstance.getValue()).toBe(props.initialValue);
|
|
45
|
+
expect(getByText(props.initialValue)).toBeTruthy();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should not allow user to create new lines", () => {
|
|
49
|
+
render(<SingleLineEditor {...props} />);
|
|
50
|
+
codeMirrorEditorInstance
|
|
51
|
+
.getDoc()
|
|
52
|
+
.setValue("I'm entering a new line \n character");
|
|
53
|
+
expect(codeMirrorEditorInstance.lineCount()).toBe(1);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -17,10 +17,9 @@ export interface IRenderModifiers {
|
|
|
17
17
|
/**
|
|
18
18
|
* Parameters for the auto-complete field parameterized by T and U.
|
|
19
19
|
* @param T is the input data structure/type of the items that can be selected.
|
|
20
|
-
* @param
|
|
21
|
-
* before onChange is called.
|
|
20
|
+
* @param UPDATE_VALUE The value type that will be pushed into the onChange callback.
|
|
22
21
|
*/
|
|
23
|
-
export interface IAutoCompleteFieldProps<T extends any,
|
|
22
|
+
export interface IAutoCompleteFieldProps<T extends any, UPDATE_VALUE extends any> {
|
|
24
23
|
/**
|
|
25
24
|
* Fired when text is typed into the input field. Returns a list of items of type T.
|
|
26
25
|
*/
|
|
@@ -31,7 +30,7 @@ export interface IAutoCompleteFieldProps<T extends any, U extends any> {
|
|
|
31
30
|
* @param value The value that has been converted with itemValueSelector.
|
|
32
31
|
* @param e The event
|
|
33
32
|
*/
|
|
34
|
-
onChange?(value:
|
|
33
|
+
onChange?(value: UPDATE_VALUE, e?: React.SyntheticEvent<HTMLElement>): any;
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
36
|
* The initial value for the auto-complete input field
|
|
@@ -58,7 +57,10 @@ export interface IAutoCompleteFieldProps<T extends any, U extends any> {
|
|
|
58
57
|
* Selects the part from the auto-completion item that is called with the onChange callback.
|
|
59
58
|
* @param item The selected item that should be converted to the value that onChange is called with.
|
|
60
59
|
*/
|
|
61
|
-
itemValueSelector(item: T):
|
|
60
|
+
itemValueSelector(item: T): UPDATE_VALUE;
|
|
61
|
+
|
|
62
|
+
/** The string representation of the actual value, i.e. without meta data etc. This will be used to compare if values are equal. */
|
|
63
|
+
itemValueString(item: T): string
|
|
62
64
|
|
|
63
65
|
/** The text that should be displayed when no search result has been found and no custom entry can be created. */
|
|
64
66
|
noResultText: string
|
|
@@ -84,7 +86,7 @@ export interface IAutoCompleteFieldProps<T extends any, U extends any> {
|
|
|
84
86
|
resettableValue(value: T): boolean;
|
|
85
87
|
|
|
86
88
|
/** The value onChange is called with when a reset action is triggered. */
|
|
87
|
-
resetValue:
|
|
89
|
+
resetValue: UPDATE_VALUE;
|
|
88
90
|
|
|
89
91
|
/** The reset button text that is shown on hovering over the reset icon. */
|
|
90
92
|
resetButtonText: string
|
|
@@ -143,7 +145,7 @@ const elementWidth = (elRef: IRefObject<HTMLInputElement> | null): IElementWidth
|
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
/** Auto-complete input widget. */
|
|
146
|
-
export function AutoCompleteField<T extends any,
|
|
148
|
+
export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props: IAutoCompleteFieldProps<T, UPDATE_VALUE>) {
|
|
147
149
|
const {
|
|
148
150
|
reset,
|
|
149
151
|
noResultText,
|
|
@@ -157,6 +159,7 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
157
159
|
createNewItem,
|
|
158
160
|
itemValueRenderer,
|
|
159
161
|
resetQueryToValue,
|
|
162
|
+
itemValueString,
|
|
160
163
|
...otherProps
|
|
161
164
|
} = props;
|
|
162
165
|
const [selectedItem, setSelectedItem] = useState<T | undefined>(initialValue);
|
|
@@ -177,8 +180,8 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
177
180
|
if (item) {
|
|
178
181
|
// If new values can be created, always reset the query value to the actual value of the selected item.
|
|
179
182
|
// This e.g. prevents that the "create new" option will be shown, since an item with the same value already exists.
|
|
180
|
-
const defaultResetValue: string = createNewItem
|
|
181
|
-
|
|
183
|
+
const defaultResetValue: string = createNewItem ? (
|
|
184
|
+
itemValueString(item) as string
|
|
182
185
|
) : itemValueRenderer(item)
|
|
183
186
|
const resetVal = resetQueryToValue ? resetQueryToValue(item) : defaultResetValue
|
|
184
187
|
setQuery(resetVal)
|
|
@@ -189,11 +192,7 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
189
192
|
|
|
190
193
|
// The key for the option elements
|
|
191
194
|
const itemKey = (item: T): string => {
|
|
192
|
-
|
|
193
|
-
if (typeof itemValue !== "string") {
|
|
194
|
-
itemValue = itemValueRenderer(item);
|
|
195
|
-
}
|
|
196
|
-
return itemValue;
|
|
195
|
+
return itemValueString(item);
|
|
197
196
|
};
|
|
198
197
|
|
|
199
198
|
useEffect(() => {
|
|
@@ -250,9 +249,9 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
250
249
|
// Return the index of the item in the array based on the itemValueRenderer value
|
|
251
250
|
const itemIndexOf = (arr: T[], searchItem: T): number => {
|
|
252
251
|
let idx = -1;
|
|
253
|
-
const searchItemString =
|
|
252
|
+
const searchItemString = itemValueString(searchItem);
|
|
254
253
|
arr.forEach((v, i) => {
|
|
255
|
-
if (
|
|
254
|
+
if (itemValueString(v) === searchItemString) {
|
|
256
255
|
idx = i;
|
|
257
256
|
}
|
|
258
257
|
});
|
|
@@ -265,7 +264,7 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
265
264
|
try {
|
|
266
265
|
let result = await onSearch(input);
|
|
267
266
|
const onlySelectItemReturned = result.length <= 1 && selectedItem && input.length > 0 &&
|
|
268
|
-
(itemValueRenderer(selectedItem) === input ||
|
|
267
|
+
(itemValueRenderer(selectedItem) === input || itemValueString(selectedItem) === input)
|
|
269
268
|
let enableHighlighting = true;
|
|
270
269
|
if (onlySelectItemReturned) {
|
|
271
270
|
// If the auto-completion only returns no suggestion or the selected item itself, query with empty string.
|
|
@@ -319,7 +318,7 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
319
318
|
}
|
|
320
319
|
};
|
|
321
320
|
// Resets the selection
|
|
322
|
-
const clearSelection = (resetValue:
|
|
321
|
+
const clearSelection = (resetValue: UPDATE_VALUE) => () => {
|
|
323
322
|
setSelectedItem(undefined);
|
|
324
323
|
onChange?.(resetValue);
|
|
325
324
|
setQuery("");
|
|
@@ -350,6 +349,7 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
350
349
|
position: "bottom",
|
|
351
350
|
popoverClassName: `${eccgui}-autocompletefield__options`,
|
|
352
351
|
wrapperTagName: "div",
|
|
352
|
+
boundary: "window",
|
|
353
353
|
onClosed: onPopoverClose,
|
|
354
354
|
...otherProps.popoverProps,
|
|
355
355
|
}
|
|
@@ -362,7 +362,7 @@ export function AutoCompleteField<T extends any, U extends any>(props: IAutoComp
|
|
|
362
362
|
const createNewItemProps = createNewItem ? {
|
|
363
363
|
createNewItemFromQuery: createNewItem.itemFromQuery,
|
|
364
364
|
createNewItemRenderer: (query: string, active: boolean, handleClick: React.MouseEventHandler<HTMLElement>) => {
|
|
365
|
-
if(selectedItem && query ===
|
|
365
|
+
if(selectedItem && query === itemValueString(selectedItem)) {
|
|
366
366
|
// Never show create new item option if the same item is already selected
|
|
367
367
|
return undefined
|
|
368
368
|
} else {
|
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
2
|
import {IElementWidth, IRenderModifiers} from "./AutoCompleteField";
|
|
3
3
|
import OverflowText from "../Typography/OverflowText";
|
|
4
4
|
import MenuItem from "../Menu/MenuItem";
|
|
5
|
+
import {ValidIconName} from "../Icon/canonicalIconNames";
|
|
5
6
|
|
|
6
7
|
/** Returns a function to be used in an AutoComplete widget for rendering custom elements based on the query string.
|
|
7
8
|
*
|
|
@@ -10,7 +11,7 @@ import MenuItem from "../Menu/MenuItem";
|
|
|
10
11
|
*/
|
|
11
12
|
export const createNewItemRendererFactory = (
|
|
12
13
|
itemTextRenderer: (query: string, styleWidth: IElementWidth) => string | JSX.Element,
|
|
13
|
-
iconName?:
|
|
14
|
+
iconName?: ValidIconName) => {
|
|
14
15
|
// Return custom render function
|
|
15
16
|
return (query: string, modifiers: IRenderModifiers, handleClick: React.MouseEventHandler<HTMLElement>) => {
|
|
16
17
|
let textElement = itemTextRenderer(query, modifiers.styleWidth)
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import Icon from "../Icon/Icon";
|
|
10
10
|
import Tooltip, { TooltipProps } from "./../Tooltip/Tooltip";
|
|
11
11
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
12
|
+
import {ValidIconName} from "../Icon/canonicalIconNames";
|
|
12
13
|
|
|
13
14
|
export type AnchorOrButtonProps = Omit<BlueprintButtonProps, "elementRef" | "icon" | "rightIcon"> | Omit<BlueprintAnchorButtonProps, "elementRef" | "icon" | "rightIcon">;
|
|
14
15
|
|
|
@@ -56,8 +57,8 @@ export interface ButtonProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
56
57
|
* If an URL is set then the button is included as HTML anchor element instead of a button form element.
|
|
57
58
|
*/
|
|
58
59
|
href?: string;
|
|
59
|
-
icon?:
|
|
60
|
-
rightIcon?:
|
|
60
|
+
icon?: ValidIconName | JSX.Element;
|
|
61
|
+
rightIcon?: ValidIconName | JSX.Element;
|
|
61
62
|
target?: string;
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -3,6 +3,12 @@ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
|
3
3
|
import ContextOverlay from "./ContextOverlay";
|
|
4
4
|
import Menu from "../Menu/Menu";
|
|
5
5
|
import IconButton from "../Icon/IconButton";
|
|
6
|
+
import {ValidIconName} from "../Icon/canonicalIconNames";
|
|
7
|
+
|
|
8
|
+
interface ContextMenuProps {
|
|
9
|
+
togglerElement?: ValidIconName | JSX.Element
|
|
10
|
+
[key: string]: any
|
|
11
|
+
}
|
|
6
12
|
|
|
7
13
|
function ContextMenu({
|
|
8
14
|
children,
|
|
@@ -14,13 +20,13 @@ function ContextMenu({
|
|
|
14
20
|
so by default we use the title attribute instead of Tooltip. */
|
|
15
21
|
tooltipAsTitle = true,
|
|
16
22
|
...restProps
|
|
17
|
-
}:
|
|
23
|
+
}: ContextMenuProps) {
|
|
18
24
|
return (
|
|
19
25
|
<ContextOverlay {...restProps} className={`${eccgui}-contextmenu ` + className}>
|
|
20
26
|
{typeof togglerElement === "string" ? (
|
|
21
27
|
<IconButton
|
|
22
28
|
tooltipAsTitle={tooltipAsTitle}
|
|
23
|
-
name={togglerElement}
|
|
29
|
+
name={[togglerElement]}
|
|
24
30
|
text={togglerText}
|
|
25
31
|
large={togglerLarge}
|
|
26
32
|
data-test-id={restProps["data-test-id"]}
|