primer_view_components 0.0.121 → 0.0.123

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/assets/styles/primer_view_components.css +2 -2
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_list/item.rb +7 -0
  8. data/app/components/primer/alpha/action_list.css.json +123 -1
  9. data/app/components/primer/alpha/auto_complete.css.json +23 -1
  10. data/app/components/primer/alpha/banner.css.json +24 -1
  11. data/app/components/primer/alpha/button_marketing.css.json +33 -1
  12. data/app/components/primer/alpha/check_box.rb +74 -0
  13. data/app/components/primer/alpha/check_box_group.rb +36 -0
  14. data/app/components/primer/alpha/dialog.css.json +82 -1
  15. data/app/components/primer/alpha/dialog.rb +1 -1
  16. data/app/components/primer/alpha/dropdown.css.json +40 -1
  17. data/app/components/primer/alpha/form_button.rb +32 -0
  18. data/app/components/primer/alpha/form_control.html.erb +26 -0
  19. data/app/components/primer/alpha/form_control.rb +105 -0
  20. data/app/components/primer/alpha/layout.css.json +80 -1
  21. data/app/components/primer/alpha/menu.css.json +28 -1
  22. data/app/components/primer/alpha/multi_input.rb +81 -0
  23. data/app/components/primer/alpha/nav_list/item.rb +4 -0
  24. data/app/components/primer/alpha/nav_list/section.rb +1 -1
  25. data/app/components/primer/alpha/nav_list.d.ts +6 -3
  26. data/app/components/primer/alpha/nav_list.js +95 -6
  27. data/app/components/primer/alpha/nav_list.rb +5 -0
  28. data/app/components/primer/alpha/nav_list.ts +105 -3
  29. data/app/components/primer/alpha/radio_button.rb +25 -0
  30. data/app/components/primer/alpha/radio_button_group.rb +36 -0
  31. data/app/components/primer/alpha/segmented_control.css +1 -1
  32. data/app/components/primer/alpha/segmented_control.css.json +31 -1
  33. data/app/components/primer/alpha/segmented_control.css.map +1 -1
  34. data/app/components/primer/alpha/segmented_control.pcss +43 -12
  35. data/app/components/primer/alpha/select.rb +37 -0
  36. data/app/components/primer/alpha/submit_button.rb +32 -0
  37. data/app/components/primer/alpha/tab_nav.css.json +24 -1
  38. data/app/components/primer/alpha/tab_panels.rb +7 -0
  39. data/app/components/primer/alpha/text_area.rb +24 -0
  40. data/app/components/primer/alpha/text_field.css +2 -2
  41. data/app/components/primer/alpha/text_field.css.json +134 -1
  42. data/app/components/primer/alpha/text_field.css.map +1 -1
  43. data/app/components/primer/alpha/text_field.pcss +27 -0
  44. data/app/components/primer/alpha/text_field.rb +15 -20
  45. data/app/components/primer/alpha/toggle_switch.css +1 -1
  46. data/app/components/primer/alpha/toggle_switch.css.json +40 -1
  47. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  48. data/app/components/primer/alpha/toggle_switch.pcss +31 -61
  49. data/app/components/primer/alpha/underline_nav.css.json +28 -1
  50. data/app/components/primer/beta/avatar.css.json +17 -1
  51. data/app/components/primer/beta/avatar_stack.css.json +28 -1
  52. data/app/components/primer/beta/blankslate.css.json +22 -1
  53. data/app/components/primer/beta/border_box.css.json +54 -1
  54. data/app/components/primer/beta/breadcrumbs.css.json +11 -1
  55. data/app/components/primer/beta/button.css.json +71 -1
  56. data/app/components/primer/beta/counter.css.json +10 -1
  57. data/app/components/primer/beta/flash.css.json +27 -1
  58. data/app/components/primer/beta/label.css.json +25 -1
  59. data/app/components/primer/beta/link.css.json +19 -1
  60. data/app/components/primer/beta/popover.css.json +39 -1
  61. data/app/components/primer/beta/progress_bar.css.json +10 -1
  62. data/app/components/primer/beta/state.css.json +13 -1
  63. data/app/components/primer/beta/subhead.css.json +12 -1
  64. data/app/components/primer/beta/timeline_item.css.json +16 -1
  65. data/app/components/primer/beta/truncate.css.json +12 -1
  66. data/app/components/primer/component.rb +10 -2
  67. data/app/components/primer/truncate.css.json +13 -1
  68. data/app/forms/{select_list_form.rb → select_form.rb} +1 -1
  69. data/app/lib/primer/css/layout.css.json +316 -1
  70. data/app/lib/primer/css/utilities.css.json +1659 -1
  71. data/lib/primer/form_components.rb +26 -6
  72. data/lib/primer/forms/builder.rb +1 -17
  73. data/lib/primer/forms/button.rb +4 -1
  74. data/lib/primer/forms/check_box_group.html.erb +14 -9
  75. data/lib/primer/forms/check_box_group.rb +5 -0
  76. data/lib/primer/forms/dsl/check_box_group_input.rb +3 -4
  77. data/lib/primer/forms/dsl/input.rb +33 -2
  78. data/lib/primer/forms/dsl/input_methods.rb +49 -1
  79. data/lib/primer/forms/dsl/radio_button_group_input.rb +2 -3
  80. data/lib/primer/forms/dsl/{select_list_input.rb → select_input.rb} +2 -2
  81. data/lib/primer/forms/dsl/text_field_input.rb +7 -5
  82. data/lib/primer/forms/form_control.rb +0 -1
  83. data/lib/primer/forms/group.html.erb +1 -1
  84. data/lib/primer/forms/multi.html.erb +8 -6
  85. data/lib/primer/forms/multi.rb +2 -0
  86. data/lib/primer/forms/radio_button_group.html.erb +14 -9
  87. data/lib/primer/forms/radio_button_group.rb +5 -0
  88. data/lib/primer/forms/{select_list.html.erb → select.html.erb} +0 -0
  89. data/lib/primer/forms/{select_list.rb → select.rb} +2 -2
  90. data/lib/primer/forms/spacing_wrapper.html.erb +1 -1
  91. data/lib/primer/forms/text_area.rb +1 -1
  92. data/lib/primer/forms/text_field.rb +5 -1
  93. data/lib/primer/forms/utils.rb +20 -0
  94. data/lib/primer/view_components/engine.rb +1 -1
  95. data/lib/primer/view_components/version.rb +1 -1
  96. data/lib/primer/yard/backend.rb +1 -15
  97. data/lib/primer/yard/component_manifest.rb +44 -25
  98. data/lib/primer/yard/component_ref.rb +40 -0
  99. data/lib/primer/yard/docs_helper.rb +16 -2
  100. data/lib/primer/yard/legacy_gatsby_backend.rb +9 -15
  101. data/lib/primer/yard/lookbook_docs_helper.rb +32 -0
  102. data/lib/primer/yard/lookbook_pages_backend.rb +194 -0
  103. data/lib/primer/yard/registry.rb +6 -21
  104. data/lib/primer/yard/renders_many_handler.rb +1 -1
  105. data/lib/primer/yard/renders_one_handler.rb +1 -1
  106. data/lib/primer/yard.rb +14 -0
  107. data/lib/tasks/docs.rake +26 -13
  108. data/previews/pages/forms/01_introduction.md.erb +44 -0
  109. data/previews/pages/forms/02_getting_started.md.erb +125 -0
  110. data/previews/pages/forms/03_caption_templates.md.erb +30 -0
  111. data/previews/pages/forms/04_after_content.md.erb +39 -0
  112. data/previews/pages/forms/05_groups_layouts.md.erb +22 -0
  113. data/previews/pages/forms/06_miscellaneous_inputs.md.erb +43 -0
  114. data/previews/pages/forms/07_toggle_switch_forms.md.erb +58 -0
  115. data/previews/pages/forms/08_validations.md.erb +28 -0
  116. data/previews/pages/forms/09_compound_forms.md.erb +97 -0
  117. data/previews/primer/alpha/check_box_group_preview.rb +89 -0
  118. data/previews/primer/alpha/check_box_preview.rb +62 -0
  119. data/previews/primer/alpha/form_control_preview/playground.html.erb +9 -0
  120. data/previews/primer/alpha/form_control_preview.rb +106 -0
  121. data/previews/primer/alpha/multi_input_preview/playground.html.erb +41 -0
  122. data/previews/primer/alpha/multi_input_preview.rb +80 -0
  123. data/previews/primer/alpha/radio_button_group_preview.rb +83 -0
  124. data/previews/primer/alpha/radio_button_preview.rb +62 -0
  125. data/previews/primer/alpha/select_preview.rb +130 -0
  126. data/previews/primer/alpha/text_area_preview.rb +87 -0
  127. data/previews/primer/alpha/text_field_preview.rb +10 -1
  128. data/previews/primer/forms/forms_preview/example_toggle_switch_form.html.erb +2 -2
  129. data/previews/primer/forms/forms_preview/{select_list_form.html.erb → select_form.html.erb} +1 -1
  130. data/previews/primer/forms/forms_preview.rb +3 -1
  131. data/static/arguments.json +1358 -1328
  132. data/static/audited_at.json +10 -0
  133. data/static/constants.json +20 -0
  134. data/static/previews.json +218 -40
  135. data/static/statuses.json +10 -0
  136. metadata +41 -7
@@ -12,24 +12,44 @@ module Primer
12
12
  attr_reader :input_klass
13
13
  end
14
14
 
15
- def initialize(**system_arguments, &block)
15
+ def initialize(**system_arguments)
16
16
  @system_arguments = system_arguments
17
- @block = block
18
17
  end
19
18
 
20
- def call
19
+ def render_in(view_context, &block)
21
20
  builder = Primer::Forms::Builder.new(
22
- nil, nil, __vc_original_view_context, {}
21
+ nil, nil, view_context, {}
23
22
  )
24
23
 
24
+ # `block` is the block passed to `#render`. We pass it to the
25
+ # input's constructor here to mimic the way constructors often
26
+ # yield in the forms framework. Only allowing the block to be
27
+ # passed to `#initialize` is awkward because `#render` is often
28
+ # called without parens, making it non-obvious which method the
29
+ # block is passed to. Moreover, users of view components expect
30
+ # `#render` to accept a block and for that block to yield the
31
+ # component, which allows setting slots, etc. They do not expect
32
+ # that block to be accepted by the component's initializer
33
+ # instead.
34
+ #
35
+ # One caveat here is that not all form input classes yield
36
+ # themselves to the block. In such a case, the block will
37
+ # never be called, which is potentially confusing. We could
38
+ # detect whether or not the block is called and yield the input
39
+ # if it is not, but that comes with its own set of problems and
40
+ # could also lead to confusion (for example, if the block is
41
+ # conditionally called with one set of arguments from the input
42
+ # and then a different set of arguments here). Since we can't
43
+ # know which arguments the input will yield, it's safer to simply
44
+ # pass the block as-is.
25
45
  input = self.class.input_klass.new(
26
46
  builder: builder,
27
47
  form: nil,
28
48
  **@system_arguments,
29
- &@block
49
+ &block
30
50
  )
31
51
 
32
- input.to_component.render_in(__vc_original_view_context) { content }
52
+ input.to_component.render_in(view_context) { content }
33
53
  end
34
54
  end
35
55
  end
@@ -29,10 +29,6 @@ module Primer
29
29
  module Forms
30
30
  # :nodoc:
31
31
  class Builder < ActionView::Helpers::FormBuilder
32
- include Primer::ClassNameHelper
33
-
34
- UTILITY_KEYS = Primer::Classify::Utilities::UTILITIES.keys.freeze
35
-
36
32
  alias primer_fields_for fields_for
37
33
 
38
34
  def label(method, text = nil, **options, &block)
@@ -67,20 +63,8 @@ module Primer
67
63
 
68
64
  private
69
65
 
70
- # This method does the following:
71
- #
72
- # 1. Runs Primer's classify routine to convert entries like mb: 1 to mb-1.
73
- # 2. Runs classify on both options[:class] and options[:classes]. The first
74
- # is expected by Rails/HTML while the second is specific to Primer.
75
- # 3. Combines options[:class] and options[:classes] into options[:class]
76
- # so the options hash can be easily passed to Rails form builder methods.
77
- #
78
66
  def classify(options)
79
- options[:classes] = class_names(options.delete(:class), options[:classes])
80
- options.merge!(Primer::Classify.call(options))
81
- options.except!(*UTILITY_KEYS)
82
- options[:class] = class_names(options[:class], options.delete(:classes))
83
- options
67
+ Primer::Forms::Utils.classify(options)
84
68
  end
85
69
  end
86
70
  end
@@ -49,7 +49,10 @@ module Primer
49
49
  def input_arguments
50
50
  @input_arguments ||= @input.input_arguments.deep_dup.tap do |args|
51
51
  # rails uses :class but PVC wants :classes
52
- args[:classes] = args.delete(:class)
52
+ args[:classes] = class_names(
53
+ args[:classes],
54
+ args.delete(:class)
55
+ )
53
56
  end
54
57
  end
55
58
 
@@ -1,12 +1,17 @@
1
- <%= content_tag(:fieldset, hidden: @input.hidden?) do %>
2
- <% if @input.label %>
3
- <%= content_tag(:legend, **@input.label_arguments) do %>
4
- <%= @input.label %>
1
+ <%= content_tag(:div, **@input.input_arguments) do %>
2
+ <fieldset>
3
+ <% if @input.label %>
4
+ <%= content_tag(:legend, **@input.label_arguments) do %>
5
+ <%= @input.label %>
6
+ <% end %>
5
7
  <% end %>
6
- <% end %>
7
- <%= render(SpacingWrapper.new) do %>
8
- <% @input.check_boxes.each do |check_box| %>
9
- <%= render(check_box.to_component) %>
8
+ <%= render(SpacingWrapper.new) do %>
9
+ <% @input.check_boxes.each do |check_box| %>
10
+ <%= render(check_box.to_component) %>
11
+ <% end %>
10
12
  <% end %>
11
- <% end %>
13
+ </fieldset>
14
+ <div class="mt-2">
15
+ <%= render(Caption.new(input: @input)) %>
16
+ </div>
12
17
  <% end %>
@@ -8,6 +8,11 @@ module Primer
8
8
 
9
9
  def initialize(input:)
10
10
  @input = input
11
+
12
+ @input.add_label_classes("FormControl-label", "mb-2")
13
+ @input.add_input_classes("FormControl-check-group-wrap")
14
+
15
+ Primer::Forms::Utils.classify(@input.input_arguments)
11
16
  end
12
17
  end
13
18
  end
@@ -14,8 +14,6 @@ module Primer
14
14
 
15
15
  super(**system_arguments)
16
16
 
17
- add_label_classes("FormControl-label", "mb-2")
18
-
19
17
  yield(self) if block_given?
20
18
  end
21
19
 
@@ -34,10 +32,11 @@ module Primer
34
32
  def check_box(**system_arguments, &block)
35
33
  args = {
36
34
  name: @name,
37
- **system_arguments,
38
35
  builder: @builder,
39
36
  form: @form,
40
- scheme: scheme
37
+ scheme: scheme,
38
+ disabled: disabled?,
39
+ **system_arguments
41
40
  }
42
41
 
43
42
  @check_boxes << CheckBoxInput.new(**args, &block)
@@ -5,6 +5,37 @@ module Primer
5
5
  module Dsl
6
6
  # :nodoc:
7
7
  class Input
8
+ # Use this macro anywhere you want to include the various params all inputs can receive.
9
+ #
10
+ # @!macro [new] form_input_arguments
11
+ # @param name [String] Value for the HTML name attribute.
12
+ # @param id [String] Value for the HTML id attribute.
13
+ # @param class [String] CSS classes to include in the input's HTML `class` attribute. Exists for compatibility with Rails form builders.
14
+ # @param classes [Array] CSS classes to include in the input's HTML `class` attribute. Combined with the `:class` argument. The list may contain strings, hashes, or `nil` values, and is automatically cleaned up by Primer's [`class_name` helper](https://github.com/primer/view_components/blob/c9cb95c98fee3e2e27f4a10683f555e22285e7f1/app/lib/primer/class_name_helper.rb) (`nils`, falsy entries, and blank strings are ignored).
15
+ # @param caption [String] A string describing the field and what sorts of input it expects. Displayed below the input.
16
+ # @param label [String] Label text displayed above the input.
17
+ # @param visually_hide_label [Boolean] When set to `true`, hides the label. Although the label will be hidden visually, it will still be visible to screen readers.
18
+ # @param disabled [Boolean] When set to `true`, the input will not accept keyboard or mouse input.
19
+ # @param hidden [Boolean] When set to `true`, visually hides the field.
20
+ # @param invalid [Boolean] If set to `true`, the input will be rendered with a red border. Implied if `validation_message` is truthy. This option is set to `true` automatically if the model object associated with the form reports that the input is invalid via Rails validations. It is provided for cases where the form does not have an associated model. If the input is invalid as determined by Rails validations, setting `invalid` to `false` will have no effect.
21
+ # @param validation_message [String] A string displayed between the caption and the input indicating the input's contents are invalid. This option is, by default, set to the first Rails validation message for the input (assuming the form is associated with a model object). Use `validation_message` to override the default or to provide a validation message in case there is no associated model object.
22
+ # @param label_arguments [Hash] Attributes that will be passed to Rails' `builder.label` method. These can be HTML attributes or any of the other label options Rails supports. They will appear as HTML attributes on the `<label>` tag.
23
+ # @param scope_name_to_model [Boolean] Default `true`. When set to `false`, prevents the model name from prefixing the field name. For example, if the field name is `my_field`, Rails will normally emit an HTML name attribute of `model[my_field]`. Setting `scope_name_to_model` to `false` will cause Rails to emit `my_field` instead.
24
+ # @param scope_id_to_model [Boolean] Default `true`. When set to `false`, prevents the model name from prefixing the field ID. For example, if the field name is `my_field`, Rails will normally emit an HTML ID attribute of `model_my_field`. Setting `scope_id_to_model` to `false` will cause Rails to emit `my_field` instead.
25
+ # @param required [Boolean] Default `false`. When set to `true`, causes an asterisk (*) to appear next to the field's label indicating it is a required field. Note that this option explicitly does _not_ add a `required` HTML attribute. Doing so would enable native browser validations, which are inaccessible and inconsistent with the Primer design system.
26
+ # @param aria [Hash] Key/value pairs that represent Aria attributes and their values. Eg. `aria: { current: true }` becomes `aria-current="true"`.
27
+ # @param data [Hash] Key/value pairs that represent data attributes and their values. Eg. `data: { foo: "bar" }` becomes `data-foo="bar"`.
28
+ # @macro form_system_arguments
29
+
30
+ # @!macro [new] form_size_arguments
31
+ # @param size [Symbol] The size of the field. <%= one_of(Primer::Forms::Dsl::Input::SIZE_OPTIONS) %>
32
+
33
+ # @!macro [new] form_full_width_arguments
34
+ # @param full_width [Boolean] When set to `true`, the field will take up all the horizontal space allowed by its container.
35
+
36
+ # @!macro [new] form_system_arguments
37
+ # @param system_arguments [Hash] A hash of attributes passed to the underlying Rails builder methods. These options may mean something special depending on the type of input, otherwise they are emitted as HTML attributes. See the [Rails documentation](https://guides.rubyonrails.org/form_helpers.html) for more information. In addition, the usual Primer utility arguments are accepted in system arguments. For example, passing `mt: 2` will add the `mt-2` class to the input. See the Primer system arguments docs for details.
38
+
8
39
  SPACE_DELIMITED_ARIA_ATTRIBUTES = %i[describedby].freeze
9
40
  DEFAULT_SIZE = :medium
10
41
  SIZE_MAPPINGS = {
@@ -181,7 +212,7 @@ module Primer
181
212
  end
182
213
 
183
214
  def disabled?
184
- input_arguments.include?(:disabled)
215
+ !!input_arguments[:disabled]
185
216
  end
186
217
 
187
218
  def full_width?
@@ -241,7 +272,7 @@ module Primer
241
272
  {
242
273
  class: "FormControl-inlineValidation",
243
274
  id: validation_id,
244
- hidden: valid?
275
+ hidden: valid? || validation_messages.empty?
245
276
  }
246
277
  end
247
278
 
@@ -5,41 +5,73 @@ module Primer
5
5
  module Dsl
6
6
  # :nodoc:
7
7
  module InputMethods
8
+ # Used to render another form object.
9
+ #
10
+ # @param args [Array] Positional arguments passed to Rails' [`fields_for` helper](https://api.rubyonrails.org/v7.0.4/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for).
11
+ # @param kwargs [Hash] The options accepted by the form reference input (see forms docs). Includes keyword arguments passed to Rails' [`fields_for` helper](https://api.rubyonrails.org/v7.0.4/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for).
8
12
  def fields_for(*args, **kwargs, &block)
9
13
  add_input FormReferenceInput.new(*args, builder: builder, form: form, **kwargs, &block)
10
14
  end
11
15
 
16
+ # Adds a multi input to this form.
17
+ #
18
+ # @param options [Hash] The options accepted by the multi input (see forms docs).
19
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
12
20
  def multi(**options, &block)
13
21
  add_input MultiInput.new(builder: builder, form: form, **options, &block)
14
22
  end
15
23
 
24
+ # Adds a hidden input to this form.
25
+ #
26
+ # @param options [Hash] The options accepted by the hidden input (see forms docs).
16
27
  def hidden(**options)
17
28
  add_input HiddenInput.new(builder: builder, form: form, **options)
18
29
  end
19
30
 
31
+ # Adds a check box to this form.
32
+ #
33
+ # @param options [Hash] The options accepted by the check box input (see forms docs).
34
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
20
35
  def check_box(**options, &block)
21
36
  add_input CheckBoxInput.new(builder: builder, form: form, **options, &block)
22
37
  end
23
38
 
39
+ # Adds a radio button group to this form.
40
+ #
41
+ # @param options [Hash] The options accepted by the radio button group input (see forms docs).
42
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
24
43
  def radio_button_group(**options, &block)
25
44
  add_input RadioButtonGroupInput.new(builder: builder, form: form, **options, &block)
26
45
  end
27
46
 
47
+ # Adds a check box group to this form.
48
+ #
49
+ # @param options [Hash] The options accepted by the check box group input (see forms docs).
50
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
28
51
  def check_box_group(**options, &block)
29
52
  add_input CheckBoxGroupInput.new(builder: builder, form: form, **options, &block)
30
53
  end
31
54
 
55
+ # Adds a horizontal separator to the form.
32
56
  def separator
33
57
  add_input Separator.new
34
58
  end
35
59
 
36
60
  # START text input methods
37
61
 
62
+ # Adds a text field to this form.
63
+ #
64
+ # @param options [Hash] The options accepted by the text field input (see forms docs).
65
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
38
66
  def text_field(**options, &block)
39
67
  options = decorate_options(**options)
40
68
  add_input TextFieldInput.new(builder: builder, form: form, **options, &block)
41
69
  end
42
70
 
71
+ # Adds a text area to this form.
72
+ #
73
+ # @param options [Hash] The options accepted by the text area input (see forms docs).
74
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
43
75
  def text_area(**options, &block)
44
76
  options = decorate_options(**options)
45
77
  add_input TextAreaInput.new(builder: builder, form: form, **options, &block)
@@ -49,20 +81,32 @@ module Primer
49
81
 
50
82
  # START select input methods
51
83
 
84
+ # Adds a select list to this form.
85
+ #
86
+ # @param options [Hash] The options accepted by the select list input (see forms docs).
87
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
52
88
  def select_list(**options, &block)
53
89
  options = decorate_options(**options)
54
- add_input SelectListInput.new(builder: builder, form: form, **options, &block)
90
+ add_input SelectInput.new(builder: builder, form: form, **options, &block)
55
91
  end
56
92
 
57
93
  # END select input methods
58
94
 
59
95
  # START button input methods
60
96
 
97
+ # Adds a submit button to this form.
98
+ #
99
+ # @param options [Hash] The options accepted by the submit button input (see forms docs).
100
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
61
101
  def submit(**options, &block)
62
102
  options = decorate_options(**options)
63
103
  add_input SubmitButtonInput.new(builder: builder, form: form, **options, &block)
64
104
  end
65
105
 
106
+ # Adds a (non-submit) button to this form.
107
+ #
108
+ # @param options [Hash] The options accepted by the button input (see forms docs).
109
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
66
110
  def button(**options, &block)
67
111
  options = decorate_options(**options)
68
112
  add_input ButtonInput.new(builder: builder, form: form, **options, &block)
@@ -70,16 +114,20 @@ module Primer
70
114
 
71
115
  # END button input methods
72
116
 
117
+ # @private
73
118
  def inputs
74
119
  @inputs ||= []
75
120
  end
76
121
 
77
122
  private
78
123
 
124
+ # @private
79
125
  def add_input(input)
80
126
  inputs << input
81
127
  end
82
128
 
129
+ # @private
130
+ #
83
131
  # Called before the corresponding Input class is instantiated. The return value of this method is passed
84
132
  # to the Input class's constructor.
85
133
  def decorate_options(**options)
@@ -14,8 +14,6 @@ module Primer
14
14
 
15
15
  super(**system_arguments)
16
16
 
17
- add_label_classes("FormControl-label", "mb-2")
18
-
19
17
  yield(self) if block_given?
20
18
  end
21
19
 
@@ -29,7 +27,8 @@ module Primer
29
27
 
30
28
  def radio_button(**system_arguments, &block)
31
29
  @radio_buttons << RadioButtonInput.new(
32
- builder: @builder, form: @form, name: @name, **system_arguments, &block
30
+ builder: @builder, form: @form, name: @name, disabled: disabled?,
31
+ **system_arguments, &block
33
32
  )
34
33
  end
35
34
  end
@@ -4,7 +4,7 @@ module Primer
4
4
  module Forms
5
5
  module Dsl
6
6
  # :nodoc:
7
- class SelectListInput < Input
7
+ class SelectInput < Input
8
8
  SELECT_ARGUMENTS = %i[multiple include_blank prompt].freeze
9
9
 
10
10
  # :nodoc:
@@ -41,7 +41,7 @@ module Primer
41
41
  end
42
42
 
43
43
  def to_component
44
- SelectList.new(input: self)
44
+ Select.new(input: self)
45
45
  end
46
46
 
47
47
  # :nocov:
@@ -23,12 +23,14 @@ module Primer
23
23
  @monospace = system_arguments.delete(:monospace)
24
24
  @auto_check_src = system_arguments.delete(:auto_check_src)
25
25
 
26
- super(**system_arguments)
26
+ if @leading_visual
27
+ @leading_visual[:classes] = class_names(
28
+ "FormControl-input-leadingVisual",
29
+ @leading_visual[:classes]
30
+ )
31
+ end
27
32
 
28
- add_input_classes(
29
- "FormControl-input",
30
- Primer::Forms::Dsl::Input::SIZE_MAPPINGS[size]
31
- )
33
+ super(**system_arguments)
32
34
 
33
35
  add_input_data(:target, "primer-text-field.inputElement") if auto_check_src.present?
34
36
  add_input_classes("FormControl-inset") if inset?
@@ -12,7 +12,6 @@ module Primer
12
12
  @form_group_arguments = {
13
13
  class: class_names(
14
14
  "FormControl",
15
- "flex-1",
16
15
  "width-full",
17
16
  "FormControl--fullWidth" => @input.full_width?
18
17
  )
@@ -1,4 +1,4 @@
1
- <%= content_tag_if(horizontal?, :div, class: "d-flex", style: "gap: 15px;") do %>
1
+ <%= content_tag_if(horizontal?, :div, class: "FormControl-horizontalGroup") do %>
2
2
  <% @inputs.each do |input| %>
3
3
  <%= render(input.to_component) %>
4
4
  <% end %>
@@ -1,7 +1,9 @@
1
- <%= render(FormControl.new(input: @input)) do %>
2
- <primer-multi-input data-name="<%= @input.name %>">
3
- <% @input.inputs.each do |child_input| %>
4
- <%= render(child_input.to_component) %>
5
- <% end %>
6
- </primer-multi-input>
1
+ <%= content_tag(:div, **@input.input_arguments) do %>
2
+ <%= render(FormControl.new(input: @input)) do %>
3
+ <primer-multi-input data-name="<%= @input.name %>">
4
+ <% @input.inputs.each do |child_input| %>
5
+ <%= render(child_input.to_component) %>
6
+ <% end %>
7
+ </primer-multi-input>
8
+ <% end %>
7
9
  <% end %>
@@ -8,6 +8,8 @@ module Primer
8
8
 
9
9
  def initialize(input:)
10
10
  @input = input
11
+
12
+ Primer::Forms::Utils.classify(@input.input_arguments)
11
13
  end
12
14
  end
13
15
  end
@@ -1,12 +1,17 @@
1
- <%= content_tag(:fieldset, hidden: @input.hidden?) do %>
2
- <% if @input.label %>
3
- <%= content_tag(:legend, **@input.label_arguments) do %>
4
- <%= @input.label %>
1
+ <%= content_tag(:div, **@input.input_arguments) do %>
2
+ <fieldset>
3
+ <% if @input.label %>
4
+ <%= content_tag(:legend, **@input.label_arguments) do %>
5
+ <%= @input.label %>
6
+ <% end %>
5
7
  <% end %>
6
- <% end %>
7
- <%= render(SpacingWrapper.new) do %>
8
- <% @input.radio_buttons.each do |radio_button| %>
9
- <%= render(radio_button.to_component) %>
8
+ <%= render(SpacingWrapper.new) do %>
9
+ <% @input.radio_buttons.each do |radio_button| %>
10
+ <%= render(radio_button.to_component) %>
11
+ <% end %>
10
12
  <% end %>
11
- <% end %>
13
+ </fieldset>
14
+ <div class="mt-2">
15
+ <%= render(Caption.new(input: @input)) %>
16
+ </div>
12
17
  <% end %>
@@ -8,6 +8,11 @@ module Primer
8
8
 
9
9
  def initialize(input:)
10
10
  @input = input
11
+
12
+ @input.add_label_classes("FormControl-label", "mb-2")
13
+ @input.add_input_classes("FormControl-radio-group-wrap")
14
+
15
+ Primer::Forms::Utils.classify(@input.input_arguments)
11
16
  end
12
17
  end
13
18
  end
@@ -3,14 +3,14 @@
3
3
  module Primer
4
4
  module Forms
5
5
  # :nodoc:
6
- class SelectList < BaseComponent
6
+ class Select < BaseComponent
7
7
  delegate :builder, :form, to: :@input
8
8
 
9
9
  def initialize(input:)
10
10
  @input = input
11
11
  @input.add_input_classes(
12
12
  "FormControl-select",
13
- "FormControl--medium"
13
+ Primer::Forms::Dsl::Input::SIZE_MAPPINGS[@input.size]
14
14
  )
15
15
 
16
16
  @field_wrap_arguments = {
@@ -1,3 +1,3 @@
1
- <div class="d-flex flex-column" style="row-gap: 15px">
1
+ <div class="FormControl-spacingWrapper">
2
2
  <%= content %>
3
3
  </div>
@@ -8,7 +8,7 @@ module Primer
8
8
 
9
9
  def initialize(input:)
10
10
  @input = input
11
- @input.add_input_classes("FormControl-input", "FormControl--medium")
11
+ @input.add_input_classes("FormControl-input", "FormControl-textarea")
12
12
 
13
13
  @field_wrap_arguments = {
14
14
  class: class_names("FormControl-input-wrap"),
@@ -9,10 +9,14 @@ module Primer
9
9
  def initialize(input:)
10
10
  @input = input
11
11
 
12
+ @input.add_input_classes(
13
+ "FormControl-input",
14
+ Primer::Forms::Dsl::Input::SIZE_MAPPINGS[@input.size]
15
+ )
16
+
12
17
  @field_wrap_arguments = {
13
18
  class: class_names(
14
19
  "FormControl-input-wrap",
15
- Primer::Forms::Dsl::Input::SIZE_MAPPINGS[@input.size],
16
20
  "FormControl-input-wrap--trailingAction": @input.show_clear_button?,
17
21
  "FormControl-input-wrap--leadingVisual": @input.leading_visual?
18
22
  ),
@@ -5,6 +5,10 @@ module Primer
5
5
  module Forms
6
6
  # :nodoc:
7
7
  module Utils
8
+ include Primer::ClassNameHelper
9
+
10
+ PRIMER_UTILITY_KEYS = Primer::Classify::Utilities::UTILITIES.keys.freeze
11
+
8
12
  # Unfortunately this bug (https://github.com/ruby/ruby/pull/5646) prevents us from using
9
13
  # Ruby's native Module.const_source_location. Instead we have to fudge it by searching
10
14
  # for the file in the configured autoload paths. Doing so relies on Rails' autoloading
@@ -21,6 +25,22 @@ module Primer
21
25
 
22
26
  nil
23
27
  end
28
+
29
+ # This method does the following:
30
+ #
31
+ # 1. Runs Primer's classify routine to convert entries like mb: 1 to mb-1.
32
+ # 2. Runs classify on both options[:class] and options[:classes]. The first
33
+ # is expected by Rails/HTML while the second is specific to Primer.
34
+ # 3. Combines options[:class] and options[:classes] into options[:class]
35
+ # so the options hash can be easily passed to Rails form builder methods.
36
+ #
37
+ def classify(options)
38
+ options[:classes] = class_names(options.delete(:class), options[:classes])
39
+ options.merge!(Primer::Classify.call(options))
40
+ options.except!(*PRIMER_UTILITY_KEYS)
41
+ options[:class] = class_names(options[:class], options.delete(:classes))
42
+ options
43
+ end
24
44
  end
25
45
 
26
46
  Utils.extend(Utils)
@@ -43,7 +43,7 @@ module Primer
43
43
  end
44
44
 
45
45
  initializer "primer.forms.helpers" do
46
- ActiveSupport.on_load :action_controller do
46
+ ActiveSupport.on_load :action_controller_base do
47
47
  require "primer/form_helper"
48
48
  helper Primer::FormHelper
49
49
 
@@ -6,7 +6,7 @@ module Primer
6
6
  module VERSION
7
7
  MAJOR = 0
8
8
  MINOR = 0
9
- PATCH = 121
9
+ PATCH = 123
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -2,27 +2,13 @@
2
2
 
3
3
  # :nocov:
4
4
  module Primer
5
- module YARD
5
+ module Yard
6
6
  # Shared functionality for generating documentation from YARD comments.
7
7
  class Backend
8
8
  include DocsHelper
9
9
 
10
10
  private
11
11
 
12
- def pretty_default_value(tag, component)
13
- params = tag.object.parameters.find { |param| [tag.name.to_s, "#{tag.name}:"].include?(param[0]) }
14
- default = tag.defaults&.first || params&.second
15
-
16
- return "N/A" unless default
17
-
18
- constant_name = "#{component.name}::#{default}"
19
- constant_value = default.safe_constantize || constant_name.safe_constantize
20
-
21
- return pretty_value(default) if constant_value.nil?
22
-
23
- pretty_value(constant_value)
24
- end
25
-
26
12
  def view_context
27
13
  @view_context ||= begin
28
14
  # Rails controller for rendering arbitrary ERB