pixel-react 1.0.1 → 1.0.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 (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
+ };