playbook_ui 14.20.0.pre.alpha.PLAY2214checkboxhiddeninput8052 → 14.20.0.pre.alpha.PLAY21817891
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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +49 -116
- data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +2 -58
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +4 -16
- data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +3 -7
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +0 -13
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +3 -10
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +0 -61
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -2
- data/app/pb_kits/playbook/pb_advanced_table/index.js +0 -2
- data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +11 -4
- data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +7 -28
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_custom.html.erb +0 -1
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +48 -2
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_options.html.erb +0 -1
- data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +0 -1
- data/app/pb_kits/playbook/pb_draggable/context/index.tsx +58 -17
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +3 -12
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +0 -2
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +1 -2
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +2 -108
- data/app/pb_kits/playbook/pb_dropdown/index.js +0 -24
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +10 -14
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +15 -26
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -1
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +0 -4
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_only_countries.jsx +1 -1
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +3 -4
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +0 -1
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +0 -3
- data/app/pb_kits/playbook/pb_popover/index.ts +4 -9
- data/app/pb_kits/playbook/pb_select/docs/example.yml +0 -2
- data/app/pb_kits/playbook/pb_select/docs/index.js +0 -1
- data/app/pb_kits/playbook/pb_table/docs/_table_with_selectable_rows.html.erb +51 -3
- data/app/pb_kits/playbook/pb_table/styles/_mobile_collapse.scss +1 -1
- data/dist/chunks/{_typeahead-CoOpeYom.js → _typeahead-BmOWdDtp.js} +2 -2
- data/dist/chunks/_weekday_stacked-CvcuQyr9.js +45 -0
- data/dist/chunks/{lib-D7Va7yqa.js → lib-D5R1BjUn.js} +1 -1
- data/dist/chunks/{pb_form_validation-DSkdRDMf.js → pb_form_validation-BZ2AVAi_.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/menu.yml +1 -1
- data/dist/playbook-doc.js +2 -2
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +6 -21
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows.jsx +0 -57
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_react.md +0 -5
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_custom_rails.md +0 -1
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_form.html.erb +0 -22
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_form.md +0 -5
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate_rails.md +0 -1
- data/app/pb_kits/playbook/pb_checkbox/index.js +0 -56
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.jsx +0 -42
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.md +0 -1
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.html.erb +0 -4
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.jsx +0 -15
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.md +0 -1
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.html.erb +0 -12
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.jsx +0 -31
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.md +0 -1
- data/dist/chunks/_weekday_stacked-BppvLxTS.js +0 -45
@@ -5,8 +5,7 @@ module Playbook
|
|
5
5
|
class Checkbox < Playbook::KitBase
|
6
6
|
prop :error, type: Playbook::Props::Boolean, default: false
|
7
7
|
prop :checked, type: Playbook::Props::Boolean, default: false
|
8
|
-
prop :
|
9
|
-
prop :indeterminate_parent
|
8
|
+
prop :indeterminate, type: Playbook::Props::Boolean, default: false
|
10
9
|
prop :text
|
11
10
|
prop :value
|
12
11
|
prop :name
|
@@ -18,43 +17,19 @@ module Playbook
|
|
18
17
|
default: false
|
19
18
|
prop :form_spacing, type: Playbook::Props::Boolean,
|
20
19
|
default: false
|
21
|
-
prop :hidden_input, type: Playbook::Props::Boolean,
|
22
|
-
default: false
|
23
|
-
prop :hidden_value
|
24
20
|
|
25
21
|
def classname
|
26
|
-
generate_classname("pb_checkbox_kit", checked_class) + error_class
|
22
|
+
generate_classname("pb_checkbox_kit", checked_class) + indeterminate_class + error_class
|
27
23
|
end
|
28
24
|
|
29
25
|
def input
|
30
|
-
|
31
|
-
effective_name = name || input_options[:name]
|
32
|
-
effective_value = value || input_options[:value] || "1"
|
33
|
-
is_checked = checked || input_options[:checked]
|
34
|
-
|
35
|
-
inputs << hidden_field_tag(effective_name, hidden_value || "0") if hidden_input && effective_name.present?
|
36
|
-
|
37
|
-
inputs << check_box_tag(
|
38
|
-
effective_name,
|
39
|
-
effective_value,
|
40
|
-
is_checked,
|
41
|
-
input_options.merge(disabled: disabled)
|
42
|
-
)
|
43
|
-
|
44
|
-
safe_join(inputs)
|
26
|
+
check_box_tag(name, value, checked, input_options.merge(disabled: disabled))
|
45
27
|
end
|
46
28
|
|
47
29
|
def checkbox_label_status
|
48
30
|
error ? "negative" : nil
|
49
31
|
end
|
50
32
|
|
51
|
-
def data
|
52
|
-
Hash(prop(:data)).merge(
|
53
|
-
pb_checkbox_indeterminate_main: indeterminate_main,
|
54
|
-
pb_checkbox_indeterminate_parent: indeterminate_parent
|
55
|
-
)
|
56
|
-
end
|
57
|
-
|
58
33
|
private
|
59
34
|
|
60
35
|
def error_class
|
@@ -64,6 +39,10 @@ module Playbook
|
|
64
39
|
def checked_class
|
65
40
|
checked ? "on" : "off"
|
66
41
|
end
|
42
|
+
|
43
|
+
def indeterminate_class
|
44
|
+
indeterminate ? " indeterminate" : ""
|
45
|
+
end
|
67
46
|
end
|
68
47
|
end
|
69
48
|
end
|
@@ -9,10 +9,11 @@
|
|
9
9
|
<tr>
|
10
10
|
<th>
|
11
11
|
<%= pb_rails("checkbox", props: {
|
12
|
+
checked: true,
|
12
13
|
text: "Uncheck All",
|
13
14
|
value: "checkbox-value",
|
14
15
|
name: "main-checkbox",
|
15
|
-
|
16
|
+
indeterminate: true,
|
16
17
|
id: "indeterminate-checkbox"
|
17
18
|
}) %>
|
18
19
|
</th>
|
@@ -29,10 +30,55 @@
|
|
29
30
|
value: checkbox[:id],
|
30
31
|
name: "#{checkbox[:id]}-indeterminate-checkbox",
|
31
32
|
id: "#{checkbox[:id]}-indeterminate-checkbox",
|
32
|
-
indeterminate_parent: "indeterminate-checkbox",
|
33
33
|
}) %>
|
34
34
|
</td>
|
35
35
|
</tr>
|
36
36
|
<% end %>
|
37
37
|
</tbody>
|
38
38
|
<% end %>
|
39
|
+
|
40
|
+
<script>
|
41
|
+
document.addEventListener('DOMContentLoaded', function() {
|
42
|
+
const mainCheckboxWrapper = document.getElementById('indeterminate-checkbox');
|
43
|
+
const mainCheckbox = document.getElementsByName("main-checkbox")[0];
|
44
|
+
const childCheckboxes = document.querySelectorAll('input[type="checkbox"][id$="indeterminate-checkbox"]');
|
45
|
+
|
46
|
+
const updateMainCheckbox = () => {
|
47
|
+
// Count the number of checked child checkboxes
|
48
|
+
const checkedCount = Array.from(childCheckboxes).filter(cb => cb.checked).length;
|
49
|
+
// Determine if the main checkbox should be in an indeterminate state
|
50
|
+
const indeterminate = checkedCount > 0 && checkedCount < childCheckboxes.length;
|
51
|
+
|
52
|
+
// Set the main checkbox states
|
53
|
+
mainCheckbox.indeterminate = indeterminate;
|
54
|
+
mainCheckbox.checked = checkedCount > 0;
|
55
|
+
|
56
|
+
// Determine the main checkbox label based on the number of checked checkboxes
|
57
|
+
const text = checkedCount === 0 ? 'Check All' : 'Uncheck All';
|
58
|
+
|
59
|
+
// Determine the icon class to add and remove based on the number of checked checkboxes
|
60
|
+
const iconClassToAdd = checkedCount === 0 ? 'pb_checkbox_checkmark' : 'pb_checkbox_indeterminate';
|
61
|
+
const iconClassToRemove = checkedCount === 0 ? 'pb_checkbox_indeterminate' : 'pb_checkbox_checkmark';
|
62
|
+
|
63
|
+
// Update main checkbox label
|
64
|
+
mainCheckboxWrapper.getElementsByClassName('pb_body_kit')[0].textContent = text;
|
65
|
+
|
66
|
+
// Add and remove the icon class to the main checkbox wrapper
|
67
|
+
mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.add(iconClassToAdd);
|
68
|
+
mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.remove(iconClassToRemove);
|
69
|
+
|
70
|
+
// Toggle the visibility of the checkbox icon based on the indeterminate state
|
71
|
+
mainCheckboxWrapper.getElementsByClassName("indeterminate_icon")[0].classList.toggle('hidden', !indeterminate);
|
72
|
+
mainCheckboxWrapper.getElementsByClassName("check_icon")[0].classList.toggle('hidden', indeterminate);
|
73
|
+
};
|
74
|
+
|
75
|
+
mainCheckbox.addEventListener('change', function() {
|
76
|
+
childCheckboxes.forEach(cb => cb.checked = this.checked);
|
77
|
+
updateMainCheckbox();
|
78
|
+
});
|
79
|
+
|
80
|
+
childCheckboxes.forEach(cb => {
|
81
|
+
cb.addEventListener('change', updateMainCheckbox);
|
82
|
+
});
|
83
|
+
});
|
84
|
+
</script>
|
@@ -1,11 +1,11 @@
|
|
1
|
-
import React, { createContext, useReducer, useContext, useEffect, useMemo } from "react";
|
1
|
+
import React, { createContext, useReducer, useContext, useEffect, useMemo, useRef, useState } from "react";
|
2
2
|
import { InitialStateType, ActionType, DraggableProviderType } from "./types";
|
3
3
|
|
4
4
|
const initialState: InitialStateType = {
|
5
5
|
items: [],
|
6
6
|
dragData: { id: "", initialGroup: "" },
|
7
7
|
isDragging: "",
|
8
|
-
activeContainer: ""
|
8
|
+
activeContainer: "",
|
9
9
|
};
|
10
10
|
|
11
11
|
const reducer = (state: InitialStateType, action: ActionType) => {
|
@@ -31,9 +31,23 @@ const reducer = (state: InitialStateType, action: ActionType) => {
|
|
31
31
|
const { dragId, targetId } = action.payload;
|
32
32
|
const newItems = [...state.items];
|
33
33
|
const draggedItem = newItems.find(item => item.id === dragId);
|
34
|
-
const
|
34
|
+
const targetItem = newItems.find(item => item.id === targetId);
|
35
|
+
|
36
|
+
if (!draggedItem || !targetItem || draggedItem.container !== targetItem.container) {
|
37
|
+
return state;
|
38
|
+
}
|
39
|
+
|
40
|
+
if (dragId === targetId) {
|
41
|
+
return state;
|
42
|
+
}
|
43
|
+
|
44
|
+
const draggedIndex = newItems.findIndex(item => item.id === dragId);
|
35
45
|
const targetIndex = newItems.findIndex(item => item.id === targetId);
|
36
46
|
|
47
|
+
if (draggedIndex === -1 || targetIndex === -1) {
|
48
|
+
return state;
|
49
|
+
}
|
50
|
+
|
37
51
|
newItems.splice(draggedIndex, 1);
|
38
52
|
newItems.splice(targetIndex, 0, draggedItem);
|
39
53
|
|
@@ -48,7 +62,11 @@ const reducer = (state: InitialStateType, action: ActionType) => {
|
|
48
62
|
const DragContext = createContext<any>({});
|
49
63
|
|
50
64
|
export const DraggableContext = () => {
|
51
|
-
|
65
|
+
const context = useContext(DragContext);
|
66
|
+
if (context === undefined) {
|
67
|
+
throw new Error('DraggableContext must be used within a DraggableProvider');
|
68
|
+
}
|
69
|
+
return context;
|
52
70
|
};
|
53
71
|
|
54
72
|
export const DraggableProvider = ({
|
@@ -63,7 +81,11 @@ export const DraggableProvider = ({
|
|
63
81
|
dropZone = { type: 'ghost', color: 'neutral', direction: 'vertical' }
|
64
82
|
}: DraggableProviderType) => {
|
65
83
|
const [state, dispatch] = useReducer(reducer, initialState);
|
66
|
-
|
84
|
+
|
85
|
+
// Store initial items in a ref to use if needed (for consistency when needed in future updates)
|
86
|
+
const initialItemsRef = useRef(initialItems);
|
87
|
+
const [isDragging, setIsDragging] = useState(false);
|
88
|
+
|
67
89
|
// Parse dropZone prop - handle both string format (backward compatibility) and object format
|
68
90
|
let dropZoneType = 'ghost';
|
69
91
|
let dropZoneColor = 'neutral';
|
@@ -86,45 +108,64 @@ export const DraggableProvider = ({
|
|
86
108
|
|
87
109
|
useEffect(() => {
|
88
110
|
dispatch({ type: 'SET_ITEMS', payload: initialItems });
|
111
|
+
initialItemsRef.current = initialItems;
|
89
112
|
}, [initialItems]);
|
90
113
|
|
91
114
|
useEffect(() => {
|
92
|
-
onReorder
|
93
|
-
|
115
|
+
if (onReorder) {
|
116
|
+
onReorder(state.items);
|
117
|
+
}
|
118
|
+
}, [state.items, onReorder]);
|
94
119
|
|
95
120
|
const handleDragStart = (id: string, container: string) => {
|
96
|
-
|
121
|
+
setIsDragging(true);
|
122
|
+
dispatch({ type: 'SET_DRAG_DATA', payload: { id, initialGroup: container } });
|
97
123
|
dispatch({ type: 'SET_IS_DRAGGING', payload: id });
|
124
|
+
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: container });
|
98
125
|
if (onDragStart) onDragStart(id, container);
|
99
126
|
};
|
100
127
|
|
101
128
|
const handleDragEnter = (id: string, container: string) => {
|
102
|
-
if (
|
103
|
-
|
104
|
-
|
129
|
+
if (!isDragging || container !== state.activeContainer) return;
|
130
|
+
|
131
|
+
if (state.dragData.id === id) return;
|
132
|
+
|
133
|
+
const draggedItem = state.items.find(item => item.id === state.dragData.id);
|
134
|
+
const targetItem = state.items.find(item => item.id === id);
|
135
|
+
|
136
|
+
if (!draggedItem || !targetItem || draggedItem.container !== targetItem.container) {
|
137
|
+
return;
|
105
138
|
}
|
139
|
+
|
140
|
+
dispatch({ type: 'REORDER_ITEMS', payload: { dragId: state.dragData.id, targetId: id } });
|
141
|
+
|
106
142
|
if (onDragEnter) onDragEnter(id, container);
|
107
143
|
};
|
108
144
|
|
109
145
|
const handleDragEnd = () => {
|
146
|
+
setIsDragging(false);
|
110
147
|
dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
|
111
148
|
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
|
112
149
|
if (onDragEnd) onDragEnd();
|
113
150
|
};
|
114
151
|
|
115
|
-
const changeCategory = (itemId: string, container: string) => {
|
116
|
-
dispatch({ type: 'CHANGE_CATEGORY', payload: { itemId, container } });
|
117
|
-
};
|
118
|
-
|
119
152
|
const handleDrop = (container: string) => {
|
153
|
+
const draggedItem = state.items.find(item => item.id === state.dragData.id);
|
154
|
+
|
155
|
+
if (draggedItem && draggedItem.container !== container) {
|
156
|
+
dispatch({ type: 'CHANGE_CATEGORY', payload: { itemId: state.dragData.id, container } });
|
157
|
+
}
|
158
|
+
|
120
159
|
dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
|
121
160
|
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
|
122
|
-
|
161
|
+
|
162
|
+
setIsDragging(false);
|
123
163
|
if (onDrop) onDrop(container);
|
124
164
|
};
|
125
165
|
|
126
166
|
const handleDragOver = (e: Event, container: string) => {
|
127
167
|
e.preventDefault();
|
168
|
+
e.stopPropagation();
|
128
169
|
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: container });
|
129
170
|
if (onDragOver) onDragOver(e, container);
|
130
171
|
};
|
@@ -144,7 +185,7 @@ export const DraggableProvider = ({
|
|
144
185
|
handleDragEnd,
|
145
186
|
handleDrop,
|
146
187
|
handleDragOver
|
147
|
-
}), [state, dropZoneType, dropZoneColor, dropZoneDirection]);
|
188
|
+
}), [state, dropZoneType, dropZoneColor, dropZoneDirection, handleDragStart, handleDragEnter, handleDragEnd, handleDrop, handleDragOver]);
|
148
189
|
|
149
190
|
return (
|
150
191
|
<DragContext.Provider value={contextValue}>{children}</DragContext.Provider>
|
@@ -25,7 +25,6 @@ type DropdownProps = {
|
|
25
25
|
blankSelection?: string;
|
26
26
|
children?: React.ReactChild[] | React.ReactChild | React.ReactElement[];
|
27
27
|
className?: string;
|
28
|
-
closeOnSelection?: boolean;
|
29
28
|
formPillProps?: GenericObject;
|
30
29
|
dark?: boolean;
|
31
30
|
data?: { [key: string]: string };
|
@@ -56,7 +55,6 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
56
55
|
blankSelection = '',
|
57
56
|
children,
|
58
57
|
className,
|
59
|
-
closeOnSelection = true,
|
60
58
|
dark = false,
|
61
59
|
data = {},
|
62
60
|
defaultValue = {},
|
@@ -154,7 +152,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
154
152
|
if (!multiSelect) return optionsWithBlankSelection;
|
155
153
|
return optionsWithBlankSelection.filter((option: GenericObject) => !selectedArray.some((sel) => sel.label === option.label));
|
156
154
|
}, [optionsWithBlankSelection, selectedArray, multiSelect]);
|
157
|
-
|
155
|
+
|
158
156
|
const filteredOptions = useMemo(() => {
|
159
157
|
return availableOptions.filter((opt: GenericObject) =>
|
160
158
|
String(opt.label).toLowerCase().includes(filterItem.toLowerCase())
|
@@ -194,18 +192,12 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
194
192
|
return next;
|
195
193
|
});
|
196
194
|
setFilterItem("");
|
197
|
-
|
198
|
-
if (closeOnSelection) {
|
199
|
-
setIsDropDownClosed(true);
|
200
|
-
}
|
195
|
+
setIsDropDownClosed(true);
|
201
196
|
} else {
|
202
197
|
setSelected(clickedItem);
|
203
198
|
setFilterItem("");
|
199
|
+
setIsDropDownClosed(true);
|
204
200
|
onSelect && onSelect(clickedItem);
|
205
|
-
// Only close dropdown if closeOnSelection is true
|
206
|
-
if (closeOnSelection) {
|
207
|
-
setIsDropDownClosed(true);
|
208
|
-
}
|
209
201
|
}
|
210
202
|
};
|
211
203
|
|
@@ -260,7 +252,6 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
260
252
|
<DropdownContext.Provider
|
261
253
|
value={{
|
262
254
|
autocomplete,
|
263
|
-
closeOnSelection,
|
264
255
|
dropdownContainerRef,
|
265
256
|
filteredOptions,
|
266
257
|
filterItem,
|
@@ -19,5 +19,4 @@ export { default as DropdownMultiSelect } from './_dropdown_multi_select.jsx'
|
|
19
19
|
export { default as DropdownMultiSelectDisplay } from './_dropdown_multi_select_display.jsx'
|
20
20
|
export { default as DropdownMultiSelectWithAutocomplete } from './_dropdown_multi_select_with_autocomplete.jsx'
|
21
21
|
export { default as DropdownMultiSelectWithDefault } from './_dropdown_multi_select_with_default.jsx'
|
22
|
-
export { default as DropdownMultiSelectWithCustomOptions } from './_dropdown_multi_select_with_custom_options.jsx'
|
23
|
-
export { default as DropdownCloseOnSelect } from './_dropdown_close_on_select.jsx'
|
22
|
+
export { default as DropdownMultiSelectWithCustomOptions } from './_dropdown_multi_select_with_custom_options.jsx'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import React from "react"
|
2
|
-
import { render, screen
|
2
|
+
import { render, screen } from "../utilities/test-utils"
|
3
3
|
|
4
4
|
import { Dropdown, Icon, IconCircle } from 'playbook-ui'
|
5
5
|
|
@@ -263,110 +263,4 @@ test("searchbar prop to render TextInput in container", () => {
|
|
263
263
|
const kit = screen.getByTestId(testId)
|
264
264
|
const searchbar = kit.querySelector('.pb_text_input_kit')
|
265
265
|
expect(searchbar).toBeInTheDocument()
|
266
|
-
})
|
267
|
-
|
268
|
-
test("MultiSelect prop to allow multiple selections + add correct Form Pills", () => {
|
269
|
-
render(
|
270
|
-
<Dropdown
|
271
|
-
data={{ testid: testId }}
|
272
|
-
multiSelect
|
273
|
-
options={options}
|
274
|
-
/>
|
275
|
-
);
|
276
|
-
|
277
|
-
const kit = screen.getByTestId(testId);
|
278
|
-
const option = Array.from(kit.querySelectorAll(".pb_dropdown_option_list"));
|
279
|
-
fireEvent.click(option[0]); // Select first option
|
280
|
-
fireEvent.click(option[1]); // Select second option
|
281
|
-
const formPills = kit.querySelectorAll(".pb_form_pill_kit_primary");
|
282
|
-
expect(formPills.length).toBe(2);
|
283
|
-
expect(formPills[0]).toHaveTextContent("United States");
|
284
|
-
expect(formPills[1]).toHaveTextContent("Canada");
|
285
|
-
});
|
286
|
-
|
287
|
-
test("hides each selected option from the dropdown", () => {
|
288
|
-
|
289
|
-
render(
|
290
|
-
<Dropdown
|
291
|
-
data={{ testid: testId }}
|
292
|
-
multiSelect
|
293
|
-
options={options}
|
294
|
-
/>
|
295
|
-
);
|
296
|
-
|
297
|
-
const kit = screen.getByTestId(testId);
|
298
|
-
const option = Array.from(kit.querySelectorAll(".pb_dropdown_option_list"));
|
299
|
-
const firstOpt = options[0].label
|
300
|
-
fireEvent.click(option[0]);
|
301
|
-
const option2 = Array.from(kit.querySelectorAll(".pb_dropdown_option_list"));
|
302
|
-
expect(option2[0]).not.toHaveTextContent(firstOpt)
|
303
|
-
})
|
304
|
-
|
305
|
-
test("renders form pills inside trigger", () => {
|
306
|
-
render(
|
307
|
-
<Dropdown
|
308
|
-
data={{ testid: testId }}
|
309
|
-
multiSelect
|
310
|
-
options={options}
|
311
|
-
/>
|
312
|
-
);
|
313
|
-
|
314
|
-
const kit = screen.getByTestId(testId)
|
315
|
-
const option = kit.querySelector('.pb_dropdown_option_list')
|
316
|
-
fireEvent.click(option)
|
317
|
-
const formPill = kit.querySelector(".pb_form_pill_kit_primary")
|
318
|
-
expect(formPill).toBeInTheDocument()
|
319
|
-
})
|
320
|
-
|
321
|
-
test("multiSelect and autocomplete to work together", () => {
|
322
|
-
render (
|
323
|
-
<Dropdown
|
324
|
-
autocomplete
|
325
|
-
data={{ testid: testId }}
|
326
|
-
multiSelect
|
327
|
-
options={options}
|
328
|
-
/>
|
329
|
-
)
|
330
|
-
|
331
|
-
const kit = screen.getByTestId(testId)
|
332
|
-
const input = kit.querySelector('.dropdown_input')
|
333
|
-
expect(input).toBeInTheDocument()
|
334
|
-
const option = kit.querySelector('.pb_dropdown_option_list')
|
335
|
-
fireEvent.click(option)
|
336
|
-
const formPill = kit.querySelector(".pb_form_pill_kit_primary")
|
337
|
-
expect(formPill).toBeInTheDocument()
|
338
|
-
})
|
339
|
-
|
340
|
-
test("renders form pills with size and color", () => {
|
341
|
-
render(
|
342
|
-
<Dropdown
|
343
|
-
data={{ testid: testId }}
|
344
|
-
formPillProps={{ size: "small", color: "neutral" }}
|
345
|
-
multiSelect
|
346
|
-
options={options}
|
347
|
-
/>
|
348
|
-
);
|
349
|
-
|
350
|
-
const kit = screen.getByTestId(testId)
|
351
|
-
const option = kit.querySelector('.pb_dropdown_option_list')
|
352
|
-
fireEvent.click(option)
|
353
|
-
const formPill = kit.querySelector(".pb_form_pill_kit_neutral")
|
354
|
-
expect(formPill).toBeInTheDocument()
|
355
|
-
expect(formPill).toHaveClass("small")
|
356
|
-
})
|
357
|
-
|
358
|
-
test("defaultValue works with multiSelect", () => {
|
359
|
-
render(
|
360
|
-
<Dropdown
|
361
|
-
data={{ testid: testId }}
|
362
|
-
defaultValue={[options[0], options[2]]}
|
363
|
-
multiSelect
|
364
|
-
options={options}
|
365
|
-
/>
|
366
|
-
)
|
367
|
-
const kit = screen.getByTestId(testId)
|
368
|
-
expect(kit.querySelectorAll(".pb_form_pill_kit_primary")).toHaveLength(2)
|
369
|
-
const option2 = Array.from(kit.querySelectorAll(".pb_dropdown_option_list"));
|
370
|
-
const firstOpt = options[0].label
|
371
|
-
expect(option2[0]).not.toHaveTextContent(firstOpt)
|
372
|
-
})
|
266
|
+
})
|
@@ -115,7 +115,6 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
115
115
|
|
116
116
|
handleSearch(term = "") {
|
117
117
|
const lcTerm = term.toLowerCase();
|
118
|
-
let hasMatch = false
|
119
118
|
this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
|
120
119
|
//make it so that if the option is selected, it will not show up in the search results
|
121
120
|
if (this.isMultiSelect && this.selectedOptions.has(opt.dataset.dropdownOptionLabel)) {
|
@@ -129,32 +128,9 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
129
128
|
// hide or show option
|
130
129
|
const match = label.includes(lcTerm);
|
131
130
|
opt.style.display = match ? "" : "none";
|
132
|
-
if (match) hasMatch = true
|
133
131
|
});
|
134
132
|
|
135
133
|
this.adjustDropdownHeight();
|
136
|
-
|
137
|
-
this.removeNoOptionsMessage()
|
138
|
-
if (!hasMatch) {
|
139
|
-
this.showNoOptionsMessage()
|
140
|
-
}
|
141
|
-
}
|
142
|
-
|
143
|
-
showNoOptionsMessage() {
|
144
|
-
if (this.element.querySelector(".dropdown_no_options")) return;
|
145
|
-
|
146
|
-
const noOptionElement = document.createElement("div");
|
147
|
-
noOptionElement.className = "pb_body_kit_light dropdown_no_options pb_item_kit p_xs display_flex justify_content_center";
|
148
|
-
noOptionElement.textContent = "no option";
|
149
|
-
|
150
|
-
this.target.appendChild(noOptionElement);
|
151
|
-
}
|
152
|
-
|
153
|
-
removeNoOptionsMessage() {
|
154
|
-
const existing = this.element.querySelector(".dropdown_no_options");
|
155
|
-
if (existing) {
|
156
|
-
existing.remove();
|
157
|
-
}
|
158
134
|
}
|
159
135
|
|
160
136
|
handleOptionClick(event) {
|
@@ -25,7 +25,7 @@ type DropdownOptionProps = {
|
|
25
25
|
key?: string | number;
|
26
26
|
option?: GenericObject;
|
27
27
|
padding?: string;
|
28
|
-
}
|
28
|
+
} & GlobalProps;
|
29
29
|
|
30
30
|
const DropdownOption = (props: DropdownOptionProps) => {
|
31
31
|
const {
|
@@ -56,17 +56,16 @@ const DropdownOption = (props: DropdownOptionProps) => {
|
|
56
56
|
|
57
57
|
// When multiSelect, then if an option is selected, remove from dropdown
|
58
58
|
const isSelected = Array.isArray(selected)
|
59
|
-
|
60
|
-
|
59
|
+
? selected.some((item) => item.label === option?.label)
|
60
|
+
: (selected as GenericObject)?.label === option?.label;
|
61
61
|
|
62
|
+
|
62
63
|
if (!isItemMatchingFilter(option) || (multiSelect && isSelected)) {
|
63
64
|
return null;
|
64
65
|
}
|
65
|
-
|
66
66
|
const isFocused =
|
67
67
|
focusedOptionIndex >= 0 &&
|
68
68
|
filteredOptions[focusedOptionIndex].label === option?.label;
|
69
|
-
|
70
69
|
const focusedClass = isFocused && "focused";
|
71
70
|
|
72
71
|
const selectedClass = isSelected ? "selected" : "list";
|
@@ -92,10 +91,7 @@ const DropdownOption = (props: DropdownOptionProps) => {
|
|
92
91
|
className={classes}
|
93
92
|
id={id}
|
94
93
|
key={key}
|
95
|
-
onClick={(
|
96
|
-
e.stopPropagation();
|
97
|
-
handleOptionClick(option);
|
98
|
-
}}
|
94
|
+
onClick= {() => handleOptionClick(option)}
|
99
95
|
>
|
100
96
|
<ListItem
|
101
97
|
cursor="pointer"
|
@@ -104,12 +100,12 @@ const DropdownOption = (props: DropdownOptionProps) => {
|
|
104
100
|
key={option?.label}
|
105
101
|
padding="none"
|
106
102
|
>
|
107
|
-
|
103
|
+
{children ?
|
108
104
|
<div className="dropdown_option_wrapper">{children}</div> :
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
105
|
+
<Body dark={dark}
|
106
|
+
text={option?.label}
|
107
|
+
/>
|
108
|
+
}
|
113
109
|
</ListItem>
|
114
110
|
</div>
|
115
111
|
);
|