playbook_ui 14.21.0.pre.alpha.renovatenpmtrixvulnerability8103 → 14.21.0.pre.rc.0
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/CustomCell.tsx +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +49 -116
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +1 -1
- 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 -40
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +3 -13
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +1 -7
- 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_no_subrows.jsx +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -6
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -3
- 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_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-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 +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 +2 -2
- metadata +6 -19
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.html.erb +0 -33
- 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_advanced_table/docs/_advanced_table_scrollbar_none.html.erb +0 -33
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_scrollbar_none.jsx +0 -53
- 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_date_picker/docs/_date_picker_quick_pick_date_display.html.erb +0 -13
- 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-B_jpa2Rz.js +0 -45
@@ -3,12 +3,6 @@ import { render, screen, waitFor } from "../utilities/test-utils"
|
|
3
3
|
|
4
4
|
import { AdvancedTable, Pill } from "playbook-ui"
|
5
5
|
|
6
|
-
global.ResizeObserver = class {
|
7
|
-
observe() {}
|
8
|
-
unobserve() {}
|
9
|
-
disconnect() {}
|
10
|
-
};
|
11
|
-
|
12
6
|
const MOCK_DATA = [
|
13
7
|
{
|
14
8
|
year: "2021",
|
@@ -78,36 +72,6 @@ const MOCK_DATA_LOADING = [
|
|
78
72
|
},
|
79
73
|
]
|
80
74
|
|
81
|
-
const MOCK_DATA_WITH_ID = [
|
82
|
-
{
|
83
|
-
id: "1",
|
84
|
-
year: "2021",
|
85
|
-
quarter: null,
|
86
|
-
month: null,
|
87
|
-
day: null,
|
88
|
-
newEnrollments: "20",
|
89
|
-
scheduledMeetings: "10",
|
90
|
-
},
|
91
|
-
{
|
92
|
-
id: "2",
|
93
|
-
year: "2022",
|
94
|
-
quarter: null,
|
95
|
-
month: null,
|
96
|
-
day: null,
|
97
|
-
newEnrollments: "25",
|
98
|
-
scheduledMeetings: "15",
|
99
|
-
},
|
100
|
-
{
|
101
|
-
id: "3",
|
102
|
-
year: "2023",
|
103
|
-
quarter: null,
|
104
|
-
month: null,
|
105
|
-
day: null,
|
106
|
-
newEnrollments: "30",
|
107
|
-
scheduledMeetings: "20",
|
108
|
-
},
|
109
|
-
]
|
110
|
-
|
111
75
|
const columnDefinitions = [
|
112
76
|
{
|
113
77
|
accessor: "year",
|
@@ -548,28 +512,3 @@ test("allowFullScreen prop adds fullscreen class", () => {
|
|
548
512
|
const tableContainer = screen.getByRole("table").closest("div")
|
549
513
|
expect(tableContainer).toHaveClass("advanced-table-allow-fullscreen")
|
550
514
|
})
|
551
|
-
|
552
|
-
test("pinnedRows prop renders pinned rows at top", () => {
|
553
|
-
const pinnedRowsControl = {
|
554
|
-
value: { top: ["1", "3"] },
|
555
|
-
onChange: jest.fn()
|
556
|
-
}
|
557
|
-
|
558
|
-
render(
|
559
|
-
<AdvancedTable
|
560
|
-
columnDefinitions={columnDefinitions}
|
561
|
-
data={{ testid: testId }}
|
562
|
-
pinnedRows={pinnedRowsControl}
|
563
|
-
tableData={MOCK_DATA_WITH_ID}
|
564
|
-
/>
|
565
|
-
)
|
566
|
-
|
567
|
-
const kit = screen.getByTestId(testId)
|
568
|
-
const pinnedRows = kit.querySelectorAll(".pinned-row")
|
569
|
-
|
570
|
-
expect(pinnedRows).toHaveLength(2)
|
571
|
-
|
572
|
-
const firstPinnedRow = pinnedRows[0]
|
573
|
-
expect(firstPinnedRow).toHaveStyle("position: sticky")
|
574
|
-
expect(firstPinnedRow).toHaveStyle("background-color: white")
|
575
|
-
})
|
@@ -13,12 +13,10 @@ examples:
|
|
13
13
|
- advanced_table_column_headers: Multi-Header Columns
|
14
14
|
- advanced_table_column_headers_multiple: Multi-Header Columns (Multiple Levels)
|
15
15
|
- advanced_table_column_border_color_rails: Column Group Border Color
|
16
|
-
- advanced_table_no_subrows: Table with No Subrows or Expansion
|
17
16
|
- advanced_table_selectable_rows_rails: Selectable Rows
|
18
17
|
- advanced_table_selectable_rows_no_subrows_rails: Selectable Rows (No Subrows)
|
19
18
|
- advanced_table_selectable_rows_actions_rails: Selectable Rows (With Actions)
|
20
19
|
- advanced_table_selectable_rows_header_rails: Selectable Rows (No Actions Bar)
|
21
|
-
- advanced_table_scrollbar_none: Advanced Table Scrollbar None
|
22
20
|
|
23
21
|
react:
|
24
22
|
- advanced_table_default: Default (Required Props)
|
@@ -44,7 +42,7 @@ examples:
|
|
44
42
|
- advanced_table_column_headers_multiple: Multi-Header Columns (Multiple Levels)
|
45
43
|
- advanced_table_column_headers_custom_cell: Multi-Header Columns with Custom Cells
|
46
44
|
- advanced_table_column_border_color: Column Group Border Color
|
47
|
-
- advanced_table_no_subrows: Table with No Subrows
|
45
|
+
# - advanced_table_no_subrows: Table with No Subrows
|
48
46
|
- advanced_table_selectable_rows: Selectable Rows
|
49
47
|
- advanced_table_selectable_rows_no_subrows_react: Selectable Rows (No Subrows)
|
50
48
|
- advanced_table_selectable_rows_actions: Selectable Rows (With Actions)
|
@@ -54,6 +52,4 @@ examples:
|
|
54
52
|
- advanced_table_column_visibility: Column Visibility Control
|
55
53
|
- advanced_table_column_visibility_with_state: Column Visibility Control With State
|
56
54
|
- advanced_table_column_visibility_custom: Column Visibility Control with Custom Dropdown
|
57
|
-
- advanced_table_column_visibility_multi: Column Visibility Control with Multi-Header Columns
|
58
|
-
- advanced_table_pinned_rows: Pinned Rows
|
59
|
-
- advanced_table_scrollbar_none: Advanced Table Scrollbar None
|
55
|
+
- advanced_table_column_visibility_multi: Column Visibility Control with Multi-Header Columns
|
@@ -31,6 +31,4 @@ export { default as AdvancedTableColumnBorderColor} from './_advanced_table_colu
|
|
31
31
|
export { default as AdvancedTableColumnVisibility } from './_advanced_table_column_visibility.jsx'
|
32
32
|
export { default as AdvancedTableColumnVisibilityCustom } from './_advanced_table_column_visibility_custom.jsx'
|
33
33
|
export { default as AdvancedTableColumnVisibilityMulti } from './_advanced_table_column_visibility_multi.jsx'
|
34
|
-
export { default as AdvancedTableColumnVisibilityWithState } from './_advanced_table_column_visibility_with_state.jsx'
|
35
|
-
export { default as AdvancedTablePinnedRows } from './_advanced_table_pinned_rows.jsx'
|
36
|
-
export { default as AdvancedTableScrollbarNone} from './_advanced_table_scrollbar_none.jsx'
|
34
|
+
export { default as AdvancedTableColumnVisibilityWithState } from './_advanced_table_column_visibility_with_state.jsx'
|
@@ -569,8 +569,6 @@ class PbAdvancedTableActionBar {
|
|
569
569
|
actionBar.style.height = 'auto';
|
570
570
|
actionBar.style.overflow = 'visible';
|
571
571
|
actionBar.style.opacity = '1';
|
572
|
-
actionBar.style.transitionProperty = 'all';
|
573
|
-
actionBar.style.transitionTimingFunction = 'ease-in-out';
|
574
572
|
actionBar.classList.remove("p_none");
|
575
573
|
actionBar.classList.add("p_xs", "is-visible", "show-action-card");
|
576
574
|
|
@@ -1,9 +1,16 @@
|
|
1
1
|
<%= pb_content_tag(:label) do %>
|
2
2
|
<%= content.presence || object.input %>
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
<% if object.indeterminate %>
|
4
|
+
<span data-pb-checkbox-icon-span="true" class="pb_checkbox_indeterminate">
|
5
|
+
<%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon", fixed_width: true}) %>
|
6
|
+
<%= pb_rails("icon", props: { icon: "check", classname: "check_icon hidden", fixed_width: true}) %>
|
7
|
+
</span>
|
8
|
+
<% else %>
|
9
|
+
<span data-pb-checkbox-icon-span="true" class="pb_checkbox_checkmark">
|
10
|
+
<%= pb_rails("icon", props: { icon: "check", classname: "check_icon", fixed_width: true}) %>
|
11
|
+
<%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon hidden", fixed_width: true}) %>
|
12
|
+
</span>
|
13
|
+
<% end %>
|
7
14
|
<span class="pb_checkbox_label">
|
8
15
|
<%= pb_rails("body", props: { status: object.checkbox_label_status, text: object.text, dark: object.dark, margin_right: object.form_spacing ? "xs" : "" }) %>
|
9
16
|
</span>
|
@@ -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
|
@@ -20,7 +19,7 @@ module Playbook
|
|
20
19
|
default: false
|
21
20
|
|
22
21
|
def classname
|
23
|
-
generate_classname("pb_checkbox_kit", checked_class) + error_class
|
22
|
+
generate_classname("pb_checkbox_kit", checked_class) + indeterminate_class + error_class
|
24
23
|
end
|
25
24
|
|
26
25
|
def input
|
@@ -31,13 +30,6 @@ module Playbook
|
|
31
30
|
error ? "negative" : nil
|
32
31
|
end
|
33
32
|
|
34
|
-
def data
|
35
|
-
Hash(prop(:data)).merge(
|
36
|
-
pb_checkbox_indeterminate_main: indeterminate_main,
|
37
|
-
pb_checkbox_indeterminate_parent: indeterminate_parent
|
38
|
-
)
|
39
|
-
end
|
40
|
-
|
41
33
|
private
|
42
34
|
|
43
35
|
def error_class
|
@@ -47,6 +39,10 @@ module Playbook
|
|
47
39
|
def checked_class
|
48
40
|
checked ? "on" : "off"
|
49
41
|
end
|
42
|
+
|
43
|
+
def indeterminate_class
|
44
|
+
indeterminate ? " indeterminate" : ""
|
45
|
+
end
|
50
46
|
end
|
51
47
|
end
|
52
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) {
|