playbook_ui 16.1.0.pre.alpha.play2712navkitadddisabledstatecollapsible13821 → 16.1.0.pre.alpha.play2724typeaheadindicator13970

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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +1 -1
  3. data/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +17 -0
  4. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +10 -1
  5. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +2 -0
  6. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.html.erb +6 -0
  7. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.jsx +17 -0
  8. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.md +3 -0
  9. data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -0
  10. data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
  11. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +8 -6
  12. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +6 -0
  13. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +37 -2
  14. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
  15. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
  16. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
  17. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
  18. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
  19. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +33 -0
  20. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +20 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.md +8 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_react.md +8 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
  26. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -0
  27. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -1
  28. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +2 -2
  29. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +6 -0
  30. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
  31. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +5 -1
  32. data/app/pb_kits/playbook/pb_dropdown/index.js +59 -4
  33. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +3 -0
  34. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -1
  35. data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +1 -1
  36. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +2 -2
  37. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +16 -9
  38. data/app/pb_kits/playbook/pb_filter/filter.rb +2 -2
  39. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +5 -2
  40. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +5 -5
  41. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +4 -4
  42. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +4 -0
  43. data/app/pb_kits/playbook/pb_nav/_item.tsx +1 -5
  44. data/app/pb_kits/playbook/pb_nav/_nav.scss +1 -27
  45. data/app/pb_kits/playbook/pb_nav/docs/example.yml +0 -2
  46. data/app/pb_kits/playbook/pb_nav/docs/index.js +1 -2
  47. data/app/pb_kits/playbook/pb_nav/item.rb +2 -7
  48. data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +20 -5
  49. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +1 -0
  50. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.html.erb +7 -0
  51. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.jsx +24 -0
  52. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.md +3 -0
  53. data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
  54. data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
  55. data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
  56. data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +30 -1
  57. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +3 -0
  58. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.html.erb +5 -0
  59. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.jsx +14 -0
  60. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.md +3 -0
  61. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +2 -0
  62. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  63. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
  64. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +34 -3
  65. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +4 -3
  66. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +0 -1
  67. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +10 -10
  68. data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +24 -1
  69. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +412 -324
  70. data/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +2 -0
  71. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +4 -1
  72. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.html.erb +16 -0
  73. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.jsx +23 -0
  74. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.md +3 -0
  75. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +1 -1
  76. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +1 -1
  77. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  78. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +22 -21
  79. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +3 -2
  80. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +7 -1
  81. data/dist/chunks/{_pb_line_graph-BgKF_zz1.js → _pb_line_graph-CFpRBk64.js} +1 -1
  82. data/dist/chunks/_typeahead-D2EbYcmG.js +1 -0
  83. data/dist/chunks/{globalProps-BhVYCqRf.js → globalProps-CXOg_9fx.js} +1 -1
  84. data/dist/chunks/lib-DTxpoePf.js +29 -0
  85. data/dist/chunks/vendor.js +2 -2
  86. data/dist/playbook-rails-react-bindings.js +1 -1
  87. data/dist/playbook-rails.js +1 -1
  88. data/dist/playbook.css +1 -1
  89. data/lib/playbook/align_content.rb +13 -3
  90. data/lib/playbook/align_items.rb +13 -3
  91. data/lib/playbook/align_self.rb +13 -3
  92. data/lib/playbook/display.rb +5 -0
  93. data/lib/playbook/flex.rb +13 -3
  94. data/lib/playbook/flex_direction.rb +13 -3
  95. data/lib/playbook/flex_grow.rb +13 -3
  96. data/lib/playbook/flex_shrink.rb +13 -3
  97. data/lib/playbook/flex_wrap.rb +13 -3
  98. data/lib/playbook/forms/builder/form_field_builder.rb +1 -1
  99. data/lib/playbook/forms/builder/phone_number_field.rb +9 -0
  100. data/lib/playbook/forms/builder/typeahead_field.rb +15 -1
  101. data/lib/playbook/forms/builder.rb +2 -2
  102. data/lib/playbook/justify_content.rb +13 -3
  103. data/lib/playbook/justify_self.rb +13 -3
  104. data/lib/playbook/order.rb +13 -3
  105. data/lib/playbook/spacing.rb +39 -9
  106. data/lib/playbook/text_align.rb +13 -3
  107. data/lib/playbook/truncate.rb +1 -1
  108. data/lib/playbook/version.rb +1 -1
  109. data/lib/playbook/vertical_align.rb +13 -3
  110. data/lib/playbook/z_index.rb +5 -0
  111. metadata +30 -9
  112. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_disabled_item.html.erb +0 -24
  113. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_disabled_item.jsx +0 -89
  114. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_rows.jsx +0 -67
  115. data/dist/chunks/_typeahead-BvrkfO0L.js +0 -1
  116. data/dist/chunks/lib-DD34ZrWL.js +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d939aea02ce3a4f2c4b4fef3a3167caffa5ffcb62f263496c3d28c69f0dcd0f4
4
- data.tar.gz: 4551f5959fe2a467396b057ad3e113d572c2dddadc5cf05e29b76302b6079ca4
3
+ metadata.gz: 8541b6e0d600a07afc16b3a1f87e7851e84af95a8fdf2299b00075eabd2799e7
4
+ data.tar.gz: 75ff644b0208f6b9da46789cdce6882ec381b3910cb1237694cca0061ab88fe1
5
5
  SHA512:
6
- metadata.gz: 162e5837f327e866b090bb5d365f16f601ed095206eb89629ae98b124c0d8e1a5c5e0682df498175f460f53f221c6091b303d6767c37f5ecb6a1ff90cd4d6458
7
- data.tar.gz: f02c17129d23078e67191024c8d1194353eb48f5623167a994c8795438954de246d9ed42d494fb18c39b48ca2726f5fdcc8522596c65d292b5141676836a3807
6
+ metadata.gz: 7f70875cad44dcc1e4e8429f6720472ee8f38dec0ecd23a540bc40467ded607d0479dcd3f400587749c40159baf4c145375872082e0260d2afbc6f5bf2484beb
7
+ data.tar.gz: 2ccc12dd5562413ba97d26fdb29fb093c8e9213febc89a52f8c55e223e6c2278279732bcd46c307f50d32018ad3aeff6dd9e387dd6d37c5ae7c6c5cc084fe846
@@ -168,4 +168,4 @@ $transition: $transition_cubic;
168
168
  border-color: $error;
169
169
  }
170
170
  }
171
- }
171
+ }
@@ -4,6 +4,8 @@ import Icon from '../pb_icon/_icon'
4
4
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
5
5
  import classnames from 'classnames'
6
6
  import { globalProps, GlobalProps } from '../utilities/globalProps'
7
+ import colors from '../tokens/exports/_colors.module.scss'
8
+ import spacing from '../tokens/exports/_spacing.module.scss'
7
9
 
8
10
  type CheckboxProps = {
9
11
  aria?: {[key: string]: string},
@@ -19,6 +21,7 @@ type CheckboxProps = {
19
21
  indeterminate?: boolean,
20
22
  name?: string,
21
23
  onChange?: (event: React.FormEvent<HTMLInputElement>) => void,
24
+ requiredIndicator?: boolean,
22
25
  tabIndex?: number,
23
26
  text?: string,
24
27
  value?: string,
@@ -39,6 +42,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
39
42
  indeterminate = false,
40
43
  name = '',
41
44
  onChange = () => { void 0 },
45
+ requiredIndicator = false,
42
46
  tabIndex,
43
47
  text = '',
44
48
  value = '',
@@ -124,7 +128,20 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
124
128
  variant={null}
125
129
  >
126
130
  {text}
131
+ {requiredIndicator && (
132
+ <span
133
+ aria-hidden="true"
134
+ className="pb_required_indicator"
135
+ style={{
136
+ color: colors.error,
137
+ marginLeft: spacing.space_xs,
138
+ }}
139
+ >
140
+ {'*'}
141
+ </span>
142
+ )}
127
143
  </Body>
144
+
128
145
  </label>
129
146
  )
130
147
  })
@@ -5,6 +5,15 @@
5
5
  <%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon hidden", fixed_width: true}) %>
6
6
  </span>
7
7
  <span class="pb_checkbox_label">
8
- <%= pb_rails("body", props: { status: object.checkbox_label_status, text: object.text, dark: object.dark, margin_right: object.form_spacing ? "xs" : "" }) %>
8
+ <%= pb_rails("body", props: { status: object.checkbox_label_status, dark: object.dark, margin_right: object.form_spacing ? "xs" : "" }) do %>
9
+ <%= object.text%>
10
+ <% if object.required_indicator %>
11
+ <span
12
+ class="pb_checkbox_required_indicator"
13
+ aria-hidden="true"
14
+ style="color: #DA0014;"
15
+ >*</span>
16
+ <% end %>
17
+ <% end %>
9
18
  </span>
10
19
  <% end %>
@@ -23,6 +23,8 @@ module Playbook
23
23
  prop :hidden_input, type: Playbook::Props::Boolean,
24
24
  default: false
25
25
  prop :hidden_value
26
+ prop :required_indicator, type: Playbook::Props::Boolean,
27
+ default: false
26
28
 
27
29
  def classname
28
30
  generate_classname("pb_checkbox_kit", checked_class) + error_class
@@ -0,0 +1,6 @@
1
+ <%= pb_rails("checkbox" , props: {
2
+ required_indicator: true,
3
+ text: "Checkbox Label",
4
+ value: "checkbox-value",
5
+ name: "checkbox-name"
6
+ }) %>
@@ -0,0 +1,17 @@
1
+ import React from 'react'
2
+ import Checkbox from '../_checkbox'
3
+
4
+ const CheckboxRequiredIndicator = () => {
5
+ return (
6
+ <div>
7
+ <Checkbox
8
+ name="checkbox-name"
9
+ requiredIndicator
10
+ text="Checkbox label"
11
+ value="check-box value"
12
+ />
13
+ </div>
14
+ )
15
+ }
16
+
17
+ export default CheckboxRequiredIndicator
@@ -0,0 +1,3 @@
1
+ The `requiredIndicator`/`required_indicator` prop displays a red asterisk (*) next to the label, visually indicating that the field is required. This is purely visual and does not enforce validation.
2
+
3
+ You can use `requiredIndicator`/`required_indicator` with any validation approach: HTML5 validation via the `required` prop, client-side validation, or backend validation. For this reason, it works independently and doesn't need to be paired with the `required` prop.
@@ -8,6 +8,7 @@ examples:
8
8
  - checkbox_indeterminate: Indeterminate Checkbox
9
9
  - checkbox_disabled: Disabled Checkbox
10
10
  - checkbox_form: Form and Hidden Input
11
+ # - checkbox_required_indicator: Required Indicator
11
12
 
12
13
  react:
13
14
  - checkbox_default: Default
@@ -17,6 +18,7 @@ examples:
17
18
  - checkbox_error: Default w/ Error
18
19
  - checkbox_indeterminate: Indeterminate Checkbox
19
20
  - checkbox_disabled: Disabled Checkbox
21
+ # - checkbox_required_indicator: Required Indicator
20
22
 
21
23
  swift:
22
24
  - checkbox_default_swift: Default
@@ -5,3 +5,4 @@ export { default as CheckboxError } from './_checkbox_error.jsx'
5
5
  export { default as CheckboxChecked } from './_checkbox_checked.jsx'
6
6
  export { default as CheckboxIndeterminate } from './_checkbox_indeterminate.jsx'
7
7
  export { default as CheckboxDisabled } from './_checkbox_disabled.jsx'
8
+ export { default as CheckboxRequiredIndicator } from './_checkbox_required_indicator.jsx'
@@ -135,6 +135,8 @@
135
135
  position: sticky;
136
136
  top: 0;
137
137
  background-color: $white;
138
+ border-top-left-radius: $border_radius_md;
139
+ border-top-right-radius: $border_radius_md;
138
140
  z-index: $z_8;
139
141
  }
140
142
 
@@ -256,7 +258,7 @@
256
258
  }
257
259
  &.full_height_left {
258
260
  justify-content: flex-start;
259
-
261
+
260
262
  .pb_dialog {
261
263
  border-radius: 0;
262
264
  height: 100%;
@@ -302,7 +304,7 @@
302
304
 
303
305
  &.full_height_center {
304
306
  justify-content: center;
305
-
307
+
306
308
  .pb_dialog {
307
309
  border-radius: 0;
308
310
  height: 100%;
@@ -346,7 +348,7 @@
346
348
 
347
349
  &.full_height_right {
348
350
  justify-content: flex-end;
349
-
351
+
350
352
  .pb_dialog {
351
353
  border-radius: 0;
352
354
  height: 100%;
@@ -417,7 +419,7 @@
417
419
  margin: unset !important;
418
420
  margin-right: auto !important;
419
421
  }
420
-
422
+
421
423
  .pb_dialog {
422
424
  border-radius: 0;
423
425
  height: 100% !important;
@@ -463,7 +465,7 @@
463
465
 
464
466
  &.full_height_center {
465
467
  justify-content: center;
466
-
468
+
467
469
  .pb_dialog {
468
470
  border-radius: 0;
469
471
  height: 100% !important;
@@ -510,7 +512,7 @@
510
512
  margin: unset !important;
511
513
  margin-left: auto !important;
512
514
  }
513
-
515
+
514
516
  .pb_dialog {
515
517
  border-radius: 0;
516
518
  height: 100% !important;
@@ -76,6 +76,12 @@
76
76
  z-index: $z_1;
77
77
  width: 100%;
78
78
 
79
+ &.constrain_height {
80
+ overflow-y: auto;
81
+ overflow-x: hidden;
82
+ max-height: 18em;
83
+ }
84
+
79
85
  .pb_dropdown_option,
80
86
  .pb_dropdown_option_list,
81
87
  .pb_dropdown_option_selected,
@@ -36,6 +36,8 @@ type DropdownProps = {
36
36
  blankSelection?: string;
37
37
  children?: React.ReactChild[] | React.ReactChild | React.ReactElement[];
38
38
  className?: string;
39
+ clearable?: boolean;
40
+ constrainHeight?: boolean;
39
41
  customQuickPickDates?: CustomQuickPickDates;
40
42
  formPillProps?: GenericObject;
41
43
  dark?: boolean;
@@ -49,6 +51,7 @@ type DropdownProps = {
49
51
  multiSelect?: boolean;
50
52
  onSelect?: (arg: GenericObject) => null;
51
53
  options?: GenericObject;
54
+ placeholder?: string;
52
55
  separators?: boolean;
53
56
  variant?: "default" | "subtle" | "quickpick";
54
57
  rangeEndsToday?: boolean;
@@ -74,6 +77,8 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
74
77
  blankSelection = '',
75
78
  children,
76
79
  className,
80
+ clearable = true,
81
+ constrainHeight = false,
77
82
  customQuickPickDates,
78
83
  dark = false,
79
84
  data = {},
@@ -87,6 +92,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
87
92
  formPillProps,
88
93
  onSelect,
89
94
  options,
95
+ placeholder,
90
96
  rangeEndsToday = false,
91
97
  controlsStartId,
92
98
  controlsEndId,
@@ -211,6 +217,34 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
211
217
  }
212
218
  }, [isDropDownClosed]);
213
219
 
220
+ // Auto-position dropdown above/below based on available space
221
+ useEffect(() => {
222
+ if (!isDropDownClosed && dropdownContainerRef.current) {
223
+ const container = dropdownContainerRef.current;
224
+ const wrapper = container.closest('.dropdown_wrapper') as HTMLElement;
225
+ if (!wrapper) return;
226
+
227
+ const wrapperRect = wrapper.getBoundingClientRect();
228
+ const h = container.getBoundingClientRect().height || container.scrollHeight;
229
+ const spaceBelow = window.innerHeight - wrapperRect.bottom;
230
+ const spaceAbove = wrapperRect.top;
231
+
232
+ // If not enough space below but enough space above, position above
233
+ if (spaceBelow < h + 10 && spaceAbove >= h + 10) {
234
+ container.style.top = "auto";
235
+ container.style.bottom = "calc(100% + 5px)";
236
+ container.style.marginTop = "0";
237
+ container.style.marginBottom = "0";
238
+ } else {
239
+ // Default: position below
240
+ container.style.top = "";
241
+ container.style.bottom = "";
242
+ container.style.marginTop = "";
243
+ container.style.marginBottom = "";
244
+ }
245
+ }
246
+ }, [isDropDownClosed, dropdownContainerRef]);
247
+
214
248
 
215
249
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
216
250
  setFilterItem(e.target.value);
@@ -375,6 +409,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
375
409
  value={{
376
410
  activeStyle,
377
411
  autocomplete,
412
+ clearable,
378
413
  dropdownContainerRef,
379
414
  filteredOptions,
380
415
  filterItem,
@@ -426,8 +461,8 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
426
461
  </>
427
462
  ) : (
428
463
  <>
429
- <DropdownTrigger />
430
- <DropdownContainer>
464
+ <DropdownTrigger placeholder={placeholder} />
465
+ <DropdownContainer constrainHeight={constrainHeight}>
431
466
  {optionsWithBlankSelection &&
432
467
  optionsWithBlankSelection?.map((option: GenericObject) => (
433
468
  <DropdownOption key={option.id}
@@ -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,33 @@
1
+ import React from 'react'
2
+ import Dropdown from '../../pb_dropdown/_dropdown'
3
+
4
+ const DropdownWithConstrainHeight = (props) => {
5
+ // Create a long list of options to demonstrate height constraint
6
+ const options = Array.from({ length: 30 }, (_, i) => ({
7
+ label: `Option ${i + 1}`,
8
+ value: `option_${i + 1}`,
9
+ id: `opt_${i + 1}`
10
+ }))
11
+
12
+ return (
13
+ <>
14
+ <Dropdown
15
+ data={{ testid: "dropdown-no-constrain" }}
16
+ label="Without Constrain Height (Default)"
17
+ marginBottom="md"
18
+ options={options}
19
+ {...props}
20
+ />
21
+
22
+ <Dropdown
23
+ constrainHeight
24
+ data={{ testid: "dropdown-constrain" }}
25
+ label="With Constrain Height"
26
+ options={options}
27
+ {...props}
28
+ />
29
+ </>
30
+ )
31
+ }
32
+
33
+ export default DropdownWithConstrainHeight
@@ -0,0 +1,20 @@
1
+ <%
2
+ # Create a long list of options to demonstrate height constraint
3
+ options = (1..30).map do |i|
4
+ { label: "Option #{i}", value: "option_#{i}", id: "opt_#{i}" }
5
+ end
6
+ %>
7
+
8
+ <%= pb_rails("dropdown", props: {
9
+ id: "dropdown-no-constrain",
10
+ options: options,
11
+ label: "Without Constrain Height (Default)",
12
+ margin_bottom: "md"
13
+ }) %>
14
+
15
+ <%= pb_rails("dropdown", props: {
16
+ id: "dropdown-constrain",
17
+ options: options,
18
+ constrain_height: true,
19
+ label: "With Constrain Height"
20
+ }) %>
@@ -0,0 +1,8 @@
1
+ The `constrain_height` prop limits the dropdown container height to 18em and enables vertical scrolling when the content exceeds this height. This prevents long dropdown lists from rendering off-screen.
2
+
3
+ When `constrain_height` is `true`, the dropdown will:
4
+ - Have a maximum height of 18em
5
+ - Show a scrollbar when content exceeds the max height
6
+ - Prevent the dropdown from extending beyond the viewport
7
+
8
+ This is particularly useful for dropdowns with many options, such as long lists or quickpick variants with many date range options.
@@ -0,0 +1,8 @@
1
+ The `constrainHeight` prop limits the dropdown container height to 18em and enables vertical scrolling when the content exceeds this height. This prevents long dropdown lists from rendering off-screen.
2
+
3
+ When `constrainHeight` is `true`, the dropdown will:
4
+ - Have a maximum height of 18em
5
+ - Show a scrollbar when content exceeds the max height
6
+ - Prevent the dropdown from extending beyond the viewport
7
+
8
+ This is particularly useful for dropdowns with many options, such as long lists or quickpick variants with many date range options.
@@ -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,10 @@ 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
27
+ - dropdown_with_constrain_height_rails: Constrain Height
25
28
  - dropdown_quickpick_rails: Quick Pick Variant
26
29
  - dropdown_quickpick_range_end_rails: Quick Pick Variant (Range Ends Today)
27
30
  - dropdown_quickpick_default_dates: Quick Pick Variant (Default Dates)
@@ -52,7 +55,10 @@ examples:
52
55
  - dropdown_default_value: Default Value
53
56
  - dropdown_multi_select_with_default: Multi Select Default Value
54
57
  - dropdown_blank_selection: Blank Selection
58
+ - dropdown_with_placeholder: Placeholder
55
59
  - dropdown_clear_selection: Clear Selection
60
+ - dropdown_with_clearable: Clearable
61
+ - dropdown_with_constrain_height: Constrain Height
56
62
  - dropdown_separators_hidden: Separators Hidden
57
63
  - dropdown_with_external_control: useDropdown Hook
58
64
  - 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,6 @@ 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'
33
+ export { default as DropdownWithConstrainHeight } from './_dropdown_with_constrain_height.jsx'
@@ -18,8 +18,8 @@
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 }) %>
22
- <%= pb_rails("dropdown/dropdown_container", props: { searchbar: object.searchbar }) do %>
21
+ <%= pb_rails("dropdown/dropdown_trigger", props:{ autocomplete: object.autocomplete, multi_select:object.multi_select, placeholder: object.placeholder }) %>
22
+ <%= pb_rails("dropdown/dropdown_container", props: { searchbar: object.searchbar, constrain_height: object.constrain_height }) do %>
23
23
  <% if options_with_blank.present? %>
24
24
  <% options_with_blank.each do |option| %>
25
25
  <%= pb_rails("dropdown/dropdown_option", props: {option: 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,16 @@ 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
50
+ prop :constrain_height, type: Playbook::Props::Boolean,
51
+ default: false
47
52
 
48
53
  def data
49
54
  Hash(prop(:data)).merge(
50
55
  pb_dropdown: true,
51
56
  pb_dropdown_multi_select: multi_select,
52
57
  pb_dropdown_variant: variant,
58
+ pb_dropdown_clearable: clearable,
53
59
  form_pill_props: form_pill_props.to_json,
54
60
  start_date_id: variant == "quickpick" ? start_date_id : nil,
55
61
  end_date_id: variant == "quickpick" ? end_date_id : nil,