Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/components/strata/us/button_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= content_tag element_tag, **element_attributes do %><%= content %><% end %>
122 changes: 122 additions & 0 deletions app/components/strata/us/button_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

module Strata
module US
# ButtonComponent renders a USWDS button as either a <button> or an <a>.
# Use the +href:+ keyword to switch from a button element to an anchor styled
# as a button.
#
# See https://designsystem.digital.gov/components/button/.
#
# For places where Rails owns the element rendering (e.g. +button_to+,
# +link_to+, +form.button+, +f.submit+), use the class-method helper
# +Strata::US::ButtonComponent.css_classes+ to produce a matching class
# string without going through the component.
#
# @example A primary button
# <%= render Strata::US::ButtonComponent.new do %>
# Save
# <% end %>
#
# @example A secondary, big, link-styled button
# <%= render Strata::US::ButtonComponent.new(href: edit_path, variant: :secondary, size: :big) do %>
# Edit
# <% end %>
#
# @example Class-string helper for button_to
# <%= button_to "Delete", path, method: :delete,
# class: Strata::US::ButtonComponent.css_classes(variant: :secondary) %>
class ButtonComponent < ViewComponent::Base
ALLOWED_VARIANTS = %i[default secondary accent_cool accent_warm base outline unstyled].freeze
ALLOWED_SIZES = %i[default big].freeze

VARIANT_MODIFIERS = {
default: nil,
secondary: "usa-button--secondary",
accent_cool: "usa-button--accent-cool",
accent_warm: "usa-button--accent-warm",
base: "usa-button--base",
outline: "usa-button--outline",
unstyled: "usa-button--unstyled"
}.freeze

def initialize(
variant: :default,
size: :default,
inverse: false,
type: :button,
href: nil,
disabled: false,
classes: nil,
**html_attributes
)
self.class.send(:validate_variant!, variant)
self.class.send(:validate_size!, size)

@variant = variant
@size = size
@inverse = inverse
@type = type
@href = href
@disabled = disabled
@classes = classes
@html_attributes = html_attributes
end

# Returns the USWDS class string for a button-styled element, without
# rendering the element itself. Use this for +button_to+, +link_to+,
# +form.button+, and other call sites where Rails owns the tag.
def self.css_classes(variant: :default, size: :default, inverse: false)
validate_variant!(variant)
validate_size!(size)

parts = [ "usa-button", VARIANT_MODIFIERS[variant] ]
parts << "usa-button--big" if size == :big
parts << "usa-button--inverse" if inverse
parts.compact.join(" ")
end

def element_tag
@href ? :a : :button
end

def element_attributes
attrs = @html_attributes.dup
attrs[:class] = combined_classes

if @href
attrs[:href] = @href
attrs[:"aria-disabled"] = "true" if @disabled
else
attrs[:type] = @type
attrs[:disabled] = true if @disabled
end

attrs
end

private

def combined_classes
base = self.class.css_classes(variant: @variant, size: @size, inverse: @inverse)
@classes.present? ? "#{base} #{@classes}" : base
end

def self.validate_variant!(variant)
return if ALLOWED_VARIANTS.include?(variant)

raise ArgumentError,
"Invalid variant: #{variant.inspect}. Must be one of #{ALLOWED_VARIANTS.inspect}"
end
private_class_method :validate_variant!

def self.validate_size!(size)
return if ALLOWED_SIZES.include?(size)

raise ArgumentError,
"Invalid size: #{size.inspect}. Must be one of #{ALLOWED_SIZES.inspect}"
end
private_class_method :validate_size!
end
end
end
5 changes: 5 additions & 0 deletions app/components/strata/us/button_group_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%= content_tag :ul, **list_attributes do %>
<% items.each do |item| %>
<%= item %>
<% end %>
<% end %>
67 changes: 67 additions & 0 deletions app/components/strata/us/button_group_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module Strata
module US
# ButtonGroupComponent renders a USWDS button group: a <ul class="usa-button-group">
# whose children are <li class="usa-button-group__item"> wrappers containing
# whatever button-styled element the caller chooses (a Strata::US::ButtonComponent,
# a link_to, an f.submit, etc.).
#
# See https://designsystem.digital.gov/components/button-group/.
#
# @example Default
# <%= render Strata::US::ButtonGroupComponent.new do |group| %>
# <% group.with_item do %>
# <%= render Strata::US::ButtonComponent.new do %>Save<% end %>
# <% end %>
# <% group.with_item do %>
# <%= link_to "Cancel", cancel_path,
# class: Strata::US::ButtonComponent.css_classes(variant: :outline) %>
# <% end %>
# <% end %>
#
# @example Segmented
# <%= render Strata::US::ButtonGroupComponent.new(segmented: true) do |group| %>
# <% group.with_item { ... } %>
# <% group.with_item { ... } %>
# <% end %>
class ButtonGroupComponent < ViewComponent::Base
renders_many :items, "Strata::US::ButtonGroupComponent::ItemComponent"

def initialize(segmented: false, classes: nil, **html_attributes)
@segmented = segmented
@classes = classes
@html_attributes = html_attributes
end

def wrapper_classes
class_names(
"usa-button-group",
{ "usa-button-group--segmented" => @segmented },
@classes
)
end

def list_attributes
attrs = @html_attributes.dup
attrs[:class] = wrapper_classes
attrs
end

# ItemComponent renders one entry as an <li class="usa-button-group__item">.
class ItemComponent < ViewComponent::Base
def initialize(classes: nil, **html_attributes)
@classes = classes
@html_attributes = html_attributes
end

def call
attrs = @html_attributes.dup
attrs[:class] = class_names("usa-button-group__item", @classes)

content_tag(:li, content, **attrs)
end
end
end
end
end
18 changes: 14 additions & 4 deletions app/helpers/strata/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,22 @@ def select(attribute, choices, options = {}, html_options = {})
form_group(attribute) { content }
end

# Renders the submit input with USWDS button styling.
#
# @option options [Symbol] :variant Visual variant
# (+:default+, +:secondary+, +:accent_cool+, +:accent_warm+, +:base+,
# +:outline+, +:unstyled+). Defaults to +:default+ (primary).
# @option options [Boolean] :big When true, applies the +usa-button--big+
# modifier along with extra vertical margin.
# @param [Object, nil] value
def submit(value = nil, options = {})
append_to_option(options, :class, " usa-button")
variant = options.delete(:variant) || :default
big = options.delete(:big)
size = big ? :big : :default

if options[:big]
append_to_option(options, :class, " usa-button--big margin-y-6")
end
button_classes = Strata::US::ButtonComponent.css_classes(variant: variant, size: size)
append_to_option(options, :class, " #{button_classes}")
append_to_option(options, :class, " margin-y-6") if big

super(value, options)
end
Expand Down
94 changes: 94 additions & 0 deletions app/previews/strata/us/button_component_preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# frozen_string_literal: true

module Strata
module US
# ButtonComponentPreview provides preview examples for the Strata::US::ButtonComponent.
class ButtonComponentPreview < Lookbook::Preview
layout "strata/component_preview"

# @label Default (primary)
def default
render Strata::US::ButtonComponent.new do
"Default"
end
end

# @label Secondary
def secondary
render Strata::US::ButtonComponent.new(variant: :secondary) do
"Secondary"
end
end

# @label Accent cool
def accent_cool
render Strata::US::ButtonComponent.new(variant: :accent_cool) do
"Accent cool"
end
end

# @label Accent warm
def accent_warm
render Strata::US::ButtonComponent.new(variant: :accent_warm) do
"Accent warm"
end
end

# @label Base
def base
render Strata::US::ButtonComponent.new(variant: :base) do
"Base"
end
end

# @label Outline
def outline
render Strata::US::ButtonComponent.new(variant: :outline) do
"Outline"
end
end

# @label Unstyled
def unstyled
render Strata::US::ButtonComponent.new(variant: :unstyled) do
"Unstyled"
end
end

# @label Big size
def big
render Strata::US::ButtonComponent.new(size: :big) do
"Big primary"
end
end

# @label Link styled as button
def as_link
render Strata::US::ButtonComponent.new(href: "#", variant: :outline) do
"Outline link"
end
end

# @label Submit-typed button
def submit_type
render Strata::US::ButtonComponent.new(type: :submit) do
"Submit"
end
end

# @label Disabled button
def disabled
render Strata::US::ButtonComponent.new(disabled: true) do
"Disabled"
end
end

# @label Disabled link
def disabled_link
render Strata::US::ButtonComponent.new(href: "#", disabled: true) do
"Disabled link"
end
end
end
end
end
18 changes: 18 additions & 0 deletions app/previews/strata/us/button_group_component_preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Strata
module US
# ButtonGroupComponentPreview provides preview examples for the Strata::US::ButtonGroupComponent.
class ButtonGroupComponentPreview < Lookbook::Preview
layout "strata/component_preview"

# @label Default
# Renders via the sibling `default.html.erb` template — nested `render` calls don't work inside slot blocks in a Lookbook preview method.
def default; end

# @label Segmented
# Renders via the sibling `segmented.html.erb` template — nested `render` calls don't work inside slot blocks in a Lookbook preview method.
def segmented; end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<%= render Strata::US::ButtonGroupComponent.new do |group| %>
<% group.with_item do %>
<%= render(Strata::US::ButtonComponent.new) { "Save" } %>
<% end %>
<% group.with_item do %>
<%= render(Strata::US::ButtonComponent.new(variant: :outline)) { "Cancel" } %>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<%= render Strata::US::ButtonGroupComponent.new(segmented: true) do |group| %>
<% group.with_item do %>
<%= render(Strata::US::ButtonComponent.new) { "Map" } %>
<% end %>
<% group.with_item do %>
<%= render(Strata::US::ButtonComponent.new) { "Satellite" } %>
<% end %>
<% group.with_item do %>
<%= render(Strata::US::ButtonComponent.new) { "Hybrid" } %>
<% end %>
<% end %>
Loading
Loading