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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/dist/cjs/cmem/markdown/Markdown.js +1 -1
  3. package/dist/cjs/cmem/markdown/Markdown.js.map +1 -1
  4. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +3 -3
  5. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  6. package/dist/cjs/components/Breadcrumb/BreadcrumbList.js +1 -1
  7. package/dist/cjs/components/Breadcrumb/BreadcrumbList.js.map +1 -1
  8. package/dist/cjs/components/Card/Card.js +3 -1
  9. package/dist/cjs/components/Card/Card.js.map +1 -1
  10. package/dist/cjs/components/MultiSelect/MultiSelect.js +8 -34
  11. package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
  12. package/dist/cjs/components/Sticky/StickyTarget.js +46 -5
  13. package/dist/cjs/components/Sticky/StickyTarget.js.map +1 -1
  14. package/dist/cjs/components/TextField/TextArea.js +85 -8
  15. package/dist/cjs/components/TextField/TextArea.js.map +1 -1
  16. package/dist/esm/cmem/markdown/Markdown.js +1 -1
  17. package/dist/esm/cmem/markdown/Markdown.js.map +1 -1
  18. package/dist/esm/components/AutocompleteField/AutoCompleteField.js +3 -3
  19. package/dist/esm/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  20. package/dist/esm/components/Breadcrumb/BreadcrumbList.js +1 -1
  21. package/dist/esm/components/Breadcrumb/BreadcrumbList.js.map +1 -1
  22. package/dist/esm/components/Card/Card.js +4 -2
  23. package/dist/esm/components/Card/Card.js.map +1 -1
  24. package/dist/esm/components/MultiSelect/MultiSelect.js +8 -34
  25. package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
  26. package/dist/esm/components/Sticky/StickyTarget.js +46 -5
  27. package/dist/esm/components/Sticky/StickyTarget.js.map +1 -1
  28. package/dist/esm/components/TextField/TextArea.js +86 -9
  29. package/dist/esm/components/TextField/TextArea.js.map +1 -1
  30. package/dist/types/cmem/ActivityControl/ActivityControlTypes.d.ts +1 -0
  31. package/dist/types/components/AutocompleteField/AutoCompleteField.d.ts +1 -1
  32. package/dist/types/components/Breadcrumb/BreadcrumbList.d.ts +2 -1
  33. package/dist/types/components/Card/Card.d.ts +8 -2
  34. package/dist/types/components/MultiSelect/MultiSelect.d.ts +14 -9
  35. package/dist/types/components/Sticky/StickyTarget.d.ts +14 -3
  36. package/dist/types/components/TextField/TextArea.d.ts +28 -3
  37. package/package.json +1 -1
  38. package/src/cmem/ActivityControl/ActivityControlTypes.ts +2 -0
  39. package/src/cmem/markdown/Markdown.tsx +1 -1
  40. package/src/components/Application/application.scss +0 -1
  41. package/src/components/AutocompleteField/AutoCompleteField.tsx +4 -4
  42. package/src/components/Breadcrumb/BreadcrumbList.tsx +3 -3
  43. package/src/components/Card/Card.tsx +15 -3
  44. package/src/components/Card/card.scss +6 -1
  45. package/src/components/Icon/stories/Icon.stories.tsx +1 -1
  46. package/src/components/MultiSelect/MultiSelect.tsx +30 -43
  47. package/src/components/MultiSuggestField/MultiSuggestField.stories.tsx +1 -2
  48. package/src/components/MultiSuggestField/tests/MultiSuggestField.test.tsx +90 -6
  49. package/src/components/Sticky/StickyTarget.tsx +63 -7
  50. package/src/components/Sticky/sticky.scss +71 -12
  51. package/src/components/TextField/TextArea.tsx +174 -12
  52. package/src/components/TextField/stories/TextArea.stories.tsx +39 -12
  53. package/src/components/TextField/textfield.scss +81 -11
  54. package/src/includes/blueprintjs/_requisits.scss +1 -1
  55. package/src/includes/blueprintjs/_variables.scss +3 -172
  56. package/src/includes/carbon-components/_requisits.scss +1 -0
@@ -13,15 +13,26 @@ export interface StickyTargetProps extends React.HTMLAttributes<HTMLDivElement>
13
13
  * The application header is not taken into offset calculation
14
14
  */
15
15
  local?: boolean;
16
+ /**
17
+ * Set additional distance to original sticky position.
18
+ */
19
+ offset?: `${number}${string}`;
16
20
  /**
17
21
  * Set the background color used for the sticky area.
18
22
  * As it can overlay other content readability could be harmed if the overlayed content is shining through.
19
23
  */
20
24
  background?: "card" | "application" | "transparent";
21
25
  /**
22
- * Set additional distance to original sticky position.
26
+ * In some situations there could be a gap between sticky target area and the border of the related scroll area.
27
+ * The main gap is the gap towards the direction of the sticky behaviour, specified by `to`.
28
+ * You can fill this gap with a gradient or full background color.
23
29
  */
24
- offset?: `${number}${string}`;
30
+ fillMainGap?: "full" | "gradient";
31
+ /**
32
+ * The secondary gap is the gap against the direction of the sticky behaviour.
33
+ * So in case of `to="top"` this is rendered on the bottom of the sticky area.
34
+ */
35
+ fillSecondaryGap?: "full" | "gradient";
25
36
  /**
26
37
  * Callback that returns an DOM element.
27
38
  * The position of `StickyTarget` is then calculated relative to that element.
@@ -37,8 +48,10 @@ export const StickyTarget = ({
37
48
  className,
38
49
  to = "top",
39
50
  local = false,
40
- background = "transparent",
41
51
  offset,
52
+ background = "transparent",
53
+ fillMainGap,
54
+ fillSecondaryGap,
42
55
  style,
43
56
  getConnectedElement,
44
57
  ...otherDivProps
@@ -50,8 +63,13 @@ export const StickyTarget = ({
50
63
  offsetStyle = { ...style, "--eccgui-sticky-target-localoffset": offset } as CSSProperties;
51
64
  }
52
65
 
53
- let connectedOffset = 0;
54
66
  React.useEffect(() => {
67
+ let removeEventForConnectedOffset = () => {
68
+ /* no event need to be removed */
69
+ };
70
+ let removeEventForStickynessCheck = () => {
71
+ /* no event need to be removed */
72
+ };
55
73
  /**
56
74
  * If the target should be sticky to a defined element then:
57
75
  * * check for the element and its scroll parent
@@ -64,6 +82,7 @@ export const StickyTarget = ({
64
82
  const scrollParentFallback = !scrollParent ? document.documentElement : false;
65
83
  if (scrollParent || scrollParentFallback) {
66
84
  const updateTargetOffset = () => {
85
+ let connectedOffset = 0;
67
86
  const scrollParentPosition = (
68
87
  (scrollParent || scrollParentFallback) as HTMLElement
69
88
  ).getBoundingClientRect();
@@ -85,20 +104,55 @@ export const StickyTarget = ({
85
104
  `${connectedOffset}px`
86
105
  );
87
106
  };
107
+
88
108
  updateTargetOffset();
89
109
  const eventListeningTarget = scrollParent || window;
90
110
  const eventListeningMethod = (_event: Event) => {
91
111
  updateTargetOffset();
92
112
  };
93
113
  eventListeningTarget.addEventListener("scroll", eventListeningMethod);
94
- return () => {
114
+ removeEventForConnectedOffset = () => {
95
115
  eventListeningTarget.removeEventListener("scroll", eventListeningMethod);
96
116
  };
97
117
  }
98
118
  }
99
119
  }
100
- return;
101
- }, [getConnectedElement, stickyTargetRef, to]);
120
+ /**
121
+ * Check if sticky target element is currently in sticky mode.
122
+ * sticky mode = current position === defined sticky position
123
+ */
124
+ if (stickyTargetRef && (fillMainGap || fillSecondaryGap)) {
125
+ const stickyTarget = stickyTargetRef.current as Element;
126
+ const scrollParent = utils.getScrollParent(stickyTarget);
127
+ const checkStickyness = () => {
128
+ const definedPosition = parseInt(window.getComputedStyle(stickyTarget)[to], 10);
129
+ const scrollParentPosition = scrollParent ? scrollParent.getBoundingClientRect()[to] : 0;
130
+ const currentPosition =
131
+ (to === "top" ? 1 : -1) * (stickyTarget.getBoundingClientRect()[to] - scrollParentPosition);
132
+ // check stickyness in a small position range (not exact value) because of float values
133
+ const isSticky = currentPosition <= definedPosition + 1 && currentPosition >= definedPosition - 1;
134
+ if (isSticky && !stickyTarget.classList.contains(`${eccgui}-sticky__target--issticky`)) {
135
+ stickyTarget.classList.add(`${eccgui}-sticky__target--issticky`);
136
+ }
137
+ if (!isSticky && stickyTarget.classList.contains(`${eccgui}-sticky__target--issticky`)) {
138
+ stickyTarget.classList.remove(`${eccgui}-sticky__target--issticky`);
139
+ }
140
+ };
141
+ checkStickyness();
142
+ const eventListeningTarget = scrollParent || window;
143
+ const eventListeningMethod = (_event: Event) => {
144
+ checkStickyness();
145
+ };
146
+ eventListeningTarget.addEventListener("scroll", eventListeningMethod);
147
+ removeEventForStickynessCheck = () => {
148
+ eventListeningTarget.removeEventListener("scroll", eventListeningMethod);
149
+ };
150
+ }
151
+ return () => {
152
+ removeEventForConnectedOffset();
153
+ removeEventForStickynessCheck();
154
+ };
155
+ }, [getConnectedElement, stickyTargetRef, to, fillMainGap, fillSecondaryGap]);
102
156
 
103
157
  return (
104
158
  <div
@@ -108,6 +162,8 @@ export const StickyTarget = ({
108
162
  (to ? ` ${eccgui}-sticky__target--${to}` : "") +
109
163
  (local ? ` ${eccgui}-sticky__target--localscrollarea` : "") +
110
164
  (background ? ` ${eccgui}-sticky__target--bg-${background}` : "") +
165
+ (fillMainGap ? ` ${eccgui}-sticky__target--maingapfill-${fillMainGap}` : "") +
166
+ (fillSecondaryGap ? ` ${eccgui}-sticky__target--secondarygapfill-${fillSecondaryGap}` : "") +
111
167
  (className ? ` ${className}` : "")
112
168
  }
113
169
  style={offset ? offsetStyle : style}
@@ -5,11 +5,14 @@
5
5
  .#{$eccgui}-sticky__target {
6
6
  position: sticky;
7
7
  z-index: 1;
8
+ background-color: var(--eccgui-sticky-target-bgcolor, transparent);
8
9
 
9
10
  --eccgui-sticky-target-applicationoffset: 0px;
11
+ --eccgui-sticky-target-maingap: 0px;
10
12
 
11
13
  .#{$eccgui}-application__content &:not(.#{$eccgui}-sticky__target--localscrollarea) {
12
- --eccgui-sticky-target-applicationoffset: $eccgui-size-block-whitespace;
14
+ --eccgui-sticky-target-applicationoffset: #{$eccgui-size-block-whitespace};
15
+ --eccgui-sticky-target-maingap: #{$eccgui-size-block-whitespace};
13
16
  }
14
17
 
15
18
  .#{$eccgui}-application__header
@@ -17,53 +20,109 @@
17
20
  &:not(.#{$eccgui}-sticky__target--localscrollarea) {
18
21
  --eccgui-sticky-target-applicationoffset: calc(#{mini-units(8)} + #{$eccgui-size-block-whitespace});
19
22
  }
23
+
24
+ &[class*="#{$eccgui}-sticky__target--maingapfill"]:before,
25
+ &[class*="#{$eccgui}-sticky__target--secondarygapfill"]:after {
26
+ position: absolute;
27
+ left: 0;
28
+ width: 100%;
29
+ height: calc(var(--eccgui-sticky-target-maingap) + var(--eccgui-sticky-target-localoffset, 0px));
30
+ }
31
+
32
+ &[class*="#{$eccgui}-sticky__target--secondarygapfill"]:after {
33
+ height: $eccgui-size-block-whitespace * 0.5;
34
+ }
20
35
  }
21
36
 
22
37
  .#{$eccgui}-sticky__target--top {
23
38
  top: calc(var(--eccgui-sticky-target-applicationoffset) + var(--eccgui-sticky-target-localoffset, 0px));
39
+
40
+ &[class*="#{$eccgui}-sticky__target--maingapfill"]:before {
41
+ bottom: 100%;
42
+ }
43
+
44
+ &[class*="#{$eccgui}-sticky__target--secondarygapfill"]:after {
45
+ top: 100%;
46
+ }
47
+
48
+ &.#{$eccgui}-sticky__target--maingapfill-gradient:before {
49
+ background: linear-gradient(transparent, 10%, var(--eccgui-sticky-target-bgcolor, transparent));
50
+ }
51
+
52
+ &.#{$eccgui}-sticky__target--secondarygapfill-gradient:after {
53
+ background: linear-gradient(var(--eccgui-sticky-target-bgcolor, transparent), transparent);
54
+ }
24
55
  }
25
56
 
26
57
  .#{$eccgui}-sticky__target--bottom {
27
58
  --eccgui-sticky-target-applicationoffset: 0px;
28
59
 
29
60
  bottom: calc(var(--eccgui-sticky-target-applicationoffset) + var(--eccgui-sticky-target-localoffset, 0px));
61
+
62
+ &[class*="#{$eccgui}-sticky__target--maingapfill"]:before {
63
+ top: 100%;
64
+ }
65
+
66
+ &[class*="#{$eccgui}-sticky__target--secondarygapfill"]:after {
67
+ bottom: 100%;
68
+ }
69
+
70
+ &.#{$eccgui}-sticky__target--maingapfill-gradient:before {
71
+ background: linear-gradient(var(--eccgui-sticky-target-bgcolor, 90%, transparent), transparent);
72
+ }
73
+
74
+ &.#{$eccgui}-sticky__target--secondarygapfill-gradient:after {
75
+ background: linear-gradient(transparent, var(--eccgui-sticky-target-bgcolor, transparent));
76
+ }
30
77
  }
31
78
 
32
79
  .#{$eccgui}-sticky__target--bg-card {
33
- background-color: $card-background-color;
80
+ --eccgui-sticky-target-bgcolor: #{$card-background-color};
34
81
 
35
82
  .#{$eccgui}-card.#{$eccgui}-intent--primary & {
36
- background-color: color.mix($eccgui-color-primary, $card-background-color, 5%);
83
+ --eccgui-sticky-target-bgcolor: #{color.mix($eccgui-color-primary, $card-background-color, 5%)};
37
84
  }
38
85
  .#{$eccgui}-card.#{$eccgui}-intent--accent & {
39
- background-color: color.mix($eccgui-color-accent, $card-background-color, 10%);
86
+ --eccgui-sticky-target-bgcolor: #{color.mix($eccgui-color-accent, $card-background-color, 10%)};
40
87
  }
41
88
  .#{$eccgui}-card.#{$eccgui}-intent--success & {
42
- background-color: $eccgui-color-success-background;
89
+ --eccgui-sticky-target-bgcolor: #{$eccgui-color-success-background};
43
90
  }
44
91
  .#{$eccgui}-card.#{$eccgui}-intent--info & {
45
- background-color: $eccgui-color-info-background;
92
+ --eccgui-sticky-target-bgcolor: #{$eccgui-color-info-background};
46
93
  }
47
94
  .#{$eccgui}-card.#{$eccgui}-intent--warning & {
48
- background-color: $eccgui-color-warning-background;
95
+ --eccgui-sticky-target-bgcolor: #{$eccgui-color-warning-background};
49
96
  }
50
97
  .#{$eccgui}-card.#{$eccgui}-intent--danger & {
51
- background-color: $eccgui-color-danger-background;
98
+ --eccgui-sticky-target-bgcolor: #{$eccgui-color-danger-background};
52
99
  }
53
100
 
54
101
  .#{$eccgui}-card.#{$ns}-interactive:hover & {
55
- background-color: $button-background-color-hover;
102
+ --eccgui-sticky-target-bgcolor: #{$button-background-color-hover};
56
103
  }
57
104
 
58
105
  .#{$eccgui}-card.#{$ns}-selected & {
59
- background-color: $card-selected-background-color;
106
+ --eccgui-sticky-target-bgcolor: #{$card-selected-background-color};
60
107
  }
61
108
 
62
109
  .#{$eccgui}-card--elevated & {
63
- background-color: $button-background-color-active;
110
+ --eccgui-sticky-target-bgcolor: #{$button-background-color-active};
64
111
  }
65
112
  }
66
113
 
67
114
  .#{$eccgui}-sticky__target--bg-application {
68
- background-color: $eccgui-color-application-background;
115
+ --eccgui-sticky-target-bgcolor: #{$eccgui-color-application-background};
116
+ }
117
+
118
+ .#{$eccgui}-sticky__target--issticky {
119
+ &[class*="#{$eccgui}-sticky__target--maingapfill"]:before,
120
+ &[class*="#{$eccgui}-sticky__target--secondarygapfill"]:after {
121
+ content: "";
122
+ }
123
+ }
124
+
125
+ .#{$eccgui}-sticky__target--maingapfill-full:before,
126
+ .#{$eccgui}-sticky__target--secondarygapfill-full:after {
127
+ background: var(--eccgui-sticky-target-bgcolor, transparent);
69
128
  }
@@ -1,35 +1,63 @@
1
1
  import React from "react";
2
2
  import {
3
+ Classes as BlueprintClassNames,
3
4
  Intent as BlueprintIntent,
5
+ MaybeElement,
4
6
  TextArea as BlueprintTextArea,
5
7
  TextAreaProps as BlueprintTextAreaProps,
6
8
  } from "@blueprintjs/core";
7
9
 
10
+ import { Definitions as IntentDefinitions, IntentTypes } from "../../common/Intent";
8
11
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
12
+ import { Icon } from "../Icon";
13
+ import { ValidIconName } from "../Icon/canonicalIconNames";
9
14
 
10
15
  import { InvisibleCharacterWarningProps, useTextValidation } from "./useTextValidation";
11
16
 
12
- export interface TextAreaProps extends Partial<BlueprintTextAreaProps> {
17
+ export interface TextAreaProps extends Omit<BlueprintTextAreaProps, "intent"> {
13
18
  /**
14
19
  * when set to true the input takes a blue border color
20
+ * @deprecated Use the `intent` property.
15
21
  */
16
22
  hasStatePrimary?: boolean;
17
23
  /**
18
24
  * when set to true the input takes a green border color
25
+ * @deprecated Use the `intent` property.
19
26
  */
20
27
  hasStateSuccess?: boolean;
21
28
  /**
22
29
  * when set to true the input takes an orange border color
30
+ * @deprecated Use the `intent` property.
23
31
  */
24
32
  hasStateWarning?: boolean;
25
33
  /**
26
34
  * when set to true the input takes a red border color
35
+ * @deprecated Use the `intent` property.
27
36
  */
28
37
  hasStateDanger?: boolean;
38
+ /**
39
+ * Intent state of the text area.
40
+ */
41
+ intent?: IntentTypes | "edited" | "removed";
29
42
  /**
30
43
  * If set, allows to be informed of invisible, hard to spot characters in the string value.
31
44
  */
32
45
  invisibleCharacterWarning?: InvisibleCharacterWarningProps;
46
+ /**
47
+ * Left aligned icon, can be a canonical icon name or an `Icon` element.
48
+ * This will update left padding on the text area.
49
+ */
50
+ leftIcon?: ValidIconName | MaybeElement;
51
+ /**
52
+ * Element to render on right side of text area. Should be not too large.
53
+ * This will update right padding on the text area.
54
+ */
55
+ rightElement?: JSX.Element;
56
+ /**
57
+ * Add HTML properties to the wrapper element.
58
+ * The element wraps `TextArea` in case of a given `wrapperDivProps`, `leftIcon` or `rightElement` property.
59
+ */
60
+ wrapperDivProps?: Omit<React.HTMLAttributes<HTMLDivElement>, "children">;
33
61
  }
34
62
 
35
63
  export const TextArea = ({
@@ -40,38 +68,172 @@ export const TextArea = ({
40
68
  hasStateDanger = false,
41
69
  rows = 5,
42
70
  invisibleCharacterWarning,
71
+ leftIcon,
72
+ rightElement,
73
+ wrapperDivProps,
43
74
  ...otherProps
44
75
  }: TextAreaProps) => {
45
- let intent;
76
+ const textAreaCallback = React.useCallback(
77
+ (textAreaElement: HTMLTextAreaElement) => {
78
+ if (textAreaElement && typeof textAreaElement === "object") {
79
+ let textAreaStyle: CSSStyleDeclaration;
80
+ if (!textAreaElement.dataset.processed) {
81
+ textAreaStyle = getComputedStyle(textAreaElement);
82
+ textAreaElement.dataset.processed = "yes";
83
+ textAreaElement.dataset.paddingtop = textAreaStyle.paddingTop ?? "0px";
84
+ textAreaElement.dataset.paddingleft = textAreaStyle.paddingLeft ?? "0px";
85
+ textAreaElement.dataset.paddingright = textAreaStyle.paddingRight ?? "0px";
86
+ } else {
87
+ textAreaStyle = {
88
+ paddingTop: textAreaElement.dataset.paddingtop ?? "0px",
89
+ paddingLeft: textAreaElement.dataset.paddingleft ?? "0px",
90
+ paddingRight: textAreaElement.dataset.paddingright ?? "0px",
91
+ } as CSSStyleDeclaration;
92
+ }
93
+ const textAreaElementRect = textAreaElement.getBoundingClientRect();
94
+ const wrapperElement = textAreaElement.parentElement;
95
+
96
+ if (leftIcon && wrapperElement) {
97
+ const leftIconElement = wrapperElement.querySelector(`.${eccgui}-textarea__icon`) as HTMLElement;
98
+ const leftIconElementRect = leftIconElement.getBoundingClientRect();
99
+ if (
100
+ parseInt(textAreaStyle.paddingTop, 10) * 2 + (leftIconElementRect.height ?? 0) <=
101
+ (textAreaElementRect.height ?? 0)
102
+ ) {
103
+ leftIconElement.style.setProperty("top", textAreaStyle.paddingTop);
104
+ } else {
105
+ leftIconElement.style.setProperty(
106
+ "top",
107
+ `${((textAreaElementRect.height ?? 0) - (leftIconElementRect.height ?? 0)) * 0.5}px`
108
+ );
109
+ }
110
+ leftIconElement.style.setProperty("left", textAreaStyle.paddingLeft);
111
+ textAreaElement.style.setProperty(
112
+ "padding-left",
113
+ `calc(${leftIconElementRect.width ? 2 : 1} * ${textAreaStyle.paddingLeft} + ${
114
+ leftIconElementRect.width ?? 0
115
+ }px)`
116
+ );
117
+ leftIconElement.addEventListener("click", (_event: MouseEvent) => {
118
+ textAreaElement.focus();
119
+ }); //onclick((_event: MouseEvent) => {textAreaElement.dispatchEvent("click")})
120
+ }
121
+
122
+ if (rightElement && wrapperElement) {
123
+ const rightElementElement = wrapperElement.querySelector(
124
+ `.${eccgui}-textarea__options`
125
+ ) as HTMLElement;
126
+ const rightElementElementRect = rightElementElement.getBoundingClientRect();
127
+ if (
128
+ parseInt(textAreaStyle.paddingTop, 10) * 2 + (rightElementElementRect.height ?? 0) <=
129
+ (textAreaElementRect.height ?? 0)
130
+ ) {
131
+ rightElementElement.style.setProperty("top", textAreaStyle.paddingTop);
132
+ } else {
133
+ rightElementElement.style.setProperty(
134
+ "top",
135
+ `${((textAreaElementRect.height ?? 0) - (rightElementElementRect.height ?? 0)) * 0.5}px`
136
+ );
137
+ }
138
+ rightElementElement.style.setProperty("right", textAreaStyle.paddingRight);
139
+ textAreaElement.style.setProperty(
140
+ "padding-right",
141
+ `calc(${rightElementElementRect.width ? 2 : 1} * ${textAreaStyle.paddingRight} + ${
142
+ rightElementElementRect.width ?? 0
143
+ }px)`
144
+ );
145
+ }
146
+ }
147
+ },
148
+ [leftIcon, rightElement]
149
+ );
150
+
151
+ let deprecatedIntent;
46
152
  switch (true) {
47
153
  case hasStatePrimary:
48
- intent = BlueprintIntent.PRIMARY;
154
+ deprecatedIntent = IntentDefinitions.PRIMARY;
49
155
  break;
50
156
  case hasStateSuccess:
51
- intent = BlueprintIntent.SUCCESS;
157
+ deprecatedIntent = IntentDefinitions.SUCCESS;
52
158
  break;
53
159
  case hasStateWarning:
54
- intent = BlueprintIntent.WARNING;
160
+ deprecatedIntent = IntentDefinitions.WARNING;
55
161
  break;
56
162
  case hasStateDanger:
57
- intent = BlueprintIntent.DANGER;
163
+ deprecatedIntent = IntentDefinitions.DANGER;
58
164
  break;
59
165
  default:
60
166
  break;
61
167
  }
62
168
 
63
- const maybeWrappedOnChange = useTextValidation({ ...otherProps, invisibleCharacterWarning });
169
+ const { intent = deprecatedIntent, ...otherBlueprintTextAreaProps } = otherProps;
64
170
 
65
- return (
171
+ let iconIntent;
172
+ switch (intent) {
173
+ case "edited":
174
+ iconIntent = IntentDefinitions.INFO;
175
+ break;
176
+ case "removed":
177
+ iconIntent = IntentDefinitions.DANGER;
178
+ break;
179
+ default:
180
+ iconIntent = intent as IntentTypes;
181
+ break;
182
+ }
183
+
184
+ const maybeWrappedOnChange = useTextValidation({ ...otherBlueprintTextAreaProps, invisibleCharacterWarning });
185
+
186
+ const textarea = (
66
187
  <BlueprintTextArea
67
- className={`${eccgui}-textarea ` + className}
68
- intent={intent}
69
- rows={rows ? rows : undefined}
70
- {...otherProps}
188
+ inputRef={textAreaCallback}
189
+ className={
190
+ `${eccgui}-textarea` +
191
+ (intent ? ` ${eccgui}-intent--${intent}` : "") +
192
+ (className ? ` ${className}` : "")
193
+ }
194
+ intent={
195
+ intent && !["info", "edited", "removed", "neutral"].includes(intent)
196
+ ? (intent as BlueprintIntent)
197
+ : undefined
198
+ }
199
+ spellCheck={intent === "removed" ? false : undefined}
200
+ rows={
201
+ rows && !otherBlueprintTextAreaProps.autoResize && !otherBlueprintTextAreaProps.growVertically
202
+ ? rows
203
+ : 1
204
+ }
205
+ {...otherBlueprintTextAreaProps}
71
206
  dir={"auto"}
72
207
  onChange={maybeWrappedOnChange}
73
208
  />
74
209
  );
210
+
211
+ const { className: wrapperClassName, ...otherWrapperDivProps } = wrapperDivProps ?? {};
212
+
213
+ return wrapperDivProps || leftIcon || rightElement ? (
214
+ <div
215
+ className={`${eccgui}-textarea__wrapper` + (wrapperClassName ? ` ${wrapperClassName}` : "")}
216
+ {...otherWrapperDivProps}
217
+ >
218
+ {textarea}
219
+ {leftIcon && (
220
+ <div className={`${eccgui}-textarea__icon`}>
221
+ {typeof leftIcon === "string" ? (
222
+ <Icon
223
+ name={leftIcon as ValidIconName}
224
+ className={BlueprintClassNames.ICON}
225
+ intent={iconIntent as IntentTypes | undefined}
226
+ />
227
+ ) : (
228
+ <span className={BlueprintClassNames.ICON}>{leftIcon}</span>
229
+ )}
230
+ </div>
231
+ )}
232
+ {rightElement && <div className={`${eccgui}-textarea__options`}>{rightElement}</div>}
233
+ </div>
234
+ ) : (
235
+ textarea
236
+ );
75
237
  };
76
238
 
77
239
  export default TextArea;
@@ -1,22 +1,49 @@
1
1
  import React from "react";
2
- import { ComponentMeta, ComponentStory } from "@storybook/react";
2
+ import { Meta, StoryFn } from "@storybook/react";
3
3
 
4
- import { TextArea as CustomTextArea } from "./../../../../index";
5
- type TextAreaType = typeof CustomTextArea;
4
+ import { helpersArgTypes } from "../../../../.storybook/helpers";
5
+
6
+ import { Button, IconButton, TextArea } from "./../../../../index";
7
+ type TextAreaType = typeof TextArea;
6
8
 
7
9
  export default {
8
10
  title: "Forms/TextArea",
9
- component: CustomTextArea,
10
- argTypes: {},
11
- } as ComponentMeta<TextAreaType>;
11
+ component: TextArea,
12
+ argTypes: {
13
+ leftIcon: {
14
+ ...helpersArgTypes.exampleIcon,
15
+ },
16
+ rightElement: {
17
+ control: helpersArgTypes.exampleIcon.control,
18
+ options: [...helpersArgTypes.exampleIcon.options, "Button element", "2 Icon buttons"],
19
+ mapping: {
20
+ ...helpersArgTypes.exampleIcon.mapping,
21
+ "Button element": (
22
+ <Button small onClick={() => alert("clicked")}>
23
+ Button label
24
+ </Button>
25
+ ),
26
+ "2 Icon buttons": (
27
+ <>
28
+ <IconButton name={"item-comment"} onClick={() => alert("1 clicked")} text="Button 1" />
29
+ <IconButton name={"item-edit"} onClick={() => alert("2 clicked")} text="Button 2" />
30
+ </>
31
+ ),
32
+ },
33
+ },
34
+ intent: {
35
+ ...helpersArgTypes.exampleIntent,
36
+ },
37
+ },
38
+ } as Meta<TextAreaType>;
12
39
 
13
- const Template: ComponentStory<TextAreaType> = (args) => <CustomTextArea {...args}></CustomTextArea>;
40
+ const Template: StoryFn<TextAreaType> = (args) => <TextArea {...args}></TextArea>;
14
41
 
15
42
  export const Default = Template.bind({});
16
43
  Default.args = {
17
- hasStatePrimary: false,
18
- hasStateSuccess: false,
19
- hasStateWarning: false,
20
- hasStateDanger: false,
21
- rows: 5,
44
+ rows: 10,
45
+ wrapperDivProps: {
46
+ "data-test-id": "textarea-test-id",
47
+ "data-testid": "textarea-testid",
48
+ },
22
49
  };