playbook_ui 14.16.0.pre.rc.6 → 14.17.0.pre.alpha.play1499backgroundkitoverlay7105

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Utilities/types.ts +1 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +104 -2
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +168 -85
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +2 -2
  6. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +10 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +20 -7
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_fullscreen.jsx +90 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_fullscreen.md +3 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination.jsx +0 -1
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows.html.erb +39 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_no_subrows.html.erb +33 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_no_subrows_rails.md +1 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_rails.md +6 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +4 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  17. data/app/pb_kits/playbook/pb_advanced_table/index.js +1 -1
  18. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +4 -2
  19. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +19 -9
  20. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +38 -1
  21. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +49 -37
  22. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +39 -0
  23. data/app/pb_kits/playbook/pb_background/_background.scss +26 -0
  24. data/app/pb_kits/playbook/pb_background/_background.tsx +8 -4
  25. data/app/pb_kits/playbook/pb_background/docs/_background_overlay.jsx +36 -0
  26. data/app/pb_kits/playbook/pb_background/docs/_background_overlay.md +1 -0
  27. data/app/pb_kits/playbook/pb_background/docs/example.yml +1 -0
  28. data/app/pb_kits/playbook/pb_background/docs/index.js +1 -0
  29. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +1 -1
  30. data/app/pb_kits/playbook/pb_button/_button.scss +5 -5
  31. data/app/pb_kits/playbook/pb_circle_chart/_circle_chart.tsx +1 -1
  32. data/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap +14 -7
  33. data/app/pb_kits/playbook/pb_contact/contact.test.js +7 -7
  34. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +34 -34
  35. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +2 -2
  36. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +16 -0
  37. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.html.erb +0 -11
  38. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.jsx +0 -7
  39. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_inline_styles.scss +28 -24
  40. data/app/pb_kits/playbook/pb_date_range_inline/date_range_inline.test.js +2 -2
  41. data/app/pb_kits/playbook/pb_date_range_stacked/date_range_stacked.test.js +1 -1
  42. data/app/pb_kits/playbook/pb_draggable/_draggable.scss +115 -5
  43. data/app/pb_kits/playbook/pb_draggable/_draggable.tsx +6 -4
  44. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +91 -24
  45. data/app/pb_kits/playbook/pb_draggable/context/types.ts +35 -28
  46. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones.jsx +184 -0
  47. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones.md +5 -0
  48. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors.jsx +97 -0
  49. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors.md +1 -0
  50. data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +3 -3
  51. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableContainer.tsx +11 -2
  52. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableItem.tsx +65 -6
  53. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.html.erb +3 -0
  54. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +1 -0
  55. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete.jsx +6 -6
  56. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_and_custom_display.jsx +6 -6
  57. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.jsx +6 -6
  58. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +8 -8
  59. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +3 -3
  60. data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +3 -4
  61. data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +2 -2
  62. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.scss +2 -2
  63. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +67 -0
  64. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +67 -0
  65. data/app/pb_kits/playbook/pb_form/pb_form_validation.js +1 -1
  66. data/app/pb_kits/playbook/pb_form_group/_form_group.scss +22 -0
  67. data/app/pb_kits/playbook/pb_gauge/_gauge.tsx +1 -1
  68. data/app/pb_kits/playbook/pb_icon/_icon.scss +8 -1
  69. data/app/pb_kits/playbook/pb_icon/docs/_icon_color.html.erb +10 -4
  70. data/app/pb_kits/playbook/pb_icon/docs/_icon_color.jsx +49 -24
  71. data/app/pb_kits/playbook/pb_icon/icon.test.js +9 -9
  72. data/app/pb_kits/playbook/pb_icon_button/_icon_button.tsx +4 -1
  73. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_click.jsx +13 -0
  74. data/app/pb_kits/playbook/pb_icon_button/docs/example.yml +1 -0
  75. data/app/pb_kits/playbook/pb_icon_button/docs/index.js +1 -0
  76. data/app/pb_kits/playbook/pb_icon_circle/icon_circle.test.js +1 -1
  77. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.test.js +1 -1
  78. data/app/pb_kits/playbook/pb_icon_value/icon_value.test.js +1 -1
  79. data/app/pb_kits/playbook/pb_label_value/label_value.test.js +1 -1
  80. data/app/pb_kits/playbook/pb_layout/_layout.scss +58 -0
  81. data/app/pb_kits/playbook/pb_layout/_layout.tsx +20 -7
  82. data/app/pb_kits/playbook/pb_layout/docs/_layout_bracket.jsx +190 -0
  83. data/app/pb_kits/playbook/pb_layout/docs/_layout_bracket.md +5 -0
  84. data/app/pb_kits/playbook/pb_layout/docs/example.yml +1 -0
  85. data/app/pb_kits/playbook/pb_layout/docs/index.js +1 -0
  86. data/app/pb_kits/playbook/pb_layout/layout.test.js +4 -0
  87. data/app/pb_kits/playbook/pb_layout/subcomponents/_game.tsx +90 -0
  88. data/app/pb_kits/playbook/pb_layout/subcomponents/_round.tsx +57 -0
  89. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slide.tsx +1 -1
  90. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slides.tsx +1 -1
  91. data/app/pb_kits/playbook/pb_lightbox/Carousel/Thumbnails.tsx +1 -1
  92. data/app/pb_kits/playbook/pb_lightbox/hooks/useVisibility.js +1 -1
  93. data/app/pb_kits/playbook/pb_line_graph/_line_graph.tsx +1 -1
  94. data/app/pb_kits/playbook/pb_link/link.test.jsx +2 -2
  95. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +23 -0
  96. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +28 -1
  97. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.html.erb +72 -0
  98. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.jsx +97 -0
  99. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +71 -0
  100. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +91 -0
  101. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +4 -0
  102. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +3 -1
  103. data/app/pb_kits/playbook/pb_multi_level_select/index.js +105 -0
  104. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +10 -0
  105. data/app/pb_kits/playbook/pb_nav/_nav.scss +5 -0
  106. data/app/pb_kits/playbook/pb_nav/_nav_item.test.js +5 -3
  107. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +29 -11
  108. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.html.erb +1 -1
  109. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +4 -4
  110. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +2 -0
  111. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_vertical.md +1 -0
  112. data/app/pb_kits/playbook/pb_stat_change/stat_change.test.js +8 -4
  113. data/app/pb_kits/playbook/pb_table/_table.tsx +4 -0
  114. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_borderless.jsx +50 -0
  115. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_borderless_react.md +1 -0
  116. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_floating.jsx +59 -0
  117. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_floating_react.md +1 -0
  118. data/app/pb_kits/playbook/pb_table/docs/example.yml +2 -0
  119. data/app/pb_kits/playbook/pb_table/docs/index.js +2 -0
  120. data/app/pb_kits/playbook/pb_table/styles/_headers.scss +76 -0
  121. data/app/pb_kits/playbook/pb_table/styles/_striped.scss +3 -3
  122. data/app/pb_kits/playbook/pb_table/subcomponents/_table_head.tsx +11 -1
  123. data/app/pb_kits/playbook/pb_table/subcomponents/_table_header.tsx +11 -1
  124. data/app/pb_kits/playbook/pb_table/subcomponents/_table_row.tsx +5 -0
  125. data/app/pb_kits/playbook/pb_table/table.test.js +17 -0
  126. data/app/pb_kits/playbook/pb_tooltip/index.js +183 -56
  127. data/app/pb_kits/playbook/pb_tooltip/tooltip.html.erb +2 -5
  128. data/app/pb_kits/playbook/pb_treemap_chart/_treemap_chart.tsx +1 -1
  129. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +4 -0
  130. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -0
  131. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_disabled.html.erb +19 -0
  132. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_disabled.jsx +23 -0
  133. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_highlight.jsx +4 -4
  134. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  135. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
  136. data/app/pb_kits/playbook/pb_typeahead/index.ts +2 -2
  137. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +2 -5
  138. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -0
  139. data/app/pb_kits/playbook/pb_user/_user.tsx +78 -13
  140. data/app/pb_kits/playbook/pb_user/docs/_user_font_options.html.erb +22 -0
  141. data/app/pb_kits/playbook/pb_user/docs/_user_font_options.jsx +40 -0
  142. data/app/pb_kits/playbook/pb_user/docs/_user_font_options_rails.md +5 -0
  143. data/app/pb_kits/playbook/pb_user/docs/_user_font_options_react.md +5 -0
  144. data/app/pb_kits/playbook/pb_user/docs/example.yml +2 -0
  145. data/app/pb_kits/playbook/pb_user/docs/index.js +1 -0
  146. data/app/pb_kits/playbook/pb_user/user.html.erb +28 -12
  147. data/app/pb_kits/playbook/pb_user/user.rb +17 -1
  148. data/app/pb_kits/playbook/pb_user/user.test.js +182 -1
  149. data/app/pb_kits/playbook/pb_user_badge/user_badge.html.erb +1 -6
  150. data/app/pb_kits/playbook/tokens/_colors.scss +1 -4
  151. data/app/pb_kits/playbook/utilities/globalProps.ts +1 -1
  152. data/app/pb_kits/playbook/utilities/object.test.js +287 -1
  153. data/app/pb_kits/playbook/utilities/object.ts +171 -3
  154. data/app/pb_kits/playbook/utilities/text.ts +1 -1
  155. data/dist/chunks/_typeahead-ySWHB-7p.js +22 -0
  156. data/dist/chunks/_weekday_stacked-36N8xzXL.js +45 -0
  157. data/dist/chunks/lib-BGzBzFZX.js +29 -0
  158. data/dist/chunks/{pb_form_validation-BvDxpfs-.js → pb_form_validation-BvNy9Bd6.js} +1 -1
  159. data/dist/chunks/vendor.js +1 -1
  160. data/dist/playbook-doc.js +1 -1
  161. data/dist/playbook-rails-react-bindings.js +1 -1
  162. data/dist/playbook-rails.js +1 -1
  163. data/dist/playbook.css +1 -1
  164. data/lib/playbook/forms/builder/multi_level_select_field.rb +2 -0
  165. data/lib/playbook/kit_base.rb +4 -4
  166. data/lib/playbook/version.rb +2 -2
  167. metadata +41 -9
  168. data/app/pb_kits/playbook/pb_tooltip/floating_ui.js +0 -282
  169. data/dist/chunks/_typeahead-CRAPc8k-.js +0 -22
  170. data/dist/chunks/_weekday_stacked-T0kFfioG.js +0 -45
  171. data/dist/chunks/lib-BeKPJYlk.js +0 -29
  172. /data/app/pb_kits/playbook/pb_advanced_table/docs/{_advanced_table_selectable_rows_no_subrows.md → _advanced_table_selectable_rows_no_subrows_react.md} +0 -0
  173. /data/app/pb_kits/playbook/pb_advanced_table/docs/{_advanced_table_selectable_rows.md → _advanced_table_selectable_rows_react.md} +0 -0
@@ -1,4 +1,4 @@
1
- import { isEmpty, get, isString, uniqueId, omitBy } from './object';
1
+ import { isEmpty, get, isString, uniqueId, omitBy, noop, merge, filter, find, partial, map, cloneDeep, omit, debounce } from './object';
2
2
 
3
3
  describe('Lodash functions', () => {
4
4
  describe('isEmpty', () => {
@@ -96,4 +96,290 @@ describe('Lodash functions', () => {
96
96
  expect(objWithSmallValues).toEqual(obj);
97
97
  });
98
98
  });
99
+
100
+ describe('noop', () => {
101
+ test('should do nothing and return undefined', () => {
102
+ expect(noop()).toBeUndefined();
103
+ });
104
+ });
105
+
106
+ describe('merge', () => {
107
+ test('merges two objects correctly', () => {
108
+ const obj1 = { a: 1, b: { x: 10 } };
109
+ const obj2 = { b: { y: 20 }, c: 3 };
110
+ expect(merge(obj1, obj2)).toEqual({ a: 1, b: { x: 10, y: 20 }, c: 3 });
111
+ });
112
+
113
+ test('when keys repeat use last occurrence value', () => {
114
+ const obj1 = { a: 1 };
115
+ const obj2 = { b: 2 };
116
+ const obj3 = { a: 3, c: 4 };
117
+ expect(merge(obj1, obj2, obj3)).toEqual({ a: 3, b: 2, c: 4 });
118
+ });
119
+
120
+ test('ignores non-object arguments', () => {
121
+ expect(merge(null, { a: 1 })).toEqual({ a: 1 });
122
+ expect(merge(undefined, { a: 1 })).toEqual({ a: 1 });
123
+ });
124
+ });
125
+
126
+ describe('filter', () => {
127
+ test('filters an array using a function predicate', () => {
128
+ const arr = [1, 2, 3, 4, 5];
129
+ const isEven = (n) => n % 2 === 0;
130
+ expect(filter(arr, isEven)).toEqual([2, 4]);
131
+ });
132
+
133
+ test('filters an array using a string predicate', () => {
134
+ const arr = [
135
+ { active: true, name: 'John' },
136
+ { active: false, name: 'Jane' },
137
+ { active: true, name: 'Doe' }
138
+ ];
139
+ expect(filter(arr, 'active')).toEqual([
140
+ { active: true, name: 'John' },
141
+ { active: true, name: 'Doe' }
142
+ ]);
143
+ });
144
+
145
+ test('filters an array using an array predicate', () => {
146
+ const arr = [
147
+ { type: 'fruit', name: 'apple' },
148
+ { type: 'vegetable', name: 'carrot' },
149
+ { type: 'fruit', name: 'banana' }
150
+ ];
151
+ expect(filter(arr, ['type', 'fruit'])).toEqual([
152
+ { type: 'fruit', name: 'apple' },
153
+ { type: 'fruit', name: 'banana' }
154
+ ]);
155
+ });
156
+
157
+ test('filters an array using an object predicate', () => {
158
+ const arr = [
159
+ { type: 'fruit', name: 'apple', color: 'red' },
160
+ { type: 'fruit', name: 'banana', color: 'yellow' },
161
+ { type: 'vegetable', name: 'carrot', color: 'orange' }
162
+ ];
163
+ expect(filter(arr, { type: 'fruit', color: 'red' })).toEqual([
164
+ { type: 'fruit', name: 'apple', color: 'red' }
165
+ ]);
166
+ });
167
+ });
168
+
169
+ describe('find', () => {
170
+ test('finds an element using a function predicate', () => {
171
+ const arr = [1, 2, 3, 4, 5];
172
+ const greaterThanThree = (n) => n > 3;
173
+ expect(find(arr, greaterThanThree)).toBe(4);
174
+ });
175
+
176
+ test('finds an element using a string predicate', () => {
177
+ const arr = [
178
+ { active: false, name: 'John' },
179
+ { active: true, name: 'Jane' },
180
+ { active: true, name: 'Doe' }
181
+ ];
182
+ expect(find(arr, 'active')).toEqual({ active: true, name: 'Jane' });
183
+ });
184
+
185
+ test('finds an element using an array predicate', () => {
186
+ const arr = [
187
+ { type: 'fruit', name: 'apple' },
188
+ { type: 'vegetable', name: 'carrot' },
189
+ { type: 'fruit', name: 'banana' }
190
+ ];
191
+ expect(find(arr, ['type', 'vegetable'])).toEqual({ type: 'vegetable', name: 'carrot' });
192
+ });
193
+
194
+ test('finds an element using an object predicate', () => {
195
+ const arr = [
196
+ { type: 'fruit', name: 'apple', color: 'red' },
197
+ { type: 'fruit', name: 'banana', color: 'yellow' },
198
+ { type: 'vegetable', name: 'carrot', color: 'orange' }
199
+ ];
200
+ expect(find(arr, { name: 'banana', color: 'yellow' })).toEqual({ type: 'fruit', name: 'banana', color: 'yellow' });
201
+ });
202
+
203
+ test('returns undefined if no element matches', () => {
204
+ const arr = [{ id: 1 }, { id: 2 }];
205
+ expect(find(arr, { id: 3 })).toBeUndefined();
206
+ });
207
+ });
208
+
209
+ describe('partial', () => {
210
+ function add(a, b, c) {
211
+ return a + b + c;
212
+ }
213
+
214
+ test('partials arguments without placeholders', () => {
215
+ const add5 = partial(add, 2, 3);
216
+ expect(add5(4)).toBe(9);
217
+ });
218
+
219
+ test('partials arguments with placeholders', () => {
220
+ const addWithPlaceholder = partial(add, partial.placeholder, 3, partial.placeholder);
221
+ expect(addWithPlaceholder(2, 4)).toBe(9);
222
+ });
223
+
224
+ test('returns correct result when all arguments are pre-filled', () => {
225
+ const addAll = partial(add, 1, 2, 3);
226
+ expect(addAll()).toBe(6);
227
+ });
228
+
229
+ test('appends extra arguments when provided', () => {
230
+ function join(...args) {
231
+ return args.join('_');
232
+ }
233
+ const joinPartial = partial(join, 'a');
234
+ expect(joinPartial('b', 'c')).toBe('a_b_c');
235
+ });
236
+ });
237
+
238
+ describe('map', () => {
239
+ test('maps over an array with a function iteratee', () => {
240
+ const arr = [1, 2, 3];
241
+ const result = map(arr, (num) => num * 2);
242
+ expect(result).toEqual([2, 4, 6]);
243
+ });
244
+
245
+ test('maps over an array with a string iteratee', () => {
246
+ const arr = [{ value: 1 }, { value: 2 }, { value: 3 }];
247
+ const result = map(arr, 'value');
248
+ expect(result).toEqual([1, 2, 3]);
249
+ });
250
+
251
+ test('maps over an object with a function iteratee', () => {
252
+ const obj = { a: 1, b: 2, c: 3 };
253
+ const result = map(obj, (val, key) => key + val);
254
+ expect(result.sort()).toEqual(['a1', 'b2', 'c3'].sort());
255
+ });
256
+
257
+ test('maps over an object with a string iteratee', () => {
258
+ const obj = {
259
+ one: { num: 1 },
260
+ two: { num: 2 },
261
+ three: { num: 3 },
262
+ };
263
+ const result = map(obj, 'num');
264
+ expect(result.sort()).toEqual([1, 2, 3].sort());
265
+ });
266
+
267
+ test('returns original values if no iteratee provided', () => {
268
+ const arr = [1, 2, 3];
269
+ const result = map(arr);
270
+ expect(result).toEqual([1, 2, 3]);
271
+ });
272
+ });
273
+
274
+ describe('cloneDeep', () => {
275
+ test('clones primitive values', () => {
276
+ expect(cloneDeep(42)).toBe(42);
277
+ expect(cloneDeep('test')).toBe('test');
278
+ expect(cloneDeep(null)).toBe(null);
279
+ });
280
+
281
+ test('clones arrays deeply', () => {
282
+ const arr = [1, [2, 3]];
283
+ const cloned = cloneDeep(arr);
284
+ expect(cloned).toEqual(arr);
285
+ cloned[1][0] = 99;
286
+ expect(arr[1][0]).toBe(2);
287
+ });
288
+
289
+ test('clones objects deeply', () => {
290
+ const obj = { a: { b: 2 } };
291
+ const cloned = cloneDeep(obj);
292
+ expect(cloned).toEqual(obj);
293
+ cloned.a.b = 99;
294
+ expect(obj.a.b).toBe(2);
295
+ });
296
+
297
+ test('clones Date objects', () => {
298
+ const date = new Date();
299
+ const cloned = cloneDeep(date);
300
+ expect(cloned).not.toBe(date);
301
+ expect(cloned.getTime()).toBe(date.getTime());
302
+ });
303
+
304
+ test('clones RegExp objects', () => {
305
+ const regex = /test/gi;
306
+ const cloned = cloneDeep(regex);
307
+ expect(cloned).not.toBe(regex);
308
+ expect(cloned.source).toBe(regex.source);
309
+ expect(cloned.flags).toBe(regex.flags);
310
+ });
311
+ });
312
+
313
+ describe('omit', () => {
314
+ test('omits specified keys from object', () => {
315
+ const obj = { a: 1, b: 2, c: 3 };
316
+ expect(omit(obj, 'a', 'c')).toEqual({ b: 2 });
317
+ });
318
+
319
+ test('supports array of keys to omit', () => {
320
+ const obj = { a: 1, b: 2, c: 3 };
321
+ expect(omit(obj, ['b'])).toEqual({ a: 1, c: 3 });
322
+ });
323
+
324
+ test('returns empty object for null or non-object input', () => {
325
+ expect(omit(null, 'a')).toEqual({});
326
+ expect(omit("string", 'a')).toEqual({});
327
+ });
328
+
329
+ test('returns original object if no keys match', () => {
330
+ const obj = { a: 1, b: 2 };
331
+ expect(omit(obj, 'c')).toEqual({ a: 1, b: 2 });
332
+ });
333
+ });
334
+
335
+ describe('debounce', () => {
336
+ beforeEach(() => {
337
+ jest.useFakeTimers();
338
+ });
339
+
340
+ afterEach(() => {
341
+ jest.useRealTimers();
342
+ });
343
+
344
+ test('delays execution until wait time has passed', () => {
345
+ const func = jest.fn();
346
+ const debounced = debounce(func, 1000);
347
+ debounced();
348
+ expect(func).not.toHaveBeenCalled();
349
+ jest.advanceTimersByTime(500);
350
+ expect(func).not.toHaveBeenCalled();
351
+ jest.advanceTimersByTime(500);
352
+ expect(func).toHaveBeenCalledTimes(1);
353
+ });
354
+
355
+ test('calls function only once when called repeatedly', () => {
356
+ const func = jest.fn();
357
+ const debounced = debounce(func, 1000);
358
+ debounced();
359
+ debounced();
360
+ debounced();
361
+ jest.advanceTimersByTime(1000);
362
+ expect(func).toHaveBeenCalledTimes(1);
363
+ });
364
+
365
+ test('immediate option calls function on first call', () => {
366
+ const func = jest.fn();
367
+ const debounced = debounce(func, 1000, true);
368
+ debounced();
369
+ expect(func).toHaveBeenCalledTimes(1);
370
+ debounced();
371
+ debounced();
372
+ jest.advanceTimersByTime(1000);
373
+ expect(func).toHaveBeenCalledTimes(1);
374
+ });
375
+
376
+ test('subsequent call after wait period works with immediate option', () => {
377
+ const func = jest.fn();
378
+ const debounced = debounce(func, 1000, true);
379
+ debounced();
380
+ jest.advanceTimersByTime(1100);
381
+ debounced();
382
+ expect(func).toHaveBeenCalledTimes(2);
383
+ });
384
+ });
99
385
  });
@@ -1,6 +1,7 @@
1
1
  /* 🛠️ Any commonly used lodash functions can be added here. 🤙 */
2
2
 
3
- export const isEmpty = (obj: any) => [Object, Array].includes((obj || {}).constructor) && !Object.entries((obj || {})).length;
3
+ export const isEmpty = (obj: any) =>
4
+ [Object, Array].includes((obj || {}).constructor) && !Object.entries((obj || {})).length
4
5
 
5
6
  export const get = <T, R = any>(obj: T, path: string, defaultValue?: R): R | any => {
6
7
  const travel = (regexp: RegExp): any =>
@@ -24,8 +25,175 @@ export const omitBy = (obj: Record<string, any>, predicate: (value: any, key: st
24
25
  if (obj === null || typeof obj !== 'object') return {}
25
26
  return Object.keys(obj).reduce((result: Record<string, any>, key: string) => {
26
27
  if (!predicate(obj[key], key)) {
27
- result[key] = obj[key];
28
+ result[key] = obj[key]
28
29
  }
29
- return result;
30
+ return result
30
31
  }, {})
31
32
  }
33
+
34
+ export const omit = (
35
+ obj: Record<string, any>,
36
+ ...paths: (string | string[])[]
37
+ ): Record<string, any> => {
38
+ if (obj === null || typeof obj !== 'object') return {}
39
+ const keysToOmit = new Set<string>()
40
+ paths.forEach(p => {
41
+ if (Array.isArray(p)) {
42
+ p.forEach(key => keysToOmit.add(key))
43
+ } else {
44
+ keysToOmit.add(p)
45
+ }
46
+ })
47
+ const result: Record<string, any> = {}
48
+ for (const key in obj) {
49
+ if (!keysToOmit.has(key)) {
50
+ result[key] = obj[key]
51
+ }
52
+ }
53
+ return result
54
+ }
55
+
56
+ export const noop = (): void => {
57
+ // empty
58
+ }
59
+
60
+ export const cloneDeep = (value: any): any => {
61
+ if (value === null || typeof value !== 'object') {
62
+ return value
63
+ }
64
+ if (Array.isArray(value)) {
65
+ return value.map(cloneDeep)
66
+ }
67
+ if (value instanceof Date) {
68
+ return new Date(value.getTime())
69
+ }
70
+ if (value instanceof RegExp) {
71
+ return new RegExp(value.source, value.flags)
72
+ }
73
+ const clonedObj: any = {}
74
+ for (const key in value) {
75
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
76
+ clonedObj[key] = cloneDeep(value[key])
77
+ }
78
+ }
79
+ return clonedObj
80
+ }
81
+
82
+ export function merge(
83
+ ...objects: Array<Record<string, unknown> | null | undefined>
84
+ ): Record<string, unknown> {
85
+ const isPlainObject = (obj: unknown): obj is Record<string, unknown> =>
86
+ !!obj && typeof obj === 'object' && !Array.isArray(obj)
87
+
88
+ const result: Record<string, unknown> = {}
89
+
90
+ for (const obj of objects) {
91
+ if (!obj || typeof obj !== 'object') continue
92
+
93
+ for (const key of Object.keys(obj)) {
94
+ const oldVal = result[key]
95
+ const newVal = (obj as Record<string, unknown>)[key]
96
+
97
+ if (Array.isArray(oldVal) && Array.isArray(newVal)) {
98
+ result[key] = newVal
99
+ } else if (isPlainObject(oldVal) && isPlainObject(newVal)) {
100
+ result[key] = merge(oldVal, newVal)
101
+ } else if (Array.isArray(oldVal) && isPlainObject(newVal)) {
102
+ result[key] = oldVal
103
+ } else if (isPlainObject(oldVal) && Array.isArray(newVal)) {
104
+ result[key] = oldVal
105
+ } else {
106
+ result[key] = newVal
107
+ }
108
+ }
109
+ }
110
+ return result
111
+ }
112
+
113
+ const createIteratee = (predicate: any) => {
114
+ if (typeof predicate === 'function') {
115
+ return predicate
116
+ }
117
+ if (typeof predicate === 'string') {
118
+ return (obj: any) => obj[predicate]
119
+ }
120
+ if (Array.isArray(predicate)) {
121
+ const [key, value] = predicate
122
+ return (obj: any) => obj[key] === value
123
+ }
124
+ if (typeof predicate === 'object' && predicate !== null) {
125
+ return (obj: any) => {
126
+ for (const key in predicate) {
127
+ if (Object.prototype.hasOwnProperty.call(predicate, key)) {
128
+ if (obj[key] !== predicate[key]) return false
129
+ }
130
+ }
131
+ return true
132
+ }
133
+ }
134
+ return () => false
135
+ }
136
+
137
+ export const filter = <T>(array: T[], predicate: any): T[] => {
138
+ const iteratee = createIteratee(predicate)
139
+ return array.filter(iteratee)
140
+ }
141
+
142
+ export const find = <T>(array: T[], predicate: any): T | undefined => {
143
+ const iteratee = createIteratee(predicate)
144
+ return array.find(iteratee)
145
+ }
146
+
147
+ export const partial = <F extends (...args: any[]) => any>(
148
+ fn: F,
149
+ ...partials: any[]
150
+ ): ((...args: any[]) => ReturnType<F>) => {
151
+ const placeholder = partial.placeholder
152
+ return (...args: any[]): ReturnType<F> => {
153
+ let argIndex = 0
154
+ const finalArgs = partials.map(arg =>
155
+ arg === placeholder ? args[argIndex++] : arg
156
+ )
157
+ return fn(...(finalArgs.concat(args.slice(argIndex)) as Parameters<F>)) as ReturnType<F>
158
+ }
159
+ }
160
+
161
+ partial.placeholder = Symbol()
162
+ export const _ = partial.placeholder
163
+
164
+ export const map = <T, U>(
165
+ collection: T[] | Record<string, T>,
166
+ iteratee?: ((value: T, index: number | string, collection: T[] | Record<string, T>) => U) | string
167
+ ): U[] => {
168
+ const fn =
169
+ typeof iteratee === "function"
170
+ ? iteratee
171
+ : typeof iteratee === "string"
172
+ ? (item: T) => (item as any)[iteratee]
173
+ : (item: T) => item as unknown as U
174
+
175
+ if (Array.isArray(collection)) {
176
+ return collection.map((value, index) => fn(value, index, collection))
177
+ }
178
+ return Object.keys(collection).map(key => fn(collection[key], key, collection))
179
+ }
180
+
181
+ export function debounce<F extends (...args: any[]) => any>(
182
+ func: F,
183
+ wait: number,
184
+ immediate?: boolean
185
+ ): (...args: Parameters<F>) => void {
186
+ let timeout: ReturnType<typeof setTimeout> | null;
187
+ return function(this: any, ...args: any[]) {
188
+ if (timeout) clearTimeout(timeout);
189
+ if (immediate && !timeout) {
190
+ func.apply(this, args);
191
+ }
192
+ timeout = setTimeout(() => {
193
+ timeout = null;
194
+ if (!immediate) {
195
+ func.apply(this, args);
196
+ }
197
+ }, wait);
198
+ };
199
+ }
@@ -1,4 +1,4 @@
1
- import { filter } from 'lodash'
1
+ import { filter } from './object'
2
2
 
3
3
  const isEmpty = (value: string | unknown): boolean => !value || value == ''
4
4