@onewelcome/react-lib-components 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/Button/Button.d.ts +0 -1
  2. package/dist/DataGrid/datagrid.interfaces.d.ts +1 -0
  3. package/dist/Form/Checkbox/Checkbox.d.ts +1 -1
  4. package/dist/Form/FormHelperText/FormHelperText.d.ts +1 -1
  5. package/dist/Form/FormSelectorWrapper/FormSelectorWrapper.d.ts +1 -1
  6. package/dist/Form/Input/Input.d.ts +2 -2
  7. package/dist/Form/Radio/Radio.d.ts +1 -1
  8. package/dist/Form/Select/Select.d.ts +1 -1
  9. package/dist/Form/Textarea/Textarea.d.ts +1 -6
  10. package/dist/Form/Toggle/Toggle.d.ts +1 -1
  11. package/dist/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.d.ts +1 -1
  12. package/dist/Form/Wrapper/InputWrapper/InputWrapper.d.ts +1 -1
  13. package/dist/Form/Wrapper/RadioWrapper/RadioWrapper.d.ts +1 -1
  14. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +1 -1
  15. package/dist/Form/Wrapper/TextareaWrapper/TextareaWrapper.d.ts +1 -1
  16. package/dist/Form/form.interfaces.d.ts +1 -0
  17. package/dist/Link/Link.d.ts +1 -2
  18. package/dist/Notifications/Banner/Banner.d.ts +11 -0
  19. package/dist/Tabs/TabButton.d.ts +0 -1
  20. package/dist/_BaseStyling_/BaseStyling.d.ts +2 -0
  21. package/dist/hooks/useDetermineStatusIcon.d.ts +3 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/react-lib-components.cjs.development.js +416 -318
  24. package/dist/react-lib-components.cjs.development.js.map +1 -1
  25. package/dist/react-lib-components.cjs.production.min.js +1 -1
  26. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  27. package/dist/react-lib-components.esm.js +416 -319
  28. package/dist/react-lib-components.esm.js.map +1 -1
  29. package/package.json +11 -11
  30. package/src/Button/BaseButton.module.scss +2 -2
  31. package/src/Button/Button.module.scss +4 -5
  32. package/src/Button/Button.tsx +0 -1
  33. package/src/Button/IconButton.module.scss +4 -5
  34. package/src/DataGrid/DataGrid.tsx +3 -2
  35. package/src/DataGrid/DataGridActions/DataGridActions.tsx +16 -9
  36. package/src/DataGrid/DataGridBody/DataGridCell.module.scss +2 -2
  37. package/src/DataGrid/DataGridHeader/DataGridHeader.test.tsx +8 -3
  38. package/src/DataGrid/DataGridHeader/DataGridHeader.tsx +3 -1
  39. package/src/DataGrid/datagrid.interfaces.ts +1 -0
  40. package/src/Form/Input/Input.module.scss +36 -25
  41. package/src/Form/Input/Input.test.tsx +10 -0
  42. package/src/Form/Input/Input.tsx +7 -5
  43. package/src/Form/Select/Select.module.scss +9 -6
  44. package/src/Form/Select/Select.test.tsx +11 -0
  45. package/src/Form/Select/Select.tsx +5 -9
  46. package/src/Form/Select/SelectService.ts +2 -2
  47. package/src/Form/Textarea/Textarea.module.scss +21 -13
  48. package/src/Form/Textarea/Textarea.test.tsx +8 -0
  49. package/src/Form/Textarea/Textarea.tsx +6 -12
  50. package/src/Form/Toggle/Toggle.module.scss +3 -3
  51. package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +7 -3
  52. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +2 -0
  53. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +12 -1
  54. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +15 -14
  55. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +2 -1
  56. package/src/Form/Wrapper/Wrapper/Wrapper.module.scss +2 -2
  57. package/src/Form/form.interfaces.ts +1 -0
  58. package/src/Link/Link.module.scss +5 -5
  59. package/src/Link/Link.tsx +14 -13
  60. package/src/Notifications/Banner/Banner.module.scss +76 -0
  61. package/src/Notifications/Banner/Banner.test.tsx +84 -0
  62. package/src/Notifications/Banner/Banner.tsx +78 -0
  63. package/src/Notifications/BaseModal/BaseModal.module.scss +2 -2
  64. package/src/Notifications/Snackbar/SnackbarContainer/SnackbarContainer.module.scss +2 -2
  65. package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.module.scss +4 -4
  66. package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.tsx +3 -2
  67. package/src/Popover/Popover.module.scss +2 -2
  68. package/src/Skeleton/Skeleton.module.scss +2 -2
  69. package/src/Tabs/TabButton.tsx +1 -2
  70. package/src/Tabs/Tabs.module.scss +2 -2
  71. package/src/Tabs/Tabs.tsx +13 -10
  72. package/src/Tiles/Tile.module.scss +4 -4
  73. package/src/Tooltip/Tooltip.module.scss +3 -3
  74. package/src/_BaseStyling_/BaseStyling.tsx +4 -0
  75. package/src/hooks/useDetermineStatusIcon.test.ts +28 -0
  76. package/src/hooks/useDetermineStatusIcon.tsx +35 -0
  77. package/src/index.ts +1 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "homepage": "http://onewelcome.github.io/react-lib-components",
3
3
  "name": "@onewelcome/react-lib-components",
4
- "version": "1.5.0",
4
+ "version": "1.6.0",
5
5
  "license": "Apache-2.0",
6
6
  "author": "OneWelcome B.V.",
7
7
  "main": "dist/index.js",
@@ -55,7 +55,7 @@
55
55
  "@babel/core": "^7.20.12",
56
56
  "@mdx-js/react": "^1.6.22",
57
57
  "@onewelcome/eslint-config-shared-codestyle": "^9.0.3",
58
- "@size-limit/preset-small-lib": "^7.0.8",
58
+ "@size-limit/preset-small-lib": "^8.1.2",
59
59
  "@storybook/addon-a11y": "^6.5.15",
60
60
  "@storybook/addon-docs": "^6.5.15",
61
61
  "@storybook/addon-essentials": "^6.5.15",
@@ -66,24 +66,24 @@
66
66
  "@storybook/preset-scss": "^1.0.3",
67
67
  "@storybook/react": "^6.5.15",
68
68
  "@storybook/theming": "^6.5.15",
69
- "@testing-library/dom": "^8.19.1",
69
+ "@testing-library/dom": "^8.20.0",
70
70
  "@testing-library/jest-dom": "^5.16.5",
71
71
  "@testing-library/react": "^12.1.5",
72
72
  "@testing-library/react-hooks": "^8.0.1",
73
73
  "@testing-library/user-event": "^13.5.0",
74
74
  "@tsconfig/create-react-app": "^1.0.3",
75
- "@tsconfig/recommended": "^1.0.1",
75
+ "@tsconfig/recommended": "^1.0.2",
76
76
  "@types/color-convert": "^2.0.0",
77
77
  "@types/mdx": "^2.0.3",
78
78
  "@types/react": "^17.0.52",
79
79
  "@types/react-dom": "^17.0.18",
80
80
  "@types/react-router": "^5.1.20",
81
81
  "@types/react-router-dom": "^5.3.3",
82
- "@typescript-eslint/eslint-plugin": "^5.48.1",
83
- "@typescript-eslint/parser": "^5.48.1",
84
- "babel-loader": "^9.1.0",
82
+ "@typescript-eslint/eslint-plugin": "^5.49.0",
83
+ "@typescript-eslint/parser": "^5.49.0",
84
+ "babel-loader": "^9.1.2",
85
85
  "dts-cli": "^1.6.3",
86
- "eslint": "^8.31.0",
86
+ "eslint": "^8.32.0",
87
87
  "eslint-config-prettier": "^8.6.0",
88
88
  "eslint-plugin-cypress": "^2.12.1",
89
89
  "eslint-plugin-jest": "^27.2.1",
@@ -95,16 +95,16 @@
95
95
  "jest-junit": "^15.0.0",
96
96
  "lint-staged": "^13.1.0",
97
97
  "npm-run-all": "^4.1.5",
98
- "prettier": "^2.8.2",
98
+ "prettier": "^2.8.3",
99
99
  "react": "^17.0.2",
100
100
  "react-dom": "^17.0.2",
101
101
  "react-is": "^18.2.0",
102
102
  "react-router": "^6.6.2",
103
- "react-router-dom": "^6.6.2",
103
+ "react-router-dom": "^6.7.0",
104
104
  "rollup-plugin-cleanup": "^3.2.1",
105
105
  "rollup-plugin-styles": "^4.0.0",
106
106
  "sass": "^1.57.1",
107
- "size-limit": "^7.0.8",
107
+ "size-limit": "^8.1.1",
108
108
  "tslib": "^2.4.1",
109
109
  "typescript": "^4.9.4"
110
110
  }
@@ -14,8 +14,8 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- @import "../mixins.module.scss";
17
+ @use "../mixins.module.scss";
18
18
 
19
19
  .button {
20
- @include buttonBase();
20
+ @include mixins.buttonBase();
21
21
  }
@@ -14,19 +14,18 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- @import "../readyclasses.module.scss";
18
- @import "../mixins.module.scss";
17
+ @use "../mixins.module.scss";
19
18
 
20
19
  .fill {
21
- @include button("fill");
20
+ @include mixins.button("fill");
22
21
  }
23
22
 
24
23
  .outline {
25
- @include button("outline");
24
+ @include mixins.button("outline");
26
25
  }
27
26
 
28
27
  .text {
29
- @include button("text");
28
+ @include mixins.button("text");
30
29
  }
31
30
 
32
31
  .has-icon {
@@ -21,7 +21,6 @@ import classes from "./Button.module.scss";
21
21
  export interface Props extends BaseButtonProps {
22
22
  startIcon?: React.ReactNode | false;
23
23
  endIcon?: React.ReactNode | false;
24
- children?: React.ReactNode;
25
24
  variant?: "text" | "fill" | "outline";
26
25
  }
27
26
 
@@ -14,8 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- @import "../readyclasses.module.scss";
18
- @import "../mixins.module.scss";
17
+ @use "../mixins.module.scss";
19
18
 
20
19
  .icon-button {
21
20
  border: var(--button-border-width) var(--button-border-style) transparent;
@@ -57,14 +56,14 @@
57
56
  }
58
57
 
59
58
  &.fill {
60
- @include button("fill", "icon");
59
+ @include mixins.button("fill", "icon");
61
60
  }
62
61
 
63
62
  &.text {
64
- @include button("text", "icon");
63
+ @include mixins.button("text", "icon");
65
64
  }
66
65
 
67
66
  &.outline {
68
- @include button("outline", "icon");
67
+ @include mixins.button("outline", "icon");
69
68
  }
70
69
  }
@@ -144,14 +144,15 @@ const DataGridInner = <T extends {}>(
144
144
  spacing={styleWithSpacing}
145
145
  />
146
146
  <DataGridBody
147
- children={children}
148
147
  data={data}
149
148
  headers={internalHeaders}
150
149
  isLoading={isLoading}
151
150
  disableContextMenuColumn={disableContextMenuColumn}
152
151
  emptyLabel={emptyLabel}
153
152
  spacing={styleWithSpacing}
154
- />
153
+ >
154
+ {children}
155
+ </DataGridBody>
155
156
  </table>
156
157
  </div>
157
158
  {paginationProps && !isLoading && (
@@ -52,6 +52,7 @@ const DataGridActionsComponent: ForwardRefRenderFunction<HTMLDivElement, Props>
52
52
  searchIconBtnProps = {},
53
53
  headers,
54
54
  onColumnToggled,
55
+ children,
55
56
  ...rest
56
57
  }: Props,
57
58
  ref
@@ -59,6 +60,9 @@ const DataGridActionsComponent: ForwardRefRenderFunction<HTMLDivElement, Props>
59
60
  const isHidden = !(enableAddBtn || enableColumnsBtn || enableSearchBtn);
60
61
  const [showColsPopover, setShowColsPopover] = useState(false);
61
62
  const showColumnBtn = useRef<HTMLButtonElement>(null);
63
+ const { children: addBtnChildren, ...restAddBtnProps } = addBtnProps;
64
+ const { children: columnsBtnChildren, ...restColumnsBtnProps } = columnsBtnProps;
65
+ const { children: searchBtnChildren, ...restSearchBtnProps } = searchBtnProps;
62
66
 
63
67
  return isHidden ? null : (
64
68
  <div {...rest} ref={ref} className={`${classes["actions"]} ${className ?? ""}`}>
@@ -70,9 +74,10 @@ const DataGridActionsComponent: ForwardRefRenderFunction<HTMLDivElement, Props>
70
74
  title="Add item"
71
75
  type="button"
72
76
  variant="outline"
73
- children="Add item"
74
- {...addBtnProps}
75
- />
77
+ {...restAddBtnProps}
78
+ >
79
+ {addBtnChildren ?? "Add item"}
80
+ </Button>
76
81
  )}
77
82
  </div>
78
83
  <div className={classes["right-actions"]}>
@@ -82,12 +87,13 @@ const DataGridActionsComponent: ForwardRefRenderFunction<HTMLDivElement, Props>
82
87
  startIcon={<Icon icon={Icons.Change} />}
83
88
  title="Show/hide columns"
84
89
  variant="text"
85
- children="Columns"
86
- {...columnsBtnProps}
90
+ {...restColumnsBtnProps}
87
91
  className={`${classes["desktop"]} ${columnsBtnProps?.className ?? ""}`}
88
92
  ref={showColumnBtn}
89
93
  onClick={() => setShowColsPopover(true)}
90
- />
94
+ >
95
+ {columnsBtnChildren ?? "Columns"}
96
+ </Button>
91
97
  <IconButton
92
98
  title="Show/hide columns"
93
99
  {...columnsBtnProps}
@@ -112,10 +118,11 @@ const DataGridActionsComponent: ForwardRefRenderFunction<HTMLDivElement, Props>
112
118
  startIcon={<Icon icon={Icons.TableSearch} />}
113
119
  title="Search"
114
120
  variant="text"
115
- children="Search"
116
- {...searchBtnProps}
121
+ {...restSearchBtnProps}
117
122
  className={`${classes["desktop"]} ${searchBtnProps?.className ?? ""}`}
118
- />
123
+ >
124
+ {searchBtnChildren ?? "Search"}
125
+ </Button>
119
126
  <IconButton
120
127
  title="Search"
121
128
  {...searchIconBtnProps}
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- @import "../../mixins.module.scss";
17
+ @use "../../mixins.module.scss";
18
18
 
19
19
  .cell {
20
20
  min-height: 3.5rem;
@@ -29,7 +29,7 @@
29
29
  }
30
30
 
31
31
  .loading {
32
- @include skeletonLoading();
32
+ @include mixins.skeletonLoading();
33
33
  border-radius: 0.5rem;
34
34
  height: 1.25rem;
35
35
  margin: 0.625rem 0;
@@ -21,8 +21,8 @@ import userEvent from "@testing-library/user-event";
21
21
 
22
22
  const defaultParams: Props = {
23
23
  headers: [
24
- { name: "firstName", headline: "First name" },
25
- { name: "lastName", headline: "Last name" }
24
+ { name: "firstName", headline: "First name", align: "center" },
25
+ { name: "lastName", headline: "Last name", align: "right" }
26
26
  ]
27
27
  };
28
28
 
@@ -36,7 +36,6 @@ const createDataGridHeader = (params?: (defaultParams: Props) => Props) => {
36
36
  container
37
37
  });
38
38
  const dataGridHeader = queries.getByTestId("dataGridHeader");
39
-
40
39
  return {
41
40
  ...queries,
42
41
  dataGridHeader
@@ -51,6 +50,12 @@ describe("DataGridHeader should render", () => {
51
50
  expect(getAllByRole("columnheader")).toHaveLength(2);
52
51
  expect(getByRole("cell")).toBeDefined(); //context-menu empty cell
53
52
 
53
+ const headerCells = dataGridHeader.querySelectorAll("th");
54
+
55
+ expect(headerCells).toHaveLength(2);
56
+ expect(headerCells[0]).toHaveStyle({ textAlign: "center" });
57
+ expect(headerCells[1]).toHaveStyle({ textAlign: "right" });
58
+
54
59
  expect(dataGridHeader.querySelectorAll("[data-icon]")).toHaveLength(0);
55
60
  });
56
61
 
@@ -77,7 +77,9 @@ const DataGridHeaderComponent: ForwardRefRenderFunction<HTMLTableSectionElement,
77
77
  return null;
78
78
  }
79
79
 
80
- let headerStyle: React.CSSProperties = {};
80
+ let headerStyle: React.CSSProperties = {
81
+ textAlign: header.align || "left"
82
+ };
81
83
 
82
84
  if (index === 0) {
83
85
  headerStyle.paddingLeft = spacing?.paddingLeft;
@@ -27,4 +27,5 @@ export interface HeaderCell {
27
27
  headline: string;
28
28
  disableSorting?: boolean;
29
29
  hidden?: boolean;
30
+ align?: "left" | "right" | "center";
30
31
  }
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- @import "../../mixins.module.scss";
17
+ @use "../../mixins.module.scss";
18
18
 
19
19
  .input-wrapper {
20
20
  position: relative;
@@ -25,7 +25,7 @@
25
25
  border-radius: var(--input-border-radius);
26
26
  background-color: var(--input-background-color);
27
27
  padding: 0 1.25rem;
28
- @include transition(all, 0.2s, ease-in-out);
28
+ @include mixins.transition(all, 0.2s, ease-in-out);
29
29
 
30
30
  // General autofill styles
31
31
  input:-webkit-autofill,
@@ -64,7 +64,33 @@
64
64
  }
65
65
  }
66
66
 
67
- @include outlineStates;
67
+ @include mixins.outlineStates;
68
+
69
+ [data-icon-status="success"],
70
+ [data-icon-status="error"] {
71
+ height: 100%;
72
+ width: 1.25rem;
73
+ box-sizing: border-box;
74
+ padding-top: calc(1rem - (1.25rem - 1rem));
75
+ font-size: 1.25rem;
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ z-index: 2;
80
+
81
+ &:before {
82
+ height: 1.25rem;
83
+ width: 1.25rem;
84
+ }
85
+ }
86
+
87
+ [data-icon-status="success"] {
88
+ color: var(--success);
89
+ }
90
+
91
+ [data-icon-status="error"] {
92
+ color: var(--error);
93
+ }
68
94
  }
69
95
 
70
96
  .input {
@@ -76,7 +102,7 @@
76
102
  box-sizing: border-box;
77
103
  border: 0;
78
104
  border-radius: var(--input-border-radius);
79
- @include transition(all, 0.2s, ease-in-out);
105
+ @include mixins.transition(all, 0.2s, ease-in-out);
80
106
  background-color: transparent;
81
107
  z-index: 1;
82
108
 
@@ -93,41 +119,26 @@
93
119
  width: auto;
94
120
  }
95
121
 
96
- @include browserOutlineDisabled;
122
+ @include mixins.browserOutlineDisabled;
97
123
  }
98
124
 
99
- @include outline;
125
+ @include mixins.outline;
100
126
 
101
127
  .error input {
102
128
  color: var(--error);
103
- padding-right: 3.5rem;
129
+ padding-right: 1.25rem;
104
130
 
105
131
  &.remove-extra-indent {
106
132
  padding-right: 0;
107
133
  }
108
134
  }
109
-
110
- .warning {
111
- color: var(--error);
112
- position: absolute;
113
- height: 100%;
114
- width: 1.25rem;
115
- right: 1.25rem;
116
- top: 0;
117
- font-size: 1.125rem;
118
- display: flex;
119
- align-items: center;
120
- justify-content: center;
121
- z-index: 2;
122
-
123
- &:before {
124
- height: 1.3125rem;
125
- }
135
+ .success input {
136
+ padding-right: 1.25rem;
126
137
  }
127
138
 
128
139
  .input-wrapper [data-prefix],
129
140
  .input-wrapper [data-suffix] {
130
- @include transition(all, 0.2s, ease-in-out);
141
+ @include mixins.transition(all, 0.2s, ease-in-out);
131
142
  display: block;
132
143
  z-index: 1;
133
144
  }
@@ -234,4 +234,14 @@ describe("It should render prefix and suffix ", () => {
234
234
  expect(input.querySelector("icon-warning")).toBeDefined();
235
235
  expect(getByText(suffix)).toBeDefined();
236
236
  });
237
+
238
+ it("success icon should be visible", () => {
239
+ const { input } = createInput(defaultParams => ({
240
+ ...defaultParams,
241
+ success: true
242
+ }));
243
+ const icon = input.querySelector(".icon-checkmark-circle-breakout");
244
+ expect(input.querySelector(".success")).toBeDefined();
245
+ expect(icon).toBeDefined();
246
+ });
237
247
  });
@@ -24,8 +24,8 @@ import React, {
24
24
  } from "react";
25
25
  import classes from "./Input.module.scss";
26
26
  import readyclasses from "../../readyclasses.module.scss";
27
- import { Icon, Icons } from "../../Icon/Icon";
28
27
  import { FormElement } from "../form.interfaces";
28
+ import { useDetermineStatusIcon } from "../../hooks/useDetermineStatusIcon";
29
29
 
30
30
  export const dateTypes = ["date", "time", "datetime-local"] as const;
31
31
 
@@ -39,7 +39,7 @@ export type Type =
39
39
  | "tel"
40
40
  | "url"
41
41
  | "hidden"
42
- | typeof dateTypes[number];
42
+ | (typeof dateTypes)[number];
43
43
 
44
44
  export interface Props extends ComponentPropsWithRef<"input">, FormElement {
45
45
  wrapperProps?: ComponentPropsWithRef<"div">;
@@ -52,6 +52,7 @@ export interface Props extends ComponentPropsWithRef<"input">, FormElement {
52
52
  const InputComponent: ForwardRefRenderFunction<HTMLInputElement, Props> = (
53
53
  {
54
54
  error = false,
55
+ success = false,
55
56
  className,
56
57
  name,
57
58
  style,
@@ -69,7 +70,6 @@ const InputComponent: ForwardRefRenderFunction<HTMLInputElement, Props> = (
69
70
  ) => {
70
71
  const [focus, setFocus] = useState(false);
71
72
  const inputWrapperRef = useRef<HTMLDivElement>(null);
72
- const errorIconRef = useRef<HTMLDivElement>(null);
73
73
  const suffixRef = useRef<HTMLDivElement>(null);
74
74
 
75
75
  useEffect(() => {
@@ -84,7 +84,6 @@ const InputComponent: ForwardRefRenderFunction<HTMLInputElement, Props> = (
84
84
  inputClassNames.push(classes["shrink-width-for-date-icon"]);
85
85
  className && inputClassNames.push(className);
86
86
 
87
- const iconClassNames = [classes["warning"]];
88
87
  const wrapperClasses = [classes["input-wrapper"]];
89
88
  const outlineClasses = [classes["outline"]];
90
89
 
@@ -95,6 +94,9 @@ const InputComponent: ForwardRefRenderFunction<HTMLInputElement, Props> = (
95
94
  disabled && wrapperClasses.push(classes["disabled"]) && outlineClasses.push(classes["disabled"]);
96
95
  error && wrapperClasses.push(classes["error"]) && outlineClasses.push(classes["error"]);
97
96
  focus && wrapperClasses.push(classes["focus"]) && outlineClasses.push(classes["focus"]);
97
+ success && wrapperClasses.push(classes["success"]);
98
+
99
+ const icon = useDetermineStatusIcon({ success, error });
98
100
 
99
101
  return (
100
102
  <div
@@ -126,12 +128,12 @@ const InputComponent: ForwardRefRenderFunction<HTMLInputElement, Props> = (
126
128
  className={inputClassNames.join(" ")}
127
129
  spellCheck={rest.spellCheck || false}
128
130
  />
131
+ {icon}
129
132
  {suffix && (
130
133
  <div ref={suffixRef} data-suffix className={classes["suffix"]}>
131
134
  <span>{suffix}</span>
132
135
  </div>
133
136
  )}
134
- {error && <Icon ref={errorIconRef} className={iconClassNames.join(" ")} icon={Icons.Error} />}
135
137
  <span className={outlineClasses.join(" ")}></span>
136
138
  </div>
137
139
  );
@@ -14,15 +14,14 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- @import "../../readyclasses.module.scss";
18
- @import "../../mixins.module.scss";
17
+ @use "../../mixins.module.scss";
19
18
 
20
19
  $listItemHeight: 2.125rem;
21
20
 
22
21
  .select {
23
22
  position: relative;
24
23
  box-sizing: border-box;
25
- @include transition(all, 0.2s, ease-in-out);
24
+ @include mixins.transition(all, 0.2s, ease-in-out);
26
25
  border: 0;
27
26
  border-radius: var(--input-border-radius);
28
27
  background-color: var(--input-background-color);
@@ -57,7 +56,6 @@ $listItemHeight: 2.125rem;
57
56
  position: relative;
58
57
  width: 100%;
59
58
  min-height: calc(4rem - (2 * var(--input-border-width)));
60
- border: 0;
61
59
  padding: 0 1.25rem;
62
60
  background-color: transparent;
63
61
  border-color: var(--light-grey-border);
@@ -66,7 +64,7 @@ $listItemHeight: 2.125rem;
66
64
  border-radius: var(--input-border-radius);
67
65
  font-size: var(--font-size);
68
66
  cursor: pointer;
69
- @include transition(all, 0.2s, ease-in-out);
67
+ @include mixins.transition(all, 0.2s, ease-in-out);
70
68
 
71
69
  &:focus {
72
70
  outline: 0;
@@ -173,7 +171,12 @@ $listItemHeight: 2.125rem;
173
171
  display: flex;
174
172
  align-items: center;
175
173
 
176
- .warning {
174
+ [data-icon-status="success"] {
175
+ color: var(--success);
176
+ font-size: 1.25rem;
177
+ }
178
+
179
+ [data-icon-status="error"] {
177
180
  color: var(--error);
178
181
  font-size: 1.25rem;
179
182
  }
@@ -119,6 +119,17 @@ describe("Select should render", () => {
119
119
  expect(button).toHaveAttribute("aria-invalid", "true");
120
120
  expect(select.querySelector("[data-clear]")).not.toBeInTheDocument();
121
121
  });
122
+
123
+ it("should have a success icon when success state", () => {
124
+ const { button } = createSelect(defaultParams => ({
125
+ ...defaultParams,
126
+ success: true
127
+ }));
128
+
129
+ const icon = button.querySelector("[class*='icon-checkmark-circle-breakout']");
130
+ expect(button).toHaveClass("success");
131
+ expect(icon).toBeDefined();
132
+ });
122
133
  });
123
134
 
124
135
  describe("ref should work", () => {
@@ -33,6 +33,7 @@ import { useBodyClick } from "../../hooks/useBodyClick";
33
33
  import readyclasses from "../../readyclasses.module.scss";
34
34
  import { filterProps } from "../../util/helper";
35
35
  import { useArrowNavigation, useSelectPositionList } from "./SelectService";
36
+ import { useDetermineStatusIcon } from "../../hooks/useDetermineStatusIcon";
36
37
 
37
38
  type PartialInputProps = Partial<InputProps>;
38
39
 
@@ -67,6 +68,7 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
67
68
  selectButtonProps,
68
69
  className,
69
70
  error = false,
71
+ success = false,
70
72
  value,
71
73
  clearLabel = "Clear selection",
72
74
  onChange,
@@ -90,7 +92,6 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
90
92
  const nativeSelect = (ref as React.RefObject<HTMLSelectElement>) || createRef();
91
93
  const searchInputRef = useRef<HTMLInputElement>(null);
92
94
  const customSelectButtonRef = useRef<HTMLButtonElement>(null);
93
-
94
95
  const { onArrowNavigation } = useArrowNavigation({
95
96
  expanded,
96
97
  setExpanded,
@@ -183,13 +184,7 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
183
184
  setFilter(event.currentTarget.value);
184
185
  };
185
186
 
186
- const statusIcon = () => {
187
- if (error) {
188
- return <Icon className={classes["warning"]} icon={Icons.Warning} />;
189
- }
190
-
191
- return null;
192
- };
187
+ const icon = useDetermineStatusIcon({ success, error });
193
188
 
194
189
  const nativeOnChangeHandler = (event: React.ChangeEvent<HTMLSelectElement>) => {
195
190
  onChange && onChange(event);
@@ -214,6 +209,7 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
214
209
  error && additionalClasses.push(classes.error);
215
210
  disabled && additionalClasses.push(classes.disabled);
216
211
  className && additionalClasses.push(className);
212
+ success && additionalClasses.push(classes.success);
217
213
 
218
214
  /** The native select is purely for external form libraries. We use it to emit an onChange with native select event object so they know exactly what's happening. */
219
215
  return (
@@ -262,7 +258,7 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
262
258
  {value?.length > 0 && <span data-display-inner>{display}</span>}
263
259
  </div>
264
260
  <div className={classes["status"]}>
265
- {statusIcon()}
261
+ {icon}
266
262
  <Icon className={classes["triangle-down"]} icon={Icons.TriangleDown} />
267
263
  </div>
268
264
  </button>
@@ -174,7 +174,7 @@ export const useSelectPositionList = ({
174
174
 
175
175
  const calculateOptionListMaxHeight = (position: Position) => {
176
176
  // Calculate max height if there's more space below the select
177
- const listHeight = optionListReference.current!.getBoundingClientRect().height;
177
+ const listHeight = optionListReference.current?.getBoundingClientRect().height;
178
178
  const transformOrigin = position.top !== "initial" ? "top" : "bottom";
179
179
 
180
180
  const availableSpace =
@@ -184,7 +184,7 @@ export const useSelectPositionList = ({
184
184
  16
185
185
  : containerReference.current!.getBoundingClientRect()[transformOrigin] - 16;
186
186
 
187
- if (availableSpace < listHeight) {
187
+ if (listHeight && availableSpace < listHeight) {
188
188
  setOptionsListMaxHeight(`${availableSpace}px`);
189
189
  setOpacity(100);
190
190
  return;