@eccenca/gui-elements 25.1.0-rc.0 → 25.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -5
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +17 -13
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +1 -1
- package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
- package/dist/cjs/common/index.js +1 -0
- package/dist/cjs/common/index.js.map +1 -1
- package/dist/cjs/common/utils/CssCustomProperties.js.map +1 -1
- package/dist/cjs/common/utils/colorHash.js +26 -12
- package/dist/cjs/common/utils/colorHash.js.map +1 -1
- package/dist/cjs/components/ColorField/ColorField.js +114 -0
- package/dist/cjs/components/ColorField/ColorField.js.map +1 -0
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js +6 -6
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/cjs/components/DecoupledOverlay/DecoupledOverlay.js +47 -0
- package/dist/cjs/components/DecoupledOverlay/DecoupledOverlay.js.map +1 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js +3 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/cjs/components/Icon/transformIcon.js +14 -0
- package/dist/cjs/components/Icon/transformIcon.js.map +1 -0
- package/dist/cjs/components/MultiSelect/MultiSelect.js +2 -1
- package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/cjs/components/RadioButton/RadioButton.js +5 -2
- package/dist/cjs/components/RadioButton/RadioButton.js.map +1 -1
- package/dist/cjs/components/TextField/useTextValidation.js +17 -8
- package/dist/cjs/components/TextField/useTextValidation.js.map +1 -1
- package/dist/cjs/components/VisualTour/VisualTour.js +24 -32
- package/dist/cjs/components/VisualTour/VisualTour.js.map +1 -1
- package/dist/cjs/components/index.js +2 -0
- package/dist/cjs/components/index.js.map +1 -1
- package/dist/cjs/extensions/codemirror/CodeMirror.js +56 -18
- package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/cjs/extensions/codemirror/toolbars/EditorAppearanceConfigMenu.js +23 -0
- package/dist/cjs/extensions/codemirror/toolbars/EditorAppearanceConfigMenu.js.map +1 -0
- package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js +5 -2
- package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeLabel.js +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeLabel.js.map +1 -1
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js +19 -14
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +1 -1
- package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
- package/dist/esm/common/index.js +2 -1
- package/dist/esm/common/index.js.map +1 -1
- package/dist/esm/common/utils/CssCustomProperties.js.map +1 -1
- package/dist/esm/common/utils/colorHash.js +26 -13
- package/dist/esm/common/utils/colorHash.js.map +1 -1
- package/dist/esm/components/ColorField/ColorField.js +140 -0
- package/dist/esm/components/ColorField/ColorField.js.map +1 -0
- package/dist/esm/components/ContextOverlay/ContextOverlay.js +3 -3
- package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/esm/components/DecoupledOverlay/DecoupledOverlay.js +41 -0
- package/dist/esm/components/DecoupledOverlay/DecoupledOverlay.js.map +1 -0
- package/dist/esm/components/Icon/canonicalIconNames.js +3 -0
- package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/esm/components/Icon/transformIcon.js +21 -0
- package/dist/esm/components/Icon/transformIcon.js.map +1 -0
- package/dist/esm/components/MultiSelect/MultiSelect.js +3 -2
- package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/RadioButton/RadioButton.js +6 -2
- package/dist/esm/components/RadioButton/RadioButton.js.map +1 -1
- package/dist/esm/components/TextField/useTextValidation.js +39 -8
- package/dist/esm/components/TextField/useTextValidation.js.map +1 -1
- package/dist/esm/components/VisualTour/VisualTour.js +25 -33
- package/dist/esm/components/VisualTour/VisualTour.js.map +1 -1
- package/dist/esm/components/index.js +2 -0
- package/dist/esm/components/index.js.map +1 -1
- package/dist/esm/extensions/codemirror/CodeMirror.js +58 -20
- package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/esm/extensions/codemirror/toolbars/EditorAppearanceConfigMenu.js +47 -0
- package/dist/esm/extensions/codemirror/toolbars/EditorAppearanceConfigMenu.js.map +1 -0
- package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js +16 -2
- package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -1
- package/dist/esm/extensions/react-flow/edges/EdgeLabel.js +1 -1
- package/dist/esm/extensions/react-flow/edges/EdgeLabel.js.map +1 -1
- package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +9 -0
- package/dist/types/common/index.d.ts +2 -1
- package/dist/types/common/utils/CssCustomProperties.d.ts +2 -2
- package/dist/types/common/utils/colorHash.d.ts +5 -4
- package/dist/types/components/ColorField/ColorField.d.ts +30 -0
- package/dist/types/components/ContextOverlay/ContextOverlay.d.ts +7 -1
- package/dist/types/components/DecoupledOverlay/DecoupledOverlay.d.ts +20 -0
- package/dist/types/components/Icon/canonicalIconNames.d.ts +2 -0
- package/dist/types/components/Icon/transformIcon.d.ts +2 -0
- package/dist/types/components/MultiSelect/MultiSelect.d.ts +1 -1
- package/dist/types/components/RadioButton/RadioButton.d.ts +8 -2
- package/dist/types/components/index.d.ts +2 -0
- package/dist/types/extensions/codemirror/CodeMirror.d.ts +12 -9
- package/dist/types/extensions/codemirror/toolbars/EditorAppearanceConfigMenu.d.ts +24 -0
- package/dist/types/extensions/codemirror/toolbars/markdown.toolbar.d.ts +2 -0
- package/package.json +1 -1
- package/src/cmem/ActivityControl/ActivityControlWidget.tsx +68 -35
- package/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx +1 -1
- package/src/common/index.ts +2 -1
- package/src/common/utils/CssCustomProperties.ts +5 -3
- package/src/common/utils/colorHash.ts +38 -20
- package/src/components/Application/_colors.scss +15 -0
- package/src/components/ColorField/ColorField.stories.tsx +72 -0
- package/src/components/ColorField/ColorField.test.tsx +101 -0
- package/src/components/ColorField/ColorField.tsx +200 -0
- package/src/components/ColorField/_colorfield.scss +67 -0
- package/src/components/ContextOverlay/ContextOverlay.tsx +20 -1
- package/src/components/DecoupledOverlay/DecoupledOverlay.stories.tsx +30 -0
- package/src/components/DecoupledOverlay/DecoupledOverlay.tsx +97 -0
- package/src/components/DecoupledOverlay/_decoupledoverlay.scss +46 -0
- package/src/components/Icon/canonicalIconNames.tsx +3 -0
- package/src/components/Icon/transformIcon.tsx +17 -0
- package/src/components/Link/Link.stories.tsx +30 -0
- package/src/components/Link/link.scss +28 -2
- package/src/components/MultiSelect/MultiSelect.tsx +12 -3
- package/src/components/RadioButton/RadioButton.tsx +15 -3
- package/src/components/RadioButton/radiobutton.scss +13 -0
- package/src/components/TextField/stories/TextField.stories.tsx +23 -0
- package/src/components/TextField/tests/useTextValidation.test.tsx +83 -0
- package/src/components/TextField/useTextValidation.ts +17 -8
- package/src/components/VisualTour/VisualTour.tsx +30 -50
- package/src/components/VisualTour/visualTour.scss +0 -34
- package/src/components/index.scss +2 -0
- package/src/components/index.ts +2 -0
- package/src/configuration/_customproperties.scss +32 -0
- package/src/configuration/stories/customproperties.stories.tsx +118 -0
- package/src/extensions/codemirror/CodeMirror.stories.tsx +9 -4
- package/src/extensions/codemirror/CodeMirror.tsx +87 -31
- package/src/extensions/codemirror/tests/CodeEditor.test.tsx +138 -0
- package/src/extensions/codemirror/tests/EditorAppearanceConfigMenu.test.tsx +131 -0
- package/src/extensions/codemirror/toolbars/EditorAppearanceConfigMenu.tsx +59 -0
- package/src/extensions/codemirror/toolbars/markdown.toolbar.tsx +17 -3
- package/src/extensions/react-flow/_config.scss +3 -3
- package/src/extensions/react-flow/edges/EdgeLabel.tsx +5 -3
- package/src/extensions/react-flow/edges/_edges.scss +3 -2
- package/src/index.scss +1 -0
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
3
|
+
import {ValidIconName} from "../../components/Icon/canonicalIconNames";
|
|
4
|
+
import {IconProps} from "../../components/Icon/Icon";
|
|
5
|
+
import {TestIconProps} from "../../components/Icon/TestIcon";
|
|
6
|
+
import {TestableComponent} from "../../components/interfaces";
|
|
7
|
+
import {ProgressBarProps} from "../../components/ProgressBar/ProgressBar";
|
|
8
|
+
import {SpinnerProps} from "../../components/Spinner/Spinner";
|
|
9
|
+
import {CLASSPREFIX as eccgui} from "../../configuration/constants";
|
|
10
10
|
import {
|
|
11
11
|
Card,
|
|
12
12
|
ContextMenu,
|
|
13
|
+
DecoupledOverlay,
|
|
13
14
|
IconButton,
|
|
14
15
|
MenuItem,
|
|
16
|
+
Notification,
|
|
17
|
+
NotificationProps,
|
|
15
18
|
OverflowText,
|
|
16
19
|
OverviewItem,
|
|
17
20
|
OverviewItemActions,
|
|
@@ -97,7 +100,7 @@ interface IActivityContextMenu extends TestableComponent {
|
|
|
97
100
|
export interface ActivityControlWidgetAction extends TestableComponent {
|
|
98
101
|
// The action that should be triggered
|
|
99
102
|
action: () => void;
|
|
100
|
-
// The tooltip that should be shown over the action icon
|
|
103
|
+
// The tooltip that should be shown over the action icon on hover
|
|
101
104
|
tooltip?: string;
|
|
102
105
|
// The icon of the action button
|
|
103
106
|
icon: ValidIconName | React.ReactElement<TestIconProps>;
|
|
@@ -105,6 +108,16 @@ export interface ActivityControlWidgetAction extends TestableComponent {
|
|
|
105
108
|
disabled?: boolean;
|
|
106
109
|
// Warning state
|
|
107
110
|
hasStateWarning?: boolean;
|
|
111
|
+
// Active state
|
|
112
|
+
active?: boolean
|
|
113
|
+
/** A notification that is shown in an overlay pointing at the activity action button. */
|
|
114
|
+
notification?: {
|
|
115
|
+
message: string
|
|
116
|
+
onClose: () => void
|
|
117
|
+
intent?: NotificationProps["intent"]
|
|
118
|
+
// Timeout in ms before notification is closed. Default: none
|
|
119
|
+
timeout?: number
|
|
120
|
+
}
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
interface IActivityMenuAction extends ActivityControlWidgetAction {
|
|
@@ -209,28 +222,11 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
|
|
|
209
222
|
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-actions` : undefined}
|
|
210
223
|
>
|
|
211
224
|
{activityActions &&
|
|
212
|
-
activityActions.map((action, idx) =>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
? action.icon
|
|
218
|
-
: action["data-test-id"] ?? action["data-testid"] ?? idx
|
|
219
|
-
}
|
|
220
|
-
data-test-id={action["data-test-id"]}
|
|
221
|
-
data-testid={action["data-testid"]}
|
|
222
|
-
name={action.icon}
|
|
223
|
-
text={action.tooltip}
|
|
224
|
-
onClick={action.action}
|
|
225
|
-
disabled={action.disabled}
|
|
226
|
-
intent={action.hasStateWarning ? "warning" : undefined}
|
|
227
|
-
tooltipProps={{
|
|
228
|
-
hoverOpenDelay: 200,
|
|
229
|
-
placement: "bottom",
|
|
230
|
-
}}
|
|
231
|
-
/>
|
|
232
|
-
);
|
|
233
|
-
})}
|
|
225
|
+
activityActions.map((action, idx) => <ActivityActionButton
|
|
226
|
+
key={idx}
|
|
227
|
+
action={action}
|
|
228
|
+
/>
|
|
229
|
+
)}
|
|
234
230
|
{additionalActions}
|
|
235
231
|
{activityContextMenu && activityContextMenu.menuItems.length > 0 && (
|
|
236
232
|
<ContextMenu
|
|
@@ -241,11 +237,7 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
|
|
|
241
237
|
return (
|
|
242
238
|
<MenuItem
|
|
243
239
|
icon={menuAction.icon}
|
|
244
|
-
key={
|
|
245
|
-
typeof menuAction.icon === "string"
|
|
246
|
-
? menuAction.icon
|
|
247
|
-
: menuAction["data-test-id"] ?? idx
|
|
248
|
-
}
|
|
240
|
+
key={idx}
|
|
249
241
|
onClick={menuAction.action}
|
|
250
242
|
text={menuAction.tooltip}
|
|
251
243
|
/>
|
|
@@ -267,3 +259,44 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
|
|
|
267
259
|
<div className={classname}>{widget}</div>
|
|
268
260
|
);
|
|
269
261
|
}
|
|
262
|
+
|
|
263
|
+
interface ActivityActionButtonProps {
|
|
264
|
+
action: ActivityControlWidgetAction
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const ActivityActionButton = ({action}: ActivityActionButtonProps) => {
|
|
268
|
+
const actionButtonRef = React.useRef(null);
|
|
269
|
+
const ActionButton = () => (
|
|
270
|
+
<IconButton
|
|
271
|
+
data-test-id={action["data-test-id"]}
|
|
272
|
+
data-testid={action["data-testid"]}
|
|
273
|
+
name={action.icon}
|
|
274
|
+
text={action.tooltip}
|
|
275
|
+
onClick={action.action}
|
|
276
|
+
disabled={action.disabled}
|
|
277
|
+
intent={action.hasStateWarning ? "warning" : undefined}
|
|
278
|
+
tooltipProps={{
|
|
279
|
+
hoverOpenDelay: 200,
|
|
280
|
+
placement: "bottom"
|
|
281
|
+
}}
|
|
282
|
+
active={action.active}
|
|
283
|
+
/>
|
|
284
|
+
)
|
|
285
|
+
return action.notification ?
|
|
286
|
+
<>
|
|
287
|
+
<span ref={actionButtonRef}>
|
|
288
|
+
<ActionButton/>
|
|
289
|
+
</span>
|
|
290
|
+
{actionButtonRef.current && (
|
|
291
|
+
<DecoupledOverlay targetSelectorOrElement={actionButtonRef.current} paddingSize={"small"}>
|
|
292
|
+
<Notification
|
|
293
|
+
message={action.notification.message}
|
|
294
|
+
intent={action.notification.intent ?? "neutral"}
|
|
295
|
+
onDismiss={action.notification.onClose}
|
|
296
|
+
timeout={action.notification.timeout}
|
|
297
|
+
/>
|
|
298
|
+
</DecoupledOverlay>
|
|
299
|
+
)}
|
|
300
|
+
</> :
|
|
301
|
+
<ActionButton/>
|
|
302
|
+
}
|
|
@@ -130,7 +130,7 @@ export const StickyNoteModal: React.FC<StickyNoteModalProps> = React.memo(
|
|
|
130
130
|
name={translate("noteLabel")}
|
|
131
131
|
id={"sticky-note-input"}
|
|
132
132
|
mode="markdown"
|
|
133
|
-
|
|
133
|
+
useToolbar
|
|
134
134
|
onChange={(value) => {
|
|
135
135
|
refNote.current = value;
|
|
136
136
|
}}
|
package/src/common/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { decode } from "he";
|
|
|
3
3
|
import { invisibleZeroWidthCharacters } from "./utils/characters";
|
|
4
4
|
import { colorCalculateDistance } from "./utils/colorCalculateDistance";
|
|
5
5
|
import decideContrastColorValue from "./utils/colorDecideContrastvalue";
|
|
6
|
-
import { getEnabledColorsFromPalette, textToColorHash } from "./utils/colorHash";
|
|
6
|
+
import { getEnabledColorPropertiesFromPalette, getEnabledColorsFromPalette, textToColorHash } from "./utils/colorHash";
|
|
7
7
|
import getColorConfiguration from "./utils/getColorConfiguration";
|
|
8
8
|
import { getScrollParent } from "./utils/getScrollParent";
|
|
9
9
|
import { getGlobalVar, setGlobalVar } from "./utils/globalVars";
|
|
@@ -22,6 +22,7 @@ export const utils = {
|
|
|
22
22
|
setGlobalVar,
|
|
23
23
|
getScrollParent,
|
|
24
24
|
getEnabledColorsFromPalette,
|
|
25
|
+
getEnabledColorPropertiesFromPalette,
|
|
25
26
|
textToColorHash,
|
|
26
27
|
reduceToText,
|
|
27
28
|
decodeHtmlEntities: decode,
|
|
@@ -28,7 +28,7 @@ export default class CssCustomProperties {
|
|
|
28
28
|
|
|
29
29
|
// Methods
|
|
30
30
|
|
|
31
|
-
customProperties = (props: getCustomPropertiesProps = {}): string
|
|
31
|
+
customProperties = (props: getCustomPropertiesProps = {}): [string, string][] | Record<string, string> => {
|
|
32
32
|
// FIXME:
|
|
33
33
|
// in case of performance issues results should get saved at least into intern variables
|
|
34
34
|
// other cache strategies could be also tested
|
|
@@ -104,7 +104,9 @@ export default class CssCustomProperties {
|
|
|
104
104
|
});
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
static listCustomProperties = (
|
|
107
|
+
static listCustomProperties = (
|
|
108
|
+
props: getCustomPropertiesProps = {}
|
|
109
|
+
): [string, string][] | Record<string, string> => {
|
|
108
110
|
const { removeDashPrefix = true, returnObject = true, filterName = () => true, ...filterProps } = props;
|
|
109
111
|
|
|
110
112
|
const customProperties = CssCustomProperties.listLocalCssStyleRuleProperties({
|
|
@@ -123,6 +125,6 @@ export default class CssCustomProperties {
|
|
|
123
125
|
|
|
124
126
|
return returnObject
|
|
125
127
|
? (Object.fromEntries(customProperties) as Record<string, string>)
|
|
126
|
-
: (customProperties as string
|
|
128
|
+
: (customProperties as [string, string][]);
|
|
127
129
|
};
|
|
128
130
|
}
|
|
@@ -6,10 +6,10 @@ import { colorCalculateDistance } from "./colorCalculateDistance";
|
|
|
6
6
|
import CssCustomProperties from "./CssCustomProperties";
|
|
7
7
|
|
|
8
8
|
type ColorOrFalse = Color | false;
|
|
9
|
-
type ColorWeight = 100 | 300 | 500 | 700 | 900;
|
|
10
|
-
type PaletteGroup = "identity" | "semantic" | "layout" | "extra";
|
|
9
|
+
export type ColorWeight = 100 | 300 | 500 | 700 | 900;
|
|
10
|
+
export type PaletteGroup = "identity" | "semantic" | "layout" | "extra";
|
|
11
11
|
|
|
12
|
-
interface getEnabledColorsProps {
|
|
12
|
+
export interface getEnabledColorsProps {
|
|
13
13
|
/** Specify the palette groups used to define the set of colors. */
|
|
14
14
|
includePaletteGroup?: PaletteGroup[];
|
|
15
15
|
/** Use only some weights of a color tint. */
|
|
@@ -21,20 +21,43 @@ interface getEnabledColorsProps {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const getEnabledColorsFromPaletteCache = new Map<string, Color[]>();
|
|
24
|
+
const getEnabledColorPropertiesFromPaletteCache = new Map<string, [string, string][]>();
|
|
24
25
|
|
|
25
|
-
export function getEnabledColorsFromPalette({
|
|
26
|
+
export function getEnabledColorsFromPalette(props: getEnabledColorsProps): Color[] {
|
|
27
|
+
const configId = JSON.stringify({
|
|
28
|
+
includePaletteGroup: props.includePaletteGroup,
|
|
29
|
+
includeColorWeight: props.includeColorWeight,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (getEnabledColorsFromPaletteCache.has(configId)) {
|
|
33
|
+
return getEnabledColorsFromPaletteCache.get(configId)!;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const colorPropertiesFromPalette = Object.values(getEnabledColorPropertiesFromPalette(props));
|
|
37
|
+
|
|
38
|
+
getEnabledColorsFromPaletteCache.set(
|
|
39
|
+
configId,
|
|
40
|
+
colorPropertiesFromPalette.map((color) => {
|
|
41
|
+
return Color(color[1]);
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return getEnabledColorsFromPaletteCache.get(configId)!;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getEnabledColorPropertiesFromPalette({
|
|
26
49
|
includePaletteGroup = ["layout"],
|
|
27
50
|
includeColorWeight = [100, 300, 500, 700, 900],
|
|
28
|
-
//
|
|
51
|
+
// (planned for later): includeMixedColors = false,
|
|
29
52
|
minimalColorDistance = COLORMINDISTANCE,
|
|
30
|
-
}: getEnabledColorsProps):
|
|
53
|
+
}: getEnabledColorsProps): [string, string][] {
|
|
31
54
|
const configId = JSON.stringify({
|
|
32
55
|
includePaletteGroup,
|
|
33
56
|
includeColorWeight,
|
|
34
57
|
});
|
|
35
58
|
|
|
36
|
-
if (
|
|
37
|
-
return
|
|
59
|
+
if (getEnabledColorPropertiesFromPaletteCache.has(configId)) {
|
|
60
|
+
return getEnabledColorPropertiesFromPaletteCache.get(configId)!;
|
|
38
61
|
}
|
|
39
62
|
|
|
40
63
|
const colorsFromPalette = new CssCustomProperties({
|
|
@@ -50,18 +73,18 @@ export function getEnabledColorsFromPalette({
|
|
|
50
73
|
const weight = parseInt(tint[2], 10) as ColorWeight;
|
|
51
74
|
return includePaletteGroup.includes(group) && includeColorWeight.includes(weight);
|
|
52
75
|
},
|
|
53
|
-
removeDashPrefix:
|
|
76
|
+
removeDashPrefix: true,
|
|
54
77
|
returnObject: true,
|
|
55
78
|
}).customProperties();
|
|
56
79
|
|
|
57
|
-
const colorsFromPaletteValues = Object.
|
|
80
|
+
const colorsFromPaletteValues = Object.entries(colorsFromPalette) as [string, string][];
|
|
58
81
|
|
|
59
82
|
const colorsFromPaletteWithEnoughDistance =
|
|
60
83
|
minimalColorDistance > 0
|
|
61
|
-
? colorsFromPaletteValues.reduce((enoughDistance: string[], color: string) => {
|
|
84
|
+
? colorsFromPaletteValues.reduce((enoughDistance: [string, string][], color: [string, string]) => {
|
|
62
85
|
if (enoughDistance.includes(color)) {
|
|
63
86
|
return enoughDistance.filter((checkColor) => {
|
|
64
|
-
const distance = colorCalculateDistance({ color1: color, color2: checkColor });
|
|
87
|
+
const distance = colorCalculateDistance({ color1: color[1], color2: checkColor[1] });
|
|
65
88
|
return checkColor === color || (distance && minimalColorDistance <= distance);
|
|
66
89
|
});
|
|
67
90
|
} else {
|
|
@@ -70,14 +93,9 @@ export function getEnabledColorsFromPalette({
|
|
|
70
93
|
}, colorsFromPaletteValues)
|
|
71
94
|
: colorsFromPaletteValues;
|
|
72
95
|
|
|
73
|
-
|
|
74
|
-
configId,
|
|
75
|
-
colorsFromPaletteWithEnoughDistance.map((color: string) => {
|
|
76
|
-
return Color(color);
|
|
77
|
-
})
|
|
78
|
-
);
|
|
96
|
+
getEnabledColorPropertiesFromPaletteCache.set(configId, colorsFromPaletteWithEnoughDistance);
|
|
79
97
|
|
|
80
|
-
return
|
|
98
|
+
return getEnabledColorPropertiesFromPaletteCache.get(configId)!;
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
function getColorcode(text: string): ColorOrFalse {
|
|
@@ -148,7 +166,7 @@ export function textToColorHash({
|
|
|
148
166
|
}
|
|
149
167
|
|
|
150
168
|
function stringToIntegerHash(inputString: string): number {
|
|
151
|
-
/* this function is
|
|
169
|
+
/* this function is idempotent, meaning it retrieves the same result for the same input
|
|
152
170
|
no matter how many times it's called */
|
|
153
171
|
// Convert the string to a hash code
|
|
154
172
|
let hashCode = 0;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
@use "sass:list";
|
|
3
3
|
|
|
4
4
|
:root {
|
|
5
|
+
// creating css custom properties from palette colors
|
|
5
6
|
@each $palette-group-name, $palette-group-tints in $eccgui-color-palette-light {
|
|
6
7
|
@each $palette-tint-name, $palette-tint-colors in $palette-group-tints {
|
|
7
8
|
@for $i from 1 through list.length($palette-tint-colors) {
|
|
@@ -12,4 +13,18 @@
|
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
// set aliases for base colors
|
|
18
|
+
--#{$eccgui}-color-primary: #{$eccgui-color-primary};
|
|
19
|
+
--#{$eccgui}-color-primary-contrast: #{$eccgui-color-primary-contrast};
|
|
20
|
+
--#{$eccgui}-color-accent: #{$eccgui-color-accent};
|
|
21
|
+
--#{$eccgui}-color-accent-contrast: #{$eccgui-color-accent-contrast};
|
|
22
|
+
--#{$eccgui}-color-success-foreground: #{$eccgui-color-success-text};
|
|
23
|
+
--#{$eccgui}-color-success-background: #{$eccgui-color-success-background};
|
|
24
|
+
--#{$eccgui}-color-info-foreground: #{$eccgui-color-info-text};
|
|
25
|
+
--#{$eccgui}-color-info-background: #{$eccgui-color-info-background};
|
|
26
|
+
--#{$eccgui}-color-warning-foreground: #{$eccgui-color-warning-text};
|
|
27
|
+
--#{$eccgui}-color-warning-background: #{$eccgui-color-warning-background};
|
|
28
|
+
--#{$eccgui}-color-danger-foreground: #{$eccgui-color-danger-text};
|
|
29
|
+
--#{$eccgui}-color-danger-background: #{$eccgui-color-danger-background};
|
|
15
30
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Meta, StoryFn } from "@storybook/react";
|
|
3
|
+
|
|
4
|
+
import { getEnabledColorsProps } from "../../common/utils/colorHash";
|
|
5
|
+
import textFieldTest from "../TextField/stories/TextField.stories";
|
|
6
|
+
|
|
7
|
+
import { ColorField, ColorFieldProps } from "./ColorField";
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: "Forms/ColorField",
|
|
11
|
+
component: ColorField,
|
|
12
|
+
argTypes: {
|
|
13
|
+
...textFieldTest.argTypes,
|
|
14
|
+
},
|
|
15
|
+
} as Meta<typeof ColorField>;
|
|
16
|
+
|
|
17
|
+
const Template: StoryFn<typeof ColorField> = (args) => <ColorField {...args}></ColorField>;
|
|
18
|
+
|
|
19
|
+
export const Default = Template.bind({});
|
|
20
|
+
Default.args = {
|
|
21
|
+
onChange: (e) => {
|
|
22
|
+
alert(e.target.value);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const NoPalettePresets = Template.bind({});
|
|
27
|
+
NoPalettePresets.args = {
|
|
28
|
+
...Default.args,
|
|
29
|
+
allowCustomColor: true,
|
|
30
|
+
colorPresets: [],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type TemplateColorHashProps = { stringForColorHashValue: string } & Pick<
|
|
34
|
+
ColorFieldProps,
|
|
35
|
+
"onChange" | "allowCustomColor"
|
|
36
|
+
> &
|
|
37
|
+
Pick<getEnabledColorsProps, "includeColorWeight" | "includePaletteGroup">;
|
|
38
|
+
|
|
39
|
+
const TemplateColorHash: StoryFn<TemplateColorHashProps> = (args: TemplateColorHashProps) => (
|
|
40
|
+
<ColorField
|
|
41
|
+
allowCustomColor={args.allowCustomColor}
|
|
42
|
+
colorPresets={ColorField.listColorPalettePresets({
|
|
43
|
+
includeColorWeight: args.includeColorWeight,
|
|
44
|
+
includePaletteGroup: args.includePaletteGroup,
|
|
45
|
+
})}
|
|
46
|
+
value={ColorField.calculateColorHashValue(args.stringForColorHashValue, {
|
|
47
|
+
allowCustomColor: args.allowCustomColor,
|
|
48
|
+
includeColorWeight: args.includeColorWeight,
|
|
49
|
+
includePaletteGroup: args.includePaletteGroup,
|
|
50
|
+
})}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Component provides a helper function to calculate a color hash from a text,
|
|
56
|
+
* that can be used as `value` or `defaultValue`.
|
|
57
|
+
*
|
|
58
|
+
* ```
|
|
59
|
+
* <ColorField value={ColorField.calculateColorHashValue("MyText")} />
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* You can add `options` to set the config for the color palette filters.
|
|
63
|
+
* The same default values like on `ColorField` are used for them.
|
|
64
|
+
*/
|
|
65
|
+
export const ColorHashValue = TemplateColorHash.bind({});
|
|
66
|
+
ColorHashValue.args = {
|
|
67
|
+
...Default.args,
|
|
68
|
+
allowCustomColor: true,
|
|
69
|
+
includeColorWeight: [300, 500, 700],
|
|
70
|
+
includePaletteGroup: ["layout", "extra"],
|
|
71
|
+
stringForColorHashValue: "My text that will used to create a color hash as initial value.",
|
|
72
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
|
|
5
|
+
import "@testing-library/jest-dom";
|
|
6
|
+
|
|
7
|
+
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
8
|
+
|
|
9
|
+
import { ColorField } from "./ColorField";
|
|
10
|
+
|
|
11
|
+
describe("ColorField", () => {
|
|
12
|
+
describe("rendering", () => {
|
|
13
|
+
it("renders without crashing, and correct component CSS class is applied", () => {
|
|
14
|
+
const { container } = render(<ColorField />);
|
|
15
|
+
expect(container).not.toBeEmptyDOMElement();
|
|
16
|
+
expect(container.getElementsByClassName(`${eccgui}-colorfield`).length).toBe(1);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("renders a color input by default (no palette presets)", () => {
|
|
20
|
+
const { container } = render(<ColorField colorPresets={[]} allowCustomColor={true} />);
|
|
21
|
+
expect(container.querySelector("input[type='color']")).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("renders a readonly text input when palette colors are configured, and custom picker CSS class is applied", () => {
|
|
25
|
+
const { container } = render(
|
|
26
|
+
<ColorField
|
|
27
|
+
className="my-custom-class"
|
|
28
|
+
colorPresets={[
|
|
29
|
+
["my-black", "#000000"],
|
|
30
|
+
["my-white", "#ffffff"],
|
|
31
|
+
]}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
// With default palette settings, a text input with readOnly is shown
|
|
35
|
+
expect(container.querySelector("input[type='text']")).toBeInTheDocument();
|
|
36
|
+
expect(container.querySelector("input[readonly]")).toBeInTheDocument();
|
|
37
|
+
expect(container.querySelector(`.${eccgui}-colorfield--custom-picker`)).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("applies additional className", () => {
|
|
41
|
+
render(<ColorField className="my-custom-class" colorPresets={[]} allowCustomColor={true} />);
|
|
42
|
+
expect(document.querySelector(".my-custom-class")).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("value handling", () => {
|
|
47
|
+
it("uses defaultValue as initial color", () => {
|
|
48
|
+
render(<ColorField defaultValue="#ff0000" colorPresets={[]} allowCustomColor={true} />);
|
|
49
|
+
const input = document.querySelector("input") as HTMLInputElement;
|
|
50
|
+
expect(input.value).toBe("#ff0000");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("uses value prop as initial color", () => {
|
|
54
|
+
render(<ColorField value="#00ff00" colorPresets={[]} allowCustomColor={true} />);
|
|
55
|
+
const input = document.querySelector("input") as HTMLInputElement;
|
|
56
|
+
expect(input.value).toBe("#00ff00");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("falls back to #000000 when no value or defaultValue is provided", () => {
|
|
60
|
+
render(<ColorField colorPresets={[]} allowCustomColor={true} />);
|
|
61
|
+
const input = document.querySelector("input") as HTMLInputElement;
|
|
62
|
+
expect(input.value).toBe("#000000");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("updates displayed value when value prop changes", () => {
|
|
66
|
+
const { rerender } = render(<ColorField value="#ff0000" colorPresets={[]} allowCustomColor={true} />);
|
|
67
|
+
let input = document.querySelector("input") as HTMLInputElement;
|
|
68
|
+
expect(input.value).toBe("#ff0000");
|
|
69
|
+
|
|
70
|
+
rerender(<ColorField value="#0000ff" colorPresets={[]} allowCustomColor={true} />);
|
|
71
|
+
input = document.querySelector("input") as HTMLInputElement;
|
|
72
|
+
expect(input.value).toBe("#0000ff");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("disabled state", () => {
|
|
77
|
+
it("is disabled when disabled prop is true", () => {
|
|
78
|
+
render(<ColorField disabled colorPresets={[]} allowCustomColor={true} />);
|
|
79
|
+
const input = document.querySelector("input") as HTMLInputElement;
|
|
80
|
+
expect(input).toBeDisabled();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("is disabled when no palette colors and allowCustomColor is false", () => {
|
|
84
|
+
render(<ColorField colorPresets={[]} allowCustomColor={false} />);
|
|
85
|
+
const input = document.querySelector("input") as HTMLInputElement;
|
|
86
|
+
expect(input).toBeDisabled();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("onChange callback", () => {
|
|
91
|
+
it("calls onChange when native color input changes", async () => {
|
|
92
|
+
const user = userEvent.setup();
|
|
93
|
+
const onChange = jest.fn();
|
|
94
|
+
render(<ColorField onChange={onChange} colorPresets={[]} allowCustomColor={true} />);
|
|
95
|
+
const input = document.querySelector("input[type='color']") as HTMLInputElement;
|
|
96
|
+
input.type = "text"; // for unknown reasons Jest seems not able to test it on color inputs
|
|
97
|
+
await user.type(input, "#123456");
|
|
98
|
+
expect(onChange).toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|