playbook_ui 12.26.1.pre.alpha.railsmultilevelimprovements842 → 12.26.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 326f8eff50b791dc21ab3ec0bdb295adef8d14d3957277f4f92d8abdfe445686
4
- data.tar.gz: 0a9d5b6672174df0cb045cc3aea6b017d0e30f1a978df27e0529b7f1d3cb3472
3
+ metadata.gz: 7fbc9b56c02a9915257f3a1169f85773e8a063c14de44a2492a250f9015dee65
4
+ data.tar.gz: f70c024e4feae26b0767595ad538df87eb08e78453a56c6ca22de07552d60d5d
5
5
  SHA512:
6
- metadata.gz: 1b079256265e6bcdea4576a6930b527b944044f0064936376278147aad59cf1a5b311ba25632ca3878bb50e3fd62a33ccdf17cf805d7dd60eaece7d5d3e640fe
7
- data.tar.gz: fa05822858223b65f2a3a2755a1f7fcecea6946d7a1f31ea40a32ad10ebd5b3df128cc3736fdab16b6329ba283e50d201412a8d79c6027745a646d8c6c94ac05
6
+ metadata.gz: e40a155458785168e1fb5f5f63efe8c5bcd7dca3d1b8c618557d86453d5008336454d205330742fc029e6593e05e566610eb853577b42a993934eeb20408ffa5
7
+ data.tar.gz: 9acc8dcd73190cc433572b49bdef61cb4e425304125aaf8d5b61c7426f5f4c2a314f1e2b38e9b55a5f37b6f45b56cb8079ec9b82070c461699ae9b2357689a2d
@@ -1,12 +1,12 @@
1
- import React, { useState, useEffect, useRef } from "react"
2
- import classnames from "classnames"
3
- import { globalProps, GlobalProps } from "../utilities/globalProps"
4
- import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props"
5
- import Checkbox from "../pb_checkbox/_checkbox"
6
- import Icon from "../pb_icon/_icon"
7
- import FormPill from "../pb_form_pill/_form_pill"
8
- import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button"
9
- import { cloneDeep } from "lodash"
1
+ import React, { useState, useEffect, useRef } from "react";
2
+ import classnames from "classnames";
3
+ import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props";
4
+ import { globalProps, GlobalProps } from "../utilities/globalProps";
5
+ import Icon from "../pb_icon/_icon";
6
+ import Checkbox from "../pb_checkbox/_checkbox";
7
+ import FormPill from "../pb_form_pill/_form_pill";
8
+ import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button";
9
+ import { cloneDeep } from "lodash";
10
10
 
11
11
  import {
12
12
  getAncestorsOfUnchecked,
@@ -15,18 +15,17 @@ import {
15
15
  getCheckedItems,
16
16
  getDefaultCheckedItems,
17
17
  recursiveCheckParent,
18
- } from "./_helper_functions"
18
+ } from "./_helper_functions";
19
19
 
20
20
  type MultiLevelSelectProps = {
21
- aria?: { [key: string]: string }
22
- className?: string
23
- data?: { [key: string]: string }
24
- id?: string
25
- name?: string
26
- returnAllSelected?: boolean
27
- treeData?: { [key: string]: string }[]
28
- onSelect?: (prop: { [key: string]: any }) => void
29
- } & GlobalProps
21
+ aria?: { [key: string]: string };
22
+ className?: string;
23
+ data?: { [key: string]: string };
24
+ id?: string;
25
+ returnAllSelected?: boolean;
26
+ treeData?: { [key: string]: string }[];
27
+ onSelect?: (prop: { [key: string]: any }) => void;
28
+ } & GlobalProps;
30
29
 
31
30
  const MultiLevelSelect = (props: MultiLevelSelectProps) => {
32
31
  const {
@@ -34,74 +33,71 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
34
33
  className,
35
34
  data = {},
36
35
  id,
37
- name,
38
36
  returnAllSelected = false,
39
37
  treeData,
40
38
  onSelect = () => {},
41
- } = props
39
+ } = props;
42
40
 
43
- const ariaProps = buildAriaProps(aria)
44
- const dataProps = buildDataProps(data)
41
+ const ariaProps = buildAriaProps(aria);
42
+ const dataProps = buildDataProps(data);
45
43
  const classes = classnames(
46
44
  buildCss("pb_multi_level_select"),
47
45
  globalProps(props),
48
46
  className
49
- )
50
-
51
- const dropdownRef = useRef(null)
47
+ );
52
48
 
49
+ const dropdownRef = useRef(null);
53
50
 
54
51
  //state for expanded property
55
- const [expanded, setExpanded] = useState([])
52
+ const [expanded, setExpanded] = useState([]);
56
53
  //state for whether dropdown is open or closed
57
- const [isClosed, setIsClosed] = useState(true)
54
+ const [isClosed, setIsClosed] = useState(true);
58
55
  //state from onchange for textinput, to use for filtering to create typeahead
59
- const [filterItem, setFilterItem] = useState("")
56
+ const [filterItem, setFilterItem] = useState("");
60
57
  //this is essentially the return that the user will get when they use the kit
61
- const [returnedArray, setReturnedArray] = useState([])
58
+ const [returnedArray, setReturnedArray] = useState([]);
62
59
  //formattedData with checked and parent_id added
63
- const [formattedData, setFormattedData] = useState([])
60
+ const [formattedData, setFormattedData] = useState([]);
64
61
  //state for return for default
65
- const [defaultReturn, setDefaultReturn] = useState([])
66
-
62
+ const [defaultReturn, setDefaultReturn] = useState([]);
67
63
 
68
64
  useEffect(() => {
69
- setFormattedData(addCheckedAndParentProperty(treeData))
70
- }, [treeData])
65
+ setFormattedData(addCheckedAndParentProperty(treeData));
66
+ }, [treeData]);
71
67
 
72
68
  useEffect(() => {
73
69
  if (returnAllSelected) {
74
- setReturnedArray(getCheckedItems(formattedData))
70
+ setReturnedArray(getCheckedItems(formattedData));
75
71
  } else {
76
- setDefaultReturn(getDefaultCheckedItems(formattedData))
72
+ setDefaultReturn(getDefaultCheckedItems(formattedData));
77
73
  }
78
- }, [formattedData])
74
+ }, [formattedData]);
79
75
 
80
76
  useEffect(() => {
81
77
  // Function to handle clicks outside the dropdown
82
78
  const handleClickOutside = (event: any) => {
83
79
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
84
- setIsClosed(true)
80
+ setIsClosed(true);
85
81
  }
86
- }
82
+ };
87
83
  // Attach the event listener
88
- window.addEventListener("click", handleClickOutside)
84
+ window.addEventListener("click", handleClickOutside);
89
85
  // Clean up the event listener on unmount
90
86
  return () => {
91
- window.removeEventListener("click", handleClickOutside)
92
- }
93
- }, [])
87
+ window.removeEventListener("click", handleClickOutside);
88
+ };
89
+ }, []);
94
90
 
95
91
  const modifyRecursive = (tree: { [key: string]: any }[], check: boolean) => {
96
92
  if (!Array.isArray(tree)) {
97
- return
93
+ return;
98
94
  }
99
95
  return tree.map((item: { [key: string]: any }) => {
100
- item.checked = check
101
- item.children = modifyRecursive(item.children, check)
102
- return item
103
- })
104
- }
96
+ item.checked = check;
97
+ item.children = modifyRecursive(item.children, check);
98
+ return item;
99
+ });
100
+ };
105
101
 
106
102
  //iterate over tree, find item and set checked or unchecked
107
103
  const modifyValue = (
@@ -110,48 +106,48 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
110
106
  check: boolean
111
107
  ) => {
112
108
  if (!Array.isArray(tree)) {
113
- return
109
+ return;
114
110
  }
115
111
  return tree.map((item: any) => {
116
- if (item.id != id) item.children = modifyValue(id, item.children, check)
112
+ if (item.id != id) item.children = modifyValue(id, item.children, check);
117
113
  else {
118
- item.checked = check
119
- item.children = modifyRecursive(item.children, check)
114
+ item.checked = check;
115
+ item.children = modifyRecursive(item.children, check);
120
116
  }
121
117
 
122
- return item
123
- })
124
- }
118
+ return item;
119
+ });
120
+ };
125
121
 
126
122
  //clone tree, check items + children
127
123
  const checkItem = (item: { [key: string]: any }) => {
128
- const tree = cloneDeep(formattedData)
124
+ const tree = cloneDeep(formattedData);
129
125
  if (returnAllSelected) {
130
- return modifyValue(item.id, tree, true)
126
+ return modifyValue(item.id, tree, true);
131
127
  } else {
132
- const checkedTree = modifyValue(item.id, tree, true)
133
- return recursiveCheckParent(item, checkedTree)
128
+ const checkedTree = modifyValue(item.id, tree, true);
129
+ return recursiveCheckParent(item, checkedTree);
134
130
  }
135
- }
131
+ };
136
132
 
137
133
  //clone tree, uncheck items + children
138
134
  const unCheckItem = (item: { [key: string]: any }) => {
139
- const tree = cloneDeep(formattedData)
135
+ const tree = cloneDeep(formattedData);
140
136
  if (returnAllSelected) {
141
- return modifyValue(item.id, tree, false)
137
+ return modifyValue(item.id, tree, false);
142
138
  } else {
143
- const uncheckedTree = modifyValue(item.id, tree, false)
144
- return getAncestorsOfUnchecked(uncheckedTree, item)
139
+ const uncheckedTree = modifyValue(item.id, tree, false);
140
+ return getAncestorsOfUnchecked(uncheckedTree, item);
145
141
  }
146
- }
142
+ };
147
143
 
148
144
  //setformattedData with proper properties
149
145
  const changeItem = (item: { [key: string]: any }, check: boolean) => {
150
- const tree = check ? checkItem(item) : unCheckItem(item)
151
- setFormattedData(tree)
146
+ const tree = check ? checkItem(item) : unCheckItem(item);
147
+ setFormattedData(tree);
152
148
 
153
- return tree
154
- }
149
+ return tree;
150
+ };
155
151
 
156
152
  //function to map over data and add parent_id + depth property to each item
157
153
  const addCheckedAndParentProperty = (
@@ -160,86 +156,87 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
160
156
  depth: number = 0
161
157
  ) => {
162
158
  if (!Array.isArray(treeData)) {
163
- return
159
+ return;
164
160
  }
165
161
  return treeData.map((item: { [key: string]: any } | any) => {
166
162
  const newItem = {
167
163
  ...item,
168
164
  parent_id,
169
165
  depth,
170
- }
166
+ };
171
167
  if (newItem.children && newItem.children.length > 0) {
172
168
  const children =
173
169
  item.checked && !returnAllSelected
174
170
  ? modifyRecursive(item.children, true)
175
- : item.children
171
+ : item.children;
176
172
  newItem.children = addCheckedAndParentProperty(
177
173
  children,
178
174
  newItem.id,
179
175
  depth + 1
180
- )
176
+ );
181
177
  }
182
- return newItem
183
- })
184
- }
178
+ return newItem;
179
+ });
180
+ };
185
181
 
186
182
  //click event for x on form pill
187
183
  const handlePillClose = (event: any, clickedItem: { [key: string]: any }) => {
188
184
  // prevents the dropdown from closing when clicking on the pill
189
- event.stopPropagation()
190
- const updatedTree = changeItem(clickedItem, false)
185
+ event.stopPropagation();
186
+ const updatedTree = changeItem(clickedItem, false);
191
187
  //logic for removing items from returnArray or defaultReturn when pills clicked
192
188
  if (returnAllSelected) {
193
- onSelect(getCheckedItems(updatedTree))
189
+ onSelect(getCheckedItems(updatedTree));
194
190
  } else {
195
- onSelect(getDefaultCheckedItems(updatedTree))
191
+ onSelect(getDefaultCheckedItems(updatedTree));
196
192
  }
197
- }
193
+ };
198
194
 
199
195
  //handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked
200
196
  const handleInputWrapperClick = (e: any) => {
201
- e.stopPropagation()
197
+ e.stopPropagation();
202
198
  if (
203
199
  e.target.id === "multiselect_input" ||
204
200
  e.target.classList.contains("pb_form_pill_tag")
205
201
  ) {
206
- return
202
+ return;
207
203
  }
208
- setIsClosed(!isClosed)
209
- }
204
+ setIsClosed(!isClosed);
205
+ };
210
206
 
211
207
  //Main function to handle any click inside dropdown
212
208
  const handledropdownItemClick = (e: any, check: boolean) => {
213
- const clickedItem = e.target.parentNode.id
209
+ const clickedItem = e.target.parentNode.id;
214
210
  //setting filterItem to "" will clear textinput and clear typeahead
215
- setFilterItem("")
211
+ setFilterItem("");
216
212
 
217
- const filtered = filterFormattedDataById(formattedData, clickedItem)
218
- const updatedTree = changeItem(filtered[0], check)
213
+ const filtered = filterFormattedDataById(formattedData, clickedItem);
214
+ const updatedTree = changeItem(filtered[0], check);
215
+ console.log(updatedTree);
219
216
  if (returnAllSelected) {
220
- onSelect(getCheckedItems(updatedTree))
217
+ onSelect(getCheckedItems(updatedTree));
221
218
  } else {
222
- onSelect(getDefaultCheckedItems(updatedTree))
219
+ onSelect(getDefaultCheckedItems(updatedTree));
223
220
  }
224
- }
221
+ };
225
222
 
226
- const isExpanded = (item: any) => expanded.indexOf(item.id) > -1
223
+ const isExpanded = (item: any) => expanded.indexOf(item.id) > -1;
227
224
 
228
225
  //handle click on chevron toggles in dropdown
229
226
  const handleToggleClick = (id: string, event: React.MouseEvent) => {
230
- event.stopPropagation()
231
- const clickedItem = filterFormattedDataById(formattedData, id)
227
+ event.stopPropagation();
228
+ const clickedItem = filterFormattedDataById(formattedData, id);
232
229
  if (clickedItem) {
233
- let expandedArray = [...expanded]
234
- const itemExpanded = isExpanded(clickedItem[0])
230
+ let expandedArray = [...expanded];
231
+ const itemExpanded = isExpanded(clickedItem[0]);
235
232
 
236
233
  if (itemExpanded)
237
- expandedArray = expandedArray.filter((i) => i != clickedItem[0].id)
238
- else expandedArray.push(clickedItem[0].id)
234
+ expandedArray = expandedArray.filter((i) => i != clickedItem[0].id);
235
+ else expandedArray.push(clickedItem[0].id);
239
236
 
240
- setExpanded(expandedArray)
237
+ setExpanded(expandedArray);
241
238
  }
242
- }
239
+ };
243
240
 
244
241
  //rendering formattedData to UI based on typeahead
245
242
  const renderNestedOptions = (items: { [key: string]: any }[]) => {
@@ -248,9 +245,9 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
248
245
  {Array.isArray(items) &&
249
246
  items.map((item: { [key: string]: any }) => {
250
247
  return (
251
- <div key={item.id}>
252
- <li className='dropdown_item' data-name={item.id}>
253
- <div className='dropdown_item_checkbox_row'>
248
+ <>
249
+ <li key={item.id} className="dropdown_item" data-name={item.id}>
250
+ <div className="dropdown_item_checkbox_row">
254
251
  <div
255
252
  key={isExpanded(item) ? "chevron-down" : "chevron-right"}
256
253
  >
@@ -266,17 +263,17 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
266
263
  onClick={(event: any) =>
267
264
  handleToggleClick(item.id, event)
268
265
  }
269
- variant='link'
266
+ variant="link"
270
267
  />
271
268
  </div>
272
269
  <Checkbox text={item.label} id={item.id}>
273
270
  <input
274
271
  checked={item.checked}
275
- type='checkbox'
272
+ type="checkbox"
276
273
  name={item.label}
277
274
  value={item.label}
278
275
  onChange={(e) => {
279
- handledropdownItemClick(e, !item.checked)
276
+ handledropdownItemClick(e, !item.checked);
280
277
  }}
281
278
  />
282
279
  </Checkbox>
@@ -288,30 +285,24 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
288
285
  <div>{renderNestedOptions(item.children)}</div>
289
286
  )}
290
287
  </li>
291
- </div>
292
- )
288
+ </>
289
+ );
293
290
  })}
294
291
  </ul>
295
- )
296
- }
292
+ );
293
+ };
297
294
 
298
295
  return (
299
296
  <div {...ariaProps} {...dataProps} className={classes} id={id}>
300
- <div ref={dropdownRef} className='wrapper'>
301
- <div className='input_wrapper' onClick={handleInputWrapperClick}>
302
- <div className='input_inner_container'>
303
- {returnedArray.length !== 0 && returnAllSelected
304
- ? returnedArray.map((item) => (
305
- <input type='hidden' name={`${name}[]`} value={item.id} />
306
- ))
307
- : null}
308
-
297
+ <div ref={dropdownRef} className="wrapper">
298
+ <div className="input_wrapper" onClick={handleInputWrapperClick}>
299
+ <div className="input_inner_container">
309
300
  {returnedArray.length !== 0 && returnAllSelected
310
301
  ? returnedArray.map((item, index) => (
311
302
  <FormPill
312
303
  key={index}
313
304
  text={item.label}
314
- size='small'
305
+ size="small"
315
306
  onClick={(event: any) => handlePillClose(event, item)}
316
307
  />
317
308
  ))
@@ -322,29 +313,29 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
322
313
  <FormPill
323
314
  key={index}
324
315
  text={item.label}
325
- size='small'
316
+ size="small"
326
317
  onClick={(event: any) => handlePillClose(event, item)}
327
318
  />
328
319
  ))}
329
320
  {returnedArray.length !== 0 && returnAllSelected && <br />}
330
321
  {defaultReturn.length !== 0 && !returnAllSelected && <br />}
331
322
  <input
332
- id='multiselect_input'
323
+ id="multiselect_input"
333
324
  onChange={(e) => {
334
- setFilterItem(e.target.value)
325
+ setFilterItem(e.target.value);
335
326
  }}
336
- placeholder='Start typing...'
327
+ placeholder="Start typing..."
337
328
  value={filterItem}
338
329
  onClick={() => setIsClosed(false)}
339
330
  />
340
331
  </div>
341
332
  {isClosed ? (
342
- <div key='chevron-down'>
343
- <Icon icon='chevron-down' />
333
+ <div key="chevron-down">
334
+ <Icon icon="chevron-down" />
344
335
  </div>
345
336
  ) : (
346
- <div key='chevron-up'>
347
- <Icon icon='chevron-up' />
337
+ <div key="chevron-up">
338
+ <Icon icon="chevron-up" />
348
339
  </div>
349
340
  )}
350
341
  </div>
@@ -355,7 +346,7 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
355
346
  </div>
356
347
  </div>
357
348
  </div>
358
- )
359
- }
349
+ );
350
+ };
360
351
 
361
- export default MultiLevelSelect
352
+ export default MultiLevelSelect;
@@ -1,72 +1,72 @@
1
1
  <% treeData = [{
2
2
  label: "Power Home Remodeling",
3
3
  value: "Power Home Remodeling",
4
- id: "100",
4
+ id: "powerhome1",
5
5
  expanded: true,
6
6
  children: [
7
7
  {
8
8
  label: "People",
9
9
  value: "People",
10
- id: "101",
10
+ id: "people1",
11
11
  children: [
12
12
  {
13
13
  label: "Talent Acquisition",
14
14
  value: "Talent Acquisition",
15
- id: "102",
15
+ id: "talent1",
16
16
  },
17
17
  {
18
18
  label: "Business Affairs",
19
19
  value: "Business Affairs",
20
- id: "103",
20
+ id: "business1",
21
21
  children: [
22
22
  {
23
23
  label: "Initiatives",
24
24
  value: "Initiatives",
25
- id: "104",
25
+ id: "initiative1",
26
26
  },
27
27
  {
28
28
  label: "Learning & Development",
29
29
  value: "Learning & Development",
30
- id: "105",
30
+ id: "development1",
31
31
  },
32
32
  ],
33
33
  },
34
34
  {
35
35
  label: "People Experience",
36
36
  value: "People Experience",
37
- id: "106",
37
+ id: "experience1",
38
38
  },
39
39
  ],
40
40
  },
41
41
  {
42
42
  label: "Contact Center",
43
43
  value: "Contact Center",
44
- id: "107",
44
+ id: "contact1",
45
45
  children: [
46
46
  {
47
47
  label: "Appointment Management",
48
48
  value: "Appointment Management",
49
- id: "108",
49
+ id: "appointment1",
50
50
  },
51
51
  {
52
52
  label: "Customer Service",
53
53
  value: "Customer Service",
54
- id: "109",
54
+ id: "customer1",
55
55
  },
56
56
  {
57
57
  label: "Energy",
58
58
  value: "Energy",
59
- id: "110",
59
+ id: "energy1",
60
60
  },
61
61
  ],
62
62
  },
63
63
  ],
64
- }] %>
64
+ }] %>
65
65
 
66
66
 
67
+ <%= pb_rails("multi_level_select", props: {
68
+ id: "default-multi-level-select",
69
+ tree_data:treeData
70
+ }) %>
71
+
67
72
 
68
- <%= pb_rails("multi_level_select", props: {
69
- id: "multi-level-select-default-rails",
70
- name: "my_array",
71
- tree_data: treeData
72
- }) %>
@@ -72,18 +72,15 @@ const MultiLevelSelectDefault = (props) => {
72
72
  return (
73
73
  <div>
74
74
  <MultiLevelSelect
75
- id='multiselect-default'
75
+ id="multiselect-default"
76
76
  onSelect={(selectedNodes) =>
77
- console.log(
78
- "Selected Items",
79
- selectedNodes
80
- )
81
- }
77
+ console.log("Selected Items", selectedNodes)
78
+ }
82
79
  treeData={treeData}
83
80
  {...props}
84
81
  />
85
82
  </div>
86
- )
83
+ );
87
84
  };
88
85
 
89
86
  export default MultiLevelSelectDefault;
@@ -1,5 +1,5 @@
1
1
  The MultiLevelSelect kit renders a multi leveled select dropdown based on data from the user. `treeData` is a required prop that is expected to contain the data in the form of an array of objects. See code snippet for an example data array.
2
2
 
3
- For the React version of the kit, the `onSelect` prop returns an array of objects. This array contains all checked items irrespective of whether it is a parent, child or grandchild. Open the console on this example and check and uncheck checkboxes to see this is action!
3
+ For the React version of the kit, the `onSelect` prop returns an array of all checked items, irrespective of whether it is a parent, child or grandchild. Open the console on this example and check and uncheck checkboxes to see this is action!
4
4
 
5
- For the Rails version of the kit, there is no onselect. The form submits as a array of strings, following the typical rails pattern of utilizing hidden inputs. The strings are the values of the selected items' ids. For example, ["103", "106", "107"].
5
+ For the Rails version, the array of checked items is attached to the DOM in a data attribute titled `data-tree` on the wrapping div around the MultiLevelSelect.