playbook_ui 14.20.0.pre.alpha.PLAY22408048 → 14.20.0.pre.alpha.play2168firstcolumnborderbug7950
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 -5
- 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/_advanced_table_table_props.html.erb +1 -1
- 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 +6 -10
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +48 -2
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +4 -13
- 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_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_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-C0OGBz_9.js → _typeahead-BmOWdDtp.js} +2 -2
- data/dist/chunks/_weekday_stacked-CvcuQyr9.js +45 -0
- data/dist/chunks/{lib-CLDGEByA.js → lib-D5R1BjUn.js} +1 -1
- data/dist/chunks/{pb_form_validation-C-ccDsK6.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 +1 -1
- 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 -15
- 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_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/dist/chunks/_weekday_stacked-BbY_mMKV.js +0 -45
@@ -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>
|
@@ -80,23 +80,14 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
80
80
|
// ===========================================================
|
81
81
|
// | Hook Definitions |
|
82
82
|
// ===========================================================
|
83
|
-
const pickerInput = document.querySelector<HTMLElement & { [x: string]: any }>(`#${pickerId}`)
|
84
|
-
const inTurboFrames = pickerInput?.closest('turbo-frame') !== null
|
85
83
|
|
86
84
|
const defaultDateGetter = () => {
|
87
|
-
if (
|
88
|
-
|
89
|
-
if (initialValue === "") return null;
|
90
|
-
if (initialValue) return initialValue;
|
91
|
-
return defaultDate || null
|
85
|
+
if (defaultDate === '') {
|
86
|
+
return null
|
92
87
|
} else {
|
93
|
-
|
94
|
-
return null;
|
95
|
-
} else {
|
96
|
-
return defaultDate;
|
97
|
-
}
|
88
|
+
return defaultDate
|
98
89
|
}
|
99
|
-
}
|
90
|
+
}
|
100
91
|
|
101
92
|
const disabledWeekDays = () => {
|
102
93
|
return (
|
@@ -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
|
);
|
@@ -44,7 +44,6 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
44
44
|
|
45
45
|
const {
|
46
46
|
autocomplete,
|
47
|
-
closeOnSelection,
|
48
47
|
filterItem,
|
49
48
|
handleBackspace,
|
50
49
|
handleChange,
|
@@ -55,7 +54,6 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
55
54
|
isInputFocused,
|
56
55
|
multiSelect,
|
57
56
|
selected,
|
58
|
-
setIsDropDownClosed,
|
59
57
|
setIsInputFocused,
|
60
58
|
toggleDropdown,
|
61
59
|
} = useContext(DropdownContext);
|
@@ -105,26 +103,11 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
105
103
|
? placeholder
|
106
104
|
: "Select...";
|
107
105
|
|
108
|
-
// Click handler that respects closeOnSelection
|
109
|
-
const handleInputClick = (e: React.MouseEvent) => {
|
110
|
-
e.stopPropagation(); // keep the wrapper's handler from firing
|
111
|
-
if (isDropDownClosed) {
|
112
|
-
// Always open if closed
|
113
|
-
setIsDropDownClosed(false);
|
114
|
-
} else if (!closeOnSelection) {
|
115
|
-
// Keep open if closeOnSelection is false
|
116
|
-
return;
|
117
|
-
} else {
|
118
|
-
// Default behavior - toggle
|
119
|
-
toggleDropdown();
|
120
|
-
}
|
121
|
-
};
|
122
|
-
|
123
106
|
return (
|
124
|
-
<div {...ariaProps}
|
125
|
-
{...dataProps}
|
107
|
+
<div {...ariaProps}
|
108
|
+
{...dataProps}
|
126
109
|
{...htmlProps}
|
127
|
-
className={classes}
|
110
|
+
className={classes}
|
128
111
|
id={id}
|
129
112
|
>
|
130
113
|
{
|
@@ -162,7 +145,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
162
145
|
{customDisplay ? (
|
163
146
|
<Flex align="center">
|
164
147
|
{customDisplay}
|
165
|
-
<Body dark={dark}
|
148
|
+
<Body dark={dark}
|
166
149
|
paddingLeft={`${joinedLabels ? "xs" : "none"}`}
|
167
150
|
>
|
168
151
|
{customDisplayPlaceholder}
|
@@ -181,7 +164,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
181
164
|
<input
|
182
165
|
className="dropdown_input"
|
183
166
|
onChange={handleChange}
|
184
|
-
onClick={
|
167
|
+
onClick={(e) => {
|
168
|
+
e.stopPropagation();// keep the wrapper’s handler from firing
|
169
|
+
toggleDropdown();
|
170
|
+
}}
|
185
171
|
onFocus={() => setIsInputFocused(true)}
|
186
172
|
onKeyDown={(e) => {
|
187
173
|
handleKeyDown(e);
|
@@ -200,8 +186,8 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
200
186
|
)}
|
201
187
|
</>
|
202
188
|
) : (
|
203
|
-
<Body dark={dark}
|
204
|
-
text={defaultDisplayPlaceholder}
|
189
|
+
<Body dark={dark}
|
190
|
+
text={defaultDisplayPlaceholder}
|
205
191
|
/>
|
206
192
|
)
|
207
193
|
)}
|
@@ -209,7 +195,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
209
195
|
<input
|
210
196
|
className="dropdown_input"
|
211
197
|
onChange={handleChange}
|
212
|
-
onClick={
|
198
|
+
onClick={(e) => {
|
199
|
+
e.stopPropagation();// keep the wrapper’s handler from firing
|
200
|
+
toggleDropdown();
|
201
|
+
}}
|
213
202
|
onFocus={() => setIsInputFocused(true)}
|
214
203
|
onKeyDown={handleKeyDown}
|
215
204
|
placeholder={
|
@@ -234,7 +223,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
234
223
|
onClick: (e: Event) => {e.stopPropagation();handleWrapperClick()}
|
235
224
|
}}
|
236
225
|
key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
|
237
|
-
>
|
226
|
+
>
|
238
227
|
{
|
239
228
|
selectedArray.length > 0 && (
|
240
229
|
<div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
|
@@ -33,7 +33,6 @@ type PhoneNumberInputProps = {
|
|
33
33
|
onChange?: (e: React.FormEvent<HTMLInputElement>) => void,
|
34
34
|
onValidate?: Callback<boolean, void>,
|
35
35
|
onlyCountries: string[],
|
36
|
-
excludeCountries: string[],
|
37
36
|
preferredCountries?: string[],
|
38
37
|
required?: boolean,
|
39
38
|
value?: string,
|
@@ -89,7 +88,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
89
88
|
},
|
90
89
|
onValidate = () => null,
|
91
90
|
onlyCountries = [],
|
92
|
-
excludeCountries = [],
|
93
91
|
required = false,
|
94
92
|
preferredCountries = [],
|
95
93
|
value = "",
|
@@ -236,7 +234,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
236
234
|
const fallbackCountry =
|
237
235
|
preferredCountries.length > 0 ? preferredCountries[0] :
|
238
236
|
onlyCountries.length > 0 ? onlyCountries.sort()[0] :
|
239
|
-
excludeCountries.length > 0 ? excludeCountries.sort()[0] :
|
240
237
|
"af";
|
241
238
|
|
242
239
|
useEffect(() => {
|
@@ -247,7 +244,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
247
244
|
autoInsertDialCode: false,
|
248
245
|
initialCountry: initialCountry || fallbackCountry,
|
249
246
|
onlyCountries,
|
250
|
-
excludeCountries,
|
251
247
|
countrySearch: countrySearch,
|
252
248
|
fixDropdownWidth: false,
|
253
249
|
formatAsYouType: formatAsYouType,
|