playbook_ui 15.6.0 → 15.7.0.pre.alpha.play263313229

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +3 -2
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +4 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +95 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_colors_rails.html.erb +43 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_colors_rails.md +1 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.html.erb +11 -5
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.md +7 -1
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background.jsx +54 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background.md +9 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_multi.jsx +80 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_multi.md +3 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +4 -1
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +3 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +2 -2
  17. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +57 -0
  18. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +6 -0
  19. data/app/pb_kits/playbook/pb_card/docs/_card_header.md +1 -1
  20. data/app/pb_kits/playbook/pb_card/docs/_card_highlight.md +1 -1
  21. data/app/pb_kits/playbook/pb_circle_chart/_circle_chart.tsx +6 -0
  22. data/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap +2 -2
  23. data/app/pb_kits/playbook/pb_collapsible/child_kits/CollapsibleIcon.tsx +10 -8
  24. data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_icons.jsx +0 -1
  25. data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_state.jsx +0 -3
  26. data/app/pb_kits/playbook/pb_contact/_contact.tsx +51 -24
  27. data/app/pb_kits/playbook/pb_contact/contact.html.erb +53 -19
  28. data/app/pb_kits/playbook/pb_contact/contact.rb +11 -1
  29. data/app/pb_kits/playbook/pb_contact/contact.test.js +76 -0
  30. data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled.html.erb +33 -0
  31. data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled.jsx +46 -0
  32. data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled_rails.md +2 -0
  33. data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled_react.md +2 -0
  34. data/app/pb_kits/playbook/pb_contact/docs/example.yml +2 -0
  35. data/app/pb_kits/playbook/pb_contact/docs/index.js +1 -0
  36. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +24 -0
  37. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +197 -7
  38. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern_rails.html.erb +23 -14
  39. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern_rails.md +1 -1
  40. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +2 -1
  41. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +1 -1
  42. data/app/pb_kits/playbook/pb_dialog/dialog.rb +1 -0
  43. data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +14 -0
  44. data/app/pb_kits/playbook/pb_dialog/dialog_header.html.erb +5 -4
  45. data/app/pb_kits/playbook/pb_dialog/dialog_header.rb +2 -0
  46. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.html.erb +24 -0
  47. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.jsx +60 -0
  48. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.md +3 -0
  49. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible.html.erb +71 -0
  50. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible.jsx +57 -0
  51. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible_rails.md +1 -0
  52. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible_react.md +1 -0
  53. data/app/pb_kits/playbook/pb_dialog/docs/example.yml +4 -0
  54. data/app/pb_kits/playbook/pb_dialog/docs/index.js +3 -1
  55. data/app/pb_kits/playbook/pb_distribution_bar/docs/_distribution_bar_custom_colors.md +1 -1
  56. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +316 -15
  57. data/app/pb_kits/playbook/pb_draggable/context/types.ts +1 -1
  58. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_rails.html.erb +7 -5
  59. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_default_dates.html.erb +19 -0
  60. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_rails.html.erb +12 -0
  61. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_rails.md +26 -0
  62. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_range_end_rails.html.erb +19 -0
  63. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_range_end_rails.md +1 -0
  64. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_default_rails.html.erb +30 -0
  65. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_default_rails.md +3 -0
  66. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_rails.html.erb +29 -0
  67. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_rails.md +13 -0
  68. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +3 -1
  69. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +5 -0
  70. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +4 -0
  71. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +39 -5
  72. data/app/pb_kits/playbook/pb_dropdown/index.js +171 -3
  73. data/app/pb_kits/playbook/pb_dropdown/quickpick_helper.rb +75 -0
  74. data/app/pb_kits/playbook/pb_filter/Filter/FilterBackground.tsx +3 -3
  75. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +1 -1
  76. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +2 -1
  77. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +14 -0
  78. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.md +3 -0
  79. data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
  80. data/app/pb_kits/playbook/pb_gauge/_gauge.tsx +6 -0
  81. data/app/pb_kits/playbook/pb_line_graph/_line_graph.tsx +6 -0
  82. data/app/pb_kits/playbook/pb_radio/docs/_radio_error.md +1 -1
  83. data/app/pb_kits/playbook/pb_select/_select.tsx +8 -3
  84. data/app/pb_kits/playbook/pb_select/docs/_select_error.md +1 -1
  85. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.html.erb +16 -0
  86. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.jsx +30 -0
  87. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.md +1 -0
  88. data/app/pb_kits/playbook/pb_select/docs/example.yml +2 -0
  89. data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
  90. data/app/pb_kits/playbook/pb_select/select.html.erb +2 -2
  91. data/app/pb_kits/playbook/pb_select/select.rb +3 -1
  92. data/app/pb_kits/playbook/pb_select/select.test.js +23 -0
  93. data/app/pb_kits/playbook/pb_table/_table.tsx +187 -33
  94. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.jsx +134 -0
  95. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.md +34 -0
  96. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.html.erb +101 -0
  97. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.md +33 -0
  98. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.jsx +180 -0
  99. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.md +3 -0
  100. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.html.erb +122 -0
  101. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.md +3 -0
  102. data/app/pb_kits/playbook/pb_table/docs/example.yml +4 -0
  103. data/app/pb_kits/playbook/pb_table/docs/index.js +2 -0
  104. data/app/pb_kits/playbook/pb_table/table.html.erb +68 -12
  105. data/app/pb_kits/playbook/pb_table/table.rb +22 -3
  106. data/app/pb_kits/playbook/pb_table/table.test.js +143 -0
  107. data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +15 -3
  108. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.md +1 -1
  109. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.html.erb +6 -0
  110. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.jsx +25 -0
  111. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.md +3 -0
  112. data/app/pb_kits/playbook/pb_text_input/docs/example.yml +3 -0
  113. data/app/pb_kits/playbook/pb_text_input/docs/index.js +1 -0
  114. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +6 -0
  115. data/app/pb_kits/playbook/pb_text_input/text_input.rb +2 -0
  116. data/app/pb_kits/playbook/pb_text_input/text_input.test.js +16 -0
  117. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.md +1 -1
  118. data/app/pb_kits/playbook/pb_time_picker/_time_picker.scss +296 -0
  119. data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +822 -0
  120. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.html.erb +2 -0
  121. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.jsx +16 -0
  122. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.md +1 -0
  123. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.html.erb +1 -0
  124. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.jsx +13 -0
  125. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.md +1 -0
  126. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.html.erb +4 -0
  127. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.jsx +29 -0
  128. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.md +1 -0
  129. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_disabled.html.erb +13 -0
  130. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_disabled.jsx +23 -0
  131. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_error.html.erb +5 -0
  132. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_error.jsx +15 -0
  133. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_input_options.html.erb +14 -0
  134. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_label.html.erb +2 -0
  135. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_label.jsx +15 -0
  136. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.html.erb +42 -0
  137. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.jsx +52 -0
  138. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.md +1 -0
  139. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +45 -0
  140. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.md +1 -0
  141. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.html.erb +3 -0
  142. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.jsx +21 -0
  143. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.md +1 -0
  144. data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +24 -0
  145. data/app/pb_kits/playbook/pb_time_picker/docs/index.js +9 -0
  146. data/app/pb_kits/playbook/pb_time_picker/index.ts +40 -0
  147. data/app/pb_kits/playbook/pb_time_picker/time_picker.html.erb +1 -0
  148. data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +80 -0
  149. data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +114 -0
  150. data/app/pb_kits/playbook/pb_time_picker/time_picker_helper.ts +662 -0
  151. data/app/pb_kits/playbook/pb_timeline/_item.tsx +3 -0
  152. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.html.erb +60 -0
  153. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.jsx +118 -0
  154. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.md +1 -0
  155. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_date.md +1 -1
  156. data/app/pb_kits/playbook/pb_timeline/docs/example.yml +2 -0
  157. data/app/pb_kits/playbook/pb_timeline/docs/index.js +1 -0
  158. data/app/pb_kits/playbook/pb_timeline/item.html.erb +1 -1
  159. data/app/pb_kits/playbook/pb_timeline/item.rb +2 -0
  160. data/app/pb_kits/playbook/pb_timeline/label.html.erb +2 -1
  161. data/app/pb_kits/playbook/pb_timeline/label.rb +2 -0
  162. data/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx +3 -0
  163. data/app/pb_kits/playbook/pb_timeline/timeline.test.js +51 -0
  164. data/app/pb_kits/playbook/tokens/_colors.scss +2 -1
  165. data/app/pb_kits/playbook/utilities/deprecated.ts +73 -0
  166. data/app/pb_kits/playbook/utilities/globalProps.ts +1 -0
  167. data/dist/chunks/_typeahead-Ckz1ce-2.js +6 -0
  168. data/dist/chunks/lib-DxDBrGZX.js +29 -0
  169. data/dist/chunks/vendor.js +3 -3
  170. data/dist/menu.yml +16 -9
  171. data/dist/playbook-rails-react-bindings.js +1 -1
  172. data/dist/playbook-rails.js +1 -1
  173. data/dist/playbook.css +1 -1
  174. data/lib/playbook/forms/builder/collection_select_field.rb +9 -1
  175. data/lib/playbook/forms/builder/form_field_builder.rb +13 -2
  176. data/lib/playbook/forms/builder/select_field.rb +9 -1
  177. data/lib/playbook/forms/builder/time_picker_field.rb +24 -0
  178. data/lib/playbook/forms/builder/time_zone_select_field.rb +9 -1
  179. data/lib/playbook/forms/builder.rb +1 -0
  180. data/lib/playbook/pb_doc_helper.rb +3 -0
  181. data/lib/playbook/pb_kit_helper.rb +35 -0
  182. data/lib/playbook/version.rb +2 -2
  183. metadata +85 -4
  184. data/dist/chunks/_typeahead-DecTL7bt.js +0 -6
  185. data/dist/chunks/lib-Dk4GKPut.js +0 -29
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "quickpick_helper"
4
+
3
5
  module Playbook
4
6
  module PbDropdown
5
7
  class Dropdown < Playbook::KitBase
@@ -14,7 +16,7 @@ module Playbook
14
16
  prop :blank_selection, type: Playbook::Props::String,
15
17
  default: ""
16
18
  prop :variant, type: Playbook::Props::Enum,
17
- values: %w[default subtle],
19
+ values: %w[default subtle quickpick],
18
20
  default: "default"
19
21
  prop :separators, type: Playbook::Props::Boolean,
20
22
  default: true
@@ -26,13 +28,32 @@ module Playbook
26
28
  default: false
27
29
  prop :form_pill_props, type: Playbook::Props::HashProp,
28
30
  default: {}
31
+ prop :range_ends_today, type: Playbook::Props::Boolean,
32
+ default: false
33
+ prop :controls_end_id, type: Playbook::Props::String,
34
+ default: ""
35
+ prop :controls_start_id, type: Playbook::Props::String,
36
+ default: ""
37
+ prop :start_date_id, type: Playbook::Props::String,
38
+ default: "start_date_id"
39
+ prop :start_date_name, type: Playbook::Props::String,
40
+ default: "start_date_name"
41
+ prop :end_date_id, type: Playbook::Props::String,
42
+ default: "end_date_id"
43
+ prop :end_date_name, type: Playbook::Props::String,
44
+ default: "end_date_name"
29
45
 
30
46
  def data
31
47
  Hash(prop(:data)).merge(
32
48
  pb_dropdown: true,
33
49
  pb_dropdown_multi_select: multi_select,
34
- form_pill_props: form_pill_props.to_json
35
- )
50
+ pb_dropdown_variant: variant,
51
+ form_pill_props: form_pill_props.to_json,
52
+ start_date_id: variant == "quickpick" ? start_date_id : nil,
53
+ end_date_id: variant == "quickpick" ? end_date_id : nil,
54
+ controls_start_id: variant == "quickpick" && controls_start_id.present? ? controls_start_id : nil,
55
+ controls_end_id: variant == "quickpick" && controls_end_id.present? ? controls_end_id : nil
56
+ ).compact
36
57
  end
37
58
 
38
59
  def classname
@@ -48,7 +69,15 @@ module Playbook
48
69
  def input_default_value
49
70
  return "" unless default_value.present?
50
71
 
51
- if multi_select
72
+ if variant == "quickpick"
73
+ d = default_value.to_s.downcase
74
+ matched_option = quickpick_options.find do |opt|
75
+ opt[:label].downcase == d
76
+ end
77
+ return matched_option[:id] if matched_option
78
+
79
+ ""
80
+ elsif multi_select
52
81
  default_value.map { |v| v.transform_keys(&:to_s)["id"] }.join(",")
53
82
  else
54
83
  default_value.transform_keys(&:to_s)["id"]
@@ -60,7 +89,12 @@ module Playbook
60
89
  end
61
90
 
62
91
  def options_with_blank
63
- blank_selection.present? ? [{ id: "", value: "", label: blank_selection }] + options : options
92
+ dropdown_options = variant == "quickpick" ? quickpick_options : options
93
+ blank_selection.present? ? [{ id: "", value: "", label: blank_selection }] + dropdown_options : dropdown_options
94
+ end
95
+
96
+ def quickpick_options
97
+ QuickpickHelper.get_quickpick_options(range_ends_today: range_ends_today)
64
98
  end
65
99
  end
66
100
  end
@@ -28,6 +28,9 @@ export default class PbDropdown extends PbEnhancedElement {
28
28
  clearBtn = null;
29
29
 
30
30
  connect() {
31
+ // Store instance on element for DatePicker sync
32
+ this.element._pbDropdownInstance = this;
33
+
31
34
  this.keyboardHandler = new PbDropdownKeyboard(this);
32
35
  this.isMultiSelect = this.element.dataset.pbDropdownMultiSelect === "true";
33
36
  this.formPillProps = this.element.dataset.formPillProps
@@ -252,6 +255,67 @@ export default class PbDropdown extends PbEnhancedElement {
252
255
  const selectedLabel = JSON.parse(value).label;
253
256
  triggerElement.textContent = selectedLabel;
254
257
  this.emitSelectionChange();
258
+
259
+ // Handle quickpick variant: populate start/end date hidden inputs
260
+ const optionData = JSON.parse(value);
261
+ const startDateId = this.element.dataset.startDateId;
262
+ const endDateId = this.element.dataset.endDateId;
263
+ const controlsStartId = this.element.dataset.controlsStartId;
264
+ const controlsEndId = this.element.dataset.controlsEndId;
265
+
266
+ if (optionData.formatted_start_date && optionData.formatted_end_date) {
267
+ // Populate date inputs when option has date fields
268
+ if (startDateId) {
269
+ const startDateInput = document.getElementById(startDateId);
270
+ if (startDateInput) startDateInput.value = optionData.formatted_start_date;
271
+ }
272
+
273
+ if (endDateId) {
274
+ const endDateInput = document.getElementById(endDateId);
275
+ if (endDateInput) endDateInput.value = optionData.formatted_end_date;
276
+ }
277
+
278
+ // Sync with DatePickers if controlsStartId/controlsEndId are present
279
+ if (controlsStartId) {
280
+ const startPicker = document.querySelector(`#${controlsStartId}`)?._flatpickr;
281
+ if (startPicker) {
282
+ startPicker.setDate(optionData.formatted_start_date, true, "m/d/Y");
283
+ }
284
+ }
285
+
286
+ if (controlsEndId) {
287
+ const endPicker = document.querySelector(`#${controlsEndId}`)?._flatpickr;
288
+ if (endPicker) {
289
+ endPicker.setDate(optionData.formatted_end_date, true, "m/d/Y");
290
+ }
291
+ }
292
+ } else if (startDateId || endDateId) {
293
+ // Clear date inputs when option doesn't have date fields (e.g., blank selection)
294
+ if (startDateId) {
295
+ const startDateInput = document.getElementById(startDateId);
296
+ if (startDateInput) startDateInput.value = "";
297
+ }
298
+
299
+ if (endDateId) {
300
+ const endDateInput = document.getElementById(endDateId);
301
+ if (endDateInput) endDateInput.value = "";
302
+ }
303
+
304
+ // Clear DatePickers as well
305
+ if (controlsStartId) {
306
+ const startPicker = document.querySelector(`#${controlsStartId}`)?._flatpickr;
307
+ if (startPicker) {
308
+ startPicker.clear();
309
+ }
310
+ }
311
+
312
+ if (controlsEndId) {
313
+ const endPicker = document.querySelector(`#${controlsEndId}`)?._flatpickr;
314
+ if (endPicker) {
315
+ endPicker.clear();
316
+ }
317
+ }
318
+ }
255
319
  }
256
320
  if (customDisplayElement) {
257
321
  triggerElement.textContent = "";
@@ -432,9 +496,50 @@ export default class PbDropdown extends PbEnhancedElement {
432
496
  if (!selectedOption) return;
433
497
 
434
498
  selectedOption.classList.add("pb_dropdown_option_selected");
435
- this.setTriggerElementText(
436
- JSON.parse(selectedOption.dataset.dropdownOptionLabel).label
437
- );
499
+ const optionData = JSON.parse(selectedOption.dataset.dropdownOptionLabel);
500
+ this.setTriggerElementText(optionData.label);
501
+
502
+ // Handle quickpick variant: populate start/end date hidden inputs and sync DatePickers
503
+ if (optionData.formatted_start_date && optionData.formatted_end_date) {
504
+ const startDateId = this.element.dataset.startDateId;
505
+ const endDateId = this.element.dataset.endDateId;
506
+ const controlsStartId = this.element.dataset.controlsStartId;
507
+ const controlsEndId = this.element.dataset.controlsEndId;
508
+
509
+ if (startDateId) {
510
+ const startDateInput = document.getElementById(startDateId);
511
+ if (startDateInput) startDateInput.value = optionData.formatted_start_date;
512
+ }
513
+
514
+ if (endDateId) {
515
+ const endDateInput = document.getElementById(endDateId);
516
+ if (endDateInput) endDateInput.value = optionData.formatted_end_date;
517
+ }
518
+
519
+ // Sync with DatePickers - retry with delays to ensure DatePickers are initialized
520
+ const syncDatePickers = () => {
521
+ if (controlsStartId) {
522
+ const startPicker = document.querySelector(`#${controlsStartId}`)?._flatpickr;
523
+ if (startPicker) {
524
+ startPicker.setDate(optionData.formatted_start_date, true, "m/d/Y");
525
+ }
526
+ }
527
+
528
+ if (controlsEndId) {
529
+ const endPicker = document.querySelector(`#${controlsEndId}`)?._flatpickr;
530
+ if (endPicker) {
531
+ endPicker.setDate(optionData.formatted_end_date, true, "m/d/Y");
532
+ }
533
+ }
534
+ };
535
+
536
+ // Try immediately
537
+ syncDatePickers();
538
+
539
+ // Retry after short delay in case DatePickers aren't ready yet
540
+ setTimeout(syncDatePickers, 100);
541
+ setTimeout(syncDatePickers, 300);
542
+ }
438
543
  }
439
544
  }
440
545
 
@@ -571,6 +676,37 @@ export default class PbDropdown extends PbEnhancedElement {
571
676
  if (customDisplay) {
572
677
  customDisplay.style.display = "none";
573
678
  }
679
+
680
+ // Clear quickpick hidden inputs
681
+ const startDateId = this.element.dataset.startDateId;
682
+ const endDateId = this.element.dataset.endDateId;
683
+ const controlsStartId = this.element.dataset.controlsStartId;
684
+ const controlsEndId = this.element.dataset.controlsEndId;
685
+
686
+ if (startDateId) {
687
+ const startDateInput = document.getElementById(startDateId);
688
+ if (startDateInput) startDateInput.value = "";
689
+ }
690
+ if (endDateId) {
691
+ const endDateInput = document.getElementById(endDateId);
692
+ if (endDateInput) endDateInput.value = "";
693
+ }
694
+
695
+ // Clear linked DatePickers if controlsStartId/controlsEndId are present
696
+ if (controlsStartId) {
697
+ const startPicker = document.querySelector(`#${controlsStartId}`)?._flatpickr;
698
+ if (startPicker) {
699
+ startPicker.clear();
700
+ }
701
+ }
702
+
703
+ if (controlsEndId) {
704
+ const endPicker = document.querySelector(`#${controlsEndId}`)?._flatpickr;
705
+ if (endPicker) {
706
+ endPicker.clear();
707
+ }
708
+ }
709
+
574
710
  this.resetDropdownValue();
575
711
  this.updatePills();
576
712
  this.updateClearButton();
@@ -578,6 +714,38 @@ export default class PbDropdown extends PbEnhancedElement {
578
714
  this.emitSelectionChange();
579
715
  }
580
716
 
717
+ // Method for DatePicker sync - only clears the dropdown, not the DatePickers
718
+ clearSelected() {
719
+ // Only clear if this is a single-select quickpick variant
720
+ if (this.element.dataset.pbDropdownVariant !== "quickpick" || this.isMultiSelect) {
721
+ return;
722
+ }
723
+
724
+ const customDisplay = this.element.querySelector(
725
+ "#dropdown_trigger_custom_display"
726
+ );
727
+ if (customDisplay) {
728
+ customDisplay.style.display = "none";
729
+ }
730
+
731
+ // Clear quickpick hidden inputs only (not the DatePickers)
732
+ const startDateId = this.element.dataset.startDateId;
733
+ const endDateId = this.element.dataset.endDateId;
734
+
735
+ if (startDateId) {
736
+ const startDateInput = document.getElementById(startDateId);
737
+ if (startDateInput) startDateInput.value = "";
738
+ }
739
+ if (endDateId) {
740
+ const endDateInput = document.getElementById(endDateId);
741
+ if (endDateInput) endDateInput.value = "";
742
+ }
743
+
744
+ this.resetDropdownValue();
745
+ this.updateClearButton();
746
+ this.emitSelectionChange();
747
+ }
748
+
581
749
  syncHiddenInputs() {
582
750
  if (!this.isMultiSelect) return;
583
751
  this.element
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Playbook
4
+ module PbDropdown
5
+ module QuickpickHelper
6
+ class << self
7
+ def get_quickpick_options(range_ends_today: false)
8
+ today = Date.today
9
+ yesterday = today - 1.day
10
+
11
+ this_week_start_date = first_day_of_week(today)
12
+ this_week_end_date = range_ends_today ? today : last_day_of_week(today)
13
+ last_week_start_date = previous_week_start_date(today)
14
+ last_week_end_date = previous_week_end_date(today)
15
+
16
+ this_month_start_date = today.beginning_of_month
17
+ this_month_end_date = range_ends_today ? today : today.end_of_month
18
+ last_month_start_date = (today - 1.month).beginning_of_month
19
+ last_month_end_date = (today - 1.month).end_of_month
20
+
21
+ this_quarter_start_date = today.beginning_of_quarter
22
+ this_quarter_end_date = range_ends_today ? today : today.end_of_quarter
23
+ last_quarter_start_date = (today - 3.months).beginning_of_quarter
24
+ last_quarter_end_date = (today - 3.months).end_of_quarter
25
+
26
+ this_year_start_date = today.beginning_of_year
27
+ this_year_end_date = range_ends_today ? today : today.end_of_year
28
+ last_year_start_date = (today - 1.year).beginning_of_year
29
+ last_year_end_date = (today - 1.year).end_of_year
30
+
31
+ [
32
+ { id: "quickpick-today", label: "Today", value: [today.to_s, today.to_s], formatted_start_date: format_date(today), formatted_end_date: format_date(today) },
33
+ { id: "quickpick-yesterday", label: "Yesterday", value: [yesterday.to_s, yesterday.to_s], formatted_start_date: format_date(yesterday), formatted_end_date: format_date(yesterday) },
34
+ { id: "quickpick-this-week", label: "This Week", value: [this_week_start_date.to_s, this_week_end_date.to_s], formatted_start_date: format_date(this_week_start_date), formatted_end_date: format_date(this_week_end_date) },
35
+ { id: "quickpick-this-month", label: "This Month", value: [this_month_start_date.to_s, this_month_end_date.to_s], formatted_start_date: format_date(this_month_start_date), formatted_end_date: format_date(this_month_end_date) },
36
+ { id: "quickpick-this-quarter", label: "This Quarter", value: [this_quarter_start_date.to_s, this_quarter_end_date.to_s], formatted_start_date: format_date(this_quarter_start_date), formatted_end_date: format_date(this_quarter_end_date) },
37
+ { id: "quickpick-this-year", label: "This Year", value: [this_year_start_date.to_s, this_year_end_date.to_s], formatted_start_date: format_date(this_year_start_date), formatted_end_date: format_date(this_year_end_date) },
38
+ { id: "quickpick-last-week", label: "Last Week", value: [last_week_start_date.to_s, last_week_end_date.to_s], formatted_start_date: format_date(last_week_start_date), formatted_end_date: format_date(last_week_end_date) },
39
+ { id: "quickpick-last-month", label: "Last Month", value: [last_month_start_date.to_s, last_month_end_date.to_s], formatted_start_date: format_date(last_month_start_date), formatted_end_date: format_date(last_month_end_date) },
40
+ { id: "quickpick-last-quarter", label: "Last Quarter", value: [last_quarter_start_date.to_s, last_quarter_end_date.to_s], formatted_start_date: format_date(last_quarter_start_date), formatted_end_date: format_date(last_quarter_end_date) },
41
+ { id: "quickpick-last-year", label: "Last Year", value: [last_year_start_date.to_s, last_year_end_date.to_s], formatted_start_date: format_date(last_year_start_date), formatted_end_date: format_date(last_year_end_date) },
42
+ ]
43
+ end
44
+
45
+ private
46
+
47
+ def format_date(date)
48
+ date.strftime("%m/%d/%Y")
49
+ end
50
+
51
+ def format_date_range(start_date, end_date)
52
+ "#{format_date(start_date)} - #{format_date(end_date)}"
53
+ end
54
+
55
+ def first_day_of_week(date)
56
+ # Monday as first day of week
57
+ date.beginning_of_week(:monday)
58
+ end
59
+
60
+ def last_day_of_week(date)
61
+ # Sunday as last day of week
62
+ date.end_of_week(:monday)
63
+ end
64
+
65
+ def previous_week_start_date(date)
66
+ first_day_of_week(date) - 1.week
67
+ end
68
+
69
+ def previous_week_end_date(date)
70
+ last_day_of_week(date) - 1.week
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -6,10 +6,10 @@ import { GlobalProps, globalProps } from '../../utilities/globalProps'
6
6
  import Card from '../../pb_card/_card'
7
7
 
8
8
  export type FilterBackgroundProps = {
9
- background: boolean,
10
- className: string,
9
+ background?: boolean,
10
+ className?: string,
11
11
  children?: React.ReactChild[] | React.ReactChild,
12
- dark: boolean,
12
+ dark?: boolean,
13
13
  } & GlobalProps
14
14
 
15
15
  const FilterBackground = (props: FilterBackgroundProps): React.ReactElement => {
@@ -116,7 +116,7 @@
116
116
  <%= form.star_rating_field :example_star_rating, props: { variant: "interactive", label: true } %>
117
117
  <%= form.time_zone_select_field :example_time_zone_select, ActiveSupport::TimeZone.us_zones, { default: "Eastern Time (US & Canada)" }, props: { label: true } %>
118
118
  <%= form.multi_level_select :example_multi_level_select, props: { id: "multi-level-select-form-default", tree_data: treeData, margin_bottom: "sm", label: "Example Multi Level Select field" } %>
119
-
119
+ <%= form.time_picker :example_time_picker, props: { label: true } %>
120
120
  <%= form.actions do |action| %>
121
121
  <%= action.submit %>
122
122
  <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
@@ -117,7 +117,8 @@
117
117
  <%= form.star_rating_field :example_star_rating_validation, props: { variant: "interactive", label: true, required: true } %>
118
118
  <%= form.time_zone_select_field :example_time_zone_select, ActiveSupport::TimeZone.us_zones, { default: "Eastern Time (US & Canada)" }, props: { label: true, blank_selection: "Select a Time Zone...", required: true } %>
119
119
  <%= form.multi_level_select :example_multi_level_select, props: { id: "multi-level-select-form", tree_data: treeData, margin_bottom: "sm", required: true, label: "Example Multi Level Select field" } %>
120
-
120
+ <%= form.time_picker :example_time_picker_validation, props: { label: true, required: true, validation_message: "Please select a time." } %>
121
+
121
122
  <%= form.actions do |action| %>
122
123
  <%= action.submit %>
123
124
  <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
@@ -0,0 +1,14 @@
1
+ <%= pb_form_with(scope: :example, url: "", method: :get, validate: true) do |form| %>
2
+ <%= form.text_field :example_text_field, props: { label: true, required: true, required_indicator: true } %>
3
+ <%= form.text_field :example_text_field_2, props: { label: "Text Field Custom Label", required: true, required_indicator: true } %>
4
+ <%= form.email_field :example_email_field, props: { label: true, required: true, required_indicator: true } %>
5
+ <%= form.number_field :example_number_field, props: { label: true, required: true, required_indicator: true } %>
6
+ <%= form.search_field :example_search_field, props: { label: true, required: true, required_indicator: true } %>
7
+ <%= form.password_field :example_password_field, props: { label: true, required: true, required_indicator: true } %>
8
+ <%= form.url_field :example_url_field, props: { label: true, required: true, required_indicator: true } %>
9
+
10
+ <%= form.actions do |action| %>
11
+ <%= action.submit %>
12
+ <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,3 @@
1
+ The `required_indicator` prop adds a red asterisk (*) to the input label, visually marking the field as required. This works with both `label: true` for auto-generated labels and `label: "Custom Text"` for custom labels.
2
+
3
+ While it's typically used alongside the `required` prop for HTML5 validation, you can use `required_indicator` independently if you're handling validation differently (e.g., client-side or backend validation).
@@ -4,3 +4,4 @@ examples:
4
4
  - form_form_with: Default
5
5
  - form_form_with_validate: Default + Validation
6
6
  - form_form_with_loading: Default + Loading
7
+ - form_with_required_indicator: With Optional Required Indicator
@@ -10,6 +10,7 @@ import solidGauge from "highcharts/modules/solid-gauge";
10
10
  import defaultColors from "../tokens/exports/_colors.module.scss";
11
11
  import typography from "../tokens/exports/_typography.module.scss";
12
12
 
13
+ import { deprecatedKitWarning } from "../utilities/deprecated";
13
14
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props";
14
15
  import { globalProps } from "../utilities/globalProps";
15
16
  import { GenericObject } from "../types";
@@ -191,6 +192,11 @@ const Gauge = ({
191
192
  // eslint-disable-next-line react-hooks/exhaustive-deps
192
193
  }, [chartData]);
193
194
 
195
+ useEffect(() => {
196
+ // Warn about deprecated kit
197
+ deprecatedKitWarning('Gauge', 'Please use "PbGaugeChart" instead.');
198
+ }, []);
199
+
194
200
  return (
195
201
  <HighchartsReact
196
202
  containerProps={{
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
2
2
  import classnames from "classnames";
3
3
  import { globalProps } from "../utilities/globalProps";
4
4
  import { buildAriaProps, buildDataProps, buildHtmlProps } from "../utilities/props";
5
+ import { deprecatedKitWarning} from "../utilities/deprecated";
5
6
 
6
7
  import HighchartsReact from "highcharts-react-official";
7
8
  import Highcharts from "highcharts";
@@ -142,6 +143,11 @@ const LineGraph = ({
142
143
  setOptions(merge(staticOptions, customOptions));
143
144
  }, [chartData]);
144
145
 
146
+ useEffect(() => {
147
+ // Warn about deprecated kit
148
+ deprecatedKitWarning('LineGraph', 'Please use "PbLineGraph" instead.');
149
+ }, []);
150
+
145
151
  return (
146
152
  <HighchartsReact
147
153
  containerProps={{
@@ -1 +1 @@
1
- Error shows that the radio option must be selected or is invalid (ie when used in a form it signals a user to fix an error).
1
+ Error shows that the radio option must be selected or is invalid (i.e. when used in a form it signals a user to fix an error).
@@ -29,6 +29,7 @@ type SelectProps = {
29
29
  id?: string,
30
30
  includeBlank?: string,
31
31
  inline?: boolean,
32
+ inputOptions?: {[key: string]: string | number | boolean | (() => void)},
32
33
  label?: string,
33
34
  margin: string,
34
35
  marginBottom: string,
@@ -63,6 +64,7 @@ const Select = ({
63
64
  label,
64
65
  htmlOptions = {},
65
66
  inline = false,
67
+ inputOptions = {},
66
68
  multiple = false,
67
69
  name,
68
70
  onChange = () => undefined,
@@ -94,14 +96,17 @@ const Select = ({
94
96
  const angleDown = getAllIcons()["angleDown"].icon as unknown as { [key: string]: SVGElement }
95
97
 
96
98
  const selectWrapperClass = classnames(buildCss('pb_select_kit_wrapper'), { error }, className)
99
+ const selectId = (inputOptions?.id as string) || name
100
+
97
101
  const selectBody =(() =>{
98
102
  if (children) return children
99
103
  return (
100
104
  <select
101
105
  {...htmlOptions}
102
106
  {...domSafeProps(props)}
107
+ {...inputOptions}
103
108
  disabled={disabled}
104
- id={name}
109
+ id={selectId}
105
110
  multiple={multiple}
106
111
  name={name}
107
112
  onChange={onChange}
@@ -125,7 +130,7 @@ const Select = ({
125
130
  {label &&
126
131
  <label
127
132
  className="pb_select_kit_label"
128
- htmlFor={name}
133
+ htmlFor={selectId}
129
134
  >
130
135
  <Caption
131
136
  dark={props.dark}
@@ -135,7 +140,7 @@ const Select = ({
135
140
  }
136
141
  <label
137
142
  className={selectWrapperClass}
138
- htmlFor={name}
143
+ htmlFor={selectId}
139
144
  >
140
145
  {selectBody}
141
146
  { multiple !== true ?
@@ -1 +1 @@
1
- Select w/ Error shows that the radio option must be selected or is invalid (ie when used in a form it signals a user to fix an error).
1
+ Select w/ Error shows that an option must be selected or is invalid (i.e. when used in a form it signals a user to fix an error).
@@ -0,0 +1,16 @@
1
+ <%= pb_rails("select", props: {
2
+ label: "Favorite Food",
3
+ name: "favorite_food",
4
+ options: [
5
+ { value: "pizza", value_text: "Pizza" },
6
+ { value: "tacos", value_text: "Tacos" },
7
+ { value: "sushi", value_text: "Sushi" }
8
+ ],
9
+ input_options: {
10
+ 'aria-label': "Select your favorite food",
11
+ class: "custom-select-class",
12
+ data: { controller: "search", action: "change->search#filter" },
13
+ id: "favorite-food-select"
14
+ }
15
+ }) %>
16
+
@@ -0,0 +1,30 @@
1
+ import React from 'react'
2
+
3
+ import Select from '../_select'
4
+
5
+ const SelectInputOptions = (props) => {
6
+ const options = [
7
+ { value: 'pizza', text: 'Pizza' },
8
+ { value: 'tacos', text: 'Tacos' },
9
+ { value: 'sushi', text: 'Sushi' },
10
+ ]
11
+
12
+ return (
13
+ <>
14
+ <Select
15
+ inputOptions={{
16
+ 'aria-label': 'Select your favorite food',
17
+ className: 'custom-select-class',
18
+ id: 'favorite-food-select',
19
+ }}
20
+ label="Favorite Food"
21
+ name="favorite_food"
22
+ options={options}
23
+ {...props}
24
+ />
25
+ </>
26
+ )
27
+ }
28
+
29
+ export default SelectInputOptions
30
+
@@ -0,0 +1 @@
1
+ Use the `input_options` / `inputOptions` prop to pass additional attributes directly to the underlying `<select>` element instead of the outer wrapper. This is useful for applying data attributes, custom IDs, or other HTML attributes that need to be on the select element itself.
@@ -15,6 +15,7 @@ examples:
15
15
  - select_inline_compact: Select Inline Compact
16
16
  - select_attributes: Select W/ Attributes
17
17
  - select_multiple: Select Multiple
18
+ - select_input_options: Input Options
18
19
 
19
20
 
20
21
 
@@ -33,6 +34,7 @@ examples:
33
34
  - select_inline_compact: Select Inline Compact
34
35
  - select_multiple: Select Multiple
35
36
  - select_react_hook: React Hook
37
+ - select_input_options: Input Options
36
38
 
37
39
  swift:
38
40
  - select_default_swift: Default
@@ -12,3 +12,4 @@ 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
14
  export { default as SelectCustomSelectSubheaders } from './_select_custom_select_subheaders.jsx'
15
+ export { default as SelectInputOptions } from './_select_input_options.jsx'
@@ -2,11 +2,11 @@
2
2
  id: nil,
3
3
  class: object.classnames ) do %>
4
4
  <% if object.label %>
5
- <label class="pb_select_kit_label" for="<%= object.name %>">
5
+ <label class="pb_select_kit_label" for="<%= object.input_options[:id] || object.name %>">
6
6
  <%= pb_rails("caption", props: { text: object.label, dark: object.dark }) %>
7
7
  </label>
8
8
  <% end %>
9
- <label class="<%= object.select_wrapper_class %>" for="<%= object.name %>">
9
+ <label class="<%= object.select_wrapper_class %>" for="<%= object.input_options[:id] || object.name %>">
10
10
  <% if content.present? %>
11
11
  <%= content %>
12
12
  <%= pb_rails("body", props: { status: "negative", text: object.error }) %>
@@ -14,6 +14,8 @@ module Playbook
14
14
  prop :error
15
15
  prop :include_blank
16
16
  prop :inline, type: Playbook::Props::Boolean, default: false
17
+ prop :input_options, type: Playbook::Props::HashProp,
18
+ default: {}
17
19
  prop :label
18
20
  prop :multiple, type: Playbook::Props::Boolean, default: false
19
21
  prop :name
@@ -38,7 +40,7 @@ module Playbook
38
40
  multiple: multiple,
39
41
  onchange: onchange,
40
42
  include_blank: include_blank,
41
- }.merge(attributes)
43
+ }.merge(attributes).merge(input_options)
42
44
  end
43
45
 
44
46
  def classname