playbook_ui 15.6.0.pre.rc.4 → 15.7.0.pre.alpha.PLAY2675dropdownquickpickcustomquickpickdates13330

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 (195) 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/_dropdown.tsx +13 -1
  59. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_rails.html.erb +7 -5
  60. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom.jsx +56 -0
  61. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom.md +10 -0
  62. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom_rails.html.erb +64 -0
  63. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom_rails.md +10 -0
  64. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_default_dates.html.erb +19 -0
  65. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_rails.html.erb +12 -0
  66. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_rails.md +26 -0
  67. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_range_end_rails.html.erb +19 -0
  68. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_range_end_rails.md +1 -0
  69. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_default_rails.html.erb +30 -0
  70. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_default_rails.md +3 -0
  71. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_rails.html.erb +29 -0
  72. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_rails.md +13 -0
  73. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +3 -1
  74. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +7 -0
  75. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
  76. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +4 -0
  77. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +44 -5
  78. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +121 -0
  79. data/app/pb_kits/playbook/pb_dropdown/index.js +171 -3
  80. data/app/pb_kits/playbook/pb_dropdown/quickpick/index.ts +85 -9
  81. data/app/pb_kits/playbook/pb_dropdown/quickpick_helper.rb +156 -0
  82. data/app/pb_kits/playbook/pb_filter/Filter/FilterBackground.tsx +3 -3
  83. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +1 -1
  84. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +2 -1
  85. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +14 -0
  86. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.md +3 -0
  87. data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
  88. data/app/pb_kits/playbook/pb_gauge/_gauge.tsx +6 -0
  89. data/app/pb_kits/playbook/pb_line_graph/_line_graph.tsx +6 -0
  90. data/app/pb_kits/playbook/pb_popover/docs/_popover_append_to.html.erb +2 -2
  91. data/app/pb_kits/playbook/pb_popover/docs/_popover_append_to.jsx +3 -2
  92. data/app/pb_kits/playbook/pb_radio/docs/_radio_error.md +1 -1
  93. data/app/pb_kits/playbook/pb_select/_select.tsx +8 -3
  94. data/app/pb_kits/playbook/pb_select/docs/_select_error.md +1 -1
  95. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.html.erb +16 -0
  96. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.jsx +30 -0
  97. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.md +1 -0
  98. data/app/pb_kits/playbook/pb_select/docs/example.yml +2 -0
  99. data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
  100. data/app/pb_kits/playbook/pb_select/select.html.erb +2 -2
  101. data/app/pb_kits/playbook/pb_select/select.rb +3 -1
  102. data/app/pb_kits/playbook/pb_select/select.test.js +23 -0
  103. data/app/pb_kits/playbook/pb_table/_table.tsx +187 -33
  104. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.jsx +134 -0
  105. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.md +34 -0
  106. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.html.erb +101 -0
  107. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.md +33 -0
  108. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.jsx +180 -0
  109. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.md +3 -0
  110. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.html.erb +122 -0
  111. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.md +3 -0
  112. data/app/pb_kits/playbook/pb_table/docs/example.yml +4 -0
  113. data/app/pb_kits/playbook/pb_table/docs/index.js +2 -0
  114. data/app/pb_kits/playbook/pb_table/table.html.erb +68 -12
  115. data/app/pb_kits/playbook/pb_table/table.rb +22 -3
  116. data/app/pb_kits/playbook/pb_table/table.test.js +143 -0
  117. data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +15 -3
  118. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.md +1 -1
  119. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.html.erb +6 -0
  120. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.jsx +25 -0
  121. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.md +3 -0
  122. data/app/pb_kits/playbook/pb_text_input/docs/example.yml +3 -0
  123. data/app/pb_kits/playbook/pb_text_input/docs/index.js +1 -0
  124. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +6 -0
  125. data/app/pb_kits/playbook/pb_text_input/text_input.rb +2 -0
  126. data/app/pb_kits/playbook/pb_text_input/text_input.test.js +16 -0
  127. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.md +1 -1
  128. data/app/pb_kits/playbook/pb_time_picker/_time_picker.scss +296 -0
  129. data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +822 -0
  130. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.html.erb +2 -0
  131. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.jsx +16 -0
  132. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.md +1 -0
  133. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.html.erb +1 -0
  134. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.jsx +13 -0
  135. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.md +1 -0
  136. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.html.erb +4 -0
  137. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.jsx +29 -0
  138. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.md +1 -0
  139. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_disabled.html.erb +13 -0
  140. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_disabled.jsx +23 -0
  141. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_error.html.erb +5 -0
  142. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_error.jsx +15 -0
  143. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_input_options.html.erb +14 -0
  144. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_label.html.erb +2 -0
  145. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_label.jsx +15 -0
  146. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.html.erb +42 -0
  147. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.jsx +52 -0
  148. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.md +1 -0
  149. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +45 -0
  150. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.md +1 -0
  151. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.html.erb +3 -0
  152. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.jsx +21 -0
  153. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.md +1 -0
  154. data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +24 -0
  155. data/app/pb_kits/playbook/pb_time_picker/docs/index.js +9 -0
  156. data/app/pb_kits/playbook/pb_time_picker/index.ts +40 -0
  157. data/app/pb_kits/playbook/pb_time_picker/time_picker.html.erb +1 -0
  158. data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +80 -0
  159. data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +114 -0
  160. data/app/pb_kits/playbook/pb_time_picker/time_picker_helper.ts +662 -0
  161. data/app/pb_kits/playbook/pb_timeline/_item.tsx +3 -0
  162. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.html.erb +60 -0
  163. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.jsx +118 -0
  164. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.md +1 -0
  165. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_date.md +1 -1
  166. data/app/pb_kits/playbook/pb_timeline/docs/example.yml +2 -0
  167. data/app/pb_kits/playbook/pb_timeline/docs/index.js +1 -0
  168. data/app/pb_kits/playbook/pb_timeline/item.html.erb +1 -1
  169. data/app/pb_kits/playbook/pb_timeline/item.rb +2 -0
  170. data/app/pb_kits/playbook/pb_timeline/label.html.erb +2 -1
  171. data/app/pb_kits/playbook/pb_timeline/label.rb +2 -0
  172. data/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx +3 -0
  173. data/app/pb_kits/playbook/pb_timeline/timeline.test.js +51 -0
  174. data/app/pb_kits/playbook/tokens/_colors.scss +2 -1
  175. data/app/pb_kits/playbook/utilities/deprecated.ts +73 -0
  176. data/app/pb_kits/playbook/utilities/globalProps.ts +1 -0
  177. data/dist/chunks/_typeahead-Ckz1ce-2.js +6 -0
  178. data/dist/chunks/lib-DxDBrGZX.js +29 -0
  179. data/dist/chunks/vendor.js +3 -3
  180. data/dist/menu.yml +16 -9
  181. data/dist/playbook-rails-react-bindings.js +1 -1
  182. data/dist/playbook-rails.js +1 -1
  183. data/dist/playbook.css +1 -1
  184. data/lib/playbook/forms/builder/collection_select_field.rb +9 -1
  185. data/lib/playbook/forms/builder/form_field_builder.rb +13 -2
  186. data/lib/playbook/forms/builder/select_field.rb +9 -1
  187. data/lib/playbook/forms/builder/time_picker_field.rb +24 -0
  188. data/lib/playbook/forms/builder/time_zone_select_field.rb +9 -1
  189. data/lib/playbook/forms/builder.rb +1 -0
  190. data/lib/playbook/pb_doc_helper.rb +3 -0
  191. data/lib/playbook/pb_kit_helper.rb +35 -0
  192. data/lib/playbook/version.rb +2 -2
  193. metadata +89 -4
  194. data/dist/chunks/_typeahead-BXM7QUuy.js +0 -6
  195. data/dist/chunks/lib-CgpqUb6l.js +0 -29
@@ -11,8 +11,78 @@ type QuickPickOption = {
11
11
  id?: string;
12
12
  };
13
13
 
14
+ type CustomQuickPickDate = {
15
+ label: string;
16
+ value: string[] | { timePeriod: string; amount: number };
17
+ };
18
+
19
+ type CustomQuickPickDates = {
20
+ override?: boolean;
21
+ dates: CustomQuickPickDate[];
22
+ };
23
+
24
+ // Helper function to calculate date range from timePeriod and amount
25
+ const calculateDateRange = (timePeriod: string, amount: number): Date[] => {
26
+ const endDate = new Date();
27
+ const startDate = new Date();
28
+
29
+ switch (timePeriod) {
30
+ case 'days':
31
+ startDate.setDate(endDate.getDate() - amount);
32
+ break;
33
+ case 'weeks':
34
+ startDate.setDate(endDate.getDate() - (amount * 7));
35
+ break;
36
+ case 'months':
37
+ startDate.setMonth(endDate.getMonth() - amount);
38
+ break;
39
+ case 'quarters':
40
+ startDate.setMonth(endDate.getMonth() - (amount * 3));
41
+ break;
42
+ case 'years':
43
+ startDate.setFullYear(endDate.getFullYear() - amount);
44
+ break;
45
+ default:
46
+ throw new Error('Invalid time period');
47
+ }
48
+ return [startDate, endDate];
49
+ };
50
+
51
+ // Helper to format date for display
52
+ const formatDate = (date: Date) => {
53
+ const day = String(date.getDate()).padStart(2, "0");
54
+ const month = String(date.getMonth() + 1).padStart(2, "0");
55
+ const year = date.getFullYear();
56
+
57
+ return `${month}/${day}/${year}`;
58
+ };
59
+
60
+ // Helper to create a QuickPickOption from a custom date
61
+ const createOptionFromCustomDate = (customDate: CustomQuickPickDate): QuickPickOption => {
62
+ let dateRange: Date[];
63
+
64
+ if (Array.isArray(customDate.value)) {
65
+ // Explicit date range as string array
66
+ dateRange = customDate.value.map((dateStr: string) => new Date(dateStr));
67
+ } else {
68
+ // Calculate from timePeriod and amount
69
+ dateRange = calculateDateRange(
70
+ customDate.value.timePeriod,
71
+ customDate.value.amount
72
+ );
73
+ }
74
+
75
+ return {
76
+ label: customDate.label,
77
+ value: dateRange,
78
+ formattedStartDate: formatDate(dateRange[0]),
79
+ formattedEndDate: formatDate(dateRange[1]),
80
+ id: `quickpick-${customDate.label.toLowerCase().replace(/\s+/g, '-')}`,
81
+ };
82
+ };
83
+
14
84
  // Helper to get QuickPick options with date ranges
15
- const getQuickPickOptions = (rangeEndsToday = false): QuickPickOption[] => {
85
+ const getQuickPickOptions = (rangeEndsToday = false, customQuickPickDates?: CustomQuickPickDates): QuickPickOption[] => {
16
86
  const today = new Date();
17
87
  const yesterday = DateTime.getYesterdayDate(new Date());
18
88
 
@@ -36,14 +106,7 @@ const getQuickPickOptions = (rangeEndsToday = false): QuickPickOption[] => {
36
106
  const lastYearStartDate = DateTime.getPreviousYearStartDate(new Date());
37
107
  const lastYearEndDate = DateTime.getPreviousYearEndDate(new Date());
38
108
 
39
- const formatDate = (date: Date) => {
40
- const day = String(date.getDate()).padStart(2, "0")
41
- const month = String(date.getMonth() + 1).padStart(2, "0")
42
- const year = date.getFullYear()
43
-
44
- return `${month}/${day}/${year}`
45
- }
46
- return [
109
+ const defaultOptions: QuickPickOption[] = [
47
110
  { label: 'Today', value: [today, today], id: 'quickpick-today' },
48
111
  { label: 'Yesterday', value: [yesterday, yesterday], formattedStartDate: `${formatDate(yesterday)}`, formattedEndDate: `${formatDate(yesterday)}`, id: 'quickpick-yesterday' },
49
112
  { label: 'This Week', value: [thisWeekStartDate, thisWeekEndDate], formattedStartDate: `${formatDate(thisWeekStartDate)}`, formattedEndDate: `${formatDate(thisWeekEndDate)}`, id: 'quickpick-this-week' },
@@ -55,6 +118,19 @@ const getQuickPickOptions = (rangeEndsToday = false): QuickPickOption[] => {
55
118
  { label: 'Last Quarter', value: [lastQuarterStartDate, lastQuarterEndDate], formattedStartDate: `${formatDate(lastQuarterStartDate)}`, formattedEndDate: `${formatDate(lastQuarterEndDate)}`, id: 'quickpick-last-quarter' },
56
119
  { label: 'Last Year', value: [lastYearStartDate, lastYearEndDate], formattedStartDate: `${formatDate(lastYearStartDate)}`, formattedEndDate: `${formatDate(lastYearEndDate)}`, id: 'quickpick-last-year' },
57
120
  ];
121
+
122
+ // Handle customQuickPickDates and override logic
123
+ if (customQuickPickDates && customQuickPickDates.dates && customQuickPickDates.dates.length > 0) {
124
+ const customOptions = customQuickPickDates.dates.map(createOptionFromCustomDate);
125
+
126
+ if (customQuickPickDates.override === false) {
127
+ return [...defaultOptions, ...customOptions];
128
+ }
129
+
130
+ return customOptions;
131
+ }
132
+
133
+ return defaultOptions;
58
134
  };
59
135
 
60
136
  export default getQuickPickOptions
@@ -0,0 +1,156 @@
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, custom_quick_pick_dates: {})
8
+ default_options = build_default_options(range_ends_today)
9
+
10
+ # Handle custom_quick_pick_dates
11
+ return default_options if custom_quick_pick_dates.blank?
12
+
13
+ dates = custom_quick_pick_dates[:dates] || custom_quick_pick_dates["dates"]
14
+ return default_options if dates.blank?
15
+
16
+ custom_options = dates.map { |date_config| build_custom_option(date_config) }
17
+
18
+ # Override logic
19
+ override = custom_quick_pick_dates[:override]
20
+ override = custom_quick_pick_dates["override"] if override.nil?
21
+
22
+ if override == false
23
+ default_options + custom_options
24
+ else
25
+ custom_options
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def build_default_options(range_ends_today)
32
+ today = Date.today
33
+ yesterday = today - 1.day
34
+
35
+ this_week_start_date = first_day_of_week(today)
36
+ this_week_end_date = range_ends_today ? today : last_day_of_week(today)
37
+ last_week_start_date = previous_week_start_date(today)
38
+ last_week_end_date = previous_week_end_date(today)
39
+
40
+ this_month_start_date = today.beginning_of_month
41
+ this_month_end_date = range_ends_today ? today : today.end_of_month
42
+ last_month_start_date = (today - 1.month).beginning_of_month
43
+ last_month_end_date = (today - 1.month).end_of_month
44
+
45
+ this_quarter_start_date = today.beginning_of_quarter
46
+ this_quarter_end_date = range_ends_today ? today : today.end_of_quarter
47
+ last_quarter_start_date = (today - 3.months).beginning_of_quarter
48
+ last_quarter_end_date = (today - 3.months).end_of_quarter
49
+
50
+ this_year_start_date = today.beginning_of_year
51
+ this_year_end_date = range_ends_today ? today : today.end_of_year
52
+ last_year_start_date = (today - 1.year).beginning_of_year
53
+ last_year_end_date = (today - 1.year).end_of_year
54
+
55
+ [
56
+ { id: "quickpick-today", label: "Today", value: [today.to_s, today.to_s], formatted_start_date: format_date(today), formatted_end_date: format_date(today) },
57
+ { id: "quickpick-yesterday", label: "Yesterday", value: [yesterday.to_s, yesterday.to_s], formatted_start_date: format_date(yesterday), formatted_end_date: format_date(yesterday) },
58
+ { 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) },
59
+ { 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) },
60
+ { 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) },
61
+ { 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) },
62
+ { 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) },
63
+ { 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) },
64
+ { 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) },
65
+ { 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) },
66
+ ]
67
+ end
68
+
69
+ def build_custom_option(date_config)
70
+ label = date_config[:label] || date_config["label"]
71
+ value = date_config[:value] || date_config["value"]
72
+
73
+ date_range = calculate_date_range(value)
74
+ start_date = date_range[0]
75
+ end_date = date_range[1]
76
+
77
+ {
78
+ id: "quickpick-#{label.downcase.gsub(/\s+/, '-')}",
79
+ label: label,
80
+ value: [start_date.to_s, end_date.to_s],
81
+ formatted_start_date: format_date(start_date),
82
+ formatted_end_date: format_date(end_date),
83
+ }
84
+ end
85
+
86
+ def calculate_date_range(value)
87
+ # Parse date strings if value is an array
88
+ if value.is_a?(Array)
89
+ [parse_date_string(value[0]), parse_date_string(value[1])]
90
+ else
91
+ # Calculate date range from time_period and amount
92
+ time_period = value[:time_period] || value["time_period"] || value[:timePeriod] || value["timePeriod"]
93
+ amount = value[:amount] || value["amount"]
94
+
95
+ end_date = Date.today
96
+ start_date = calculate_start_date(time_period, amount, end_date)
97
+
98
+ [start_date, end_date]
99
+ end
100
+ end
101
+
102
+ def parse_date_string(date_str)
103
+ # Handle US date format (MM/DD/YYYY) because Ruby's Date.parse defaults to European format (DD/MM/YYYY)
104
+ if date_str.include?("/")
105
+ Date.strptime(date_str, "%m/%d/%Y")
106
+ else
107
+ Date.parse(date_str)
108
+ end
109
+ end
110
+
111
+ def calculate_start_date(time_period, amount, end_date)
112
+ case time_period.to_s
113
+ when "days"
114
+ end_date - amount.days
115
+ when "weeks"
116
+ end_date - amount.weeks
117
+ when "months"
118
+ end_date - amount.months
119
+ when "quarters"
120
+ end_date - (amount * 3).months
121
+ when "years"
122
+ end_date - amount.years
123
+ else
124
+ raise ArgumentError, "Invalid time period: #{time_period}"
125
+ end
126
+ end
127
+
128
+ def format_date(date)
129
+ date.strftime("%m/%d/%Y")
130
+ end
131
+
132
+ def format_date_range(start_date, end_date)
133
+ "#{format_date(start_date)} - #{format_date(end_date)}"
134
+ end
135
+
136
+ def first_day_of_week(date)
137
+ # Monday as first day of week
138
+ date.beginning_of_week(:monday)
139
+ end
140
+
141
+ def last_day_of_week(date)
142
+ # Sunday as last day of week
143
+ date.end_of_week(:monday)
144
+ end
145
+
146
+ def previous_week_start_date(date)
147
+ first_day_of_week(date) - 1.week
148
+ end
149
+
150
+ def previous_week_end_date(date)
151
+ last_day_of_week(date) - 1.week
152
+ end
153
+ end
154
+ end
155
+ end
156
+ 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={{
@@ -39,8 +39,8 @@
39
39
  tooltip_id: "append-to-tooltip-2",
40
40
  offset: true,
41
41
  position: "top",
42
- append_to: ".kit-show-wrapper",
42
+ append_to: ".pb--page--sideNav",
43
43
  }) do %>
44
- I'm a popover. I have been appended to the .kit-show-wrapper.
44
+ I'm a popover. I have been appended to the .pb--page--sideNav.
45
45
  <% end %>
46
46
  <% end %>
@@ -54,14 +54,15 @@ const PopoverAppendTo = (props) => {
54
54
  <Body text="Click info for more details" />
55
55
  &nbsp;
56
56
  <PbReactPopover
57
- appendTo=".kit-show-wrapper"
57
+ appendTo=".pb--page--sideNav"
58
58
  offset
59
59
  placement="top"
60
60
  reference={selectorPopoverReference}
61
61
  show={showSelectorPopover}
62
+ zIndex={10}
62
63
  {...props}
63
64
  >
64
- {'I\'m a popover. I have been appended to the .kit-show-wrapper.'}
65
+ {'I\'m a popover. I have been appended to the .pb--page--sideNav.'}
65
66
  </PbReactPopover>
66
67
  </Flex>
67
68
  </>
@@ -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
@@ -65,4 +65,27 @@ test('returns multiple variant', () => {
65
65
  const selectElement = kit.querySelector('select');
66
66
 
67
67
  expect(selectElement).toHaveAttribute('multiple', '');
68
+ });
69
+
70
+ test('inputOptions are passed to select element', () => {
71
+ render(
72
+ <Select
73
+ data={{ testid: testId }}
74
+ inputOptions={{
75
+ id: 'custom-select-id',
76
+ className: 'custom-select-class',
77
+ 'aria-label': 'Custom aria label',
78
+ }}
79
+ label="Favorite Food"
80
+ name="food"
81
+ options={options}
82
+ />
83
+ )
84
+
85
+ const kit = screen.getByTestId(testId)
86
+ const selectElement = kit.querySelector('select')
87
+
88
+ expect(selectElement).toHaveAttribute('id', 'custom-select-id')
89
+ expect(selectElement).toHaveClass('custom-select-class')
90
+ expect(selectElement).toHaveAttribute('aria-label', 'Custom aria label')
68
91
  });