pixel-react 1.0.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/lib/components/MultiSelect/MultiSelect.d.ts +1 -1
  3. package/lib/components/MultiSelect/MultiSelectTypes.d.ts +2 -0
  4. package/lib/components/MultiSelect/dropdownTypes.d.ts +2 -0
  5. package/lib/index.d.ts +3 -1
  6. package/lib/index.esm.js +512 -2327
  7. package/lib/index.esm.js.map +1 -1
  8. package/lib/index.js +512 -2328
  9. package/lib/index.js.map +1 -1
  10. package/lib/tsconfig.tsbuildinfo +1 -1
  11. package/lib/utils/compareArrays/compareArrays.d.ts +11 -0
  12. package/lib/utils/compareArrays/compareArrays.stories.d.ts +6 -0
  13. package/lib/utils/compareObjects/compareObjects.d.ts +2 -0
  14. package/lib/utils/compareObjects/compareObjects.stories.d.ts +6 -0
  15. package/lib/utils/debounce/debounce.d.ts +6 -0
  16. package/lib/utils/debounce/debounce.stories.d.ts +6 -0
  17. package/lib/utils/find/findAndInsert.d.ts +7 -0
  18. package/lib/utils/find/findAndInsert.stories.d.ts +7 -0
  19. package/lib/utils/throttle/throttle.d.ts +6 -0
  20. package/lib/utils/throttle/throttle.stories.d.ts +6 -0
  21. package/package.json +3 -1
  22. package/rollup.config.mjs +10 -5
  23. package/src/StyleGuide/ColorPalette/ColorPalette.scss +4 -5
  24. package/src/StyleGuide/Typography/Typography.scss +4 -5
  25. package/src/assets/Themes/Theme.scss +7 -191
  26. package/src/assets/icons/filter.svg +5 -0
  27. package/src/components/Accordion/Accordion.scss +1 -2
  28. package/src/components/Button/Button.scss +1 -1
  29. package/src/components/Charts/DonutChart/DonutChart.scss +1 -1
  30. package/src/components/Charts/RadialChart/RadialChart.scss +1 -1
  31. package/src/components/Drawer/Drawer.scss +9 -10
  32. package/src/components/ExpandableMenu/ExpandableMenu.scss +1 -1
  33. package/src/components/Icon/Icons.scss +5 -6
  34. package/src/components/Icon/iconList.ts +2 -0
  35. package/src/components/MenuOption/MenuOption.scss +1 -1
  36. package/src/components/MultiSelect/Dropdown.scss +27 -16
  37. package/src/components/MultiSelect/Dropdown.tsx +51 -28
  38. package/src/components/MultiSelect/MultiSelect.scss +1 -1
  39. package/src/components/MultiSelect/MultiSelect.stories.tsx +9 -5
  40. package/src/components/MultiSelect/MultiSelect.tsx +4 -0
  41. package/src/components/MultiSelect/MultiSelectTypes.ts +2 -0
  42. package/src/components/MultiSelect/dropdownTypes.ts +2 -0
  43. package/src/components/RadioButton/RadioButton.scss +2 -3
  44. package/src/components/Search/Search.scss +1 -1
  45. package/src/components/Table/Table.scss +1 -1
  46. package/src/components/TextArea/Textarea.scss +1 -2
  47. package/src/components/Toggle/Toggle.scss +5 -4
  48. package/src/components/Tooltip/Tooltip.scss +1 -1
  49. package/src/index.ts +1 -4
  50. package/src/utils/checkEmpty/checkEmpty.stories.tsx +1 -1
  51. package/src/utils/checkEmpty/checkEmpty.ts +21 -7
  52. package/src/utils/compareArrays/compareArrays.stories.tsx +62 -0
  53. package/src/utils/compareArrays/compareArrays.ts +31 -0
  54. package/src/utils/compareObjects/compareObjects.stories.tsx +51 -0
  55. package/src/utils/compareObjects/compareObjects.ts +53 -0
  56. package/src/utils/debounce/debounce.stories.tsx +81 -0
  57. package/src/utils/debounce/debounce.ts +28 -0
  58. package/src/utils/find/findAndInsert.stories.tsx +119 -0
  59. package/src/utils/find/findAndInsert.ts +49 -0
  60. package/src/utils/throttle/throttle.stories.tsx +100 -0
  61. package/src/utils/throttle/throttle.ts +33 -0
  62. package/vite.config.js +8 -16
@@ -10,25 +10,27 @@
10
10
  box-sizing: border-box;
11
11
  margin-block: 4px;
12
12
  max-height: 160px;
13
- overflow-y: auto; // Change from scroll to auto to avoid arrow buttons
14
13
 
15
- // WebKit scrollbar styles
16
- &::-webkit-scrollbar {
17
- width: 4px;
18
- height: 40px;
19
- border-radius: 12px 0px 0px 0px;
20
- background: var(--description-text);
14
+ .ff-multiselect-options-wrapper {
15
+ max-height: 128px;
16
+ overflow-y: auto;
17
+ &::-webkit-scrollbar {
18
+ width: 4px;
19
+ height: 40px;
20
+ border-radius: 12px 0px 0px 0px;
21
+ background: var(--description-text);
21
22
 
22
- &-track {
23
- background: var(--ff-select-scroll-track-bg);
24
- }
23
+ &-track {
24
+ background: var(--ff-select-scroll-track-bg);
25
+ }
25
26
 
26
- &-thumb {
27
- background: var(--description-text);
28
- border-radius: 10px;
27
+ &-thumb {
28
+ background: var(--ff-select-scroll-thumb-color);
29
+ border-radius: 10px;
29
30
 
30
- &:hover {
31
- background: var(--ff-select-scroll-thumb-hove);
31
+ &:hover {
32
+ background: var(--ff-select-scroll-thumb-hover);
33
+ }
32
34
  }
33
35
  }
34
36
  }
@@ -53,7 +55,7 @@
53
55
 
54
56
  .dropdown-option-label {
55
57
  @extend .fontMd;
56
- color: var(--pop-up-background-blur);
58
+ color: var(--ff-select-text-color);
57
59
  }
58
60
  &:hover {
59
61
  background-color: var(--hover-color);
@@ -63,4 +65,13 @@
63
65
  &:focus {
64
66
  outline: none;
65
67
  }
68
+ .select-button-container {
69
+ box-sizing: border-box;
70
+ width: 100%;
71
+ border-top: 1px solid var(--slider-table-color);
72
+ height: 30px;
73
+ display: flex;
74
+ justify-content: center;
75
+ align-items: center;
76
+ }
66
77
  }
@@ -1,9 +1,10 @@
1
- import { forwardRef, useContext } from 'react';
1
+ import { forwardRef, useContext, useMemo } from 'react';
2
2
  import { dropdownDefaultCSSData, DropdownProps } from './dropdownTypes';
3
3
 
4
4
  import './Dropdown.scss';
5
5
  import { ThemeContext } from '../ThemeProvider/ThemeProvider';
6
6
  import classNames from 'classnames';
7
+ import Button from '../Button';
7
8
 
8
9
  const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
9
10
  (
@@ -13,6 +14,8 @@ const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
13
14
  searchedKeyword = '',
14
15
  dropdownPosition = {},
15
16
  zIndex,
17
+ withSelectButton,
18
+ onSelect,
16
19
  },
17
20
  ref
18
21
  ) => {
@@ -23,19 +26,29 @@ const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
23
26
  : [];
24
27
  const { verticalMargin, optionHeight, maxDropdownHeight } =
25
28
  dropdownDefaultCSSData;
26
- const dropdownHeight = Math.min(
27
- filteredOptions.length * optionHeight,
28
- maxDropdownHeight
29
- );
30
- const isEnoughBottomSpaceAvailable =
31
- dropdownPosition.fromBottom >= dropdownHeight + verticalMargin;
32
29
 
33
- const topPosition = isEnoughBottomSpaceAvailable
34
- ? dropdownPosition.top
35
- : dropdownPosition.top -
36
- dropdownHeight -
37
- dropdownPosition.selectHeight -
38
- 2 * verticalMargin;
30
+ const topPosition = useMemo(() => {
31
+ let calculatedDropdownHeight = Math.min(
32
+ filteredOptions.length * optionHeight,
33
+ maxDropdownHeight
34
+ );
35
+
36
+ if (filteredOptions.length < 5 && withSelectButton) {
37
+ calculatedDropdownHeight += 32;
38
+ }
39
+
40
+ const isEnoughSpaceAvailable =
41
+ dropdownPosition.fromBottom >=
42
+ calculatedDropdownHeight + verticalMargin;
43
+ const topPosition = isEnoughSpaceAvailable
44
+ ? dropdownPosition.top
45
+ : dropdownPosition.top -
46
+ calculatedDropdownHeight -
47
+ dropdownPosition.selectHeight -
48
+ 2 * verticalMargin;
49
+
50
+ return topPosition;
51
+ }, [filteredOptions.length, withSelectButton, dropdownPosition]);
39
52
  const themeContext = useContext(ThemeContext);
40
53
  const currentTheme = themeContext?.currentTheme;
41
54
  return (
@@ -52,22 +65,32 @@ const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
52
65
  zIndex,
53
66
  }}
54
67
  >
55
- {filteredOptions.length === 0 ? (
56
- <div className="dropdown-option-container">
57
- <p className="no-options">No Option</p>
58
- </div>
59
- ) : (
60
- filteredOptions.map((info) => (
61
- <div
62
- role="option"
63
- className={`dropdown-option-container`}
64
- key={info.label}
65
- onClick={() => handleOptionChange(info, !info.isChecked)}
66
- >
67
- <input type="checkbox" checked={info.isChecked} readOnly />
68
- <span className="dropdown-option-label">{info.label}</span>
68
+ <div
69
+ className="ff-multiselect-options-wrapper"
70
+ style={{ maxHeight: withSelectButton ? '128px' : '160px' }}
71
+ >
72
+ {filteredOptions.length === 0 ? (
73
+ <div className="dropdown-option-container ">
74
+ <p className="no-options">No Option</p>
69
75
  </div>
70
- ))
76
+ ) : (
77
+ filteredOptions.map((info) => (
78
+ <div
79
+ role="option"
80
+ className={`dropdown-option-container`}
81
+ key={info.label}
82
+ onClick={() => handleOptionChange(info, !info.isChecked)}
83
+ >
84
+ <input type="checkbox" checked={info.isChecked} readOnly />
85
+ <span className="dropdown-option-label">{info.label}</span>
86
+ </div>
87
+ ))
88
+ )}
89
+ </div>
90
+ {withSelectButton && filteredOptions.length > 0 && (
91
+ <div className="select-button-container">
92
+ <Button label="Select" variant="tertiary" onClick={onSelect} />
93
+ </div>
71
94
  )}
72
95
  </div>
73
96
  );
@@ -1,4 +1,4 @@
1
- @import '../../assets/styles/fonts';
1
+ @use '../../assets/styles/fonts';
2
2
 
3
3
  @mixin flex-center($justify: space-between) {
4
4
  display: flex;
@@ -22,17 +22,21 @@ export const Default: Story = {
22
22
  args: {
23
23
  ...defaultArgs,
24
24
  required: true,
25
+ withSelectButton: true,
26
+ onSelect: () => {
27
+ alert('selected');
28
+ },
25
29
  options: [
26
30
  { label: 'Apple', value: 'apple' },
27
31
  { label: 'Banana', value: 'banana' },
28
32
  { label: 'Cherry', value: 'cherry' },
29
33
  { label: 'Date', value: 'date' },
30
34
  { label: 'Grape', value: 'grape' },
31
- { label: 'Kiwi', value: 'kiwi' },
32
- { label: 'Mango', value: 'mango' },
33
- { label: 'Orange', value: 'orange' },
34
- { label: 'Peach', value: 'peach' },
35
- { label: 'Strawberry', value: 'strawberry' },
35
+ // { label: 'Kiwi', value: 'kiwi' },
36
+ // { label: 'Mango', value: 'mango' },
37
+ // { label: 'Orange', value: 'orange' },
38
+ // { label: 'Peach', value: 'peach' },
39
+ // { label: 'Strawberry', value: 'strawberry' },
36
40
  ],
37
41
  // selectedOptions: [{ label: 'Apple', value: 'apple' }],
38
42
  },
@@ -38,6 +38,8 @@ const MultiSelect = ({
38
38
  required = false,
39
39
  disabled = false,
40
40
  errorMessage = 'Fill this field',
41
+ withSelectButton = false,
42
+ onSelect = () => {},
41
43
  }: MultiSelectProps) => {
42
44
  const [isOpen, setIsOpen] = useState<boolean>(false);
43
45
  const [allOptions, setAllOptions] = useState(options);
@@ -249,6 +251,8 @@ const MultiSelect = ({
249
251
  options={allOptions}
250
252
  dropdownPosition={dropdownPosition}
251
253
  zIndex={zIndex}
254
+ withSelectButton={withSelectButton}
255
+ onSelect={onSelect}
252
256
  />,
253
257
  document.body
254
258
  )}
@@ -14,6 +14,8 @@ interface MultiSelectProps {
14
14
  zIndex?: number;
15
15
  required?: boolean;
16
16
  errorMessage?: string;
17
+ withSelectButton?: boolean;
18
+ onSelect?: () => void;
17
19
  }
18
20
 
19
21
  export { Option, MultiSelectProps };
@@ -12,4 +12,6 @@ export interface DropdownProps {
12
12
  searchedKeyword: undefined | string;
13
13
  dropdownPosition: any;
14
14
  zIndex: number;
15
+ withSelectButton?: boolean;
16
+ onSelect?: () => void;
15
17
  }
@@ -1,5 +1,4 @@
1
- @import '../../assets/styles/_colors';
2
- @import '../../assets/styles/_fonts';
1
+ @use '../../assets/styles/_fonts';
3
2
 
4
3
  @mixin circle($size, $border: none) {
5
4
  width: $size;
@@ -16,7 +15,7 @@
16
15
  .ff-radio {
17
16
  @extend .fontSm;
18
17
  position: relative;
19
- color: $text-color;
18
+ color: var(--text-color);
20
19
  display: flex;
21
20
  align-items: center;
22
21
  cursor: pointer;
@@ -1,4 +1,4 @@
1
- @import '../../assets/styles/fonts';
1
+ @use '../../assets/styles/fonts';
2
2
 
3
3
 
4
4
  .ff-search-container {
@@ -1,4 +1,4 @@
1
- @import '../../assets/styles/_fonts';
1
+ @use '../../assets/styles/_fonts';
2
2
 
3
3
  .ff-fixed-header-table {
4
4
  overflow-y: auto;
@@ -1,5 +1,4 @@
1
- @use '../../assets/styles/colors' as *;
2
- @import '../../assets/styles/fonts';
1
+ @use '../../assets/styles/fonts';
3
2
 
4
3
  .ff-textarea-container {
5
4
  display: flex;
@@ -1,3 +1,4 @@
1
+ @use "sass:map";
1
2
  $switch-sizes: (
2
3
  small: (
3
4
  button-size: 16px,
@@ -20,10 +21,10 @@ $switch-sizes: (
20
21
  );
21
22
 
22
23
  @mixin switch-size($size) {
23
- $button-size: map-get(map-get($switch-sizes, $size), button-size);
24
- $container-width: map-get(map-get($switch-sizes, $size), container-width);
25
- $container-height: map-get(map-get($switch-sizes, $size), container-height);
26
- $translate-x: map-get(map-get($switch-sizes, $size), translate-x);
24
+ $button-size: map.get(map.get($switch-sizes, $size), button-size);
25
+ $container-width: map.get(map.get($switch-sizes, $size), container-width);
26
+ $container-height: map.get(map.get($switch-sizes, $size), container-height);
27
+ $translate-x: map.get(map.get($switch-sizes, $size), translate-x);
27
28
 
28
29
  width: $container-width;
29
30
  height: $container-height;
@@ -1,4 +1,4 @@
1
- @import '../../assets/styles/fonts';
1
+ @use '../../assets/styles/fonts';
2
2
 
3
3
  .ff-tooltip-container {
4
4
  cursor: auto;
package/src/index.ts CHANGED
@@ -26,7 +26,6 @@ import Typography from './components/Typography';
26
26
  import useTheme from './hooks/useTheme';
27
27
  import Form from './components/Form';
28
28
 
29
-
30
29
  import InputWithDropdown from './components/InputWithDropdown';
31
30
  import RadioButton from './components/RadioButton';
32
31
  import RadioGroup from './components/RadioGroup';
@@ -35,7 +34,7 @@ import TableTree from './components/TableTree';
35
34
  import Tabs from './components/Tabs';
36
35
  import HighlightText from './components/HighlightText';
37
36
  import Checkbox from './components/Checkbox';
38
- import Search from './components/Search/Search'
37
+ import Search from './components/Search/Search';
39
38
 
40
39
  // Utils imports
41
40
  import { checkEmpty } from './utils/checkEmpty/checkEmpty';
@@ -69,11 +68,9 @@ export {
69
68
  useTheme,
70
69
  InputWithDropdown,
71
70
  Table,
72
-
73
71
  RadioButton,
74
72
  RadioGroup,
75
73
  Form,
76
-
77
74
  MiniModal,
78
75
  Container,
79
76
  Row,
@@ -1,7 +1,7 @@
1
1
  import { checkEmpty } from './checkEmpty';
2
2
 
3
3
  export default {
4
- title: 'Utils/isEmptyValue',
4
+ title: 'Utils/checkEmpty',
5
5
  component: checkEmpty,
6
6
  };
7
7
 
@@ -1,10 +1,24 @@
1
1
  type valueType = any;
2
2
 
3
- export const checkEmpty = (value: valueType) => {
4
- if (!value) {
5
- return true;
6
- } else if (typeof value === 'object') {
7
- if (Array.isArray(value)) return value.length === 0;
8
- else return Object.keys(value).length === 0;
9
- } else return false;
3
+ export const checkEmpty = (value: valueType): boolean => {
4
+ // Check for null or undefined
5
+ if (value == null) return true;
6
+
7
+ // Check for strings
8
+ if (typeof value === 'string') return value.trim().length === 0;
9
+
10
+ // Check for arrays
11
+ if (Array.isArray(value)) return value.length === 0;
12
+
13
+ // Check for objects
14
+ if (typeof value === 'object') {
15
+ // Handle Map and Set
16
+ if (value instanceof Map || value instanceof Set) return value.size === 0;
17
+
18
+ // Handle regular objects
19
+ return Object.keys(value).length === 0;
20
+ }
21
+
22
+ // For all other types (numbers, booleans, etc.), return false as they are not "empty"
23
+ return false;
10
24
  };
@@ -0,0 +1,62 @@
1
+ import { compareArrays } from './compareArrays'; // Adjust the import path as necessary
2
+
3
+ export default {
4
+ title: 'Utils/compareArrays',
5
+ component: compareArrays,
6
+ };
7
+
8
+ export const Default = () => {
9
+ const testCases = [
10
+ { arr1: [1, 2, 3], arr2: [1, 2, 3], expected: true },
11
+ { arr1: [1, 2, 3], arr2: [1, 2, 4], expected: false },
12
+ { arr1: [1, { a: 1 }], arr2: [1, { a: 1 }], expected: true },
13
+ { arr1: [1, { a: 1 }], arr2: [1, { a: 2 }], expected: false },
14
+ { arr1: [1, 2, 3], arr2: [1, 2, 3, 4], expected: false },
15
+ { arr1: [], arr2: [], expected: true },
16
+ { arr1: [null, undefined], arr2: [null, undefined], expected: true },
17
+ { arr1: [null], arr2: [undefined], expected: false },
18
+ {
19
+ arr1: [
20
+ [1, 2],
21
+ [3, 4],
22
+ ],
23
+ arr2: [
24
+ [1, 2],
25
+ [3, 4],
26
+ ],
27
+ expected: true,
28
+ },
29
+ {
30
+ arr1: [
31
+ [1, 2],
32
+ [3, 4],
33
+ ],
34
+ arr2: [
35
+ [1, 2],
36
+ [4, 3],
37
+ ],
38
+ expected: false,
39
+ },
40
+ ];
41
+
42
+ return (
43
+ <div>
44
+ <h1>
45
+ <u>compareArrays(arr1, arr2)</u> - Expected / Actual
46
+ </h1>
47
+ {testCases.map(({ arr1, arr2, expected }, index) => {
48
+ const actual = compareArrays(arr1, arr2);
49
+ return (
50
+ <div key={index}>
51
+ <strong>
52
+ compareArrays({JSON.stringify(arr1)}, {JSON.stringify(arr2)}) -
53
+ </strong>
54
+ <span style={{ color: actual === expected ? 'green' : 'red' }}>
55
+ {` Expected: ${expected}, Actual: ${actual}`}
56
+ </span>
57
+ </div>
58
+ );
59
+ })}
60
+ </div>
61
+ );
62
+ };
@@ -0,0 +1,31 @@
1
+ import { compareObjects } from '../compareObjects/compareObjects';
2
+
3
+ // Define a type for any object
4
+ export type AnyObject = Record<string, unknown>;
5
+
6
+ /**
7
+ * Compare two arrays for equality.
8
+ * This function checks if both arrays contain the same elements in the same order,
9
+ * including nested structures.
10
+ *
11
+ * @param arr1 - The first array to compare.
12
+ * @param arr2 - The second array to compare.
13
+ * @returns - A boolean indicating if the arrays are equal.
14
+ */
15
+ export const compareArrays = (arr1: unknown[], arr2: unknown[]): boolean => {
16
+ // Check if both are arrays
17
+ if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false;
18
+
19
+ // Check if lengths are different
20
+ if (arr1.length !== arr2.length) return false;
21
+
22
+ // Compare each element
23
+ return arr1.every((element, index) => {
24
+ const otherElement = arr2[index];
25
+ // Recursively compare objects or arrays, or check for strict equality
26
+ return compareObjects(
27
+ element as AnyObject | null,
28
+ otherElement as AnyObject | null
29
+ );
30
+ });
31
+ };
@@ -0,0 +1,51 @@
1
+ import { compareObjects } from './compareObjects'; // Adjust the import path as necessary
2
+
3
+ export default {
4
+ title: 'Utils/compareObjects',
5
+ component: compareObjects,
6
+ };
7
+
8
+ export const Default = () => {
9
+ const testCases = [
10
+ {
11
+ obj1: { a: 1, b: [2, { c: 3 }] },
12
+ obj2: { a: 1, b: [2, { c: 3 }] },
13
+ expected: true,
14
+ },
15
+ {
16
+ obj1: { a: 1, b: [2, { c: 3 }] },
17
+ obj2: { a: 1, b: [3, 2] },
18
+ expected: false,
19
+ },
20
+ {
21
+ obj1: { a: 1, b: { c: 2 } },
22
+ obj2: { a: 1, b: { c: 2 } },
23
+ expected: true,
24
+ },
25
+ {
26
+ obj1: { a: 1, b: { c: 2 } },
27
+ obj2: { a: 1, b: { c: 3 } },
28
+ expected: false,
29
+ },
30
+ { obj1: null, obj2: null, expected: true },
31
+ { obj1: { a: null }, obj2: { a: null }, expected: true },
32
+ { obj1: { a: 1 }, obj2: { a: null }, expected: false },
33
+ { obj1: {}, obj2: {}, expected: true },
34
+ { obj1: { a: 0 }, obj2: { a: 0 }, expected: true },
35
+ { obj1: { a: 0 }, obj2: { a: 1 }, expected: false },
36
+ ];
37
+
38
+ return (
39
+ <div>
40
+ <h1>
41
+ <u>compareObjects(obj1, obj2)</u> - true / false
42
+ </h1>
43
+ {testCases.map(({ obj1, obj2, expected }, index) => (
44
+ <div key={index}>
45
+ compareObjects({JSON.stringify(obj1)}, {JSON.stringify(obj2)}) -
46
+ {compareObjects(obj1, obj2) === expected ? ' True' : ' False'}
47
+ </div>
48
+ ))}
49
+ </div>
50
+ );
51
+ };
@@ -0,0 +1,53 @@
1
+ // Define a type for any object
2
+ export type AnyObject = Record<string, unknown>;
3
+
4
+ export const compareObjects = (
5
+ obj1: AnyObject | null,
6
+ obj2: AnyObject | null
7
+ ): boolean => {
8
+ // Check if both are strictly equal (handles primitive types and same reference)
9
+ if (obj1 === obj2) return true;
10
+
11
+ // Check if either is null or not an object
12
+ if (
13
+ obj1 == null ||
14
+ obj2 == null ||
15
+ typeof obj1 !== 'object' ||
16
+ typeof obj2 !== 'object'
17
+ ) {
18
+ return false;
19
+ }
20
+
21
+ // Handle array comparison
22
+ const isArray1 = Array.isArray(obj1);
23
+ const isArray2 = Array.isArray(obj2);
24
+ if (isArray1 !== isArray2) return false; // One is an array, the other is not
25
+
26
+ // Create arrays of keys for both objects
27
+ const keys1 = isArray1 ? obj1 : Object.keys(obj1);
28
+ const keys2 = isArray2 ? obj2 : Object.keys(obj2);
29
+
30
+ // Check if the number of keys is different
31
+ if (keys1.length !== keys2.length) return false;
32
+
33
+ // Create a Set for keys2 for O(1) lookups (only for objects)
34
+ if (!isArray1) {
35
+ const keysSet2 = new Set<string>(keys2 as string[]);
36
+
37
+ // Check each key and value
38
+ return keys1.every((key) => {
39
+ return (
40
+ keysSet2.has(key) &&
41
+ compareObjects(
42
+ obj1[key] as AnyObject | null,
43
+ obj2[key] as AnyObject | null
44
+ )
45
+ );
46
+ });
47
+ } else {
48
+ // If arrays, compare elements directly
49
+ return keys1.every((item, index) =>
50
+ compareObjects(item as AnyObject | null, keys2[index] as AnyObject | null)
51
+ );
52
+ }
53
+ };
@@ -0,0 +1,81 @@
1
+ import { debounce } from './debounce'; // Adjust the import path as necessary
2
+
3
+ export default {
4
+ title: 'Utils/debounce',
5
+ component: debounce,
6
+ };
7
+
8
+ export const Default = () => {
9
+ const testCases = [
10
+ {
11
+ description: 'Basic function call',
12
+ expectedOutput: 'Function called! (after delay)',
13
+ delay: 300,
14
+ setup: () => {
15
+ const debouncedFunc = debounce(
16
+ () => console.log('Function called!'),
17
+ 300
18
+ );
19
+ debouncedFunc(); // Call the debounced function
20
+ },
21
+ code: `const debouncedFunc = debounce(() => console.log('Function called!'), 300);\ndebouncedFunc();`,
22
+ },
23
+ {
24
+ description: 'Rapid calls within delay',
25
+ expectedOutput: 'Debounced call! (only once after delay)',
26
+ delay: 500,
27
+ setup: () => {
28
+ const debouncedFunc = debounce(
29
+ () => console.log('Debounced call!'),
30
+ 500
31
+ );
32
+ for (let i = 0; i < 5; i++) {
33
+ debouncedFunc(); // Call the debounced function multiple times
34
+ }
35
+ },
36
+ code: `const debouncedFunc = debounce(() => console.log('Debounced call!'), 500);\nfor (let i = 0; i < 5; i++) {\n debouncedFunc();\n}`,
37
+ },
38
+ {
39
+ description: 'Cancel debounced function',
40
+ expectedOutput: 'Function should not be called if canceled',
41
+ delay: 200,
42
+ setup: () => {
43
+ const debouncedFunc = debounce(
44
+ () => console.log('Should not be called!'),
45
+ 200
46
+ );
47
+ debouncedFunc(); // Call the debounced function
48
+ debouncedFunc.cancel(); // Cancel the function
49
+ },
50
+ code: `const debouncedFunc = debounce(() => console.log('Should not be called!'), 200);\ndebouncedFunc();\ndebouncedFunc.cancel();`,
51
+ },
52
+ ];
53
+
54
+ return (
55
+ <div>
56
+ <h1>
57
+ <u>debounce(function, delay)</u> - Demonstrating debounce functionality
58
+ </h1>
59
+ {testCases.map(
60
+ ({ description, expectedOutput, delay, setup, code }, index) => (
61
+ <div key={index}>
62
+ <h3>{description}</h3>
63
+ <button
64
+ onClick={() => {
65
+ setup(); // Run the setup for the test case
66
+ setTimeout(() => {
67
+ console.log(expectedOutput);
68
+ }, delay + 100); // Wait a bit longer than the delay to check output
69
+ }}
70
+ >
71
+ Run Test
72
+ </button>
73
+ <pre>
74
+ <code>{code}</code>
75
+ </pre>
76
+ </div>
77
+ )
78
+ )}
79
+ </div>
80
+ );
81
+ };