@onewelcome/react-lib-components 1.5.0 → 1.7.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 (101) hide show
  1. package/README.md +4 -4
  2. package/dist/Button/Button.d.ts +0 -1
  3. package/dist/DataGrid/datagrid.interfaces.d.ts +1 -0
  4. package/dist/Form/Checkbox/Checkbox.d.ts +1 -1
  5. package/dist/Form/FileUpload/FileItem/FileItem.d.ts +17 -0
  6. package/dist/Form/FileUpload/FileUpload.d.ts +26 -0
  7. package/dist/Form/FormHelperText/FormHelperText.d.ts +1 -1
  8. package/dist/Form/FormSelectorWrapper/FormSelectorWrapper.d.ts +1 -1
  9. package/dist/Form/Input/Input.d.ts +2 -2
  10. package/dist/Form/Radio/Radio.d.ts +1 -1
  11. package/dist/Form/Select/Select.d.ts +1 -1
  12. package/dist/Form/Textarea/Textarea.d.ts +1 -6
  13. package/dist/Form/Toggle/Toggle.d.ts +1 -1
  14. package/dist/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.d.ts +1 -1
  15. package/dist/Form/Wrapper/InputWrapper/InputWrapper.d.ts +1 -1
  16. package/dist/Form/Wrapper/RadioWrapper/RadioWrapper.d.ts +1 -1
  17. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +1 -1
  18. package/dist/Form/Wrapper/TextareaWrapper/TextareaWrapper.d.ts +1 -1
  19. package/dist/Form/form.interfaces.d.ts +1 -0
  20. package/dist/Icon/Icon.d.ts +4 -1
  21. package/dist/Link/Link.d.ts +1 -2
  22. package/dist/Notifications/Banner/Banner.d.ts +11 -0
  23. package/dist/ProgressBar/ProgressBar.d.ts +2 -1
  24. package/dist/Tabs/TabButton.d.ts +0 -1
  25. package/dist/_BaseStyling_/BaseStyling.d.ts +5 -0
  26. package/dist/hooks/useDetermineStatusIcon.d.ts +3 -0
  27. package/dist/hooks/useUploadFile.d.ts +22 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/react-lib-components.cjs.development.js +431 -326
  30. package/dist/react-lib-components.cjs.development.js.map +1 -1
  31. package/dist/react-lib-components.cjs.production.min.js +1 -1
  32. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  33. package/dist/react-lib-components.esm.js +431 -327
  34. package/dist/react-lib-components.esm.js.map +1 -1
  35. package/dist/util/helper.d.ts +5 -0
  36. package/package.json +28 -25
  37. package/src/Button/BaseButton.module.scss +2 -2
  38. package/src/Button/Button.module.scss +4 -5
  39. package/src/Button/Button.tsx +0 -1
  40. package/src/Button/IconButton.module.scss +4 -5
  41. package/src/DataGrid/DataGrid.tsx +3 -2
  42. package/src/DataGrid/DataGridActions/DataGridActions.tsx +16 -9
  43. package/src/DataGrid/DataGridBody/DataGridCell.module.scss +2 -2
  44. package/src/DataGrid/DataGridHeader/DataGridHeader.test.tsx +8 -3
  45. package/src/DataGrid/DataGridHeader/DataGridHeader.tsx +3 -1
  46. package/src/DataGrid/datagrid.interfaces.ts +1 -0
  47. package/src/Form/FileUpload/FileItem/FileItem.modules.scss +75 -0
  48. package/src/Form/FileUpload/FileItem/FileItem.test.tsx +103 -0
  49. package/src/Form/FileUpload/FileItem/FileItem.tsx +141 -0
  50. package/src/Form/FileUpload/FileUpload.module.scss +106 -0
  51. package/src/Form/FileUpload/FileUpload.test.tsx +374 -0
  52. package/src/Form/FileUpload/FileUpload.tsx +251 -0
  53. package/src/Form/Input/Input.module.scss +36 -26
  54. package/src/Form/Input/Input.test.tsx +10 -0
  55. package/src/Form/Input/Input.tsx +7 -5
  56. package/src/Form/Select/Select.module.scss +9 -6
  57. package/src/Form/Select/Select.test.tsx +11 -0
  58. package/src/Form/Select/Select.tsx +5 -9
  59. package/src/Form/Select/SelectService.ts +2 -2
  60. package/src/Form/Textarea/Textarea.module.scss +21 -13
  61. package/src/Form/Textarea/Textarea.test.tsx +8 -0
  62. package/src/Form/Textarea/Textarea.tsx +6 -12
  63. package/src/Form/Toggle/Toggle.module.scss +3 -3
  64. package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +7 -3
  65. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +2 -0
  66. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +12 -1
  67. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +15 -14
  68. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +2 -1
  69. package/src/Form/Wrapper/Wrapper/Wrapper.module.scss +2 -2
  70. package/src/Form/form.interfaces.ts +1 -0
  71. package/src/Icon/Icon.module.scss +12 -0
  72. package/src/Icon/Icon.tsx +4 -1
  73. package/src/Link/Link.module.scss +5 -5
  74. package/src/Link/Link.tsx +14 -13
  75. package/src/Notifications/Banner/Banner.module.scss +76 -0
  76. package/src/Notifications/Banner/Banner.test.tsx +84 -0
  77. package/src/Notifications/Banner/Banner.tsx +78 -0
  78. package/src/Notifications/BaseModal/BaseModal.module.scss +2 -2
  79. package/src/Notifications/Snackbar/SnackbarContainer/SnackbarContainer.module.scss +2 -2
  80. package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.module.scss +4 -4
  81. package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.tsx +3 -2
  82. package/src/Popover/Popover.module.scss +2 -2
  83. package/src/ProgressBar/ProgressBar.module.scss +11 -9
  84. package/src/ProgressBar/ProgressBar.test.tsx +21 -0
  85. package/src/ProgressBar/ProgressBar.tsx +7 -2
  86. package/src/Skeleton/Skeleton.module.scss +2 -2
  87. package/src/Tabs/TabButton.tsx +1 -2
  88. package/src/Tabs/Tabs.module.scss +2 -2
  89. package/src/Tabs/Tabs.tsx +13 -10
  90. package/src/Tiles/Tile.module.scss +4 -4
  91. package/src/Tooltip/Tooltip.module.scss +3 -3
  92. package/src/Typography/Typography.module.scss +2 -2
  93. package/src/_BaseStyling_/BaseStyling.tsx +13 -3
  94. package/src/hooks/useDetermineStatusIcon.test.ts +28 -0
  95. package/src/hooks/useDetermineStatusIcon.tsx +35 -0
  96. package/src/hooks/useUploadFile.test.ts +211 -0
  97. package/src/hooks/useUploadFile.tsx +136 -0
  98. package/src/index.ts +1 -0
  99. package/src/mixins.module.scss +24 -5
  100. package/src/util/helper.test.tsx +156 -1
  101. package/src/util/helper.tsx +33 -0
@@ -0,0 +1,136 @@
1
+ /*
2
+ * Copyright 2022 OneWelcome B.V.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { FileType } from "../Form/FileUpload/FileUpload";
18
+ import { useEffect, useState } from "react";
19
+ import { getValueByPath } from "../util/helper";
20
+
21
+ export interface UploadResponseType {
22
+ fileList: FileType[];
23
+ status: XMLHttpRequest["status"];
24
+ }
25
+ export interface UseUploadFileCallback {
26
+ onComplete?: (response: UploadResponseType) => void;
27
+ onProgress?: Function;
28
+ }
29
+
30
+ export interface UploadFileRequestParams {
31
+ url: string;
32
+ headers?: Headers;
33
+ responseErrorPath?: string;
34
+ networkErrorText?: string;
35
+ withCredentials?: boolean;
36
+ }
37
+
38
+ export const useUploadFile = (
39
+ files: FileType[],
40
+ request: UploadFileRequestParams,
41
+ callbacks?: UseUploadFileCallback
42
+ ) => {
43
+ const { url, headers, withCredentials, networkErrorText, responseErrorPath } = request;
44
+
45
+ const { onComplete, onProgress } = callbacks || {};
46
+
47
+ const [uploadingFiles, setUploadingFiles] = useState<FileType[]>([]);
48
+ const [updatedFiles, setUpdatedFiles] = useState<FileType[]>([...files]);
49
+
50
+ const getUpdatedList = (
51
+ fileName: string,
52
+ fileStatus: FileType["status"],
53
+ progress?: number,
54
+ error?: string
55
+ ) => {
56
+ return files.map(file => {
57
+ if (file.name === fileName) {
58
+ file.progress = progress;
59
+ file.error = error;
60
+ file.status = fileStatus;
61
+ }
62
+ return { ...file };
63
+ });
64
+ };
65
+
66
+ const recordProgress = (e: ProgressEvent<XMLHttpRequestEventTarget>, fileName: string) => {
67
+ const progress = (e.loaded / e.total) * 100;
68
+ const updatedData = getUpdatedList(fileName, "uploading", progress);
69
+ setUpdatedFiles(updatedData);
70
+ onProgress && onProgress(fileName, progress);
71
+ };
72
+
73
+ const getFileStatus = (
74
+ requestStatus: XMLHttpRequest["status"],
75
+ responseText: XMLHttpRequest["responseText"]
76
+ ) => {
77
+ let fileStatus: FileType["status"] = undefined;
78
+ let error = "";
79
+ if (requestStatus >= 200 && requestStatus < 400) {
80
+ fileStatus = "completed";
81
+ } else if (requestStatus === 0) {
82
+ fileStatus = "retry";
83
+ error =
84
+ networkErrorText || "Network error. Check internet connection and retry uploading the file";
85
+ } else if (requestStatus >= 400 && requestStatus < 500) {
86
+ const response = responseText && JSON.parse(JSON.stringify(responseText));
87
+ fileStatus = "error";
88
+ error = responseErrorPath ? getValueByPath(response, responseErrorPath) : "Bad request";
89
+ } else if (requestStatus >= 500) {
90
+ const response = responseText && JSON.parse(JSON.stringify(responseText));
91
+ fileStatus = "error";
92
+ error = responseErrorPath ? getValueByPath(response, responseErrorPath) : "Server Error";
93
+ }
94
+ return { fileStatus, error };
95
+ };
96
+
97
+ const handleOnComplete = (xhr: XMLHttpRequest, fileName: string) => {
98
+ const { status, readyState, responseText } = xhr;
99
+ if (readyState === xhr.DONE) {
100
+ const { fileStatus, error } = getFileStatus(status, responseText);
101
+ const updatedList = getUpdatedList(fileName, fileStatus, undefined, error);
102
+ setUpdatedFiles(updatedList);
103
+ const response = { fileList: updatedList, status };
104
+ setUploadingFiles(prevState => prevState.filter(selected => selected.name === fileName));
105
+ onComplete && onComplete(response);
106
+ }
107
+ };
108
+
109
+ const uploadFile = (file: FileType) => {
110
+ const xhr = new XMLHttpRequest();
111
+ xhr.upload.addEventListener("progress", e => recordProgress(e, file.name));
112
+ xhr.addEventListener("readystatechange", () => handleOnComplete(xhr, file.name));
113
+ headers && headers.forEach((value, key) => xhr.setRequestHeader(key, value));
114
+ withCredentials && (xhr.withCredentials = true);
115
+ xhr.open("POST", url, true);
116
+ const formData = new FormData();
117
+ formData.append("file", file.data as File);
118
+ formData.append("name", file.name);
119
+ xhr.send(formData);
120
+ };
121
+
122
+ useEffect(() => {
123
+ if (!url || !files.length) {
124
+ return;
125
+ }
126
+
127
+ files.forEach(file => {
128
+ if (!file.status && !uploadingFiles.find(selected => selected.name === file.name)) {
129
+ setUploadingFiles(prevState => [...prevState, file]);
130
+ uploadFile(file);
131
+ }
132
+ });
133
+ }, [url, files]);
134
+
135
+ return { updatedFiles, setUpdatedFiles, uploadingFiles };
136
+ };
package/src/index.ts CHANGED
@@ -123,3 +123,4 @@ export { HeaderCell } from "./DataGrid/datagrid.interfaces";
123
123
  export { DataGrid, Props as DataGridProps } from "./DataGrid/DataGrid";
124
124
  export { DataGridRow, Props as DataGridRowProps } from "./DataGrid/DataGridBody/DataGridRow";
125
125
  export { DataGridCell, Props as DataGridCellProps } from "./DataGrid/DataGridBody/DataGridCell";
126
+ export { Banner, Props as BannerProps } from "./Notifications/Banner/Banner";
@@ -156,17 +156,26 @@
156
156
  }
157
157
  }
158
158
 
159
- @mixin outline {
159
+ @mixin outline(
160
+ $color: var(--light-grey-border),
161
+ $style: var(--input-border-style),
162
+ $width: var(--input-border-width),
163
+ $borderRadius: var(--input-border-radius),
164
+ $backgroundColor: ""
165
+ ) {
160
166
  .outline {
161
167
  pointer-events: none;
162
168
  position: absolute;
163
169
  margin: 0;
164
170
  padding: 0;
165
171
  inset: 0;
166
- border-color: var(--light-grey-border);
167
- border-style: var(--input-border-style);
168
- border-width: var(--input-border-width);
169
- border-radius: var(--input-border-radius);
172
+ border-color: $color;
173
+ border-style: $style;
174
+ border-width: $width;
175
+ border-radius: $borderRadius;
176
+ @if ($backgroundColor != "") {
177
+ background-color: $backgroundColor;
178
+ }
170
179
  @include transition(all, 0.2s, ease-in-out);
171
180
  }
172
181
  }
@@ -249,3 +258,13 @@
249
258
  transition-duration: 0.1ms;
250
259
  }
251
260
  }
261
+
262
+ @mixin width-size($size) {
263
+ .w-#{$size} {
264
+ width: $size * 1%;
265
+ }
266
+ }
267
+
268
+ @for $i from 1 through 100 {
269
+ @include width-size($i + 5);
270
+ }
@@ -16,7 +16,15 @@
16
16
 
17
17
  import React, { useCallback, useEffect, useState } from "react";
18
18
  import { fireEvent, waitFor } from "@testing-library/dom";
19
- import { generateID, filterProps, debounce, throttle } from "./helper";
19
+ import {
20
+ generateID,
21
+ filterProps,
22
+ debounce,
23
+ throttle,
24
+ areArraysDifferent,
25
+ getValueByPath,
26
+ isEqual
27
+ } from "./helper";
20
28
  import { render } from "@testing-library/react";
21
29
 
22
30
  /* Generate an ID of 20 characters with a string woven in */
@@ -165,3 +173,150 @@ describe("throttling works", () => {
165
173
  expect(exampleFunction).not.toHaveBeenCalledTimes(10);
166
174
  });
167
175
  });
176
+
177
+ describe("areArraysDifferent works as expected", () => {
178
+ it("should return true for different arrays", () => {
179
+ const arr1 = [
180
+ {
181
+ name: "test1"
182
+ },
183
+ {
184
+ name: "test2"
185
+ }
186
+ ];
187
+
188
+ const arr2 = [
189
+ {
190
+ name: "test1"
191
+ },
192
+ {
193
+ name: "test3"
194
+ }
195
+ ];
196
+
197
+ const result = areArraysDifferent(arr1, arr2, "name");
198
+ expect(result).toBe(true);
199
+ });
200
+
201
+ it("should return false for arrays with same values", () => {
202
+ const arr1 = [
203
+ {
204
+ name: "test1"
205
+ }
206
+ ];
207
+
208
+ const arr2 = [
209
+ {
210
+ name: "test1"
211
+ }
212
+ ];
213
+
214
+ const result = areArraysDifferent(arr1, arr2, "name");
215
+ expect(result).toBe(false);
216
+ });
217
+
218
+ it("should return false for falsy values", () => {
219
+ const arr1 = [
220
+ {
221
+ name: "test1"
222
+ }
223
+ ];
224
+
225
+ const arr2 = [
226
+ {
227
+ label: "test1"
228
+ }
229
+ ];
230
+
231
+ const result = areArraysDifferent(arr1, arr2, "name");
232
+ expect(result).toBe(true);
233
+ });
234
+ });
235
+
236
+ describe("return correct values from getValueByPath", () => {
237
+ it("should return the correct value form a multi layered object", () => {
238
+ const val = "test";
239
+ const obj = {
240
+ firstNode: {
241
+ secondNode: {
242
+ thirdNode: {
243
+ val
244
+ }
245
+ }
246
+ }
247
+ };
248
+
249
+ const result = getValueByPath(obj, "firstNode.secondNode.thirdNode.val");
250
+ expect(result).toBe(val);
251
+ });
252
+ });
253
+
254
+ describe("verifies if isEqual returns the correct value", () => {
255
+ it("should return true for equal values objects", () => {
256
+ const obj1 = {
257
+ name1: "test1",
258
+ name2: {
259
+ val: "test2"
260
+ }
261
+ };
262
+
263
+ const obj2 = {
264
+ name1: "test1",
265
+ name2: {
266
+ val: "test2"
267
+ }
268
+ };
269
+
270
+ const res = isEqual(obj1, obj2);
271
+ expect(res).toBe(true);
272
+ });
273
+
274
+ it("should return false for unequal values objects", () => {
275
+ const obj1 = {
276
+ name1: "test1",
277
+ name2: {
278
+ val: "test2"
279
+ }
280
+ };
281
+
282
+ const obj2 = {
283
+ name1: "test1"
284
+ };
285
+
286
+ const res = isEqual(obj1, obj2);
287
+ expect(res).toBe(false);
288
+ });
289
+
290
+ it("should return false for falsy values", () => {
291
+ const obj1 = {
292
+ name1: "test1",
293
+ name2: {
294
+ val: "test2"
295
+ }
296
+ };
297
+ const obj2 = null;
298
+
299
+ const res = isEqual(obj1, obj2);
300
+ expect(res).toBe(false);
301
+ });
302
+
303
+ it("should return false for different types", () => {
304
+ const obj1 = [
305
+ {
306
+ name1: "test1",
307
+ name2: {
308
+ val: "test2"
309
+ }
310
+ }
311
+ ];
312
+ const obj2 = {
313
+ name1: "test1",
314
+ name2: {
315
+ val: "test2"
316
+ }
317
+ };
318
+
319
+ const res = isEqual(obj1, obj2);
320
+ expect(res).toBe(false);
321
+ });
322
+ });
@@ -143,3 +143,36 @@ export const throttle = (fn: (...args: unknown[]) => unknown, delay: number) =>
143
143
  }
144
144
  };
145
145
  };
146
+
147
+ export const isEqual = (x: any, y: any): boolean => {
148
+ const typesCoincide = x && y && typeof x === "object" && typeof y === "object";
149
+ return typesCoincide
150
+ ? Object.keys(x).length === Object.keys(y).length &&
151
+ Object.keys(x).every(key => isEqual(x[key], y[key]))
152
+ : x === y;
153
+ };
154
+
155
+ export const areArraysDifferent = (
156
+ arr1: Record<string, any>[],
157
+ arr2: Record<string, any>[],
158
+ key: string
159
+ ) => {
160
+ if (arr1.length !== arr2.length) {
161
+ return true;
162
+ } else {
163
+ const firstFilteredArray = arr1.filter(arr1Item =>
164
+ arr2.some((arr2Item: { [x: string]: any }) => !isEqual(arr1Item[key], arr2Item[key]))
165
+ );
166
+ const secondFilteredArray = arr2.filter(arr2Item =>
167
+ arr1.some((arr1Item: { [x: string]: any }) => !isEqual(arr1Item[key], arr2Item[key]))
168
+ );
169
+
170
+ return !!firstFilteredArray.length || !!secondFilteredArray.length;
171
+ }
172
+ };
173
+
174
+ export const getValueByPath = (obj: { [key: string]: any }, path: string): any => {
175
+ return path.split(".").reduce((res, prop) => {
176
+ return res[prop];
177
+ }, obj);
178
+ };