playbook_ui 14.20.0 → 14.21.0.pre.alpha.PLAY2167advtablenitrorowborderdoubling8097
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 +116 -49
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +58 -2
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +16 -4
- data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +7 -3
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +40 -2
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +13 -3
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +16 -8
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +16 -1
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +61 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta.md +6 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_default.md +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.html.erb +33 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.jsx +0 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows.jsx +57 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_react.md +5 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_scrollbar_none.html.erb +33 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_scrollbar_none.jsx +53 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_actions_rails.html.erb +137 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_actions_rails.md +3 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_header_rails.html.erb +40 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_header_rails.md +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +8 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +3 -1
- data/app/pb_kits/playbook/pb_advanced_table/index.js +157 -12
- data/app/pb_kits/playbook/pb_advanced_table/table_action_bar.html.erb +23 -0
- data/app/pb_kits/playbook/pb_advanced_table/table_action_bar.rb +19 -0
- data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +4 -0
- data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +4 -11
- data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +10 -6
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +2 -48
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate_rails.md +1 -0
- data/app/pb_kits/playbook/pb_checkbox/index.js +56 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_date_display.html.erb +13 -0
- data/app/pb_kits/playbook/pb_draggable/context/index.tsx +17 -58
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +12 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.jsx +42 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +108 -2
- data/app/pb_kits/playbook/pb_dropdown/index.js +24 -0
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +14 -10
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +26 -15
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +4 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.html.erb +4 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.jsx +15 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.md +1 -0
- 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 +4 -3
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
- data/app/pb_kits/playbook/pb_popover/index.ts +9 -4
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.html.erb +12 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.jsx +31 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.md +1 -0
- data/app/pb_kits/playbook/pb_select/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_selectable_rows.html.erb +3 -51
- data/app/pb_kits/playbook/pb_table/styles/_mobile_collapse.scss +1 -1
- data/dist/chunks/_typeahead-CoOpeYom.js +22 -0
- data/dist/chunks/_weekday_stacked-B_jpa2Rz.js +45 -0
- data/dist/chunks/lazysizes-B7xYodB-.js +1 -0
- data/dist/chunks/lib-D7Va7yqa.js +29 -0
- data/dist/chunks/{pb_form_validation-D1Bwm-op.js → pb_form_validation-DSkdRDMf.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 +2 -2
- metadata +29 -7
- data/dist/chunks/_typeahead-MUu4QW0I.js +0 -22
- data/dist/chunks/_weekday_stacked-CZJor-EY.js +0 -45
- data/dist/chunks/lazysizes-DHz07jlL.js +0 -1
- data/dist/chunks/lib-DFF1N868.js +0 -29
@@ -25,6 +25,7 @@ type DropdownProps = {
|
|
25
25
|
blankSelection?: string;
|
26
26
|
children?: React.ReactChild[] | React.ReactChild | React.ReactElement[];
|
27
27
|
className?: string;
|
28
|
+
closeOnSelection?: boolean;
|
28
29
|
formPillProps?: GenericObject;
|
29
30
|
dark?: boolean;
|
30
31
|
data?: { [key: string]: string };
|
@@ -55,6 +56,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
55
56
|
blankSelection = '',
|
56
57
|
children,
|
57
58
|
className,
|
59
|
+
closeOnSelection = true,
|
58
60
|
dark = false,
|
59
61
|
data = {},
|
60
62
|
defaultValue = {},
|
@@ -152,7 +154,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
152
154
|
if (!multiSelect) return optionsWithBlankSelection;
|
153
155
|
return optionsWithBlankSelection.filter((option: GenericObject) => !selectedArray.some((sel) => sel.label === option.label));
|
154
156
|
}, [optionsWithBlankSelection, selectedArray, multiSelect]);
|
155
|
-
|
157
|
+
|
156
158
|
const filteredOptions = useMemo(() => {
|
157
159
|
return availableOptions.filter((opt: GenericObject) =>
|
158
160
|
String(opt.label).toLowerCase().includes(filterItem.toLowerCase())
|
@@ -192,12 +194,18 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
192
194
|
return next;
|
193
195
|
});
|
194
196
|
setFilterItem("");
|
195
|
-
|
197
|
+
// Only close dropdown if closeOnSelection is true
|
198
|
+
if (closeOnSelection) {
|
199
|
+
setIsDropDownClosed(true);
|
200
|
+
}
|
196
201
|
} else {
|
197
202
|
setSelected(clickedItem);
|
198
203
|
setFilterItem("");
|
199
|
-
setIsDropDownClosed(true);
|
200
204
|
onSelect && onSelect(clickedItem);
|
205
|
+
// Only close dropdown if closeOnSelection is true
|
206
|
+
if (closeOnSelection) {
|
207
|
+
setIsDropDownClosed(true);
|
208
|
+
}
|
201
209
|
}
|
202
210
|
};
|
203
211
|
|
@@ -252,6 +260,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
252
260
|
<DropdownContext.Provider
|
253
261
|
value={{
|
254
262
|
autocomplete,
|
263
|
+
closeOnSelection,
|
255
264
|
dropdownContainerRef,
|
256
265
|
filteredOptions,
|
257
266
|
filterItem,
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import Dropdown from '../../pb_dropdown/_dropdown'
|
3
|
+
|
4
|
+
const DropdownCloseOnSelect = (props) => {
|
5
|
+
|
6
|
+
const options = [
|
7
|
+
{
|
8
|
+
label: "United States",
|
9
|
+
value: "United States",
|
10
|
+
},
|
11
|
+
{
|
12
|
+
label: "Canada",
|
13
|
+
value: "Canada",
|
14
|
+
},
|
15
|
+
{
|
16
|
+
label: "Pakistan",
|
17
|
+
value: "Pakistan",
|
18
|
+
}
|
19
|
+
];
|
20
|
+
|
21
|
+
|
22
|
+
return (
|
23
|
+
<div>
|
24
|
+
<Dropdown
|
25
|
+
closeOnSelection={false}
|
26
|
+
label="Default"
|
27
|
+
options={options}
|
28
|
+
{...props}
|
29
|
+
/>
|
30
|
+
<br />
|
31
|
+
<Dropdown
|
32
|
+
closeOnSelection={false}
|
33
|
+
label="Multi Select"
|
34
|
+
multiSelect
|
35
|
+
options={options}
|
36
|
+
{...props}
|
37
|
+
/>
|
38
|
+
</div>
|
39
|
+
)
|
40
|
+
}
|
41
|
+
|
42
|
+
export default DropdownCloseOnSelect
|
@@ -0,0 +1 @@
|
|
1
|
+
By default, the dropdown menu will close when a selection is made. You can prevent this behavior by using the `closeOnSelection` prop, which will leave the menu open after a selection is made when set to 'false'.
|
@@ -19,4 +19,5 @@ 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'
|
22
|
+
export { default as DropdownMultiSelectWithCustomOptions } from './_dropdown_multi_select_with_custom_options.jsx'
|
23
|
+
export { default as DropdownCloseOnSelect } from './_dropdown_close_on_select.jsx'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import React from "react"
|
2
|
-
import { render, screen } from "../utilities/test-utils"
|
2
|
+
import { render, screen, fireEvent } from "../utilities/test-utils"
|
3
3
|
|
4
4
|
import { Dropdown, Icon, IconCircle } from 'playbook-ui'
|
5
5
|
|
@@ -263,4 +263,110 @@ 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
|
-
})
|
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
|
+
})
|
@@ -115,6 +115,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
115
115
|
|
116
116
|
handleSearch(term = "") {
|
117
117
|
const lcTerm = term.toLowerCase();
|
118
|
+
let hasMatch = false
|
118
119
|
this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
|
119
120
|
//make it so that if the option is selected, it will not show up in the search results
|
120
121
|
if (this.isMultiSelect && this.selectedOptions.has(opt.dataset.dropdownOptionLabel)) {
|
@@ -128,9 +129,32 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
128
129
|
// hide or show option
|
129
130
|
const match = label.includes(lcTerm);
|
130
131
|
opt.style.display = match ? "" : "none";
|
132
|
+
if (match) hasMatch = true
|
131
133
|
});
|
132
134
|
|
133
135
|
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
|
+
}
|
134
158
|
}
|
135
159
|
|
136
160
|
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,16 +56,17 @@ 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
|
-
|
63
62
|
if (!isItemMatchingFilter(option) || (multiSelect && isSelected)) {
|
64
63
|
return null;
|
65
64
|
}
|
65
|
+
|
66
66
|
const isFocused =
|
67
67
|
focusedOptionIndex >= 0 &&
|
68
68
|
filteredOptions[focusedOptionIndex].label === option?.label;
|
69
|
+
|
69
70
|
const focusedClass = isFocused && "focused";
|
70
71
|
|
71
72
|
const selectedClass = isSelected ? "selected" : "list";
|
@@ -91,7 +92,10 @@ const DropdownOption = (props: DropdownOptionProps) => {
|
|
91
92
|
className={classes}
|
92
93
|
id={id}
|
93
94
|
key={key}
|
94
|
-
onClick=
|
95
|
+
onClick={(e) => {
|
96
|
+
e.stopPropagation();
|
97
|
+
handleOptionClick(option);
|
98
|
+
}}
|
95
99
|
>
|
96
100
|
<ListItem
|
97
101
|
cursor="pointer"
|
@@ -100,12 +104,12 @@ const DropdownOption = (props: DropdownOptionProps) => {
|
|
100
104
|
key={option?.label}
|
101
105
|
padding="none"
|
102
106
|
>
|
103
|
-
|
107
|
+
{children ?
|
104
108
|
<div className="dropdown_option_wrapper">{children}</div> :
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
+
<Body dark={dark}
|
110
|
+
text={option?.label}
|
111
|
+
/>
|
112
|
+
}
|
109
113
|
</ListItem>
|
110
114
|
</div>
|
111
115
|
);
|
@@ -44,6 +44,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
44
44
|
|
45
45
|
const {
|
46
46
|
autocomplete,
|
47
|
+
closeOnSelection,
|
47
48
|
filterItem,
|
48
49
|
handleBackspace,
|
49
50
|
handleChange,
|
@@ -54,6 +55,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
54
55
|
isInputFocused,
|
55
56
|
multiSelect,
|
56
57
|
selected,
|
58
|
+
setIsDropDownClosed,
|
57
59
|
setIsInputFocused,
|
58
60
|
toggleDropdown,
|
59
61
|
} = useContext(DropdownContext);
|
@@ -103,11 +105,26 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
103
105
|
? placeholder
|
104
106
|
: "Select...";
|
105
107
|
|
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
|
+
|
106
123
|
return (
|
107
|
-
<div {...ariaProps}
|
108
|
-
{...dataProps}
|
124
|
+
<div {...ariaProps}
|
125
|
+
{...dataProps}
|
109
126
|
{...htmlProps}
|
110
|
-
className={classes}
|
127
|
+
className={classes}
|
111
128
|
id={id}
|
112
129
|
>
|
113
130
|
{
|
@@ -145,7 +162,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
145
162
|
{customDisplay ? (
|
146
163
|
<Flex align="center">
|
147
164
|
{customDisplay}
|
148
|
-
<Body dark={dark}
|
165
|
+
<Body dark={dark}
|
149
166
|
paddingLeft={`${joinedLabels ? "xs" : "none"}`}
|
150
167
|
>
|
151
168
|
{customDisplayPlaceholder}
|
@@ -164,10 +181,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
164
181
|
<input
|
165
182
|
className="dropdown_input"
|
166
183
|
onChange={handleChange}
|
167
|
-
onClick={
|
168
|
-
e.stopPropagation();// keep the wrapper’s handler from firing
|
169
|
-
toggleDropdown();
|
170
|
-
}}
|
184
|
+
onClick={handleInputClick}
|
171
185
|
onFocus={() => setIsInputFocused(true)}
|
172
186
|
onKeyDown={(e) => {
|
173
187
|
handleKeyDown(e);
|
@@ -186,8 +200,8 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
186
200
|
)}
|
187
201
|
</>
|
188
202
|
) : (
|
189
|
-
<Body dark={dark}
|
190
|
-
text={defaultDisplayPlaceholder}
|
203
|
+
<Body dark={dark}
|
204
|
+
text={defaultDisplayPlaceholder}
|
191
205
|
/>
|
192
206
|
)
|
193
207
|
)}
|
@@ -195,10 +209,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
195
209
|
<input
|
196
210
|
className="dropdown_input"
|
197
211
|
onChange={handleChange}
|
198
|
-
onClick={
|
199
|
-
e.stopPropagation();// keep the wrapper’s handler from firing
|
200
|
-
toggleDropdown();
|
201
|
-
}}
|
212
|
+
onClick={handleInputClick}
|
202
213
|
onFocus={() => setIsInputFocused(true)}
|
203
214
|
onKeyDown={handleKeyDown}
|
204
215
|
placeholder={
|
@@ -223,7 +234,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
223
234
|
onClick: (e: Event) => {e.stopPropagation();handleWrapperClick()}
|
224
235
|
}}
|
225
236
|
key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
|
226
|
-
>
|
237
|
+
>
|
227
238
|
{
|
228
239
|
selectedArray.length > 0 && (
|
229
240
|
<div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
|
@@ -33,6 +33,7 @@ type PhoneNumberInputProps = {
|
|
33
33
|
onChange?: (e: React.FormEvent<HTMLInputElement>) => void,
|
34
34
|
onValidate?: Callback<boolean, void>,
|
35
35
|
onlyCountries: string[],
|
36
|
+
excludeCountries: string[],
|
36
37
|
preferredCountries?: string[],
|
37
38
|
required?: boolean,
|
38
39
|
value?: string,
|
@@ -88,6 +89,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
88
89
|
},
|
89
90
|
onValidate = () => null,
|
90
91
|
onlyCountries = [],
|
92
|
+
excludeCountries = [],
|
91
93
|
required = false,
|
92
94
|
preferredCountries = [],
|
93
95
|
value = "",
|
@@ -234,6 +236,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
234
236
|
const fallbackCountry =
|
235
237
|
preferredCountries.length > 0 ? preferredCountries[0] :
|
236
238
|
onlyCountries.length > 0 ? onlyCountries.sort()[0] :
|
239
|
+
excludeCountries.length > 0 ? excludeCountries.sort()[0] :
|
237
240
|
"af";
|
238
241
|
|
239
242
|
useEffect(() => {
|
@@ -244,6 +247,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
244
247
|
autoInsertDialCode: false,
|
245
248
|
initialCountry: initialCountry || fallbackCountry,
|
246
249
|
onlyCountries,
|
250
|
+
excludeCountries,
|
247
251
|
countrySearch: countrySearch,
|
248
252
|
fixDropdownWidth: false,
|
249
253
|
formatAsYouType: formatAsYouType,
|
data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.jsx
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import PhoneNumberInput from '../../pb_phone_number_input/_phone_number_input'
|
3
|
+
|
4
|
+
const PhoneNumberInputExcludeCountries = (props) => (
|
5
|
+
<>
|
6
|
+
<PhoneNumberInput
|
7
|
+
excludeCountries={['us', 'br']}
|
8
|
+
id='exclude'
|
9
|
+
initialCountry='gb'
|
10
|
+
{...props}
|
11
|
+
/>
|
12
|
+
</>
|
13
|
+
)
|
14
|
+
|
15
|
+
export default PhoneNumberInputExcludeCountries
|
@@ -0,0 +1 @@
|
|
1
|
+
Excluding countries removes the selected countries from the dropdown.
|
@@ -4,7 +4,8 @@ examples:
|
|
4
4
|
- phone_number_input_default: Default
|
5
5
|
- phone_number_input_preferred_countries: Preferred Countries
|
6
6
|
- phone_number_input_initial_country: Initial Country
|
7
|
-
- phone_number_input_only_countries:
|
7
|
+
- phone_number_input_only_countries: Only Countries
|
8
|
+
- phone_number_input_exclude_countries: Exclude Countries
|
8
9
|
- phone_number_input_validation: Form Validation
|
9
10
|
- phone_number_input_clear_field: Clearing the Input Field
|
10
11
|
- phone_number_input_access_input_element: Accessing the Input Element
|
@@ -15,9 +16,9 @@ examples:
|
|
15
16
|
- phone_number_input_default: Default
|
16
17
|
- phone_number_input_preferred_countries: Preferred Countries
|
17
18
|
- phone_number_input_initial_country: Initial Country
|
18
|
-
- phone_number_input_only_countries:
|
19
|
+
- phone_number_input_only_countries: Only Countries
|
20
|
+
- phone_number_input_exclude_countries: Exclude Countries
|
19
21
|
- phone_number_input_validation: Form Validation
|
20
22
|
- phone_number_input_format: Format as You Type
|
21
23
|
- phone_number_input_hidden_inputs: Hidden Inputs
|
22
24
|
- phone_number_input_country_search: Country Search
|
23
|
-
|
@@ -2,6 +2,7 @@ export { default as PhoneNumberInputDefault } from './_phone_number_input_defaul
|
|
2
2
|
export { default as PhoneNumberInputPreferredCountries } from './_phone_number_input_preferred_countries'
|
3
3
|
export { default as PhoneNumberInputInitialCountry } from './_phone_number_input_initial_country'
|
4
4
|
export { default as PhoneNumberInputOnlyCountries } from './_phone_number_input_only_countries'
|
5
|
+
export { default as PhoneNumberInputExcludeCountries } from './_phone_number_input_exclude_countries'
|
5
6
|
export { default as PhoneNumberInputValidation } from './_phone_number_input_validation'
|
6
7
|
export { default as PhoneNumberInputClearField } from './_phone_number_input_clear_field'
|
7
8
|
export { default as PhoneNumberInputAccessInputElement } from './_phone_number_input_access_input_element'
|
@@ -15,6 +15,8 @@ module Playbook
|
|
15
15
|
default: ""
|
16
16
|
prop :only_countries, type: Playbook::Props::Array,
|
17
17
|
default: []
|
18
|
+
prop :exclude_countries, type: Playbook::Props::Array,
|
19
|
+
default: []
|
18
20
|
prop :preferred_countries, type: Playbook::Props::Array,
|
19
21
|
default: []
|
20
22
|
prop :error, type: Playbook::Props::String,
|
@@ -44,6 +46,7 @@ module Playbook
|
|
44
46
|
label: label,
|
45
47
|
name: name,
|
46
48
|
onlyCountries: only_countries,
|
49
|
+
excludeCountries: exclude_countries,
|
47
50
|
preferredCountries: preferred_countries,
|
48
51
|
required: required,
|
49
52
|
value: value,
|
@@ -13,19 +13,24 @@ export default class PbPopover extends PbEnhancedElement {
|
|
13
13
|
}
|
14
14
|
|
15
15
|
moveTooltip() {
|
16
|
-
let container: HTMLElement | null;
|
16
|
+
let container: HTMLElement | null = document.querySelector('body');
|
17
17
|
|
18
18
|
if (this.appendTo === "parent") {
|
19
|
-
container = this.element.parentElement
|
19
|
+
container = this.element.parentElement && this.element.parentElement
|
20
20
|
} else if (this.appendTo) {
|
21
|
-
container = document.querySelector(this.appendTo)
|
21
|
+
container = document.querySelector(this.appendTo)
|
22
22
|
}
|
23
23
|
|
24
|
-
|
24
|
+
container.appendChild(this.tooltip);
|
25
25
|
}
|
26
26
|
|
27
27
|
connect() {
|
28
|
+
if (!this.triggerElement || !this.tooltip) {
|
29
|
+
console.warn('Popover requires both trigger and tooltip elements to be defined.')
|
30
|
+
return
|
31
|
+
}
|
28
32
|
this.moveTooltip()
|
33
|
+
|
29
34
|
this.popper = createPopper (this.triggerElement, this.tooltip, {
|
30
35
|
placement: this.position as Placement,
|
31
36
|
strategy: 'fixed',
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%= pb_rails("select", props: { label: "Favorite Animal" }) do %>
|
2
|
+
<select name="animal" id="animal">
|
3
|
+
<optgroup label="Mammal">
|
4
|
+
<option value="1">Cat</option>
|
5
|
+
<option value="2">Dog</option>
|
6
|
+
</optgroup>
|
7
|
+
<optgroup label="Amphibian">
|
8
|
+
<option value="3">Frog</option>
|
9
|
+
<option value="4">Salamander</option>
|
10
|
+
</optgroup>
|
11
|
+
</select>
|
12
|
+
<% end %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
|
3
|
+
import Select from '../_select'
|
4
|
+
|
5
|
+
const SelectCustomSelectSubheaders = (props) => {
|
6
|
+
return (
|
7
|
+
<div>
|
8
|
+
<Select
|
9
|
+
label="Favorite Animal"
|
10
|
+
{...props}
|
11
|
+
>
|
12
|
+
<select
|
13
|
+
id="animal"
|
14
|
+
name="animal"
|
15
|
+
{...props}
|
16
|
+
>
|
17
|
+
<optgroup label="Mammal">
|
18
|
+
<option value="1">{'Cat'}</option>
|
19
|
+
<option value="2">{'Dog'}</option>
|
20
|
+
</optgroup>
|
21
|
+
<optgroup label="Amphibian">
|
22
|
+
<option value="3">{'Frog'}</option>
|
23
|
+
<option value="4">{'Salamander'}</option>
|
24
|
+
</optgroup>
|
25
|
+
</select>
|
26
|
+
</Select>
|
27
|
+
</div>
|
28
|
+
)
|
29
|
+
}
|
30
|
+
|
31
|
+
export default SelectCustomSelectSubheaders
|
@@ -0,0 +1 @@
|
|
1
|
+
To create a select with non-selectable subheaders, use a Custom Select component to render a native `<select>` containing `<optgroup>` elements. The [optgroup HTML element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/optgroup) groups related options under a non-selectable label in the dropdown.
|
@@ -8,6 +8,7 @@ examples:
|
|
8
8
|
- select_required: Required Select Field
|
9
9
|
- select_value_text_same: Equal option value and value text
|
10
10
|
- select_custom_select: Custom Select
|
11
|
+
- select_custom_select_subheaders: Custom Select Subheaders
|
11
12
|
- select_error: Select w/ Error
|
12
13
|
- select_inline: Select Inline
|
13
14
|
- select_inline_show_arrow: Select Inline (Always Show Arrow)
|
@@ -25,6 +26,7 @@ examples:
|
|
25
26
|
- select_required: Required Select Field
|
26
27
|
- select_value_text_same: Equal option value and value text
|
27
28
|
- select_custom_select: Custom Select
|
29
|
+
- select_custom_select_subheaders: Custom Select Subheaders
|
28
30
|
- select_error: Select w/ Error
|
29
31
|
- select_inline: Select Inline
|
30
32
|
- select_inline_show_arrow: Select Inline (Always Show Arrow)
|
@@ -11,3 +11,4 @@ export { default as SelectInlineShowArrow } from './_select_inline_show_arrow.js
|
|
11
11
|
export { default as SelectInlineCompact } from './_select_inline_compact.jsx'
|
12
12
|
export { default as SelectMultiple } from './_select_multiple.jsx'
|
13
13
|
export { default as SelectReactHook } from './_select_react_hook.jsx'
|
14
|
+
export { default as SelectCustomSelectSubheaders } from './_select_custom_select_subheaders.jsx'
|