playbook_ui 16.7.0 → 16.8.0.pre.alpha.PLAY2935formbuilderrequiredindicatorbug16780

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +2 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +2 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +5 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +24 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/Utilities/ColumnLayoutHelper.ts +138 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +144 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.jsx +1 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.md +6 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_width.jsx +57 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_width.md +66 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_enable_toggle_expansion_rails.html.erb +62 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_enable_toggle_expansion_rails.md +7 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header.jsx +12 -4
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header.md +4 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header_rails.html.erb +16 -2
  17. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header_rails.md +4 -0
  18. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_scroll_limitation.jsx +68 -0
  19. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_scroll_limitation.md +7 -0
  20. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header.html.erb +16 -2
  21. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header.jsx +12 -5
  22. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header_rails.md +4 -0
  23. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header_react.md +5 -3
  24. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.json +180 -5839
  25. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.overrides.json +5 -30
  26. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_column_definitions_styling.json +4 -1
  27. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +3 -0
  28. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -0
  29. data/app/pb_kits/playbook/pb_card/_card.tsx +1 -1
  30. data/app/pb_kits/playbook/pb_card/card.html.erb +1 -1
  31. data/app/pb_kits/playbook/pb_currency/_currency.tsx +9 -6
  32. data/app/pb_kits/playbook/pb_currency/currency.rb +5 -10
  33. data/app/pb_kits/playbook/pb_currency/currency.test.js +44 -1
  34. data/app/pb_kits/playbook/pb_date/docs/_playground.json +13 -17
  35. data/app/pb_kits/playbook/pb_date/docs/_playground.overrides.json +13 -16
  36. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +3 -2
  37. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +38 -23
  38. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +2 -1
  39. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +31 -0
  40. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_dialog_submission.jsx +2 -2
  41. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.html.erb +0 -2
  42. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.jsx +0 -2
  43. data/app/pb_kits/playbook/pb_date_picker/docs/_playground.json +136 -42
  44. data/app/pb_kits/playbook/pb_date_picker/docs/_playground.overrides.json +113 -45
  45. data/app/pb_kits/playbook/pb_date_range_inline/docs/_playground.json +48 -6
  46. data/app/pb_kits/playbook/pb_date_range_inline/docs/_playground.overrides.json +57 -0
  47. data/app/pb_kits/playbook/pb_date_range_stacked/docs/_playground.json +28 -5
  48. data/app/pb_kits/playbook/pb_date_range_stacked/docs/_playground.overrides.json +38 -0
  49. data/app/pb_kits/playbook/pb_date_stacked/docs/_playground.json +1 -1
  50. data/app/pb_kits/playbook/pb_date_stacked/docs/_playground.overrides.json +1 -1
  51. data/app/pb_kits/playbook/pb_date_time/docs/_playground.json +16 -3
  52. data/app/pb_kits/playbook/pb_date_time/docs/_playground.overrides.json +16 -3
  53. data/app/pb_kits/playbook/pb_date_time_stacked/docs/_playground.json +11 -15
  54. data/app/pb_kits/playbook/pb_date_time_stacked/docs/_playground.overrides.json +11 -15
  55. data/app/pb_kits/playbook/pb_date_year_stacked/docs/_playground.json +4 -4
  56. data/app/pb_kits/playbook/pb_date_year_stacked/docs/_playground.overrides.json +4 -4
  57. data/app/pb_kits/playbook/pb_detail/docs/_playground.json +12 -18
  58. data/app/pb_kits/playbook/pb_detail/docs/_playground.overrides.json +13 -12
  59. data/app/pb_kits/playbook/pb_dialog/docs/_playground.json +108 -42
  60. data/app/pb_kits/playbook/pb_dialog/docs/_playground.overrides.json +88 -40
  61. data/app/pb_kits/playbook/pb_distribution_bar/docs/_playground.json +65 -7
  62. data/app/pb_kits/playbook/pb_distribution_bar/docs/_playground.overrides.json +45 -0
  63. data/app/pb_kits/playbook/pb_draggable/_draggable.scss +19 -0
  64. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_rails.md +2 -0
  65. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_react.md +1 -0
  66. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_rails.md +2 -0
  67. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_react.md +3 -1
  68. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_rails.md +3 -1
  69. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_react.md +3 -1
  70. data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +16 -0
  71. data/app/pb_kits/playbook/pb_draggable/draggable_container.html.erb +3 -1
  72. data/app/pb_kits/playbook/pb_draggable/draggable_item.html.erb +1 -0
  73. data/app/pb_kits/playbook/pb_draggable/index.js +149 -7
  74. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableContainer.tsx +1 -0
  75. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableItem.tsx +67 -1
  76. data/app/pb_kits/playbook/pb_draggable/touchDrag.test.js +38 -0
  77. data/app/pb_kits/playbook/pb_draggable/utilities/touchDrag.ts +173 -0
  78. data/app/pb_kits/playbook/pb_dropdown/docs/_playground.json +318 -21
  79. data/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json +192 -19
  80. data/app/pb_kits/playbook/pb_empty_state/docs/_playground.json +77 -12
  81. data/app/pb_kits/playbook/pb_empty_state/docs/_playground.overrides.json +79 -0
  82. data/app/pb_kits/playbook/pb_file_upload/docs/_playground.json +98 -13
  83. data/app/pb_kits/playbook/pb_file_upload/docs/_playground.overrides.json +99 -0
  84. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +20 -19
  85. data/app/pb_kits/playbook/pb_icon/_icon.scss +2 -1
  86. data/app/pb_kits/playbook/pb_icon/docs/example.yml +0 -2
  87. data/app/pb_kits/playbook/pb_icon/docs/index.js +0 -1
  88. data/app/pb_kits/playbook/pb_link/docs/_playground.json +81 -40
  89. data/app/pb_kits/playbook/pb_link/docs/_playground.overrides.json +88 -30
  90. data/app/pb_kits/playbook/pb_list/_list_item.tsx +4 -1
  91. data/app/pb_kits/playbook/pb_list/item.html.erb +1 -1
  92. data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_centered_data.html.erb +90 -0
  93. data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_centered_data.jsx +100 -0
  94. data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_centered_data.md +1 -0
  95. data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_default.jsx +1 -1
  96. data/app/pb_kits/playbook/pb_pb_circle_chart/docs/example.yml +2 -0
  97. data/app/pb_kits/playbook/pb_pb_circle_chart/docs/index.js +2 -1
  98. data/app/pb_kits/playbook/pb_phone_number_input/docs/_playground.json +4 -2
  99. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +1 -1
  100. data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_styles.scss +262 -43
  101. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.html.erb +1 -0
  102. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.md +12 -0
  103. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_simple.html.erb +9 -0
  104. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_simple.md +8 -0
  105. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +2 -0
  106. data/app/pb_kits/playbook/pb_rich_text_editor/kit.schema.json +18 -9
  107. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +162 -0
  108. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +71 -0
  109. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_rails.js +202 -0
  110. data/app/pb_kits/playbook/pb_table/docs/_table_sticky.html.erb +85 -83
  111. data/app/pb_kits/playbook/pb_table/docs/_table_sticky.jsx +88 -86
  112. data/app/pb_kits/playbook/pb_table/docs/_table_sticky.md +3 -1
  113. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_external_filter_rails.md +1 -1
  114. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +37 -0
  115. data/app/pb_kits/playbook/pb_title/docs/_playground.json +72 -23
  116. data/app/pb_kits/playbook/pb_title/docs/_playground.overrides.json +80 -16
  117. data/app/pb_kits/playbook/pb_tooltip/_tooltip.scss +133 -102
  118. data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +54 -41
  119. data/app/pb_kits/playbook/pb_tooltip/tooltip.test.jsx +60 -2
  120. data/dist/chunks/{_pb_line_graph-CIWJe3Gr.js → _pb_line_graph-BgsTI0CL.js} +1 -1
  121. data/dist/chunks/_typeahead-DA__Kgp5.js +5 -0
  122. data/dist/chunks/{globalProps-CqO4Tko1.js → globalProps-DOB47YGB.js} +1 -1
  123. data/dist/chunks/{lib-czQnE40X.js → lib-BzglXly2.js} +2 -2
  124. data/dist/chunks/vendor.js +4 -4
  125. data/dist/menu.yml +71 -132
  126. data/dist/playbook-rails-react-bindings.js +1 -1
  127. data/dist/playbook-rails.js +1 -1
  128. data/dist/playbook.css +1 -1
  129. data/lib/playbook/forms/builder/form_field_builder.rb +2 -0
  130. data/lib/playbook/version.rb +2 -2
  131. metadata +31 -10
  132. data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.html.erb +0 -1
  133. data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.jsx +0 -21
  134. data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.md +0 -7
  135. data/dist/chunks/_typeahead-B_Ac4z84.js +0 -1
@@ -34,11 +34,6 @@
34
34
  "columnDefinitionsFile": "advanced_table_column_definitions_standard.json",
35
35
  "tableDataFile": "advanced_table_mock_data_no_subrows.json"
36
36
  },
37
- "pagination": {
38
- "label": "Pagination Example",
39
- "columnDefinitionsFile": "advanced_table_column_definitions_standard.json",
40
- "tableDataFile": "advanced_table_pagination_mock_data.json"
41
- },
42
37
  "with_ids": {
43
38
  "label": "Data with row IDs",
44
39
  "columnDefinitionsFile": "advanced_table_column_definitions_standard.json",
@@ -80,15 +75,15 @@
80
75
  "sortControl",
81
76
  "fullScreenControl",
82
77
  "expandedControl",
83
- "allowFullScreen"
78
+ "allowFullScreen",
79
+ "pagination",
80
+ "paginationProps"
84
81
  ],
85
82
  "requiredProps": {
86
83
  "columnDefinitionsFile": "advanced_table_column_definitions_standard.json",
87
84
  "tableDataFile": "advanced_table_mock_data.json"
88
85
  },
89
86
  "propSyncOnEnable": {
90
- "pagination": { "dataPreset": "pagination" },
91
- "paginationProps": { "dataPreset": "pagination" },
92
87
  "inlineRowLoading": { "dataPreset": "inline_loading" },
93
88
  "selectableRows": { "dataPreset": "with_ids" },
94
89
  "onRowSelectionChange": { "dataPreset": "with_ids" },
@@ -144,10 +139,8 @@
144
139
  ]
145
140
  },
146
141
  {
147
- "name": "Pagination & Loading",
142
+ "name": "Loading",
148
143
  "props": [
149
- "pagination",
150
- "paginationProps",
151
144
  "loading",
152
145
  "inlineRowLoading",
153
146
  "initialLoadingRowsCount"
@@ -215,12 +208,6 @@
215
208
  "enableSorting": true
216
209
  }
217
210
  },
218
- {
219
- "name": "With Pagination",
220
- "props": {
221
- "pagination": true
222
- }
223
- },
224
211
  {
225
212
  "name": "Selectable Rows",
226
213
  "props": {
@@ -260,11 +247,6 @@
260
247
  }
261
248
  ],
262
249
  "conditionals": {
263
- "paginationProps": {
264
- "requires": {
265
- "pagination": true
266
- }
267
- },
268
250
  "initialLoadingRowsCount": {
269
251
  "requires": {
270
252
  "loading": true
@@ -350,13 +332,6 @@
350
332
  "message": "Header sorting UI is only applied in \"Subcomponent Structure\" mode (explicit Header and Body). For sorting by column, pass columnDefinitions with enableSort: true to the columnDefinitions array. Click 'sorting by column' to see an example.",
351
333
  "type": "info"
352
334
  },
353
- "pagination_info": {
354
- "when": {
355
- "pagination": true
356
- },
357
- "message": "Use paginationProps to customize page size and appearance.",
358
- "type": "info"
359
- },
360
335
  "selectable_info": {
361
336
  "when": {
362
337
  "selectableRows": true
@@ -396,7 +371,7 @@
396
371
  },
397
372
  "column_styling_info": {
398
373
  "presetName": "Column Styling",
399
- "message": "This sample uses columnStyling on column definitions. You can style headers and cells for the whole table or per column.",
374
+ "message": "This sample uses columnStyling on column definitions. Year uses width for a fixed hierarchy column; other columns show background colors. See the Column Styling Width doc for minWidth and min/pref/max bands.",
400
375
  "type": "info"
401
376
  }
402
377
  }
@@ -2,7 +2,10 @@
2
2
  {
3
3
  "accessor": "year",
4
4
  "label": "Year",
5
- "cellAccessors": ["quarter", "month", "day"]
5
+ "cellAccessors": ["quarter", "month", "day"],
6
+ "columnStyling": {
7
+ "width": 124
8
+ }
6
9
  },
7
10
  {
8
11
  "accessor": "newEnrollments",
@@ -3,6 +3,7 @@ examples:
3
3
  - advanced_table_beta: Default (Required Props)
4
4
  - advanced_table_loading: Loading State
5
5
  - advanced_table_beta_subrow_headers: SubRow Headers
6
+ - advanced_table_enable_toggle_expansion_rails: Enable Toggle Expansion
6
7
  - advanced_table_collapsible_trail_rails: Collapsible Trail
7
8
  - advanced_table_table_props: Table Props
8
9
  - advanced_table_sticky_header_rails: Sticky Header
@@ -52,6 +53,7 @@ examples:
52
53
  - advanced_table_table_props_sticky_header: Sticky Header for Responsive Table
53
54
  - advanced_table_sticky_columns: Sticky Columns
54
55
  - advanced_table_sticky_columns_and_header: Sticky Columns with Sticky Header
56
+ - advanced_table_sticky_scroll_limitation: Sticky Header and Column Scroll Limitation
55
57
  - advanced_table_responsive: Responsive Tables
56
58
  - advanced_table_custom_cell: Custom Components for Cells
57
59
  - advanced_table_with_custom_header: Custom Header Cell
@@ -80,6 +82,7 @@ examples:
80
82
  - advanced_table_row_styling: Row Styling
81
83
  - advanced_table_padding_control_per_row: Padding Control using Row Styling
82
84
  - advanced_table_column_styling: Column Styling
85
+ - advanced_table_column_styling_width: Column Styling Width
83
86
  - advanced_table_column_styling_column_headers: Column Styling with Multiple Headers
84
87
  - advanced_table_column_styling_background: Column Styling Background Color
85
88
  - advanced_table_column_styling_background_custom: Column Styling Background Color (Custom)
@@ -27,6 +27,7 @@ export { default as AdvancedTableFullscreen } from './_advanced_table_fullscreen
27
27
  export { default as AdvancedTableStickyColumns } from './_advanced_table_sticky_columns.jsx'
28
28
  export { default as AdvancedTableStickyHeader } from './_advanced_table_sticky_header.jsx'
29
29
  export { default as AdvancedTableStickyColumnsAndHeader } from './_advanced_table_sticky_columns_and_header.jsx'
30
+ export { default as AdvancedTableStickyScrollLimitation } from './_advanced_table_sticky_scroll_limitation.jsx'
30
31
  export { default as AdvancedTableExpandByDepth } from './_advanced_table_expand_by_depth.jsx'
31
32
  export { default as AdvancedTableColumnBorderColor} from './_advanced_table_column_border_color.jsx'
32
33
  export { default as AdvancedTableColumnVisibility } from './_advanced_table_column_visibility.jsx'
@@ -37,6 +38,7 @@ export { default as AdvancedTablePinnedRows } from './_advanced_table_pinned_row
37
38
  export { default as AdvancedTableScrollbarNone} from './_advanced_table_scrollbar_none.jsx'
38
39
  export { default as AdvancedTableRowStyling } from './_advanced_table_row_styling.jsx'
39
40
  export { default as AdvancedTableColumnStyling } from './_advanced_table_column_styling.jsx'
41
+ export { default as AdvancedTableColumnStylingWidth } from './_advanced_table_column_styling_width.jsx'
40
42
  export { default as AdvancedTableColumnStylingColumnHeaders } from './_advanced_table_column_styling_column_headers.jsx'
41
43
  export { default as AdvancedTableInfiniteScroll} from './_advanced_table_infinite_scroll.jsx'
42
44
  export {default as AdvancedTableWithCustomHeader} from './_advanced_table_with_custom_header.jsx'
@@ -162,7 +162,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {
162
162
  {
163
163
  dragHandle ? (
164
164
  <Flex>
165
- <span className="card_draggable_handle">
165
+ <span className="card_draggable_handle pb_draggable_handle">
166
166
  <Icon
167
167
  icon="grip-dots-vertical"
168
168
  paddingRight="xs"
@@ -4,7 +4,7 @@
4
4
  <% if object.draggable_item %>
5
5
  <%= pb_rails("flex", props: { align: "center" }) do %>
6
6
  <% if object.drag_handle %>
7
- <span classname="card_draggable_handle">
7
+ <span class="card_draggable_handle pb_draggable_handle">
8
8
  <%= pb_rails("icon", props: { icon: "grip-dots-vertical", padding_right: "xs" }) %>
9
9
  </span>
10
10
  <% end %>
@@ -59,12 +59,13 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
59
59
  commaSeparator = false,
60
60
  } = props
61
61
 
62
+ const isAmountEmpty = (value: string | number): boolean => (
63
+ value === '' || value == null || (typeof value === 'string' && value.trim() === '')
64
+ )
65
+
62
66
  // Convert numeric input to string format
63
67
  const convertAmount = (input: string | number): string => {
64
68
  if (typeof input === 'number') {
65
- if (input === 0 && !nullDisplay) {
66
- return ""
67
- }
68
69
  return input.toFixed(2)
69
70
  }
70
71
  return input
@@ -81,7 +82,9 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
81
82
  variantClass = '_bold'
82
83
  }
83
84
 
84
- const [whole, decimal = '00'] = currencyAmount.split('.')
85
+ const [wholePart, decimalPart = '00'] = currencyAmount ? currencyAmount.split('.') : ['', '00']
86
+ const decimal = (decimalPart || '00').padEnd(2, '0').slice(0, 2)
87
+ const whole = currencyAmount === '' ? '' : (wholePart === '' ? '0' : wholePart)
85
88
  const ariaProps = buildAriaProps(aria)
86
89
  const dataProps = buildDataProps(data)
87
90
  const htmlProps = buildHtmlProps(htmlOptions)
@@ -135,7 +138,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
135
138
  <Caption dark={dark}>{label}</Caption>
136
139
  <div className={`pb_currency_wrapper${variantClass || emphasizedClass}`}>
137
140
  {unstyled ? (
138
- nullDisplay && !amount ? (
141
+ nullDisplay && isAmountEmpty(amount) ? (
139
142
  <div>{nullDisplay}</div>
140
143
  ) : (
141
144
  <>
@@ -148,7 +151,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
148
151
  </>
149
152
  )
150
153
  ) : (
151
- nullDisplay && !amount ? (
154
+ nullDisplay && isAmountEmpty(amount) ? (
152
155
  <Title
153
156
  className="pb_currency_value"
154
157
  dark={dark}
@@ -68,7 +68,7 @@ module Playbook
68
68
  end
69
69
 
70
70
  def title_text
71
- if null_display
71
+ if null_display && amount.blank?
72
72
  null_display
73
73
  elsif swap_negative
74
74
  absolute_amount(abbr_or_format_amount)
@@ -125,14 +125,7 @@ module Playbook
125
125
  # Convert numeric input to string format
126
126
  def convert_amount(input)
127
127
  if input.is_a?(Numeric)
128
- if input.zero? && null_display.nil?
129
- ""
130
- else
131
- format("%.2f", input)
132
- end
133
- # Handle string representations of zero
134
- elsif input.to_s.strip.match?(/^-?0+(\.0+)?$/) && null_display.nil?
135
- ""
128
+ Kernel.format("%.2f", input)
136
129
  else
137
130
  input.to_s
138
131
  end
@@ -142,6 +135,7 @@ module Playbook
142
135
  return "" if currency_amount.blank?
143
136
 
144
137
  value = currency_amount.split(".").first
138
+ value = "0" if value.blank?
145
139
  if comma_separator
146
140
  number_with_delimiter(value.gsub(",", ""))
147
141
  else
@@ -152,7 +146,8 @@ module Playbook
152
146
  def decimal_value
153
147
  return "00" if currency_amount.blank?
154
148
 
155
- currency_amount.split(".")[1] || "00"
149
+ fraction = currency_amount.split(".")[1] || "00"
150
+ fraction.ljust(2, "0")[0, 2]
156
151
  end
157
152
 
158
153
  def units_element
@@ -178,5 +178,48 @@ test('handles numeric amounts correctly', () => {
178
178
  expect(screen.getByTestId('test-numeric-no-symbol')).toHaveTextContent('400.50')
179
179
  expect(screen.getByTestId('test-numeric-medium-size')).toHaveTextContent('$500.55')
180
180
  expect(screen.getByTestId('test-numeric-negative')).toHaveTextContent('-$600.70')
181
- expect(screen.getByTestId('test-numeric-null')).toHaveTextContent('$.00')
181
+ expect(screen.getByTestId('test-numeric-null')).toHaveTextContent('$0.00')
182
+ })
183
+
184
+ test('renders zero values with leading digit', () => {
185
+ render(
186
+ <>
187
+ <Currency
188
+ amount="0"
189
+ data={{ testid: 'test-string-zero' }}
190
+ />
191
+ <Currency
192
+ amount={0}
193
+ data={{ testid: 'test-numeric-zero' }}
194
+ />
195
+ </>
196
+ )
197
+
198
+ expect(screen.getByTestId('test-string-zero')).toHaveTextContent('$0.00')
199
+ expect(screen.getByTestId('test-numeric-zero')).toHaveTextContent('$0.00')
200
+ })
201
+
202
+ test('normalizes string zero with single decimal digit to .00', () => {
203
+ render(
204
+ <Currency
205
+ amount="0.0"
206
+ commaSeparator
207
+ data={{ testid: 'test-string-zero-single-decimal' }}
208
+ />
209
+ )
210
+
211
+ expect(screen.getByTestId('test-string-zero-single-decimal')).toHaveTextContent('$0.00')
212
+ })
213
+
214
+ test('nullDisplay only applies when amount is empty', () => {
215
+ render(
216
+ <Currency
217
+ amount="-.53"
218
+ data={{ testid: 'test-null-display-negative' }}
219
+ nullDisplay="$0.00"
220
+ />
221
+ )
222
+
223
+ expect(screen.getByTestId('test-null-display-negative')).toHaveTextContent('-$')
224
+ expect(screen.getByTestId('test-null-display-negative')).not.toHaveTextContent('$0.00')
182
225
  })
@@ -1,5 +1,5 @@
1
1
  {
2
- "template": "<Date{{props}} />",
2
+ "template": "<FormattedDate{{props}} />",
3
3
  "propTargets": {},
4
4
  "defaults": {
5
5
  "alignment": "left",
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "groups": [
13
13
  {
14
- "name": "Content",
14
+ "name": "Date Config",
15
15
  "props": [
16
16
  "value",
17
17
  "showDayOfWeek",
@@ -32,21 +32,20 @@
32
32
  {
33
33
  "name": "Default",
34
34
  "props": {
35
- "value": "2026-04-09T15:30:00.000Z"
35
+ "value": "2026-04-09"
36
36
  }
37
37
  },
38
38
  {
39
- "name": "Large & centered",
39
+ "name": "centered",
40
40
  "props": {
41
- "value": "2026-04-09T15:30:00.000Z",
42
- "size": "lg",
41
+ "value": "2026-04-09",
43
42
  "alignment": "center"
44
43
  }
45
44
  },
46
45
  {
47
46
  "name": "Small with weekday",
48
47
  "props": {
49
- "value": "2026-06-18T10:00:00.000Z",
48
+ "value": "2026-06-18",
50
49
  "size": "sm",
51
50
  "showDayOfWeek": true
52
51
  }
@@ -54,20 +53,17 @@
54
53
  {
55
54
  "name": "With icon & year",
56
55
  "props": {
57
- "value": "2024-11-02T09:00:00.000Z",
56
+ "value": "2024-11-02",
58
57
  "showIcon": true,
59
58
  "showCurrentYear": true
60
59
  }
61
- },
62
- {
63
- "name": "Unstyled",
64
- "props": {
65
- "value": "2026-04-09T15:30:00.000Z",
66
- "unstyled": true,
67
- "showDayOfWeek": true
68
- }
69
60
  }
70
61
  ],
71
62
  "conditionals": {},
72
- "hints": {}
63
+ "hints": {
64
+ "default": {
65
+ "message": "Use as FormattedDate to avoid naming collisions with global Date object",
66
+ "type": "info"
67
+ }
68
+ }
73
69
  }
@@ -1,7 +1,8 @@
1
1
  {
2
+ "template": "<FormattedDate{{props}} />",
2
3
  "groups": [
3
4
  {
4
- "name": "Content",
5
+ "name": "Date Config",
5
6
  "props": ["value", "showDayOfWeek", "showCurrentYear", "showIcon"]
6
7
  },
7
8
  {
@@ -13,21 +14,20 @@
13
14
  {
14
15
  "name": "Default",
15
16
  "props": {
16
- "value": "2026-04-09T15:30:00.000Z"
17
+ "value": "2026-04-09"
17
18
  }
18
19
  },
19
20
  {
20
- "name": "Large & centered",
21
+ "name": "centered",
21
22
  "props": {
22
- "value": "2026-04-09T15:30:00.000Z",
23
- "size": "lg",
23
+ "value": "2026-04-09",
24
24
  "alignment": "center"
25
25
  }
26
26
  },
27
27
  {
28
28
  "name": "Small with weekday",
29
29
  "props": {
30
- "value": "2026-06-18T10:00:00.000Z",
30
+ "value": "2026-06-18",
31
31
  "size": "sm",
32
32
  "showDayOfWeek": true
33
33
  }
@@ -35,20 +35,17 @@
35
35
  {
36
36
  "name": "With icon & year",
37
37
  "props": {
38
- "value": "2024-11-02T09:00:00.000Z",
38
+ "value": "2024-11-02",
39
39
  "showIcon": true,
40
40
  "showCurrentYear": true
41
41
  }
42
- },
43
- {
44
- "name": "Unstyled",
45
- "props": {
46
- "value": "2026-04-09T15:30:00.000Z",
47
- "unstyled": true,
48
- "showDayOfWeek": true
49
- }
50
42
  }
51
43
  ],
52
44
  "conditionals": {},
53
- "hints": {}
45
+ "hints": {
46
+ "default": {
47
+ "message": "Use as FormattedDate to avoid naming collisions with global Date object",
48
+ "type": "info"
49
+ }
50
+ }
54
51
  }
@@ -180,6 +180,7 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
180
180
  //@ts-ignore
181
181
  globalProps(filteredProps),
182
182
  error ? 'error' : null,
183
+ inLine && 'inline-date-picker',
183
184
  className
184
185
  )
185
186
 
@@ -262,7 +263,7 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
262
263
  }
263
264
  </div>
264
265
 
265
- {!hideIcon &&
266
+ {!hideIcon && !inLine &&
266
267
  <div
267
268
  className={iconWrapperClass()}
268
269
  id={`cal-icon-${pickerId}`}
@@ -274,7 +275,7 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
274
275
  </div>
275
276
  }
276
277
 
277
- {hideIcon && inLine ?
278
+ {inLine ?
278
279
  <div>
279
280
  <div
280
281
  className={`${iconWrapperClass()} date-picker-inline-icon-plus`}
@@ -39,7 +39,7 @@
39
39
  <input type="hidden" id="<%= "#{object.end_date_id}" %>" name="<%= "#{object.end_date_name}" %>">
40
40
  <% end %>
41
41
 
42
- <% if !object.hide_icon %>
42
+ <% if !object.hide_icon && !object.inline %>
43
43
  <div
44
44
  class="<%= object.icon_wrapper_class %>"
45
45
  id="cal-icon-<%= object.picker_id %>"
@@ -52,7 +52,7 @@
52
52
  <% end %>
53
53
 
54
54
  <!-- Inline -->
55
- <% if object.hide_icon && object.inline %>
55
+ <% if object.inline %>
56
56
  <!-- Plus Icon -->
57
57
  <div
58
58
  class="<%= object.icon_wrapper_class %> date-picker-inline-icon-plus"
@@ -79,42 +79,57 @@
79
79
 
80
80
  <%= javascript_tag do %>
81
81
  (function() {
82
+ const initQuickPickChangeListener = (input) => {
83
+ if (!<%= object.selection_type == "quickpick" %>) return
84
+ if (input.dataset.quickpickInitialized) return
85
+
86
+ input.dataset.quickpickInitialized = "true"
87
+ input.addEventListener("change", ({ target }) => {
88
+ const startDate = document.getElementById("<%= object.start_date_id %>")
89
+ const endDate = document.getElementById("<%= object.end_date_id %>")
90
+ const splittedValue = target.value.split(" to ")
91
+ startDate.value = splittedValue[0]
92
+ endDate.value = splittedValue[1] ? splittedValue[1] : splittedValue[0]
93
+ })
94
+ }
95
+
82
96
  const loadDatePicker = () => {
83
- const input = document.getElementById("<%= object.picker_id %>");
84
- if (input && !input._flatpickr) {
85
- datePickerHelper(<%= object.date_picker_config %>, "<%= object.scroll_container %>")
97
+ const input = document.getElementById("<%= object.picker_id %>")
98
+
99
+ if (!input || input._flatpickr) return true
100
+ if (typeof window.datePickerHelper !== "function") return false
86
101
 
87
- if (<%= object.selection_type == "quickpick" %>) {
88
- document.getElementById("<%= object.picker_id %>").addEventListener("change", ({ target }) => {
89
- const startDate = document.getElementById("<%= object.start_date_id %>")
90
- const endDate = document.getElementById("<%= object.end_date_id %>")
91
- const splittedValue = target.value.split(" to ")
92
- startDate.value = splittedValue[0]
93
- endDate.value = splittedValue[1] ? splittedValue[1] : splittedValue[0]
94
- })
95
- }
102
+ window.datePickerHelper(<%= object.date_picker_config %>, "<%= object.scroll_container %>")
103
+ initQuickPickChangeListener(input)
104
+
105
+ return true
106
+ }
107
+
108
+ let attempts = 0
109
+ const retryLoad = () => {
110
+ if (loadDatePicker()) return
111
+
112
+ if (attempts++ < 20) {
113
+ setTimeout(retryLoad, 100)
96
114
  }
97
115
  }
98
116
 
99
- // Try to initialize immediately if DOM is ready
100
117
  if (document.readyState === "loading") {
101
- window.addEventListener("DOMContentLoaded", loadDatePicker)
118
+ window.addEventListener("DOMContentLoaded", retryLoad)
102
119
  } else {
103
- loadDatePicker()
120
+ retryLoad()
104
121
  }
105
122
 
106
123
  // For dynamically added content (modals, etc.), check again after a brief delay
107
124
  setTimeout(() => {
108
- const input = document.getElementById("<%= object.picker_id %>");
125
+ const input = document.getElementById("<%= object.picker_id %>")
109
126
  if (input && !input._flatpickr) {
110
- loadDatePicker();
127
+ retryLoad()
111
128
  }
112
- }, 100);
129
+ }, 100)
113
130
 
114
131
  if (<%= !object.custom_event_type.empty? %>) {
115
- window.addEventListener("<%= object.custom_event_type %>", () => {
116
- loadDatePicker()
117
- })
132
+ window.addEventListener("<%= object.custom_event_type %>", retryLoad)
118
133
  }
119
134
  })()
120
135
  <% end %>
@@ -92,7 +92,8 @@ module Playbook
92
92
 
93
93
  def classname
94
94
  default_margin_bottom = margin_bottom.present? ? "" : " mb_sm"
95
- generate_classname("pb_date_picker_kit") + default_margin_bottom
95
+ inline_class = inline ? " inline-date-picker" : ""
96
+ generate_classname("pb_date_picker_kit") + default_margin_bottom + inline_class
96
97
  end
97
98
 
98
99
  def date_picker_config
@@ -40,6 +40,37 @@ describe('DatePicker Kit', () => {
40
40
  expect(kit).toHaveClass('pb_date_picker_kit mb_sm')
41
41
  })
42
42
 
43
+ test('inLine alone adds inline-date-picker class and inline control icons, not the calendar icon', () => {
44
+ const testId = 'datepicker-inline-only'
45
+ render(
46
+ <DatePicker
47
+ data={{ testid: testId }}
48
+ inLine
49
+ pickerId="date-picker-inline-only"
50
+ />
51
+ )
52
+
53
+ const kit = screen.getByTestId(testId)
54
+ expect(kit).toHaveClass('inline-date-picker')
55
+ expect(kit.querySelector('#cal-icon-date-picker-inline-only')).not.toBeInTheDocument()
56
+ expect(kit.querySelector('#date-picker-inline-only-icon-plus')).toBeInTheDocument()
57
+ expect(kit.querySelector('#date-picker-inline-only-angle-down')).toBeInTheDocument()
58
+ })
59
+
60
+ test('hideIcon without inLine does not render inline control icons', () => {
61
+ const testId = 'datepicker-hide-icon-only'
62
+ render(
63
+ <DatePicker
64
+ data={{ testid: testId }}
65
+ hideIcon
66
+ pickerId="date-picker-hide-icon-only"
67
+ />
68
+ )
69
+
70
+ const kit = screen.getByTestId(testId)
71
+ expect(kit.querySelector('#date-picker-hide-icon-only-icon-plus')).not.toBeInTheDocument()
72
+ })
73
+
43
74
  test('shows DatePicker date format m/d/Y', async () => {
44
75
  const testId = 'datepicker-date'
45
76
  render(
@@ -36,10 +36,10 @@ const DatePickerDialogSubmission = () => {
36
36
  <Dialog.Body>
37
37
  <DatePicker
38
38
  defaultDate={dateFixed || undefined}
39
- key={`fixed-${pickerInstance}`}
39
+ key={"fixed-" + pickerInstance}
40
40
  label="Date"
41
41
  onChange={(dateStr) => setDateFixed(dateStr || "")}
42
- pickerId={`datePickerFixed-${pickerInstance}`}
42
+ pickerId={"datePickerFixed-" + pickerInstance}
43
43
  staticPosition={false}
44
44
  />
45
45
  </Dialog.Body>
@@ -1,6 +1,4 @@
1
1
  <%= pb_rails("date_picker", props: {
2
- classname: "inline-date-picker",
3
- hide_icon: true,
4
2
  inline: true,
5
3
  picker_id: "date-picker-inline"
6
4
  }) %>
@@ -6,8 +6,6 @@ const DatePickerInline = (props) => {
6
6
  return (
7
7
  <div>
8
8
  <DatePicker
9
- className="inline-date-picker"
10
- hideIcon
11
9
  inLine
12
10
  pickerId="date-picker-inline"
13
11
  {...props}