playbook_ui 16.0.0.pre.alpha.PLAY2722advancedtableinlinerowloadingrails13598 → 16.0.0.pre.alpha.PLAY2744dropdownquickpickpt113758
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_background/docs/_background_responsive.jsx +30 -0
- data/app/pb_kits/playbook/pb_background/docs/_background_responsive.md +1 -0
- data/app/pb_kits/playbook/pb_background/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_background/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +3 -1
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +34 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +4 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +3 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +1 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +4 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
- data/app/pb_kits/playbook/pb_dropdown/index.js +47 -3
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -1
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +3 -1
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_input_display.html.erb +74 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_input_display.jsx +87 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_input_display.md +3 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +35 -33
- data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +33 -6
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +35 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.html.erb +10 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +21 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +3 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +5 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.test.js +33 -18
- data/app/pb_kits/playbook/pb_table/docs/_sections.yml +68 -0
- data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +29 -11
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_input_options.html.erb +39 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_input_options.md +3 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_required_indicator.html.erb +5 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_required_indicator.jsx +25 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_textarea/docs/example.yml +4 -1
- data/app/pb_kits/playbook/pb_textarea/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_textarea/index.ts +12 -5
- data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +10 -10
- data/app/pb_kits/playbook/pb_textarea/textarea.rb +30 -0
- data/app/pb_kits/playbook/pb_textarea/textarea.test.js +18 -1
- data/app/pb_kits/playbook/utilities/test/globalProps/globalProps.integration.test.js +936 -0
- data/dist/chunks/{_pb_line_graph-hxi01lk7.js → _pb_line_graph-BgKF_zz1.js} +1 -1
- data/dist/chunks/{_typeahead-BgLnlhzP.js → _typeahead-B9a6ZsEP.js} +1 -1
- data/dist/chunks/{globalProps-DgYwLYNx.js → globalProps-BhVYCqRf.js} +1 -1
- data/dist/chunks/{lib-NLxTo8OB.js → lib-DD34ZrWL.js} +1 -1
- data/dist/chunks/vendor.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 +31 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77d2aa38ac1c38d9be4fa1b827ac346255f9742aa6e4c4569845b3f8335c57cd
|
|
4
|
+
data.tar.gz: e1034bf5c9ea8bc54b2e4c606f68dc0c0beea877674ac459aa767232bbdab299
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa2c94e31f5bacca5366fcf5c4438b0f740d85df8cf642af32b63ff588bf4f8c5b31b3016e981a44143068349b3ef833d1bd0a72c920d67d6fce157c11378cce
|
|
7
|
+
data.tar.gz: f2a531ff9faca9f3524227c518482938e6c0ae648582befe352fcdfd3a11220c315b9a873e505ba8fd7bf4265b3d671c42d413606204405b76a74e35aa4dc4cb
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Background from '../../pb_background/_background'
|
|
3
|
+
|
|
4
|
+
const BackgroundResponsive = (props) => (
|
|
5
|
+
<>
|
|
6
|
+
<Background
|
|
7
|
+
alt="colorful background"
|
|
8
|
+
backgroundColor={{ xs: "primary", sm: "warning", md: "success", lg: "error", xl: "category_1" }}
|
|
9
|
+
className="background lazyload"
|
|
10
|
+
padding="xl"
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
<br/>
|
|
14
|
+
<Background
|
|
15
|
+
alt="colorful background"
|
|
16
|
+
className="background lazyload"
|
|
17
|
+
imageUrl={{
|
|
18
|
+
xs: "https://unsplash.it/500/400/?image=633",
|
|
19
|
+
sm: "https://images.unsplash.com/photo-1528459801416-a9e53bbf4e17?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1000&q=80",
|
|
20
|
+
md: "https://unsplash.it/500/400/?image=633",
|
|
21
|
+
lg: "https://images.unsplash.com/photo-1528459801416-a9e53bbf4e17?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1000&q=80",
|
|
22
|
+
xl: "https://unsplash.it/500/400/?image=633"
|
|
23
|
+
}}
|
|
24
|
+
padding="xl"
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
</>
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
export default BackgroundResponsive
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The `backgroundColor`, `backgroundSize`, `backgroundPosition`, `backgroundRepeat`, and `imageUrl` props support responsive sizes. To use them, pass an object to the prop containing your values relative to responsive break points. To test this here, resize your browser window to responsively change these Backgrounds' `backgroundColor` and `imageUrl`.
|
|
@@ -7,3 +7,4 @@ export { default as BackgroundStatusSubtle } from './_background_status_subtle.j
|
|
|
7
7
|
export { default as BackgroundCategory } from './_background_category.jsx'
|
|
8
8
|
export { default as BackgroundSize } from './_background_size.jsx'
|
|
9
9
|
export { default as BackgroundOverlay } from './_background_overlay.jsx'
|
|
10
|
+
export { default as BackgroundResponsive } from './_background_responsive.jsx'
|
|
@@ -68,7 +68,9 @@
|
|
|
68
68
|
.pb_dropdown_container {
|
|
69
69
|
position: absolute;
|
|
70
70
|
background-color: $white;
|
|
71
|
-
overflow:
|
|
71
|
+
overflow-y: auto;
|
|
72
|
+
overflow-x: hidden;
|
|
73
|
+
max-height: 18em;
|
|
72
74
|
box-shadow: $shadow_deep;
|
|
73
75
|
border-radius: $border_rad_heavier;
|
|
74
76
|
border: 1px solid $border_light;
|
|
@@ -36,6 +36,7 @@ type DropdownProps = {
|
|
|
36
36
|
blankSelection?: string;
|
|
37
37
|
children?: React.ReactChild[] | React.ReactChild | React.ReactElement[];
|
|
38
38
|
className?: string;
|
|
39
|
+
clearable?: boolean;
|
|
39
40
|
customQuickPickDates?: CustomQuickPickDates;
|
|
40
41
|
formPillProps?: GenericObject;
|
|
41
42
|
dark?: boolean;
|
|
@@ -49,6 +50,7 @@ type DropdownProps = {
|
|
|
49
50
|
multiSelect?: boolean;
|
|
50
51
|
onSelect?: (arg: GenericObject) => null;
|
|
51
52
|
options?: GenericObject;
|
|
53
|
+
placeholder?: string;
|
|
52
54
|
separators?: boolean;
|
|
53
55
|
variant?: "default" | "subtle" | "quickpick";
|
|
54
56
|
rangeEndsToday?: boolean;
|
|
@@ -74,6 +76,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
74
76
|
blankSelection = '',
|
|
75
77
|
children,
|
|
76
78
|
className,
|
|
79
|
+
clearable = true,
|
|
77
80
|
customQuickPickDates,
|
|
78
81
|
dark = false,
|
|
79
82
|
data = {},
|
|
@@ -87,6 +90,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
87
90
|
formPillProps,
|
|
88
91
|
onSelect,
|
|
89
92
|
options,
|
|
93
|
+
placeholder,
|
|
90
94
|
rangeEndsToday = false,
|
|
91
95
|
controlsStartId,
|
|
92
96
|
controlsEndId,
|
|
@@ -211,6 +215,34 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
211
215
|
}
|
|
212
216
|
}, [isDropDownClosed]);
|
|
213
217
|
|
|
218
|
+
// Auto-position dropdown above/below based on available space
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
if (!isDropDownClosed && dropdownContainerRef.current) {
|
|
221
|
+
const container = dropdownContainerRef.current;
|
|
222
|
+
const wrapper = container.closest('.dropdown_wrapper') as HTMLElement;
|
|
223
|
+
if (!wrapper) return;
|
|
224
|
+
|
|
225
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
226
|
+
const h = container.getBoundingClientRect().height || container.scrollHeight;
|
|
227
|
+
const spaceBelow = window.innerHeight - wrapperRect.bottom;
|
|
228
|
+
const spaceAbove = wrapperRect.top;
|
|
229
|
+
|
|
230
|
+
// If not enough space below but enough space above, position above
|
|
231
|
+
if (spaceBelow < h + 10 && spaceAbove >= h + 10) {
|
|
232
|
+
container.style.top = "auto";
|
|
233
|
+
container.style.bottom = "calc(100% + 5px)";
|
|
234
|
+
container.style.marginTop = "0";
|
|
235
|
+
container.style.marginBottom = "0";
|
|
236
|
+
} else {
|
|
237
|
+
// Default: position below
|
|
238
|
+
container.style.top = "";
|
|
239
|
+
container.style.bottom = "";
|
|
240
|
+
container.style.marginTop = "";
|
|
241
|
+
container.style.marginBottom = "";
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}, [isDropDownClosed, dropdownContainerRef]);
|
|
245
|
+
|
|
214
246
|
|
|
215
247
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
216
248
|
setFilterItem(e.target.value);
|
|
@@ -375,6 +407,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
375
407
|
value={{
|
|
376
408
|
activeStyle,
|
|
377
409
|
autocomplete,
|
|
410
|
+
clearable,
|
|
378
411
|
dropdownContainerRef,
|
|
379
412
|
filteredOptions,
|
|
380
413
|
filterItem,
|
|
@@ -426,7 +459,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
426
459
|
</>
|
|
427
460
|
) : (
|
|
428
461
|
<>
|
|
429
|
-
<DropdownTrigger />
|
|
462
|
+
<DropdownTrigger placeholder={placeholder} />
|
|
430
463
|
<DropdownContainer>
|
|
431
464
|
{optionsWithBlankSelection &&
|
|
432
465
|
optionsWithBlankSelection?.map((option: GenericObject) => (
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
The `blank_selection` prop adds a blank option at the top of the dropdown options list. This allows users to explicitly clear their selection by choosing the blank option.
|
|
2
|
+
|
|
3
|
+
The blank selection option appears as the first item in the dropdown and has an empty value (`id: ""`, `value: ""`). When selected, it effectively clears the dropdown selection.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
The `blankSelection` prop adds a blank option at the top of the dropdown options list. This allows users to explicitly clear their selection by choosing the blank option.
|
|
2
|
+
|
|
3
|
+
The blank selection option appears as the first item in the dropdown and has an empty value (`id: ""`, `value: ""`). When selected, it effectively clears the dropdown selection.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<%
|
|
2
|
+
options = [
|
|
3
|
+
{ label: 'United States', value: 'unitedStates', id: 'us' },
|
|
4
|
+
{ label: 'Canada', value: 'canada', id: 'ca' },
|
|
5
|
+
{ label: 'Pakistan', value: 'pakistan', id: 'pk' },
|
|
6
|
+
]
|
|
7
|
+
%>
|
|
8
|
+
|
|
9
|
+
<%= pb_rails("dropdown", props: {
|
|
10
|
+
id: "date-range-quickpick-reset-closeable",
|
|
11
|
+
label: "Quick Pick",
|
|
12
|
+
variant: "quickpick",
|
|
13
|
+
clearable: false
|
|
14
|
+
}) %>
|
|
15
|
+
|
|
16
|
+
<%= pb_rails("button", props: {
|
|
17
|
+
margin_y: "md",
|
|
18
|
+
text: "Reset",
|
|
19
|
+
html_options: {
|
|
20
|
+
onclick: "handleReset()"
|
|
21
|
+
}
|
|
22
|
+
}) %>
|
|
23
|
+
|
|
24
|
+
<%= pb_rails("dropdown", props: {
|
|
25
|
+
id: "closeable-default",
|
|
26
|
+
options: options,
|
|
27
|
+
clearable: false,
|
|
28
|
+
default_value: options.last,
|
|
29
|
+
margin_bottom: "md",
|
|
30
|
+
label: "Default"
|
|
31
|
+
}) %>
|
|
32
|
+
|
|
33
|
+
<%= pb_rails("dropdown", props: {
|
|
34
|
+
id: "closeable-subtle",
|
|
35
|
+
options: options,
|
|
36
|
+
clearable: false,
|
|
37
|
+
default_value: options.second,
|
|
38
|
+
variant: "subtle",
|
|
39
|
+
separators: false,
|
|
40
|
+
label: "Subtle"
|
|
41
|
+
}) %>
|
|
42
|
+
|
|
43
|
+
<script>
|
|
44
|
+
function handleReset() {
|
|
45
|
+
const dropdown = document.querySelector("#date-range-quickpick-reset-closeable[data-pb-dropdown]");
|
|
46
|
+
const instance = dropdown?._pbDropdownInstance;
|
|
47
|
+
|
|
48
|
+
if (instance) {
|
|
49
|
+
instance.clearSelection();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import Button from '../../pb_button/_button'
|
|
4
|
+
import Dropdown from '../../pb_dropdown/_dropdown'
|
|
5
|
+
|
|
6
|
+
const DropdownWithClearable = (props) => {
|
|
7
|
+
const dropdownRef = useRef(null)
|
|
8
|
+
|
|
9
|
+
const options = [
|
|
10
|
+
{
|
|
11
|
+
label: "United States",
|
|
12
|
+
value: "unitedStates",
|
|
13
|
+
id: "us"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: "Canada",
|
|
17
|
+
value: "canada",
|
|
18
|
+
id: "ca"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Pakistan",
|
|
22
|
+
value: "pakistan",
|
|
23
|
+
id: "pk"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
const handleReset = () => {
|
|
28
|
+
if (dropdownRef.current) {
|
|
29
|
+
dropdownRef.current.clearSelected()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<Dropdown
|
|
36
|
+
clearable={false}
|
|
37
|
+
label="Quick Pick"
|
|
38
|
+
onSelect={() => {}}
|
|
39
|
+
ref={dropdownRef}
|
|
40
|
+
variant="quickpick"
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
<Button
|
|
44
|
+
marginY="md"
|
|
45
|
+
onClick={handleReset}
|
|
46
|
+
text="Reset"
|
|
47
|
+
/>
|
|
48
|
+
|
|
49
|
+
<Dropdown
|
|
50
|
+
clearable={false}
|
|
51
|
+
defaultValue={options[options.length - 1]}
|
|
52
|
+
label="Default"
|
|
53
|
+
marginBottom="md"
|
|
54
|
+
options={options}
|
|
55
|
+
variant="default"
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
|
|
59
|
+
<Dropdown
|
|
60
|
+
clearable={false}
|
|
61
|
+
defaultValue={options[1]}
|
|
62
|
+
label="Subtle"
|
|
63
|
+
options={options}
|
|
64
|
+
separators={false}
|
|
65
|
+
variant="subtle"
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
</>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default DropdownWithClearable
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
The `clearable` prop controls whether the clear (X) button appears in the dropdown. When set to `false`, the clear button is hidden.
|
|
2
|
+
|
|
3
|
+
This is useful in two scenarios:
|
|
4
|
+
1. When you have a separate "Reset" or "Defaults" button that handles clearing the selection (as shown in the Quick Pick example)
|
|
5
|
+
2. When you don't want to provide any way to clear the selection (as shown in the Default and Subtle examples)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%
|
|
2
|
+
options = [
|
|
3
|
+
{ label: 'United States', value: 'unitedStates', id: 'us' },
|
|
4
|
+
{ label: 'Canada', value: 'canada', id: 'ca' },
|
|
5
|
+
{ label: 'Pakistan', value: 'pakistan', id: 'pk' },
|
|
6
|
+
]
|
|
7
|
+
%>
|
|
8
|
+
|
|
9
|
+
<%= pb_rails("dropdown", props: { options: options, placeholder: "Choose a country" }) %>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Dropdown from '../../pb_dropdown/_dropdown'
|
|
3
|
+
|
|
4
|
+
const DropdownWithPlaceholder = (props) => {
|
|
5
|
+
|
|
6
|
+
const options = [
|
|
7
|
+
{
|
|
8
|
+
label: "United States",
|
|
9
|
+
value: "unitedStates",
|
|
10
|
+
id: "us"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: "Canada",
|
|
14
|
+
value: "canada",
|
|
15
|
+
id: "ca"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: "Pakistan",
|
|
19
|
+
value: "pakistan",
|
|
20
|
+
id: "pk"
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Dropdown
|
|
26
|
+
options={options}
|
|
27
|
+
placeholder="Choose a country"
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default DropdownWithPlaceholder
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
The `placeholder` prop allows you to customize the placeholder text that appears when no option is selected in the dropdown.
|
|
2
|
+
|
|
3
|
+
The placeholder prop works with all dropdown variants (`default`, `subtle`, and `quickpick`). When no option is selected, the placeholder text is displayed. When an option is selected, the placeholder is replaced by the selected option's label. The default placeholder text is "Select..." if no placeholder is provided.
|
|
@@ -21,7 +21,9 @@ examples:
|
|
|
21
21
|
- dropdown_default_value: Default Value
|
|
22
22
|
- dropdown_multi_select_with_default: Multi Select Default Value
|
|
23
23
|
- dropdown_blank_selection: Blank Selection
|
|
24
|
+
- dropdown_with_placeholder: Placeholder
|
|
24
25
|
- dropdown_separators_hidden: Separators Hidden
|
|
26
|
+
- dropdown_with_clearable: Clearable
|
|
25
27
|
- dropdown_quickpick_rails: Quick Pick Variant
|
|
26
28
|
- dropdown_quickpick_range_end_rails: Quick Pick Variant (Range Ends Today)
|
|
27
29
|
- dropdown_quickpick_default_dates: Quick Pick Variant (Default Dates)
|
|
@@ -52,7 +54,9 @@ examples:
|
|
|
52
54
|
- dropdown_default_value: Default Value
|
|
53
55
|
- dropdown_multi_select_with_default: Multi Select Default Value
|
|
54
56
|
- dropdown_blank_selection: Blank Selection
|
|
57
|
+
- dropdown_with_placeholder: Placeholder
|
|
55
58
|
- dropdown_clear_selection: Clear Selection
|
|
59
|
+
- dropdown_with_clearable: Clearable
|
|
56
60
|
- dropdown_separators_hidden: Separators Hidden
|
|
57
61
|
- dropdown_with_external_control: useDropdown Hook
|
|
58
62
|
- dropdown_quickpick: Quick Pick Variant
|
|
@@ -11,6 +11,7 @@ export { default as DropdownSubcomponentStructure } from './_dropdown_subcompone
|
|
|
11
11
|
export { default as DropdownError } from './_dropdown_error.jsx'
|
|
12
12
|
export { default as DropdownDefaultValue } from './_dropdown_default_value.jsx'
|
|
13
13
|
export { default as DropdownBlankSelection } from './_dropdown_blank_selection.jsx'
|
|
14
|
+
export { default as DropdownWithPlaceholder } from './_dropdown_with_placeholder.jsx'
|
|
14
15
|
export { default as DropdownClearSelection } from './_dropdown_clear_selection.jsx'
|
|
15
16
|
export { default as DropdownSubtleVariant } from './_dropdown_subtle_variant.jsx'
|
|
16
17
|
export { default as DropdownSeparatorsHidden } from './_dropdown_separators_hidden.jsx'
|
|
@@ -27,4 +28,5 @@ export { default as DropdownQuickpick } from './_dropdown_quickpick.jsx'
|
|
|
27
28
|
export { default as DropdownQuickpickRangeEnd } from './_dropdown_quickpick_range_end.jsx'
|
|
28
29
|
export { default as DropdownQuickpickDefaultDates } from './_dropdown_quickpick_default_dates.jsx'
|
|
29
30
|
export { default as DropdownQuickpickWithDatePickers } from './_dropdown_quickpick_with_date_pickers.jsx'
|
|
30
|
-
export { default as DropdownQuickpickCustom } from './_dropdown_quickpick_custom.jsx'
|
|
31
|
+
export { default as DropdownQuickpickCustom } from './_dropdown_quickpick_custom.jsx'
|
|
32
|
+
export { default as DropdownWithClearable } from './_dropdown_with_clearable.jsx'
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<%= content.presence %>
|
|
19
19
|
<%= pb_rails("body", props: { status: "negative", text: object.error }) %>
|
|
20
20
|
<% else %>
|
|
21
|
-
<%= pb_rails("dropdown/dropdown_trigger", props:{ autocomplete: object.autocomplete, multi_select:object.multi_select }) %>
|
|
21
|
+
<%= pb_rails("dropdown/dropdown_trigger", props:{ autocomplete: object.autocomplete, multi_select:object.multi_select, placeholder: object.placeholder }) %>
|
|
22
22
|
<%= pb_rails("dropdown/dropdown_container", props: { searchbar: object.searchbar }) do %>
|
|
23
23
|
<% if options_with_blank.present? %>
|
|
24
24
|
<% options_with_blank.each do |option| %>
|
|
@@ -36,6 +36,8 @@ module Playbook
|
|
|
36
36
|
default: ""
|
|
37
37
|
prop :controls_start_id, type: Playbook::Props::String,
|
|
38
38
|
default: ""
|
|
39
|
+
prop :clearable, type: Playbook::Props::Boolean,
|
|
40
|
+
default: true
|
|
39
41
|
prop :start_date_id, type: Playbook::Props::String,
|
|
40
42
|
default: "start_date_id"
|
|
41
43
|
prop :start_date_name, type: Playbook::Props::String,
|
|
@@ -44,12 +46,14 @@ module Playbook
|
|
|
44
46
|
default: "end_date_id"
|
|
45
47
|
prop :end_date_name, type: Playbook::Props::String,
|
|
46
48
|
default: "end_date_name"
|
|
49
|
+
prop :placeholder, type: Playbook::Props::String
|
|
47
50
|
|
|
48
51
|
def data
|
|
49
52
|
Hash(prop(:data)).merge(
|
|
50
53
|
pb_dropdown: true,
|
|
51
54
|
pb_dropdown_multi_select: multi_select,
|
|
52
55
|
pb_dropdown_variant: variant,
|
|
56
|
+
pb_dropdown_clearable: clearable,
|
|
53
57
|
form_pill_props: form_pill_props.to_json,
|
|
54
58
|
start_date_id: variant == "quickpick" ? start_date_id : nil,
|
|
55
59
|
end_date_id: variant == "quickpick" ? end_date_id : nil,
|
|
@@ -122,6 +122,80 @@ test('generated placeholder prop', () => {
|
|
|
122
122
|
|
|
123
123
|
})
|
|
124
124
|
|
|
125
|
+
test('placeholder prop passed directly to Dropdown', () => {
|
|
126
|
+
render(
|
|
127
|
+
<Dropdown
|
|
128
|
+
data={{ testid: testId }}
|
|
129
|
+
options={options}
|
|
130
|
+
placeholder="Choose a country"
|
|
131
|
+
/>
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
const kit = screen.getByTestId(testId)
|
|
135
|
+
const trigger = kit.querySelector('.pb_dropdown_trigger')
|
|
136
|
+
expect(trigger).toHaveTextContent('Choose a country')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('placeholder works with default variant', () => {
|
|
140
|
+
render(
|
|
141
|
+
<Dropdown
|
|
142
|
+
data={{ testid: testId }}
|
|
143
|
+
options={options}
|
|
144
|
+
placeholder="Select an option"
|
|
145
|
+
variant="default"
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const kit = screen.getByTestId(testId)
|
|
150
|
+
const trigger = kit.querySelector('.pb_dropdown_trigger')
|
|
151
|
+
expect(trigger).toHaveTextContent('Select an option')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('placeholder works with subtle variant', () => {
|
|
155
|
+
render(
|
|
156
|
+
<Dropdown
|
|
157
|
+
data={{ testid: testId }}
|
|
158
|
+
options={options}
|
|
159
|
+
placeholder="Pick an option"
|
|
160
|
+
separators={false}
|
|
161
|
+
variant="subtle"
|
|
162
|
+
/>
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
const kit = screen.getByTestId(testId)
|
|
166
|
+
expect(kit).toHaveClass('pb_dropdown_subtle_separators_hidden')
|
|
167
|
+
const trigger = kit.querySelector('.pb_dropdown_trigger')
|
|
168
|
+
expect(trigger).toHaveTextContent('Pick an option')
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
test('placeholder works with quickpick variant', () => {
|
|
172
|
+
render(
|
|
173
|
+
<Dropdown
|
|
174
|
+
data={{ testid: testId }}
|
|
175
|
+
placeholder="Select a date range"
|
|
176
|
+
variant="quickpick"
|
|
177
|
+
/>
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
const kit = screen.getByTestId(testId)
|
|
181
|
+
expect(kit).toHaveClass('pb_dropdown_quickpick')
|
|
182
|
+
const trigger = kit.querySelector('.pb_dropdown_trigger')
|
|
183
|
+
expect(trigger).toHaveTextContent('Select a date range')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('placeholder shows default "Select..." when not provided', () => {
|
|
187
|
+
render(
|
|
188
|
+
<Dropdown
|
|
189
|
+
data={{ testid: testId }}
|
|
190
|
+
options={options}
|
|
191
|
+
/>
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
const kit = screen.getByTestId(testId)
|
|
195
|
+
const trigger = kit.querySelector('.pb_dropdown_trigger')
|
|
196
|
+
expect(trigger).toHaveTextContent('Select...')
|
|
197
|
+
})
|
|
198
|
+
|
|
125
199
|
test('generated label prop', () => {
|
|
126
200
|
render (
|
|
127
201
|
<Dropdown
|
|
@@ -466,7 +540,27 @@ test("quickpick clears selection when clicking X icon", () => {
|
|
|
466
540
|
expect(trigger).toHaveTextContent("Select...")
|
|
467
541
|
})
|
|
468
542
|
|
|
543
|
+
test("quickpick hides clear icon when clearable is false", () => {
|
|
544
|
+
render(
|
|
545
|
+
<Dropdown
|
|
546
|
+
clearable={false}
|
|
547
|
+
data={{ testid: testId }}
|
|
548
|
+
defaultValue="This Week"
|
|
549
|
+
variant="quickpick"
|
|
550
|
+
/>
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
const kit = screen.getByTestId(testId)
|
|
554
|
+
const trigger = kit.querySelector('.pb_dropdown_trigger')
|
|
555
|
+
|
|
556
|
+
expect(trigger).toHaveTextContent("This Week")
|
|
557
|
+
|
|
558
|
+
const clearIcon = kit.querySelector('[aria-label="times icon"]')
|
|
559
|
+
expect(clearIcon).not.toBeInTheDocument()
|
|
560
|
+
})
|
|
561
|
+
|
|
469
562
|
test("quickpick returns date array values when option selected", () => {
|
|
563
|
+
// eslint-disable-next-line react/no-multi-comp
|
|
470
564
|
const TestComponent = () => {
|
|
471
565
|
const [selected, setSelected] = useState(null)
|
|
472
566
|
return (
|
|
@@ -48,6 +48,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
48
48
|
this.updatePills();
|
|
49
49
|
|
|
50
50
|
this.clearBtn = this.element.querySelector(CLEAR_ICON_SELECTOR);
|
|
51
|
+
this.isClearable = this.element.dataset.pbDropdownClearable !== "false";
|
|
51
52
|
if (this.clearBtn) {
|
|
52
53
|
this.clearBtn.style.display = "none";
|
|
53
54
|
this.clearBtn.addEventListener("click", (e) => {
|
|
@@ -60,6 +61,10 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
60
61
|
|
|
61
62
|
updateClearButton() {
|
|
62
63
|
if (!this.clearBtn) return;
|
|
64
|
+
if (!this.isClearable) {
|
|
65
|
+
this.clearBtn.style.display = "none";
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
63
68
|
const hasSelection = this.isMultiSelect
|
|
64
69
|
? this.selectedOptions.size > 0
|
|
65
70
|
: Boolean(this.element.querySelector(DROPDOWN_INPUT).value);
|
|
@@ -111,13 +116,43 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
111
116
|
const el = this.target;
|
|
112
117
|
el.style.height = "auto";
|
|
113
118
|
requestAnimationFrame(() => {
|
|
114
|
-
|
|
119
|
+
// Calculate 18em in pixels (matches SCSS max-height: 18em)
|
|
120
|
+
const fontSize = parseFloat(getComputedStyle(el).fontSize) || 16;
|
|
121
|
+
const maxHeight = fontSize * 18;
|
|
122
|
+
const scrollHeight = el.scrollHeight;
|
|
123
|
+
const newHeight = Math.min(scrollHeight, maxHeight);
|
|
115
124
|
el.offsetHeight; // force reflow
|
|
116
|
-
el.style.height = newHeight;
|
|
125
|
+
el.style.height = newHeight + "px";
|
|
117
126
|
});
|
|
118
127
|
}
|
|
119
128
|
}
|
|
120
129
|
|
|
130
|
+
adjustDropdownPosition(container) {
|
|
131
|
+
if (!container) return;
|
|
132
|
+
|
|
133
|
+
const wrapper = this.element.querySelector(".dropdown_wrapper");
|
|
134
|
+
if (!wrapper) return;
|
|
135
|
+
|
|
136
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
137
|
+
const h = container.getBoundingClientRect().height || container.scrollHeight;
|
|
138
|
+
const spaceBelow = window.innerHeight - wrapperRect.bottom;
|
|
139
|
+
const spaceAbove = wrapperRect.top;
|
|
140
|
+
|
|
141
|
+
// If not enough space below but enough space above, position above
|
|
142
|
+
if (spaceBelow < h + 10 && spaceAbove >= h + 10) {
|
|
143
|
+
container.style.top = "auto";
|
|
144
|
+
container.style.bottom = "calc(100% + 5px)";
|
|
145
|
+
container.style.marginTop = "0";
|
|
146
|
+
container.style.marginBottom = "0";
|
|
147
|
+
} else {
|
|
148
|
+
// Default: position below
|
|
149
|
+
container.style.top = "";
|
|
150
|
+
container.style.bottom = "";
|
|
151
|
+
container.style.marginTop = "";
|
|
152
|
+
container.style.marginBottom = "";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
121
156
|
handleSearch(term = "") {
|
|
122
157
|
const lcTerm = term.toLowerCase();
|
|
123
158
|
let hasMatch = false
|
|
@@ -365,7 +400,16 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
365
400
|
showElement(elem) {
|
|
366
401
|
elem.classList.remove("close");
|
|
367
402
|
elem.classList.add("open");
|
|
368
|
-
|
|
403
|
+
|
|
404
|
+
// Calculate height respecting max-height constraint (18em)
|
|
405
|
+
const fontSize = parseFloat(getComputedStyle(elem).fontSize) || 16;
|
|
406
|
+
const maxHeight = fontSize * 18; // matches SCSS max-height: 18em
|
|
407
|
+
const scrollHeight = elem.scrollHeight;
|
|
408
|
+
const height = Math.min(scrollHeight, maxHeight);
|
|
409
|
+
elem.style.height = height + "px";
|
|
410
|
+
|
|
411
|
+
// Auto-position dropdown above if not enough space below
|
|
412
|
+
this.adjustDropdownPosition(elem);
|
|
369
413
|
}
|
|
370
414
|
|
|
371
415
|
hideElement(elem) {
|
|
@@ -44,6 +44,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
44
44
|
|
|
45
45
|
const {
|
|
46
46
|
autocomplete,
|
|
47
|
+
clearable,
|
|
47
48
|
filterItem,
|
|
48
49
|
handleBackspace,
|
|
49
50
|
handleChange,
|
|
@@ -225,7 +226,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
225
226
|
key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
|
|
226
227
|
>
|
|
227
228
|
{
|
|
228
|
-
selectedArray.length > 0 && (
|
|
229
|
+
clearable !== false && selectedArray.length > 0 && (
|
|
229
230
|
<div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
|
|
230
231
|
<Icon
|
|
231
232
|
cursor="pointer"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<%= pb_form_with(scope: :example, url: "", method: :get, validate: true) do |form| %>
|
|
2
2
|
<%= form.text_field :example_text_field, props: { label: true, required: true, required_indicator: true } %>
|
|
3
3
|
<%= form.text_field :example_text_field_2, props: { label: "Text Field Custom Label", required: true, required_indicator: true } %>
|
|
4
|
+
<%= form.text_area :example_text_area, props: { label: true, required: true, required_indicator: true } %>
|
|
5
|
+
<%= form.text_area :example_text_area_2, props: { label: "Textarea Custom Label", required: true, required_indicator: true } %>
|
|
4
6
|
<%= form.email_field :example_email_field, props: { label: true, required: true, required_indicator: true } %>
|
|
5
7
|
<%= form.number_field :example_number_field, props: { label: true, required: true, required_indicator: true } %>
|
|
6
8
|
<%= form.search_field :example_search_field, props: { label: true, required: true, required_indicator: true } %>
|
|
@@ -11,4 +13,4 @@
|
|
|
11
13
|
<%= action.submit %>
|
|
12
14
|
<%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
|
|
13
15
|
<% end %>
|
|
14
|
-
<% end %>
|
|
16
|
+
<% end %>
|