playbook_ui 18.2.0.pre.rc.1 → 18.2.0.pre.rc.2

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +26 -9
  3. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +8 -3
  4. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +19 -6
  5. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +1 -1
  6. data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +4 -0
  7. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +51 -49
  8. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +56 -56
  9. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss +1 -1
  10. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +3 -0
  11. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +2 -0
  12. data/app/pb_kits/playbook/pb_dropdown/index.js +5 -0
  13. data/app/pb_kits/playbook/pb_dropdown/kit.schema.json +7 -0
  14. data/app/pb_kits/playbook/pb_form/docs/_form_validation.html.erb +27 -4
  15. data/app/pb_kits/playbook/pb_form/docs/_form_validation.md +4 -4
  16. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_checkbox.txt +6 -1
  17. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_dropdown.txt +6 -1
  18. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_multi_level_select.txt +1 -0
  19. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_multi_select.txt +1 -0
  20. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_phone_number.txt +1 -0
  21. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_star_rating.txt +6 -1
  22. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_textarea.txt +5 -1
  23. data/app/pb_kits/playbook/pb_form/docs/snippets/validation_time_zone_select.txt +1 -0
  24. data/app/pb_kits/playbook/pb_form/pb_form_validation.js +51 -34
  25. data/app/pb_kits/playbook/pb_map/docs/_map_default.jsx +1 -1
  26. data/app/pb_kits/playbook/pb_map/docs/_map_default.md +2 -2
  27. data/app/pb_kits/playbook/pb_map/docs/_map_with_custom_button.jsx +1 -1
  28. data/app/pb_kits/playbook/pb_map/docs/_map_with_plugin.jsx +1 -1
  29. data/app/pb_kits/playbook/pb_map/docs/_playground.json +3 -3
  30. data/app/pb_kits/playbook/pb_map/docs/_playground.overrides.json +3 -3
  31. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +1 -0
  32. data/app/pb_kits/playbook/pb_multi_level_select/index.js +19 -4
  33. data/app/pb_kits/playbook/pb_multi_level_select/kit.schema.json +7 -0
  34. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.html.erb +4 -1
  35. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +7 -3
  36. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +10 -7
  37. data/app/pb_kits/playbook/pb_phone_number_input/docs/_playground.json +1 -0
  38. data/app/pb_kits/playbook/pb_phone_number_input/index.ts +7 -2
  39. data/app/pb_kits/playbook/pb_phone_number_input/kit.schema.json +8 -0
  40. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +5 -1
  41. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +12 -0
  42. data/app/pb_kits/playbook/pb_star_rating/index.js +8 -4
  43. data/app/pb_kits/playbook/pb_star_rating/kit.schema.json +7 -0
  44. data/app/pb_kits/playbook/pb_star_rating/star_rating.rb +6 -1
  45. data/app/pb_kits/playbook/pb_table/styles/_vertical_border.scss +27 -8
  46. data/dist/ai/all-schemas.json +113 -50
  47. data/dist/ai/forms.json +26 -20
  48. data/dist/ai/index.json +2 -2
  49. data/dist/ai/kits/button.schema.json +1 -1
  50. data/dist/ai/kits/checkbox.schema.json +7 -5
  51. data/dist/ai/kits/date_picker.schema.json +4 -3
  52. data/dist/ai/kits/dropdown.schema.json +13 -5
  53. data/dist/ai/kits/multi_level_select.schema.json +15 -6
  54. data/dist/ai/kits/phone_number_input.schema.json +18 -7
  55. data/dist/ai/kits/select.schema.json +25 -6
  56. data/dist/ai/kits/star_rating.schema.json +14 -5
  57. data/dist/ai/kits/text_input.schema.json +4 -3
  58. data/dist/ai/kits/textarea.schema.json +4 -3
  59. data/dist/ai/kits/time_picker.schema.json +4 -3
  60. data/dist/ai/kits/typeahead.schema.json +4 -3
  61. data/dist/ai/playgrounds/map.json +1 -1
  62. data/dist/chunks/vendor.js +2 -2
  63. data/dist/playbook-rails.js +1 -1
  64. data/dist/playbook.css +1 -1
  65. data/lib/playbook/forms/builder/checkbox_field.rb +1 -0
  66. data/lib/playbook/forms/builder/collection_select_field.rb +5 -1
  67. data/lib/playbook/forms/builder/select_field.rb +5 -1
  68. data/lib/playbook/version.rb +1 -1
  69. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5db309b02e5dd780da0f53f519c006634be2dd50e13553ce229ddd8be84a5527
4
- data.tar.gz: dd7da084d43bf1060792d5dcc88dcc5fe2eaabe30d143f262083c80bef7f0a90
3
+ metadata.gz: fbc2a1ba01cca1bd4a5b48488fafecee3697e273fd940d942a9de5de93a76921
4
+ data.tar.gz: e8cf1cc4ec63bd1957d455ed4766db51865e0f42b9369ae59065f89bb7d78946
5
5
  SHA512:
6
- metadata.gz: fcc52f997e3940eae2b3c00d36620ab00d2f44fddbce652671a6a3f6223877ce3cce1310d994e405dffad6fa92629727c8b1651a777cf379dd198a810ba1c93d
7
- data.tar.gz: 18cd7637fe9d879a22a4df5505afda151de2e0a8f6bb02dc742016efe4bf4692bc2d08b50c88f078be99b0ea95c95e543dea5061489c4ffe9eada2799900a509
6
+ metadata.gz: f5af4f9cf873c44ba0c76176e17130f0cf77fbd9c526ccea6041a70b0e1da2951a16da702259a01d16df03628a30402bcc1fdc47f32675af28aaf16f1fdb8ffb
7
+ data.tar.gz: 598126c77b37bd8885bfeb8fa1ebcc30d5f30adab8dcd92b46ea71ce8b038314dda4e774d3fea789ba3e7462ff5ea83276bd10229ce43efaa767e33fe5c430c4
@@ -491,12 +491,10 @@
491
491
  }
492
492
  }
493
493
 
494
- // First column separator/border
494
+ // First data column separator/border (Year when no separate checkbox column)
495
495
  .table-header-cells:first-child,
496
- .table-header-cells-custom:first-child,
497
- td:first-child,
498
- .pb_table_td:first-child,
499
- .checkbox-cell.checkbox-cell-header:first-child {
496
+ td:first-child:not(.checkbox-cell),
497
+ .pb_table_td:first-child:not(.checkbox-cell) {
500
498
  box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
501
499
  }
502
500
 
@@ -504,6 +502,14 @@
504
502
  box-shadow: inset 1px 0px 0px 0px var(--column-border-color) !important;
505
503
  }
506
504
 
505
+ // Separate checkbox column (Rails + React): separator on the first data column
506
+ .table-header-cells-custom + th[colspan="1"],
507
+ .table-header-cells-custom + .table-header-cells[colspan="1"],
508
+ td.checkbox-cell + td,
509
+ td.checkbox-cell + .pb_table_td {
510
+ box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
511
+ }
512
+
507
513
  // Color for collapsible trail
508
514
  .collapsible-trail {
509
515
  background-color: $border_light !important;
@@ -715,6 +721,12 @@
715
721
  box-shadow: 0 0 0 0 !important;
716
722
  }
717
723
 
724
+ // Re-apply after the nth-child(2) clear above (checkbox column layouts)
725
+ td.checkbox-cell + td,
726
+ td.checkbox-cell + .pb_table_td {
727
+ box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
728
+ }
729
+
718
730
  .pb_advanced_table_header,
719
731
  .pb_advanced_table_body {
720
732
  th.sticky-left,
@@ -816,10 +828,15 @@
816
828
  }
817
829
 
818
830
  .table-header-cells:first-child,
819
- .table-header-cells-custom:first-child,
820
- td:first-child,
821
- .pb_table_td:first-child,
822
- .checkbox-cell.checkbox-cell-header:first-child {
831
+ td:first-child:not(.checkbox-cell),
832
+ .pb_table_td:first-child:not(.checkbox-cell) {
833
+ box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
834
+ }
835
+
836
+ .table-header-cells-custom + th[colspan="1"],
837
+ .table-header-cells-custom + .table-header-cells[colspan="1"],
838
+ td.checkbox-cell + td,
839
+ td.checkbox-cell + .pb_table_td {
823
840
  box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
824
841
  }
825
842
 
@@ -1,14 +1,19 @@
1
1
  <%= pb_content_tag(:thead) do %>
2
2
  <% object.header_rows.each_with_index do |header_row, row_index| %>
3
+ <% is_lowest_header_row = row_index == header_rows.size - 1 %>
3
4
  <%= pb_rails("table/table_row") do %>
4
- <% if row_index == 0 && object.selectable_rows && object.enable_toggle_expansion == "none" %>
5
- <%= object.render_select_all_header %>
5
+ <% if object.selectable_rows && object.enable_toggle_expansion == "none" %>
6
+ <% if is_lowest_header_row %>
7
+ <%= object.render_select_all_header %>
8
+ <% else %>
9
+ <%= object.render_select_all_header_spacer %>
10
+ <% end %>
6
11
  <% end %>
7
12
  <% header_row.each_with_index do |cell, cell_index| %>
8
13
  <% header_component = object.header_component_info(cell, cell_index, row_index) %>
9
14
  <%= pb_rails(header_component[:name], props: header_component[:props]) do %>
10
15
  <%= pb_rails("flex", props: { align: "center", justify: object.header_flex_justify(cell, cell_index, row_index), text_align: object.header_flex_text_align(cell) }) do %>
11
- <% if cell_index.zero? && row_index === header_rows.size - 1 %>
16
+ <% if cell_index.zero? && is_lowest_header_row %>
12
17
  <% if object.selectable_rows && object.enable_toggle_expansion != "none" %>
13
18
  <%= pb_rails("flex/flex_item", props: { padding_right: "xs" }) do %>
14
19
  <%= object.render_select_all_checkbox %>
@@ -58,13 +58,8 @@ module Playbook
58
58
  # Selectable Rows No Subrows - checkboxes in their own first cell
59
59
  def render_select_all_header
60
60
  if selectable_rows
61
- additional_classes = []
62
- additional_classes << "table-header-cells-custom"
63
- additional_classes << "header-cells-with-actions" if show_actions_bar
64
- additional_classes << "checkbox-cell-header"
65
- additional_classes << "pinned-left" if responsive == "scroll"
66
61
  pb_rails("table/table_header", props: {
67
- classname: additional_classes.join(" "),
62
+ classname: select_all_header_classname,
68
63
  }) do
69
64
  pb_rails("checkbox", props: {
70
65
  id: "#{table_id ? "#{table_id}-" : ''}select-all-rows",
@@ -76,6 +71,15 @@ module Playbook
76
71
  end
77
72
  end
78
73
 
74
+ # Empty checkbox-column cell for upper multi-header rows so columns stay aligned
75
+ def render_select_all_header_spacer
76
+ if selectable_rows
77
+ pb_rails("table/table_header", props: {
78
+ classname: select_all_header_classname,
79
+ })
80
+ end
81
+ end
82
+
79
83
  # Selectable Rows w/ Subrows - checkboxes part of toggleable first cell
80
84
  def render_select_all_checkbox
81
85
  if selectable_rows
@@ -212,6 +216,15 @@ module Playbook
212
216
 
213
217
  private
214
218
 
219
+ def select_all_header_classname
220
+ additional_classes = []
221
+ additional_classes << "table-header-cells-custom"
222
+ additional_classes << "header-cells-with-actions" if show_actions_bar
223
+ additional_classes << "checkbox-cell-header"
224
+ additional_classes << "pinned-left" if responsive == "scroll"
225
+ additional_classes.join(" ")
226
+ end
227
+
215
228
  def header_layout_styles(cell)
216
229
  original_def = find_original_column_def_for_cell(cell)
217
230
  return {} unless original_def
@@ -189,7 +189,7 @@ module Playbook
189
189
 
190
190
  pb_rails("checkbox", props: {
191
191
  id: "#{prefix}select-row-#{row_id || row.object_id}",
192
- indeterminate_main: !last_row,
192
+ indeterminate_main: row_children.present?,
193
193
  indeterminate_main_labels: ["", ""],
194
194
  indeterminate_parent: indeterminate_parent,
195
195
  name: "#{prefix}select-row-#{row_id || row.object_id}",
@@ -16,6 +16,10 @@
16
16
  display: block;
17
17
  }
18
18
 
19
+ .date_picker_input_container {
20
+ position: relative;
21
+ }
22
+
19
23
  @import "./sass_partials/calendar_input_icon";
20
24
  }
21
25
 
@@ -297,19 +297,57 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
297
297
  )}
298
298
  <>
299
299
  <div className="date_picker_input_wrapper">
300
- <input
301
- aria-describedby={errorId}
302
- aria-invalid={!!error}
303
- autoComplete="off"
304
- className="date_picker_input"
305
- disabled={disableInput}
306
- id={pickerId}
307
- name={name}
308
- onChange={inputOnChange}
309
- placeholder={placeholder}
310
- style={{ cursor: getCursorStyle(filteredProps.cursor) }}
311
- value={inputValue}
312
- />
300
+ <div className="date_picker_input_container">
301
+ <input
302
+ aria-describedby={errorId}
303
+ aria-invalid={!!error}
304
+ autoComplete="off"
305
+ className="date_picker_input"
306
+ disabled={disableInput}
307
+ id={pickerId}
308
+ name={name}
309
+ onChange={inputOnChange}
310
+ placeholder={placeholder}
311
+ style={{ cursor: getCursorStyle(filteredProps.cursor) }}
312
+ value={inputValue}
313
+ />
314
+
315
+ {!hideIcon && !inLine &&
316
+ <div
317
+ className={iconWrapperClass()}
318
+ id={`cal-icon-${pickerId}`}
319
+ >
320
+ <Icon
321
+ className="cal_icon"
322
+ icon="calendar-alt"
323
+ />
324
+ </div>
325
+ }
326
+
327
+ {inLine ?
328
+ <div>
329
+ <div
330
+ className={`${iconWrapperClass()} date-picker-inline-icon-plus`}
331
+ id={`${pickerId}-icon-plus`}
332
+ >
333
+ <Icon
334
+ className="date-picker-plus-icon"
335
+ icon="plus"
336
+ />
337
+ </div>
338
+ <div
339
+ className={`${iconWrapperClass()} date-picker-inline-angle-down`}
340
+ id={`${pickerId}-angle-down`}
341
+ >
342
+ <Icon
343
+ className="angle_down_icon svg-inline--fa"
344
+ customIcon={angleDown}
345
+ />
346
+ </div>
347
+ </div>
348
+ : null
349
+ }
350
+ </div>
313
351
 
314
352
  {error &&
315
353
  <Body
@@ -322,42 +360,6 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
322
360
  />
323
361
  }
324
362
  </div>
325
-
326
- {!hideIcon && !inLine &&
327
- <div
328
- className={iconWrapperClass()}
329
- id={`cal-icon-${pickerId}`}
330
- >
331
- <Icon
332
- className="cal_icon"
333
- icon="calendar-alt"
334
- />
335
- </div>
336
- }
337
-
338
- {inLine ?
339
- <div>
340
- <div
341
- className={`${iconWrapperClass()} date-picker-inline-icon-plus`}
342
- id={`${pickerId}-icon-plus`}
343
- >
344
- <Icon
345
- className="date-picker-plus-icon"
346
- icon="plus"
347
- />
348
- </div>
349
- <div
350
- className={`${iconWrapperClass()} date-picker-inline-angle-down`}
351
- id={`${pickerId}-angle-down`}
352
- >
353
- <Icon
354
- className="angle_down_icon svg-inline--fa"
355
- customIcon={angleDown}
356
- />
357
- </div>
358
- </div>
359
- : null
360
- }
361
363
  </>
362
364
  </div>
363
365
  </div>
@@ -18,66 +18,66 @@
18
18
  <% end %>
19
19
  </label>
20
20
  <% end %>
21
- <% if content.present? %>
22
- <%= content %>
23
- <% else %>
24
- <%= pb_rails("text_input", props: {
25
- aria: object.input_aria,
26
- autocomplete: false,
27
- cursor: object.cursor,
28
- dark: object.dark,
29
- data: object.input_data,
30
- disabled: object.disable_input,
31
- error: object.error,
32
- id: object.picker_id,
33
- margin_bottom: "none",
34
- name: object.name,
35
- placeholder: object.placeholder,
36
- required: object.required,
37
- validation: object.validation_message.present? ? { message: object.validation_message } : {},
38
- }) %>
39
- <% end %>
21
+ <div class="date_picker_input_container">
22
+ <% if content.present? %>
23
+ <%= content %>
24
+ <% else %>
25
+ <%= pb_rails("text_input", props: {
26
+ aria: object.input_aria,
27
+ autocomplete: false,
28
+ cursor: object.cursor,
29
+ dark: object.dark,
30
+ data: object.input_data,
31
+ disabled: object.disable_input,
32
+ error: object.error,
33
+ id: object.picker_id,
34
+ margin_bottom: "none",
35
+ name: object.name,
36
+ placeholder: object.placeholder,
37
+ required: object.required,
38
+ validation: object.validation_message.present? ? { message: object.validation_message } : {},
39
+ }) %>
40
+ <% end %>
41
+ <% if !object.hide_icon && !object.inline %>
42
+ <div
43
+ class="<%= object.icon_wrapper_class %>"
44
+ id="cal-icon-<%= object.picker_id %>"
45
+ >
46
+ <%= pb_rails("icon", props: {
47
+ classname: "cal_icon",
48
+ icon: "calendar-alt"
49
+ }) %>
50
+ </div>
51
+ <% end %>
52
+ <!-- Inline -->
53
+ <% if object.inline %>
54
+ <!-- Plus Icon -->
55
+ <div
56
+ class="<%= object.icon_wrapper_class %> date-picker-inline-icon-plus"
57
+ id="<%= object.picker_id %>-icon-plus"
58
+ >
59
+ <%= pb_rails("icon", props: {
60
+ classname: "plus-icon",
61
+ icon: "plus"
62
+ }) %>
63
+ </div>
64
+
65
+ <!-- Angle Down Icon -->
66
+ <div
67
+ class="<%= object.icon_wrapper_class %> date-picker-inline-angle-down"
68
+ id="<%= object.picker_id %>-angle-down"
69
+ >
70
+ <%= pb_rails("icon", props: {
71
+ classname: "angle_down_icon",
72
+ custom_icon: Playbook::Engine::root.join(angle_down_path)
73
+ }) %>
74
+ </div>
75
+ <% end %>
76
+ </div>
40
77
  <% if object.selection_type == "quickpick" %>
41
78
  <input type="hidden" id="<%= "#{object.start_date_id}" %>" name="<%= "#{object.start_date_name}" %>">
42
79
  <input type="hidden" id="<%= "#{object.end_date_id}" %>" name="<%= "#{object.end_date_name}" %>">
43
80
  <% end %>
44
-
45
- <% if !object.hide_icon && !object.inline %>
46
- <div
47
- class="<%= object.icon_wrapper_class %>"
48
- id="cal-icon-<%= object.picker_id %>"
49
- >
50
- <%= pb_rails("icon", props: {
51
- classname: "cal_icon",
52
- icon: "calendar-alt"
53
- }) %>
54
- </div>
55
- <% end %>
56
-
57
- <!-- Inline -->
58
- <% if object.inline %>
59
- <!-- Plus Icon -->
60
- <div
61
- class="<%= object.icon_wrapper_class %> date-picker-inline-icon-plus"
62
- id="<%= object.picker_id %>-icon-plus"
63
- >
64
- <%= pb_rails("icon", props: {
65
- classname: "plus-icon",
66
- icon: "plus"
67
- }) %>
68
- </div>
69
-
70
- <!-- Angle Down Icon -->
71
- <div
72
- class="<%= object.icon_wrapper_class %> date-picker-inline-angle-down"
73
- id="<%= object.picker_id %>-angle-down"
74
- >
75
- <%= pb_rails("icon", props: {
76
- classname: "angle_down_icon",
77
- custom_icon: Playbook::Engine::root.join(angle_down_path)
78
- }) %>
79
- </div>
80
- <% end %>
81
81
  </div>
82
82
 
83
83
  <%= javascript_tag do %>
@@ -3,7 +3,7 @@
3
3
  .cal_icon_wrapper {
4
4
  pointer-events: none;
5
5
  position: absolute;
6
- top: $space_md + 2;
6
+ top: 0;
7
7
  right: 0;
8
8
  border-left: 1px solid $input_border_default;
9
9
  width: $space_xl + $space_xs;
@@ -15,6 +15,9 @@
15
15
  <% if input_default_value.present? %>
16
16
  data-default-value="<%= input_default_value %>"
17
17
  <% end %>
18
+ <% if object.validation_message.present? %>
19
+ data-message="<%= object.validation_message %>"
20
+ <% end %>
18
21
  data-dropdown-selected-option
19
22
  name="<%= object.name %><%= '[]' if object.multi_select %>"
20
23
  style="display: none"
@@ -13,6 +13,8 @@ module Playbook
13
13
  prop :id, type: Playbook::Props::String
14
14
  prop :required, type: Playbook::Props::Boolean,
15
15
  default: false
16
+ prop :validation_message, type: Playbook::Props::String,
17
+ default: ""
16
18
  prop :default_value
17
19
  prop :blank_selection, type: Playbook::Props::String,
18
20
  default: ""
@@ -1670,6 +1670,10 @@ export default class PbDropdown extends PbEnhancedElement {
1670
1670
  }
1671
1671
 
1672
1672
  clearFormValidation(input) {
1673
+ // PbFormValidation may have setCustomValidity from data-message; clear it
1674
+ // before checkValidity so a filled value can pass constraint validation.
1675
+ input.setCustomValidity("");
1676
+
1673
1677
  if (this.isMultiSelect) {
1674
1678
  if (this.selectedOptions.size > 0) {
1675
1679
  const dropdownWrapperElement = input.closest(".dropdown_wrapper");
@@ -2084,6 +2088,7 @@ export default class PbDropdown extends PbEnhancedElement {
2084
2088
  if (this.selectedOptions.size > 0) {
2085
2089
  baseInput.value = "";
2086
2090
  baseInput.removeAttribute("required");
2091
+ baseInput.setCustomValidity("");
2087
2092
  } else {
2088
2093
  baseInput.value = "";
2089
2094
  if (this.wasOriginallyRequired) {
@@ -278,6 +278,13 @@
278
278
  "type": "boolean",
279
279
  "default": false
280
280
  },
281
+ "validationMessage": {
282
+ "platforms": [
283
+ "rails"
284
+ ],
285
+ "type": "string",
286
+ "default": ""
287
+ },
281
288
  "searchTermMinimumLength": {
282
289
  "platforms": [
283
290
  "rails"
@@ -89,7 +89,11 @@
89
89
  validation: { message: "Website is required" },
90
90
  } %>
91
91
  <% when "textarea" %>
92
- <%= form.text_area :bio, props: { label: "Bio", required: true } %>
92
+ <%= form.text_area :bio, props: {
93
+ label: "Bio",
94
+ required: true,
95
+ validation: { message: "Bio is required" },
96
+ } %>
93
97
  <% when "select" %>
94
98
  <%= form.select :favorite_color, select_choices,
95
99
  props: {
@@ -108,7 +112,12 @@
108
112
  } %>
109
113
  <% when "dropdown" %>
110
114
  <%= form.dropdown_field "validation[country]",
111
- props: { label: "Country", options: dropdown_options, required: true } %>
115
+ props: {
116
+ label: "Country",
117
+ options: dropdown_options,
118
+ required: true,
119
+ validation_message: "Please select a country",
120
+ } %>
112
121
  <% when "multi_select" %>
113
122
  <%= form.dropdown_field "validation[countries]",
114
123
  props: {
@@ -116,10 +125,16 @@
116
125
  options: dropdown_options,
117
126
  multi_select: true,
118
127
  required: true,
128
+ validation_message: "Please select at least one country",
119
129
  } %>
120
130
  <% when "checkbox" %>
121
131
  <%= form.check_box :newsletter,
122
- props: { text: "Subscribe to newsletter", label: "Checkbox", required: true },
132
+ props: {
133
+ text: "Subscribe to newsletter",
134
+ label: "Checkbox",
135
+ required: true,
136
+ validation_message: "Please subscribe to continue",
137
+ },
123
138
  checked_value: "yes",
124
139
  unchecked_value: "no" %>
125
140
  <% when "date_picker" %>
@@ -142,6 +157,7 @@
142
157
  label: "Time Zone",
143
158
  blank_selection: "Select a Time Zone...",
144
159
  required: true,
160
+ validation_message: "Please select a time zone",
145
161
  } %>
146
162
  <% when "typeahead_default" %>
147
163
  <%= form.typeahead "validation[color_search_single]",
@@ -167,10 +183,16 @@
167
183
  <%= form.phone_number_field "validation[phone]", props: {
168
184
  label: "Phone Number",
169
185
  required: true,
186
+ validation_message: "Phone number is required",
170
187
  } %>
171
188
  <% when "star_rating" %>
172
189
  <%= form.star_rating_field "validation[rating]",
173
- props: { variant: "interactive", label: "Rating", required: true } %>
190
+ props: {
191
+ variant: "interactive",
192
+ label: "Rating",
193
+ required: true,
194
+ validation_message: "Please provide a rating",
195
+ } %>
174
196
  <% when "multi_level_select" %>
175
197
  <%= form.multi_level_select "validation[department]",
176
198
  props: {
@@ -179,6 +201,7 @@
179
201
  margin_bottom: "sm",
180
202
  label: "Department",
181
203
  required: true,
204
+ validation_message: "Please select a department",
182
205
  } %>
183
206
  <% end %>
184
207
  </div>
@@ -4,21 +4,21 @@ For `typeahead`, `phone_number_field`, and `dropdown_field`, pass a scoped strin
4
4
 
5
5
  ### Custom validation messages
6
6
 
7
- Not every form builder field accepts a custom validation message. Where supported, there are two props:
7
+ All form builder fields support a custom required-field message when `validate: true` is set on the form. There are two props, depending on the field:
8
8
 
9
- **Text-based inputs** (`text_field`, `email_field`, `number_field`, `search_field`, `password_field`, `url_field`, and `typeahead`) use `validation` with a `message` key (optional `pattern` for format checks):
9
+ **Text-based inputs** (`text_field`, `email_field`, `number_field`, `search_field`, `password_field`, `url_field`, `telephone_field`, `text_area`, and `typeahead`) use `validation` with a `message` key (optional `pattern` for format checks):
10
10
 
11
11
  ```ruby
12
12
  validation: { message: "Please enter a valid email address." }
13
13
  ```
14
14
 
15
- **Selection-based inputs** (`select`, `collection_select`, `date_picker`, `time_picker`, and `time_zone_select_field`) use `validation_message`:
15
+ **Selection-based and other inputs** (`select`, `collection_select`, `date_picker`, `time_picker`, `time_zone_select_field`, `dropdown_field`, `check_box`, `phone_number_field`, `intl_telephone`, `star_rating_field`, and `multi_level_select`) use `validation_message`:
16
16
 
17
17
  ```ruby
18
18
  validation_message: "Please select an option."
19
19
  ```
20
20
 
21
- Other builder fields (for example `dropdown_field`, `check_box`, `phone_number_field`, `text_area`, `star_rating_field`, and `multi_level_select`) can still use `required: true` with `validate: true` on the form, but they do not take these custom message props.
21
+ `phone_number_field` and `intl_telephone` own their validation UI. `validation_message` replaces the default required empty-field copy (`"Missing phone number"`); format errors remain kit-owned.
22
22
 
23
23
  ### Required indicator
24
24
 
@@ -1,4 +1,9 @@
1
1
  <%= form.check_box :newsletter,
2
- props: { text: "Subscribe to newsletter", label: "Checkbox", required: true },
2
+ props: {
3
+ text: "Subscribe to newsletter",
4
+ label: "Checkbox",
5
+ required: true,
6
+ validation_message: "Please subscribe to continue",
7
+ },
3
8
  checked_value: "yes",
4
9
  unchecked_value: "no" %>
@@ -4,4 +4,9 @@
4
4
  { label: "Pakistan", value: "Pakistan", id: "pk" },
5
5
  ] %>
6
6
  <%= form.dropdown_field "validation[country]",
7
- props: { label: "Country", options: dropdown_options, required: true } %>
7
+ props: {
8
+ label: "Country",
9
+ options: dropdown_options,
10
+ required: true,
11
+ validation_message: "Please select a country",
12
+ } %>
@@ -22,4 +22,5 @@
22
22
  margin_bottom: "sm",
23
23
  label: "Department",
24
24
  required: true,
25
+ validation_message: "Please select a department",
25
26
  } %>
@@ -8,4 +8,5 @@
8
8
  options: dropdown_options,
9
9
  multi_select: true,
10
10
  required: true,
11
+ validation_message: "Please select at least one country",
11
12
  } %>
@@ -1,4 +1,5 @@
1
1
  <%= form.phone_number_field "validation[phone]", props: {
2
2
  label: "Phone Number",
3
3
  required: true,
4
+ validation_message: "Phone number is required",
4
5
  } %>
@@ -1,2 +1,7 @@
1
1
  <%= form.star_rating_field "validation[rating]",
2
- props: { variant: "interactive", label: "Rating", required: true } %>
2
+ props: {
3
+ variant: "interactive",
4
+ label: "Rating",
5
+ required: true,
6
+ validation_message: "Please provide a rating",
7
+ } %>
@@ -1 +1,5 @@
1
- <%= form.text_area :bio, props: { label: "Bio", required: true } %>
1
+ <%= form.text_area :bio, props: {
2
+ label: "Bio",
3
+ required: true,
4
+ validation: { message: "Bio is required" },
5
+ } %>
@@ -4,4 +4,5 @@
4
4
  label: "Time Zone",
5
5
  blank_selection: "Select a Time Zone...",
6
6
  required: true,
7
+ validation_message: "Please select a time zone",
7
8
  } %>