Skip to content

Commit

Permalink
ToggleSwitch form (#1715)
Browse files Browse the repository at this point in the history
Co-authored-by: Cameron Dutro <camertron@gmail.com>
  • Loading branch information
neall and camertron authored Dec 20, 2022
1 parent 9a4ad69 commit 34dfc3a
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/lovely-boats-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/view-components': patch
---

Add Primer::Forms::ToggleSwitchForm
8 changes: 8 additions & 0 deletions app/forms/example_toggle_switch_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

# :nodoc:
class ExampleToggleSwitchForm < Primer::Forms::ToggleSwitchForm
def initialize(**system_arguments)
super(name: :example_field, label: "Example", **system_arguments)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
My <em>favorite</em> caption.
35 changes: 35 additions & 0 deletions lib/primer/forms/dsl/toggle_switch_input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Primer
module Forms
module Dsl
# :nodoc:
class ToggleSwitchInput < Input
attr_reader :name, :label, :src, :csrf

def initialize(
name:,
label:,
src:,
csrf: nil,
**system_arguments
)
@name = name
@label = label
@src = src
@csrf = csrf

super(**system_arguments)
end

def to_component
ToggleSwitch.new(input: self)
end

def type
:toggle_switch
end
end
end
end
end
17 changes: 17 additions & 0 deletions lib/primer/forms/toggle_switch.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<%= content_tag(:div, **@form_group_arguments) do %>
<span style="flex-grow: 1">
<%= builder.label(@input.name, **@input.label_arguments) do %>
<%= @input.label %>
<% end %>
<%= render(Caption.new(input: @input)) %>
</span>
<%
csrf = @input.csrf || @view_context.form_authenticity_token(
form_options: {
method: :post,
action: @input.src
}
)
%>
<%= render(Primer::Alpha::ToggleSwitch.new(src: @input.src, csrf: csrf)) %>
<% end %>
19 changes: 19 additions & 0 deletions lib/primer/forms/toggle_switch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module Primer
module Forms
# :nodoc:
class ToggleSwitch < BaseComponent
delegate :builder, :form, to: :@input

def initialize(input:)
@input = input
@input.add_label_classes("FormControl-label")

@form_group_arguments = { class: "d-flex" }

@form_group_arguments[:hidden] = "hidden" if @input.hidden?
end
end
end
end
74 changes: 74 additions & 0 deletions lib/primer/forms/toggle_switch_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

module Primer
module Forms
# Toggle switches are designed to submit an on/off value to the server immediately
# upon click. For that reason they are not designed to be used in "regular" forms
# that have other fields, etc. Instead they should be used independently via this
# class.
#
# ToggleSwitchForm can be used directly or via inheritance.
#
# Via inheritance:
#
# # app/forms/my_toggle_form.rb
# class MyToggleForm < Primer::Forms::ToggleSwitchForm
# def initialize(**system_arguments)
# super(name: :foo, label: "Foo", src: "/foo", **system_arguments)
# end
# end
#
# # app/views/some_view.html.erb
# <%= render(MyToggleForm.new) %>
#
# Directly:
#
# # app/views/some_view.html.erb
# <%= render(
# Primer::Forms::ToggleSwitchForm.new(
# name: :foo, label: "Foo", src: "/foo"
# )
# ) %>
#
class ToggleSwitchForm < Primer::Forms::Base
# Define the form on subclasses so render(Subclass.new) works as expected.
def self.inherited(base)
base.form do |toggle_switch_form|
input = Dsl::ToggleSwitchInput.new(
builder: toggle_switch_form.builder, form: self, **@system_arguments
)

toggle_switch_form.send(:add_input, input)
end
end

# Define the form on self so render(ToggleSwitchForm.new) works as expected.
inherited(self)

# Override to avoid accepting a builder argument. We create our own builder
# on render. See the implementation of render_in below.
def self.new(**options)
allocate.tap { |obj| obj.send(:initialize, **options) }
end

def initialize(**system_arguments)
@system_arguments = system_arguments
end

# Unlike other instances of Base, ToggleSwitchForm defines its own form and
# is not given a Rails form builder on instantiation. We do this mostly for
# ergonomic reasons; it's much less verbose than if you were required to
# call form_with/form_for, etc. That said, the rest of the forms framework
# assumes the presence of a builder so we create our own here. A builder
# cannot be constructed without a corresponding view context, which is why
# we have to override render_in and can't create it in the initializer.
def render_in(view_context, &block)
@builder = Primer::Forms::Builder.new(
nil, nil, view_context, {}
)

super
end
end
end
end
2 changes: 2 additions & 0 deletions previews/primer/forms/forms_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def multi_input_form; end
def name_with_question_mark_form; end

def immediate_validation_form; end

def example_toggle_switch_form; end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= render(ExampleToggleSwitchForm.new(csrf: "let_me_in", src: toggle_switch_index_path)) %>
26 changes: 26 additions & 0 deletions test/lib/primer/forms/toggle_switch_form_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require "lib/test_helper"

class Primer::Forms::ToggleSwitchFormTest < Minitest::Test
include Primer::ComponentTestHelpers

def test_it_renders_with_a_name
bogus_csrf = "let me in"
render_inline(ExampleToggleSwitchForm.new(csrf: bogus_csrf, src: "/toggle_switch"))

assert_selector "toggle-switch[src='/toggle_switch'][csrf='#{bogus_csrf}']"
end

def test_can_render_without_subclass
render_inline(
Primer::Forms::ToggleSwitchForm.new(
name: :example_field,
label: "Example",
src: "/toggle_switch"
)
)

assert_selector "toggle-switch[src='/toggle_switch']"
end
end

0 comments on commit 34dfc3a

Please sign in to comment.