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
@@ -275,6 +275,80 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
275
275
 
276
276
  const { setMinDate, setMaxDate } = getMinMaxDates()
277
277
 
278
+ // Default Date + Min/Max Date Initialization Helper Functions section ----/
279
+ const toDateObject = (dateValue: any): Date | null => {
280
+ if (!dateValue) return null
281
+ if (dateValue instanceof Date) return dateValue
282
+ if (typeof dateValue === 'string') {
283
+ const parsed = new Date(dateValue)
284
+ return isNaN(parsed.getTime()) ? null : parsed
285
+ }
286
+ if (typeof dateValue === 'number') {
287
+ return new Date(dateValue)
288
+ }
289
+ return null
290
+ }
291
+
292
+ // Formatting Date for Flatpickr
293
+ const formatDateForFlatpickr = (dateValue: any): string | null => {
294
+ const dateObj = toDateObject(dateValue)
295
+ if (!dateObj) return null
296
+
297
+ const year = dateObj.getFullYear()
298
+ const month = String(dateObj.getMonth() + 1).padStart(2, '0')
299
+ const day = String(dateObj.getDate()).padStart(2, '0')
300
+ return `${year}-${month}-${day}`
301
+ }
302
+
303
+ // Helper to check if defaultDate is earlier than minDate
304
+ const isDefaultDateBeforeMinDate = (defaultDateValue: any, minDateValue: any): boolean => {
305
+ if (!defaultDateValue || !minDateValue) return false
306
+
307
+ const defaultDateObj = toDateObject(defaultDateValue)
308
+ const minDateObj = toDateObject(minDateValue)
309
+
310
+ if (!defaultDateObj || !minDateObj) return false
311
+
312
+ const defaultDateOnly = new Date(defaultDateObj.getFullYear(), defaultDateObj.getMonth(), defaultDateObj.getDate())
313
+ const minDateOnly = new Date(minDateObj.getFullYear(), minDateObj.getMonth(), minDateObj.getDate())
314
+
315
+ return defaultDateOnly < minDateOnly
316
+ }
317
+
318
+ // Helper to check if defaultDate is later than maxDate
319
+ const isDefaultDateAfterMaxDate = (defaultDateValue: any, maxDateValue: any): boolean => {
320
+ if (!defaultDateValue || !maxDateValue) return false
321
+
322
+ const defaultDateObj = toDateObject(defaultDateValue)
323
+ const maxDateObj = toDateObject(maxDateValue)
324
+
325
+ if (!defaultDateObj || !maxDateObj) return false
326
+
327
+ const defaultDateOnly = new Date(defaultDateObj.getFullYear(), defaultDateObj.getMonth(), defaultDateObj.getDate())
328
+ const maxDateOnly = new Date(maxDateObj.getFullYear(), maxDateObj.getMonth(), maxDateObj.getDate())
329
+
330
+ return defaultDateOnly > maxDateOnly
331
+ }
332
+
333
+ const defaultDateValue: any = defaultDateGetter()
334
+ // Only check for and out-of-range if user actually provided minDate/maxDate constraints
335
+ const isBeforeMin = minDate && isDefaultDateBeforeMinDate(defaultDateValue, setMinDate)
336
+ const isAfterMax = maxDate && isDefaultDateAfterMaxDate(defaultDateValue, setMaxDate)
337
+
338
+ // Store these values for use in onClose handler
339
+ const hasOutOfRangeDefault = (isBeforeMin || isAfterMax) && defaultDateValue
340
+
341
+ // Temporarily adjust minDate/maxDate to allow defaultDate to render if it's out of range via user provided minDate/maxDate constraints
342
+ const effectiveMinDate = isBeforeMin && defaultDateValue && minDate
343
+ ? formatDateForFlatpickr(defaultDateValue) || setMinDate
344
+ : setMinDate
345
+
346
+ const effectiveMaxDate = isAfterMax && defaultDateValue && maxDate
347
+ ? formatDateForFlatpickr(defaultDateValue) || setMaxDate
348
+ : setMaxDate
349
+
350
+ // End of Default Date + Min/Max Date Initialization Helper Functions section ----/
351
+
278
352
  flatpickr(`#${pickerId}`, {
279
353
  allowInput,
280
354
  closeOnSelect,
@@ -286,11 +360,32 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
286
360
  locale: {
287
361
  rangeSeparator: ' to '
288
362
  },
289
- maxDate: setMaxDate,
290
- minDate: setMinDate,
363
+ maxDate: effectiveMaxDate,
364
+ minDate: effectiveMinDate,
291
365
  mode,
292
366
  nextArrow: '<i class="far fa-angle-right"></i>',
293
367
  onOpen: [(_selectedDates, _dateStr, fp) => {
368
+ // If defaultDate was out of range of a dev set min/max date, restore it when calendar opens (in situation where the input was manually cleared or the calendar was closed without selection)
369
+ if (hasOutOfRangeDefault) {
370
+ const dateObj = toDateObject(defaultDateValue)
371
+ if (dateObj) {
372
+ const inputIsBlank = !fp.input.value || fp.input.value.trim() === ''
373
+ const noSelection = !fp.selectedDates || fp.selectedDates.length === 0
374
+
375
+ if (inputIsBlank || noSelection) {
376
+ const formattedDate = fp.formatDate(dateObj, getDateFormat())
377
+ if (formattedDate) {
378
+ fp.input.value = formattedDate
379
+ }
380
+ fp.selectedDates = [dateObj]
381
+ fp.jumpToDate(dateObj)
382
+ setTimeout(() => {
383
+ yearChangeHook(fp)
384
+ }, 0)
385
+ }
386
+ }
387
+ }
388
+
294
389
  calendarResizer()
295
390
  if (resizeRepositionHandlerRef) {
296
391
  window.removeEventListener('resize', resizeRepositionHandlerRef)
@@ -303,12 +398,30 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
303
398
  if (!staticPosition && scrollContainer) attachToScroll(scrollContainer)
304
399
  positionCalendarIfNeeded(fp)
305
400
  }],
306
- onClose: [(selectedDates, dateStr) => {
401
+ onClose: [(selectedDates, dateStr, fp) => {
307
402
  if (resizeRepositionHandlerRef) {
308
403
  window.removeEventListener('resize', resizeRepositionHandlerRef)
309
404
  resizeRepositionHandlerRef = null
310
405
  }
311
406
  if (!staticPosition && scrollContainer) detachFromScroll(scrollContainer as HTMLElement)
407
+
408
+ // If defaultDate was out of range and no date was selected, preserve the default date
409
+ if (hasOutOfRangeDefault && (!selectedDates || selectedDates.length === 0)) {
410
+ const dateObj = toDateObject(defaultDateValue)
411
+ if (dateObj && fp.input) {
412
+ const formattedDate = fp.formatDate(dateObj, getDateFormat())
413
+ if (formattedDate) {
414
+ setTimeout(() => {
415
+ if (fp.input && (!fp.selectedDates || fp.selectedDates.length === 0)) {
416
+ fp.input.value = formattedDate
417
+ fp.selectedDates = [dateObj]
418
+ fp.jumpToDate(dateObj)
419
+ }
420
+ }, 0)
421
+ }
422
+ }
423
+ }
424
+
312
425
  onClose(selectedDates, dateStr)
313
426
  }],
314
427
  onChange: [(selectedDates, dateStr, fp) => {
@@ -330,6 +443,71 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
330
443
  const picker = document.querySelector<HTMLElement & { [x: string]: any }>(`#${pickerId}`)._flatpickr
331
444
  picker.innerContainer.parentElement.id = `cal-${pickerId}`
332
445
 
446
+ // If defaultDate was out of range, restore the original minDate/maxDate after initialization (defaultDate displayed, still cannot select dates outside the actual range via user provided minDate/maxDate constraints)
447
+ if ((isBeforeMin || isAfterMax) && defaultDateValue) {
448
+ const dateObj = toDateObject(defaultDateValue)
449
+ const formattedDate = dateObj ? picker.formatDate(dateObj, getDateFormat()) : null
450
+
451
+ setTimeout(() => {
452
+ if (!dateObj || !picker.input || !formattedDate) return
453
+
454
+ picker.setDate(dateObj, false)
455
+
456
+ if (isBeforeMin && setMinDate && minDate) {
457
+ picker.set('minDate', setMinDate)
458
+ }
459
+ if (isAfterMax && setMaxDate && maxDate) {
460
+ picker.set('maxDate', setMaxDate)
461
+ }
462
+ picker.input.value = formattedDate
463
+
464
+ picker.selectedDates = [dateObj]
465
+
466
+ setTimeout(() => {
467
+ yearChangeHook(picker)
468
+ }, 0)
469
+
470
+ // Restore function for out-of-range default dates
471
+ const restoreOutOfRangeValue = () => {
472
+ if (!picker.input) return
473
+
474
+ const inputIsBlank = !picker.input.value || picker.input.value.trim() === ''
475
+ const noSelection = !picker.selectedDates || picker.selectedDates.length === 0
476
+
477
+ if (inputIsBlank || noSelection) {
478
+ setTimeout(() => {
479
+ if (picker.input && (!picker.input.value || picker.input.value.trim() === '')) {
480
+ picker.input.value = formattedDate
481
+ }
482
+ if (!picker.selectedDates || picker.selectedDates.length === 0) {
483
+ picker.selectedDates = [dateObj]
484
+ if (picker.isOpen) {
485
+ picker.jumpToDate(dateObj)
486
+ picker.redraw()
487
+ setTimeout(() => {
488
+ yearChangeHook(picker)
489
+ }, 0)
490
+ }
491
+ }
492
+ }, 0)
493
+ }
494
+ }
495
+
496
+ const originalClear = picker.clear.bind(picker)
497
+ picker.clear = function(...args: any[]) {
498
+ const result = originalClear(...args)
499
+ setTimeout(() => restoreOutOfRangeValue(), 0)
500
+ return result
501
+ }
502
+
503
+ picker.input.addEventListener('input', restoreOutOfRangeValue)
504
+
505
+ picker.config.onClose.push(() => {
506
+ restoreOutOfRangeValue()
507
+ })
508
+ }, 10)
509
+ }
510
+
333
511
  // replace year selector with dropdown
334
512
  picker.yearElements[0].parentElement.innerHTML = `<select class="numInput cur-year" type="number" tabIndex="-1" aria-label="Year" id="year-${pickerId}"></select>`
335
513
 
@@ -392,10 +570,16 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
392
570
  picker.config.onClose.push((selectedDates: string) => {
393
571
  if (selectedDates?.length) {
394
572
  const element = document.querySelector(`#${syncStartWith}`) as any;
395
- // Check if it's a Dropdown QuickPick (has _dropdownRef) or DatePicker QuickPick (has _flatpickr)
573
+ // Check if it's a React Dropdown (has _dropdownRef)
396
574
  if (element?._dropdownRef?.current) {
397
575
  element._dropdownRef.current.clearSelected();
398
- } else {
576
+ }
577
+ // Check if it's a Rails Dropdown (has _pbDropdownInstance)
578
+ else if (element?._pbDropdownInstance) {
579
+ element._pbDropdownInstance.clearSelected();
580
+ }
581
+ // Check if it's a DatePicker QuickPick (has _flatpickr)
582
+ else {
399
583
  const quickpick = element?._flatpickr;
400
584
  quickpick?.clear();
401
585
  }
@@ -408,10 +592,16 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
408
592
  picker.config.onClose.push((selectedDates: string) => {
409
593
  if (selectedDates?.length) {
410
594
  const element = document.querySelector(`#${syncEndWith}`) as any;
411
- // Check if it's a Dropdown QuickPick (has _dropdownRef) or DatePicker QuickPick (has _flatpickr)
595
+ // Check if it's a React Dropdown (has _dropdownRef)
412
596
  if (element?._dropdownRef?.current) {
413
597
  element._dropdownRef.current.clearSelected();
414
- } else {
598
+ }
599
+ // Check if it's a Rails Dropdown (has _pbDropdownInstance)
600
+ else if (element?._pbDropdownInstance) {
601
+ element._pbDropdownInstance.clearSelected();
602
+ }
603
+ // Check if it's a DatePicker QuickPick (has _flatpickr)
604
+ else {
415
605
  const quickpick = element?._flatpickr;
416
606
  quickpick?.clear();
417
607
  }
@@ -1,20 +1,29 @@
1
- <%= pb_rails("date_picker", props: {
2
- mode: "range",
3
- picker_id: "quick-pick-date-range",
4
- placeholder: "Select a Date Range",
5
- selection_type: "quickpick",
6
- controls_end_id: "quick-pick-date-range-end",
7
- controls_start_id: "quick-pick-date-range-start",
1
+ <%= pb_rails("dropdown", props: {
2
+ id: "dropdown-quickpick-with-date-pickers-1",
3
+ label: "Date Range",
4
+ name: "date_range",
5
+ margin_bottom: "sm",
6
+ variant: "quickpick",
7
+ controls_start_id: "start-date-picker-1",
8
+ controls_end_id: "end-date-picker-1",
9
+ start_date_id: "quickpick_start_date_1",
10
+ start_date_name: "start_date",
11
+ end_date_id: "quickpick_end_date_1",
12
+ end_date_name: "end_date"
8
13
  }) %>
9
14
 
10
15
  <%= pb_rails("date_picker", props: {
11
- picker_id: "quick-pick-date-range-start",
12
- placeholder: "Select a Start Date",
13
- sync_start_with:"quick-pick-date-range",
16
+ picker_id: "start-date-picker-1",
17
+ label: "Start Date",
18
+ name: "start_date_picker",
19
+ placeholder: "Select Start Date",
20
+ sync_start_with: "dropdown-quickpick-with-date-pickers-1"
14
21
  }) %>
15
22
 
16
23
  <%= pb_rails("date_picker", props: {
17
- picker_id: "quick-pick-date-range-end",
18
- placeholder: "Select an End Date",
19
- sync_end_with:"quick-pick-date-range",
20
- }) %>
24
+ picker_id: "end-date-picker-1",
25
+ label: "End Date",
26
+ name: "end_date_picker",
27
+ placeholder: "Select End Date",
28
+ sync_end_with: "dropdown-quickpick-with-date-pickers-1"
29
+ }) %>
@@ -1,4 +1,4 @@
1
- You can link a Quickpick DatePicker to standard DatePickers using the following props:
1
+ You can link a Quickpick Dropdown to standard DatePickers using the following props:
2
2
 
3
3
  **For the Quickpick DatePicker**:
4
4
  `controls_start_id`: ID of the DatePicker that should receive the start date.
@@ -51,6 +51,7 @@ const Dialog = (props: DialogProps): React.ReactElement => {
51
51
  cancelButton,
52
52
  confirmButton,
53
53
  className,
54
+ closeable,
54
55
  data = {},
55
56
  htmlOptions = {},
56
57
  id,
@@ -187,7 +188,7 @@ const Dialog = (props: DialogProps): React.ReactElement => {
187
188
  style={{ content: dynamicInlineProps }}
188
189
  >
189
190
  <>
190
- {title && !status ? <Dialog.Header>{title}</Dialog.Header> : null}
191
+ {title && !status ? <Dialog.Header closeable={closeable}>{title}</Dialog.Header> : null}
191
192
  {!status && text ? <Dialog.Body>{text}</Dialog.Body> : null}
192
193
  {status && (
193
194
  <Dialog.Body
@@ -14,7 +14,7 @@
14
14
  >
15
15
  <%= pb_content_tag(:dialog, role: "dialog", "aria-modal": "true", "aria-label": "Dialog") do %>
16
16
  <% if object.status === "" && object.title %>
17
- <%= pb_rails("dialog/dialog_header", props: { title: object.title, id: object.id }) %>
17
+ <%= pb_rails("dialog/dialog_header", props: { title: object.title, id: object.id, closeable: object.closeable }) %>
18
18
  <% end %>
19
19
  <% if object.status === "" && object.text %>
20
20
  <%= pb_rails("dialog/dialog_body", props: { text: object.text }) %>
@@ -23,6 +23,7 @@ module Playbook
23
23
  default: ""
24
24
  prop :custom_event_type, type: Playbook::Props::String,
25
25
  default: ""
26
+ prop :closeable, type: Playbook::Props::Boolean, default: true
26
27
 
27
28
  def classname
28
29
  generate_classname("pb_dialog pb_dialog_rails pb_dialog_#{size}_#{placement}")
@@ -129,3 +129,17 @@ test('renders loading dialog with disabled buttons', async () => {
129
129
 
130
130
  cleanup()
131
131
  })
132
+
133
+ test('renders dialog without close button when closeable is false', async () => {
134
+
135
+ const { queryByText, container } = render(<DialogTest closeable={false} />);
136
+
137
+ fireEvent.click(queryByText('Open Dialog'));
138
+
139
+ await waitFor(() => expect(queryByText("Header Title is the Title Prop")));
140
+
141
+ const closeBtn = container.querySelector('.pb_dialog_close_icon');
142
+ expect(closeBtn).not.toBeInTheDocument();
143
+
144
+ cleanup()
145
+ })
@@ -1,10 +1,11 @@
1
1
  <%= pb_content_tag(:div, class: object.sticky_header) do %>
2
2
  <%= pb_rails("flex", props: {classname:object.classname, spacing:"between", padding:"sm", align:"center"}) do %>
3
3
  <%= content.presence || object.title %>
4
-
5
- <button class="dialog-button-class pb_dialog_close_icon" type="button" data-close-dialog= <%= object.id %> aria-label="Close Dialog" >
6
- <%= pb_rails("icon", props: { custom_icon: Playbook::Engine::root.join(times_icon), aria: { hidden: true } }) %>
7
- </button>
4
+ <% if object.closeable %>
5
+ <button class="dialog-button-class pb_dialog_close_icon" type="button" data-close-dialog= <%= object.id %> aria-label="Close Dialog" >
6
+ <%= pb_rails("icon", props: { custom_icon: Playbook::Engine::root.join(times_icon), aria: { hidden: true } }) %>
7
+ </button>
8
+ <% end %>
8
9
  <% end %>
9
10
  <%= pb_rails("section_separator") %>
10
11
  <% end %>
@@ -4,6 +4,8 @@ module Playbook
4
4
  module PbDialog
5
5
  class DialogHeader < Playbook::KitBase
6
6
  prop :title
7
+ prop :closeable, type: Playbook::Props::Boolean,
8
+ default: true
7
9
 
8
10
  def classname
9
11
  generate_classname("dialog_header")
@@ -0,0 +1,24 @@
1
+ <%= pb_rails("flex", props:{wrap:true}) do %>
2
+ <%= pb_rails("button", props: { text: "Open Simple Dialog", data: {"open-dialog": "dialog-simple"}, margin_right:"md" }) %>
3
+ <%= pb_rails("button", props: { text: "Open Complex Dialog", data: {"open-dialog": "dialog-complex2"} }) %>
4
+ <% end %>
5
+
6
+ <%= pb_rails("dialog", props: {
7
+ id:"dialog-simple",
8
+ size: "sm",
9
+ title: "Header Title is the Title Prop",
10
+ text: "Hello Body Text, Nice to meet ya.",
11
+ cancel_button: "Cancel Button",
12
+ closeable: false,
13
+ confirm_button: "Okay",
14
+ confirm_button_id: "confirm-button-simple"
15
+ }) %>
16
+
17
+ <%= pb_rails("dialog", props: {
18
+ id:"dialog-complex2",
19
+ size: "sm"
20
+ }) do %>
21
+ <%= pb_rails("dialog/dialog_header", props: { id: "dialog-complex2", title:"Header Title inside Dialog Header", closeable: false } ) %>
22
+ <%= pb_rails("dialog/dialog_body", props:{text: "Hello Body Text, Nice to meet ya."}) %>
23
+ <%= pb_rails("dialog/dialog_footer", props: {cancel_button: "Cancel Button", confirm_button: "Okay", confirm_button_id:"confirm-complex2", id: "dialog-complex2"}) %>
24
+ <% end %>
@@ -0,0 +1,60 @@
1
+ import React, { useState } from 'react'
2
+ import Button from '../../pb_button/_button'
3
+ import Dialog from '../../pb_dialog/_dialog'
4
+
5
+
6
+ const DialogCloseable = () => {
7
+ // Simple example
8
+ const [isOpenSimple, setIsOpenSimple] = useState(false)
9
+ const closeSimple = () => setIsOpenSimple(false)
10
+ const openSimple = () => setIsOpenSimple(true)
11
+
12
+ // Complex example
13
+ const [isOpenComplex, setIsOpenComplex] = useState(false)
14
+ const closeComplex = () => setIsOpenComplex(false)
15
+ const openComplex = () => setIsOpenComplex(true)
16
+
17
+ return (
18
+ <>
19
+ <Button
20
+ marginRight='md'
21
+ onClick={openSimple}
22
+ >
23
+ {'Open Simple Dialog'}
24
+ </Button>
25
+ <Button onClick={openComplex}>{'Open Complex Dialog'}</Button>
26
+
27
+ <Dialog
28
+ cancelButton="Cancel Button"
29
+ closeable={false}
30
+ confirmButton="Okay"
31
+ onCancel={closeSimple}
32
+ onClose={closeSimple}
33
+ onConfirm={closeSimple}
34
+ opened={isOpenSimple}
35
+ size="sm"
36
+ text="Hello Body Text, Nice to meet ya."
37
+ title="Header Title is the Title Prop"
38
+ />
39
+ <Dialog
40
+ onClose={closeComplex}
41
+ opened={isOpenComplex}
42
+ size="sm"
43
+ >
44
+ <Dialog.Header closeable={false}>{'Header Title inside Dialog.Header'}</Dialog.Header>
45
+ <Dialog.Body>{'Hello Body Text, Nice to meet ya.'}</Dialog.Body>
46
+ <Dialog.Footer>
47
+ <Button onClick={closeComplex}>{'Okay'}</Button>
48
+ <Button
49
+ onClick={closeComplex}
50
+ variant="link"
51
+ >
52
+ {'Cancel Button'}
53
+ </Button>
54
+ </Dialog.Footer>
55
+ </Dialog>
56
+ </>
57
+ )
58
+ }
59
+
60
+ export default DialogCloseable
@@ -0,0 +1,3 @@
1
+ The `closeable` prop can be set to false to optionally render the Dialog header without the close “X” button. `closeable` is set to true by default.
2
+
3
+ This prop can be used with the simple as well as the complex version of the Dialog as can be seen here.
@@ -0,0 +1,71 @@
1
+ <%
2
+ typeahead_options = [
3
+ {
4
+ label: "United States",
5
+ value: "unitedStates",
6
+ id: "us"
7
+ },
8
+ {
9
+ label: "United Kingdom",
10
+ value: "unitedKingdom",
11
+ id: "gb"
12
+ },
13
+ {
14
+ label: "Canada",
15
+ value: "canada",
16
+ id: "ca"
17
+ },
18
+ {
19
+ label: "Australia",
20
+ value: "australia",
21
+ id: "au"
22
+ },
23
+ {
24
+ label: "Germany",
25
+ value: "germany",
26
+ id: "de"
27
+ },
28
+ {
29
+ label: "France",
30
+ value: "france",
31
+ id: "fr"
32
+ },
33
+ {
34
+ label: "Japan",
35
+ value: "japan",
36
+ id: "jp"
37
+ },
38
+ {
39
+ label: "Brazil",
40
+ value: "brazil",
41
+ id: "br"
42
+ }
43
+ ]
44
+ %>
45
+
46
+ <%= pb_rails("button", props: { text: "Open Dialog with Overflow Visible", data: {"open-dialog": "dialog-overflow-visible"} }) %>
47
+
48
+ <%= pb_rails("dialog", props: {
49
+ id: "dialog-overflow-visible",
50
+ size: "md",
51
+ overflow: "visible"
52
+ }) do %>
53
+ <%= pb_rails("dialog/dialog_header", props: { id: "dialog-overflow-visible" } ) do %>
54
+ <%= pb_rails("body", props: { text: "Select Location" }) %>
55
+ <% end %>
56
+ <%= pb_rails("dialog/dialog_body") do %>
57
+ <%= pb_rails("typeahead", props: {
58
+ options: typeahead_options,
59
+ placeholder: "Select a location...",
60
+ id: "location-typeahead",
61
+ is_multi: false,
62
+ }) %>
63
+ <% end %>
64
+ <%= pb_rails("dialog/dialog_footer", props: {
65
+ cancel_button: "Cancel",
66
+ confirm_button: "Save",
67
+ confirm_button_id: "confirm-overflow-visible",
68
+ id: "dialog-overflow-visible"
69
+ }) %>
70
+ <% end %>
71
+
@@ -0,0 +1,57 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import Body from '../../pb_body/_body'
4
+ import Button from '../../pb_button/_button'
5
+ import Dialog from '../../pb_dialog/_dialog'
6
+ import Typeahead from '../../pb_typeahead/_typeahead'
7
+
8
+ const DialogOverflowVisible = () => {
9
+ const [isOpen, setIsOpen] = useState(false)
10
+ const close = () => setIsOpen(false)
11
+ const open = () => setIsOpen(true)
12
+
13
+ const typeaheadOptions = [
14
+ { label: "United States", value: "unitedStates", id: "us" },
15
+ { label: "United Kingdom", value: "unitedKingdom", id: "gb" },
16
+ { label: "Canada", value: "canada", id: "ca" },
17
+ { label: "Australia", value: "australia", id: "au" },
18
+ { label: "Germany", value: "germany", id: "de" },
19
+ { label: "France", value: "france", id: "fr" },
20
+ { label: "Japan", value: "japan", id: "jp" },
21
+ { label: "Brazil", value: "brazil", id: "br" },
22
+ ]
23
+
24
+ return (
25
+ <>
26
+ <Button onClick={open}>{'Open Dialog with Overflow Visible'}</Button>
27
+ <Dialog
28
+ onClose={close}
29
+ opened={isOpen}
30
+ overflow="visible"
31
+ size="md"
32
+ >
33
+ <Dialog.Header>
34
+ <Body>{'Select Location'}</Body>
35
+ </Dialog.Header>
36
+ <Dialog.Body>
37
+ <Typeahead
38
+ options={typeaheadOptions}
39
+ placeholder="Select a location..."
40
+ />
41
+ </Dialog.Body>
42
+ <Dialog.Footer>
43
+ <Button onClick={close}>{'Save'}</Button>
44
+ <Button
45
+ onClick={close}
46
+ variant="link"
47
+ >
48
+ {'Cancel'}
49
+ </Button>
50
+ </Dialog.Footer>
51
+ </Dialog>
52
+ </>
53
+ )
54
+ }
55
+
56
+ export default DialogOverflowVisible
57
+
@@ -0,0 +1 @@
1
+ Use the `overflow: "visible"` global prop to allow Typeahead dropdown menus to appear outside the dialog boundaries. By default, dialogs clip content that extends beyond their edges, but this prop enables pop-up elements to render above the dialog overlay.
@@ -0,0 +1 @@
1
+ Use the `overflow="visible"` global prop to allow Date Picker and Typeahead dropdown menus to appear outside the dialog boundaries. By default, dialogs clip content that extends beyond their edges, but this prop enables pop-up elements to render above the dialog overlay.
@@ -12,6 +12,8 @@ examples:
12
12
  - dialog_full_height_placement: Full Height Placement
13
13
  - dialog_loading: Loading
14
14
  - dialog_turbo_frames: Within Turbo Frames
15
+ - dialog_overflow_visible: Overflow Visible
16
+ - dialog_closeable: Close Button in Header
15
17
 
16
18
 
17
19
  react:
@@ -25,6 +27,8 @@ examples:
25
27
  - dialog_full_height: Full Height
26
28
  - dialog_full_height_placement: Full Height Placement
27
29
  - dialog_loading: Loading
30
+ - dialog_overflow_visible: Overflow Visible
31
+ - dialog_closeable: Close Button in Header
28
32
 
29
33
  swift:
30
34
  - dialog_default_swift: Simple
@@ -8,4 +8,6 @@ export { default as DialogStatus } from './_dialog_status.jsx'
8
8
  export { default as DialogStackedAlert } from './_dialog_stacked_alert.jsx'
9
9
  export { default as DialogFullHeight } from './_dialog_full_height.jsx'
10
10
  export { default as DialogFullHeightPlacement } from './_dialog_full_height_placement.jsx'
11
- export { default as DialogLoading } from './_dialog_loading.jsx'
11
+ export { default as DialogLoading } from './_dialog_loading.jsx'
12
+ export { default as DialogOverflowVisible } from './_dialog_overflow_visible.jsx'
13
+ export { default as DialogCloseable } from './_dialog_closeable.jsx'
@@ -1 +1 @@
1
- You can customize the order of the colors you would like to use by using the `colors` prop. Only the data and status colors will work for Playbook charts. See the [design page](https://playbook.powerapp.cloud/token/colors) for reference.
1
+ You can customize the order of the colors you would like to use by using the `colors` prop. Only the data and status colors will work for Playbook charts. See the [design page](https://playbook.powerapp.cloud/tokens/colors) for reference.