Skip to content

Commit

Permalink
Enable validation messages on radio button and check box groups (#2241)
Browse files Browse the repository at this point in the history
Co-authored-by: camertron <[email protected]>
Co-authored-by: Keith Cirkel <[email protected]>
  • Loading branch information
3 people authored Sep 14, 2023
1 parent aa2909e commit 3f7e619
Show file tree
Hide file tree
Showing 19 changed files with 149 additions and 9 deletions.
7 changes: 7 additions & 0 deletions .changeset/plenty-hotels-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@primer/view-components': minor
---

Enable validation messages on radio button and check box groups

<!-- Changed components: Primer::Alpha::CheckBoxGroup, Primer::Alpha::RadioButtonGroup -->
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions app/components/primer/alpha/check_box_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class CheckBoxGroup < Primer::Component
# @param label [String] Label text displayed above the input.
# @param hidden [Boolean] When set to `true`, visually hides the group.
# @param caption [String] A string describing the field and what sorts of input it expects. Displayed below the group.
# @param invalid [Boolean] If set to `true`, the input will be marked as invalid. 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.
# @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.
# @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.

# @!method check_box
Expand Down
2 changes: 2 additions & 0 deletions app/components/primer/alpha/radio_button_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class RadioButtonGroup < Primer::Component
# @param label [String] Label text displayed above the input.
# @param hidden [Boolean] When set to `true`, visually hides the group.
# @param caption [String] A string describing the field and what sorts of input it expects. Displayed below the group.
# @param invalid [Boolean] If set to `true`, the input will be marked as invalid. 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.
# @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.
# @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.

# @!method radio_button
Expand Down
3 changes: 3 additions & 0 deletions lib/primer/forms/check_box_group.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<% end %>
<% end %>
</fieldset>
<div class="mt-2">
<%= render(ValidationMessage.new(input: @input)) %>
</div>
<div class="mt-2">
<%= render(Caption.new(input: @input)) %>
</div>
Expand Down
6 changes: 1 addition & 5 deletions lib/primer/forms/dsl/check_box_group_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Forms
module Dsl
# :nodoc:
class CheckBoxGroupInput < Input
attr_reader :label, :check_boxes
attr_reader :name, :label, :check_boxes

def initialize(name: nil, label: nil, **system_arguments)
@name = name
Expand All @@ -21,10 +21,6 @@ def to_component
CheckBoxGroup.new(input: self)
end

def name
nil
end

def type
:check_box_group
end
Expand Down
5 changes: 5 additions & 0 deletions lib/primer/forms/dsl/check_box_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def initialize(name:, label:, value: nil, unchecked_value: nil, scheme: DEFAULT_
yield(self) if block_given?
end

# check boxes cannot be invalid, as both checked and unchecked are valid states
def valid?
true
end

def to_component
CheckBox.new(input: self)
end
Expand Down
5 changes: 5 additions & 0 deletions lib/primer/forms/dsl/radio_button_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def initialize(name:, value:, label:, **system_arguments)
yield(self) if block_given?
end

# radio buttons cannot be invalid, as both selected and unselected are valid states
def valid?
true
end

def to_component
RadioButton.new(input: self)
end
Expand Down
5 changes: 1 addition & 4 deletions lib/primer/forms/form_control.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
<% end %>
<% end %>
<%= content %>
<%= content_tag(:div, **@input.validation_arguments) do %>
<span class="FormControl-inlineValidation--visual"><%= render(Primer::Beta::Octicon.new(icon: :"alert-fill", size: :xsmall, aria: { hidden: true })) %></span>
<%= content_tag(:span, @input.invalid? ? @input.validation_messages.first : "", **@input.validation_message_arguments) %>
<% end %>
<%= render(ValidationMessage.new(input: @input)) %>
<%= render(Caption.new(input: @input)) %>
<% end %>
<% else %>
Expand Down
3 changes: 3 additions & 0 deletions lib/primer/forms/radio_button_group.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<% end %>
<% end %>
</fieldset>
<div class="mt-2">
<%= render(ValidationMessage.new(input: @input)) %>
</div>
<div class="mt-2">
<%= render(Caption.new(input: @input)) %>
</div>
Expand Down
4 changes: 4 additions & 0 deletions lib/primer/forms/validation_message.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= content_tag(:div, **@input.validation_arguments) do %>
<span class="FormControl-inlineValidation--visual"><%= render(Primer::Beta::Octicon.new(icon: :"alert-fill", size: :xsmall, aria: { hidden: true })) %></span>
<%= content_tag(:span, @input.invalid? ? @input.validation_messages.first : "", **@input.validation_message_arguments) %>
<% end %>
14 changes: 14 additions & 0 deletions lib/primer/forms/validation_message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module Primer
module Forms
# :nodoc:
class ValidationMessage < BaseComponent
attr_reader :input

def initialize(input:)
@input = input
end
end
end
end
13 changes: 13 additions & 0 deletions previews/primer/alpha/check_box_group_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ class CheckBoxGroupPreview < ViewComponent::Preview
# @param name text
# @param label text
# @param caption text
# @param validation_message text
# @param disabled toggle
def playground(
name: "my-check-group",
label: "I would go into battle with:",
caption: "Qa'pla!",
validation_message: nil,
disabled: false
)
system_arguments = {
name: name,
label: label,
caption: caption,
validation_message: validation_message,
disabled: disabled
}

Expand All @@ -41,6 +44,16 @@ def default
end
end

# @label Invalid
def invalid
render(Primer::Alpha::CheckBoxGroup.new(validation_message: "Please choose at least one", name: "my-check-group", label: "I would go into battle with:")) do |component|
component.check_box(label: "Jean-Luc Picard", value: "picard4")
component.check_box(label: "Hikaru Sulu", value: "sulu4")
component.check_box(label: "Kathryn Janeway", value: "janeway4")
component.check_box(label: "Benjamin Sisko", value: "sisko4")
end
end

# @!group Options
# @snapshot
#
Expand Down
13 changes: 13 additions & 0 deletions previews/primer/alpha/radio_button_group_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ class RadioButtonGroupPreview < ViewComponent::Preview
# @param name text
# @param label text
# @param caption text
# @param validation_message text
# @param disabled toggle
def playground(
name: "my-radio-group",
label: "Question: what kind of bear is best?",
caption: "There are basically two schools of thought",
validation_message: nil,
disabled: false
)
system_arguments = {
name: name,
label: label,
caption: caption,
validation_message: validation_message,
disabled: disabled
}

Expand All @@ -40,6 +43,16 @@ def default
end
end

# @label Invalid
# @snapshot
def invalid
render(Primer::Alpha::RadioButtonGroup.new(validation_message: "Please select an option", name: "my-radio-group", label: "Question: what kind of bear is best?")) do |component|
component.radio_button(label: "Bears", value: "bears")
component.radio_button(label: "Beets", value: "beets")
component.radio_button(label: "Battlestar Galactica", value: "bsg")
end
end

# @!group Options
#
# @label With caption
Expand Down
19 changes: 19 additions & 0 deletions test/lib/primer/forms/checkbox_group_input_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "lib/test_helper"
require_relative "models/trip"

class Primer::Forms::CheckboxGroupInputTest < Minitest::Test
include Primer::ComponentTestHelpers
Expand Down Expand Up @@ -47,4 +48,22 @@ def test_checkbox_can_be_individually_disabled_in_group

assert_selector ".FormControl-checkbox-wrap input[disabled]"
end

def test_validations
trip = Trip.new(places: ["lopez"])
trip.valid? # populate validation messages

render_in_view_context do
primer_form_with(model: trip, url: "/trips") do |f|
render(ArrayCheckBoxGroupForm.new(f))
end
end

# the wrapper should be marked invalid, but not individual check boxes
assert_selector ".FormControl-check-group-wrap[invalid=true][aria-invalid=true]"
refute_selector "input[type=checkbox][invalid]"

# should have a validation message
assert_selector ".FormControl-inlineValidation", text: "Places must have at least two selections"
end
end
16 changes: 16 additions & 0 deletions test/lib/primer/forms/models/survey.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

# :nocov:
class Survey
if Rails::VERSION::STRING < "7.0"
include ActiveModel::Model
else
include ActiveModel::API
end

include ActiveModel::Validations

attr_accessor :channel

validates :channel, presence: true
end
22 changes: 22 additions & 0 deletions test/lib/primer/forms/models/trip.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

# :nocov:
class Trip
if Rails::VERSION::STRING < "7.0"
include ActiveModel::Model
else
include ActiveModel::API
end

include ActiveModel::Validations

attr_accessor :places

validates :places, length: { minimum: 2, too_short: "must have at least two selections" }

def initialize(**params)
super

@places ||= []
end
end
19 changes: 19 additions & 0 deletions test/lib/primer/forms/radio_button_group_input_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "lib/test_helper"
require_relative "models/survey"

class Primer::Forms::RadioButtonGroupInputTest < Minitest::Test
include Primer::ComponentTestHelpers
Expand Down Expand Up @@ -47,4 +48,22 @@ def test_radio_can_be_individually_disabled_in_group

assert_selector ".FormControl-radio-wrap input[disabled]"
end

def test_validations
survey = Survey.new
survey.valid? # populate validation messages

render_in_view_context do
primer_form_with(model: survey, url: "/surveys") do |f|
render(RadioButtonGroupForm.new(f))
end
end

# the wrapper should be marked invalid, but not individual check boxes
assert_selector ".FormControl-radio-group-wrap[invalid=true][aria-invalid=true]"
refute_selector "input[type=radio][invalid]"

# should have a validation message
assert_selector ".FormControl-inlineValidation", text: /Channel can['’]t be blank/
end
end

0 comments on commit 3f7e619

Please sign in to comment.