Merge pull request #6275 from chaimann/admin-ui-checkbox-refactor

[Admin][UI] checkbox refactor
This commit is contained in:
Thomas von Deyen 2025-06-11 13:17:35 +02:00 committed by GitHub
commit ffa13fcbaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 379 additions and 229 deletions

View File

@ -4,16 +4,10 @@
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<%= render component("ui/forms/field").text_field(f, :code, class: "required") %>
<label class="flex gap-2 items-center">
<%= hidden_field_tag "#{f.object_name}[active]", "0" %>
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::AdjustmentReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::AdjustmentReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -4,16 +4,10 @@
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<%= render component("ui/forms/field").text_field(f, :code, class: "required") %>
<label class="flex gap-2 items-center">
<%= hidden_field_tag "#{f.object_name}[active]", "0" %>
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::AdjustmentReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::AdjustmentReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -32,19 +32,13 @@
<% end %>
</div>
<label class="flex gap-2 items-center">
<%= form.hidden_field use_attribute, value: '0', id: false %>
<%= render component("ui/forms/checkbox").new(
name: "#{form.object_name}[#{use_attribute}]",
checked: @address == (@type == 'ship' ? @order.bill_address : @order.ship_address),
value: '1'
) %>
<span class="font-normal text-xs">
<%= t(".use_this_address.#{@type}") %>
</span>
</label>
<%= render component("ui/forms/checkbox").new(
object_name: form.object_name,
method: use_attribute,
checked: @address == (@type == 'ship' ? @order.bill_address : @order.ship_address)
) do |checkbox| %>
<%= checkbox.with_label(text: t(".use_this_address.#{@type}"), size: :xs) %>
<% end %>
</div>
<% end %>

View File

@ -117,14 +117,10 @@
type: :date,
value: f.object.discontinue_on&.to_date
) %>
<label class="flex gap-2 items-center">
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[promotionable]",
checked: f.object.promotionable
) %>
<span class="font-normal text-xs"><%= Spree::Product.human_attribute_name :promotionable %></span>
<%= render component("ui/toggletip").new(text: t(".hints.promotionable_html")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :promotionable, checked: f.object.promotionable) do |checkbox| %>
<%= checkbox.with_label(text: Spree::Product.human_attribute_name(:promotionable), size: :xs) %>
<%= checkbox.with_hint(text: t(".hints.promotionable_html")) %>
<% end %>
<% end %>
<%= render component("ui/panel").new(title: t(".product_organization")) do %>

View File

@ -4,16 +4,10 @@
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<%= render component("ui/forms/field").text_field(f, :code, class: "required") %>
<label class="flex gap-2 items-center">
<%= hidden_field_tag "#{f.object_name}[active]", "0" %>
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::RefundReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::RefundReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -4,15 +4,10 @@
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<%= render component("ui/forms/field").text_field(f, :code, class: "required") %>
<label class="flex gap-2 items-center">
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::RefundReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::RefundReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -3,16 +3,10 @@
<%= form_for @return_reason, url: form_url, html: { id: form_id } do |f| %>
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<label class="flex gap-2 items-center">
<%= hidden_field_tag "#{f.object_name}[active]", "0" %>
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::ReturnReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::ReturnReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -3,16 +3,10 @@
<%= form_for @return_reason, url: form_url, html: { id: form_id } do |f| %>
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<label class="flex gap-2 items-center">
<%= hidden_field_tag "#{f.object_name}[active]", "0" %>
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::ReturnReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::ReturnReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -3,16 +3,10 @@
<%= form_for @store_credit_reason, url: form_url, html: { id: form_id } do |f| %>
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<label class="flex gap-2 items-center">
<%= hidden_field_tag "#{f.object_name}[active]", "0" %>
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::StoreCreditReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::StoreCreditReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -3,16 +3,10 @@
<%= form_for @store_credit_reason, url: form_url, html: { id: form_id } do |f| %>
<div class="flex flex-col gap-6 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, class: "required") %>
<label class="flex gap-2 items-center">
<%= hidden_field_tag "#{f.object_name}[active]", "0" %>
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[active]",
value: "1",
checked: f.object.active
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::StoreCreditReason.human_attribute_name :active %></span>
<%= render component("ui/toggletip").new(text: t(".hints.active")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :active, checked: f.object.active) do |checkbox| %>
<%= checkbox.with_label(text: Spree::StoreCreditReason.human_attribute_name(:active), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.active")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -5,15 +5,10 @@
<%= render component("ui/forms/field").text_field(f, :name) %>
<%= render component("ui/forms/field").text_field(f, :tax_code) %>
<%= render component("ui/forms/field").text_field(f, :description) %>
<label class="flex gap-2 items-center">
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[is_default]",
value: "1",
checked: f.object.is_default
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::TaxCategory.human_attribute_name :is_default %></span>
<%= render component("ui/toggletip").new(text: t(".hints.is_default")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :is_default, checked: f.object.is_default) do |checkbox| %>
<%= checkbox.with_label(text: Spree::TaxCategory.human_attribute_name(:is_default), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.is_default")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -5,15 +5,10 @@
<%= render component("ui/forms/field").text_field(f, :name) %>
<%= render component("ui/forms/field").text_field(f, :tax_code) %>
<%= render component("ui/forms/field").text_field(f, :description) %>
<label class="flex gap-2 items-center">
<%= render component("ui/forms/checkbox").new(
name: "#{f.object_name}[is_default]",
value: "1",
checked: f.object.is_default
) %>
<span class="font-semibold text-xs ml-2"><%= Spree::TaxCategory.human_attribute_name :is_default %></span>
<%= render component("ui/toggletip").new(text: t(".hints.is_default")) %>
</label>
<%= render component("ui/forms/checkbox").new(object_name: f.object_name, method: :is_default, checked: f.object.is_default) do |checkbox| %>
<%= checkbox.with_label(text: Spree::TaxCategory.human_attribute_name(:is_default), weight: :semibold, size: :xs, classes: 'ml-2') %>
<%= checkbox.with_hint(text: t(".hints.is_default")) %>
<% end %>
</div>
<% modal.with_actions do %>
<form method="dialog">

View File

@ -0,0 +1,42 @@
# frozen_string_literal: true
class SolidusAdmin::UI::Checkbox::Component < SolidusAdmin::BaseComponent
SIZES = {
s: 'w-4 h-4',
m: 'w-5 h-5',
}.freeze
def initialize(size: :m, **attributes)
@size = size
@attributes = attributes
end
def call
tag.input(
type: 'checkbox',
class: "
#{SIZES.fetch(@size)}
form-checkbox
cursor-pointer
disabled:cursor-not-allowed
bg-white rounded border border-2 border-gray-300
hover:border-gray-700
focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0
active:ring active:ring-gray-300 active:ring-0.5
disabled:border-gray-300
indeterminate:border-gray-700 indeterminate:bg-gray-700 indeterminate:text-white
indeterminate:hover:border-gray-500 indeterminate:hover:bg-gray-500
indeterminate:focus:bg-gray-700
indeterminate:disabled:border-gray-300 indeterminate:disabled:bg-gray-300
checked:border-gray-700 checked:bg-gray-700 checked:text-white
checked:hover:border-gray-500 checked:hover:bg-gray-500
checked:focus:bg-gray-700
checked:disabled:border-gray-300 checked:disabled:bg-gray-300
",
**@attributes,
)
end
end

View File

@ -0,0 +1,14 @@
<div class="flex gap-2 items-center">
<label class="flex gap-2 items-center">
<%= hidden_field_tag @name, "0" %>
<%= render component("ui/checkbox").new(
name: @name,
value: "1",
checked: @checked,
**@attributes
) %>
<%= label %>
</label>
<%= hint if hint? %>
</div>

View File

@ -1,42 +1,35 @@
# frozen_string_literal: true
class SolidusAdmin::UI::Forms::Checkbox::Component < SolidusAdmin::BaseComponent
SIZES = {
s: 'w-4 h-4',
m: 'w-5 h-5',
FONT_WEIGHTS = {
normal: 'font-normal',
semibold: 'font-semibold',
}.freeze
def initialize(size: :m, **attributes)
@size = size
@attributes = attributes
end
FONT_SIZES = {
xs: 'text-xs',
s: 'text-sm',
}.freeze
def call
tag.input(
type: 'checkbox',
renders_one :label, ->(text:, weight: :normal, size: :s, **options) do
tag.span(
text,
class: "
#{SIZES.fetch(@size)}
form-checkbox
cursor-pointer
disabled:cursor-not-allowed
bg-white rounded border border-2 border-gray-300
hover:border-gray-700
focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0
active:ring active:ring-gray-300 active:ring-0.5
disabled:border-gray-300
indeterminate:border-gray-700 indeterminate:bg-gray-700 indeterminate:text-white
indeterminate:hover:border-gray-500 indeterminate:hover:bg-gray-500
indeterminate:focus:bg-gray-700
indeterminate:disabled:border-gray-300 indeterminate:disabled:bg-gray-300
checked:border-gray-700 checked:bg-gray-700 checked:text-white
checked:hover:border-gray-500 checked:hover:bg-gray-500
checked:focus:bg-gray-700
checked:disabled:border-gray-300 checked:disabled:bg-gray-300
#{FONT_WEIGHTS.fetch(weight)}
#{FONT_SIZES.fetch(size)}
#{options.delete(:classes)}
",
**@attributes,
**options
)
end
renders_one :hint, ->(text:, position: :above) do
render component("ui/toggletip").new(text:, position:)
end
def initialize(object_name:, method:, checked:, **attributes)
@name = "#{object_name}[#{method}]"
@checked = !!checked
@attributes = attributes
end
end

View File

@ -71,7 +71,7 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
def selectable_column
@selectable_column ||= Column.new(
header: -> {
component("ui/forms/checkbox").new(
component("ui/checkbox").new(
form: batch_actions_form_id,
"data-action": "#{stimulus_id}#selectAllRows",
"data-#{stimulus_id}-target": "headerCheckbox",
@ -79,7 +79,7 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
)
},
data: ->(data) {
component("ui/forms/checkbox").new(
component("ui/checkbox").new(
name: "id[]",
form: batch_actions_form_id,
value: data.id,

View File

@ -46,7 +46,7 @@
name="<%= selection.predicate.name %>"
value="<%= selection.predicate.value %>">
<%= render component('ui/forms/checkbox').new(
<%= render component('ui/checkbox').new(
id: selection.id,
name: selection.option.name,
value: selection.option.value,

View File

@ -51,6 +51,16 @@ module SolidusAdmin
expect(control).to have_text(val)
end
end
def checkbox(locator)
find(:checkbox, locator)
end
def clear_search
within('div[role="search"]') do
find('button[aria-label="Clear"]').click
end
end
end
end
end

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
# @component "ui/checkbox"
class SolidusAdmin::UI::Checkbox::ComponentPreview < ViewComponent::Preview
include SolidusAdmin::Preview
# **With a form builder**
#
# The checkbox component is used to render a checkbox input.
# It can be used with a Rails form builder by setting the `name` attribute
# with `forom.object_name`.
#
# ```erb
# <%= form_for @product do |form| %>
# ...
# <%= render component('ui/checkbox').new(
# name: "#{form.object_name}[accept_tos]",
# checked: form.object.accept_tos,
# ) %>
# ...
# <% end %>
# ```
#
# **With stimulus**
#
# The checkbox component can be used with stimulus to toggle the `indeterminate`
# state of the checkbox.
#
# ```erb
# <%= render component('ui/checkbox').new(
# "data-action": "click->#{stimulus_id}#toggleIndeterminate",
# "data-#{stimulus_id}-target": "checkbox",
# ) %>
# ```
#
# ```js
# import { Controller } from "stimulus"
#
# export default class extends Controller {
# static targets = ["checkbox"]
#
# toggleIndeterminate() {
# this.checkboxTarget.indeterminate = !this.checkboxTarget.indeterminate
# }
# }
# ```
#
def overview
render_with_template
end
# @param size select { choices: [s, m] }
# @param checked toggle
# @param disabled toggle
def playground(size: :m, checked: false, disabled: false)
render current_component.new(size: size.to_sym, checked:, disabled:)
end
end

View File

@ -0,0 +1,32 @@
<table>
<tr>
<td></td>
<% current_component::SIZES.keys.each do |size| %>
<td class="px-3 py-1 text-gray-500 text-center text-[16px]" colspan="2"><%= size.to_s.humanize %></td>
<% end %>
</tr>
<tr>
<td></td>
<% current_component::SIZES.keys.each do |size| %>
<% %i[default disabled].each do |state| %>
<td class="px-3 py-1 text-gray-500 text-center"><%= state.to_s.humanize %></td>
<% end %>
<% end %>
</tr>
<% %i[off on intermediate].each do |checked| %>
<tr>
<td class="font-bold px-3 py-1"><%= checked.to_s.humanize %></td>
<% current_component::SIZES.keys.each do |size| %>
<% %i[default disabled].each do |state| %>
<% cell_id = SecureRandom.uuid %>
<td class="px-3 py-1 text-center" id="<%= cell_id %>">
<%= render current_component.new(size: size, checked: checked == :on, disabled: state == :disabled) %>
<script>
document.getElementById("<%= cell_id %>").querySelector("input").indeterminate = <%= checked == :intermediate %>
</script>
</td>
<% end %>
<% end %>
</tr>
<% end %>
</table>

View File

@ -4,55 +4,39 @@
class SolidusAdmin::UI::Forms::Checkbox::ComponentPreview < ViewComponent::Preview
include SolidusAdmin::Preview
# **With a form builder**
# Forms checkbox component utilises regular checkbox component and encapsulates some functionality
# that is shared between admin forms checkboxes:
# - adds `hidden_field_tag` to function properly
# - provides a way to customise label (font size/weight, custom styles)
# - optionally include a toggletip hint
#
# The checkbox component is used to render a checkbox input.
# It can be used with a Rails form builder by setting the `name` attribute
# with `forom.object_name`.
# Requires `object_name` and `method` parameters that will form a name of the hidden input and checkbox input fields.
#
# Requires `checked` boolean parameter that will be passed directly to `ui/checkbox` component.
#
# Accepts and passes along to `ui/checkbox` component every other attribute that is accepted by it, e.g. `size`.
#
# ```erb
# <%= form_for @product do |form| %>
# ...
# <%= render component('ui/forms/checkbox').new(
# name: "#{form.object_name}[accept_tos]",
# checked: form.object.accept_tos,
# ) %>
# ...
# <%= render component('ui/forms/checkbox').new(object_name: 'stock_location', method: :default, checked: true) do |checkbox| %>
# <%= checkbox.with_label(text: "Default") %>
# <%= checkbox.with_hint(text: "Will be used by default") %>
# <% end %>
# ```
#
# **With stimulus**
#
# The checkbox component can be used with stimulus to toggle the `indeterminate`
# state of the checkbox.
#
# ```erb
# <%= render component('ui/forms/checkbox').new(
# "data-action": "click->#{stimulus_id}#toggleIndeterminate",
# "data-#{stimulus_id}-target": "checkbox",
# ) %>
# ```
#
# ```js
# import { Controller } from "stimulus"
#
# export default class extends Controller {
# static targets = ["checkbox"]
#
# toggleIndeterminate() {
# this.checkboxTarget.indeterminate = !this.checkboxTarget.indeterminate
# }
# }
# ```
#
def overview
render_with_template
end
# @param size select { choices: [s, m] }
# @param checked toggle
# @param disabled toggle
def playground(size: :m, checked: false, disabled: false)
render current_component.new(size: size.to_sym, checked:, disabled:)
# @param caption_size select { choices: [xs, s] }
# @param caption_weight select { choices: [normal, semibold] }
# @param caption_classes text
# @param hint toggle
# @param hint_text text
# @param hint_position select { choices: [above, below] }
def playground(caption_size: :s, caption_weight: :normal, caption_classes: '', hint: true, hint_text: "This will be helpful", hint_position: :above)
render current_component.new(object_name: "store", method: :active, checked: true) do |component|
component.with_label(text: "Active", size: caption_size, weight: caption_weight, classes: caption_classes)
component.with_hint(text: hint_text, position: hint_position) if hint
end
end
end

View File

@ -1,32 +1,56 @@
<table>
<tr>
<td></td>
<% current_component::SIZES.keys.each do |size| %>
<td class="px-3 py-1 text-gray-500 text-center text-[16px]" colspan="2"><%= size.to_s.humanize %></td>
<% end %>
</tr>
<tr>
<td></td>
<% current_component::SIZES.keys.each do |size| %>
<% %i[default disabled].each do |state| %>
<td class="px-3 py-1 text-gray-500 text-center"><%= state.to_s.humanize %></td>
<div class="flex gap-6">
<div class="flex flex-col">
<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
Regular label
</h6>
<%= render current_component.new(object_name: 'store', method: :active, checked: true) do |checkbox| %>
<%= checkbox.with_label(text: 'Active') %>
<% end %>
<% end %>
</tr>
<% %i[off on intermediate].each do |checked| %>
<tr>
<td class="font-bold px-3 py-1"><%= checked.to_s.humanize %></td>
<% current_component::SIZES.keys.each do |size| %>
<% %i[default disabled].each do |state| %>
<% cell_id = SecureRandom.uuid %>
<td class="px-3 py-1 text-center" id="<%= cell_id %>">
<%= render current_component.new(size: size, checked: checked == :on, disabled: state == :disabled) %>
<script>
document.getElementById("<%= cell_id %>").querySelector("input").indeterminate = <%= checked == :intermediate %>
</script>
</td>
<% end %>
</div>
<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
Smaller label
</h6>
<%= render current_component.new(object_name: 'store', method: :active, checked: true) do |checkbox| %>
<%= checkbox.with_label(text: 'Active', size: :xs) %>
<% end %>
</tr>
<% end %>
</table>
</div>
<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
Semibold label
</h6>
<%= render current_component.new(object_name: 'store', method: :active, checked: true) do |checkbox| %>
<%= checkbox.with_label(text: 'Active', weight: :semibold) %>
<% end %>
</div>
</div>
<div class="flex flex-col">
<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
Label with custom styles applied
</h6>
<%= render current_component.new(object_name: 'store', method: :active, checked: true) do |checkbox| %>
<%= checkbox.with_label(text: 'Active', classes: 'text-gray-500') %>
<% end %>
</div>
<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
With label and hint above
</h6>
<%= render current_component.new(object_name: 'store', method: :active, checked: true) do |checkbox| %>
<%= checkbox.with_label(text: 'Active') %>
<%= checkbox.with_hint(text: 'This means something') %>
<% end %>
</div>
<div class="mb-8">
<h6 class="text-gray-500 mb-3 mt-0">
With label and hint below
</h6>
<%= render current_component.new(object_name: 'store', method: :active, checked: true) do |checkbox| %>
<%= checkbox.with_label(text: 'Active') %>
<%= checkbox.with_hint(text: 'This means something', position: :below) %>
<% end %>
</div>
</div>
</div>

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe SolidusAdmin::UI::Checkbox::Component, type: :component do
it "renders the overview preview" do
render_preview(:overview)
end
end

View File

@ -40,9 +40,12 @@ describe "Product", type: :feature do
visit "/admin/products/just-a-prod"
fill_in "Name", with: "Just a product (updated)"
uncheck 'Promotable'
within('header') { click_button "Save" }
expect(page).to have_content("Just a product (updated)")
expect(checkbox("Promotable")).not_to be_checked
fill_in "Name", with: ""
within('header') { click_button "Save" }

View File

@ -22,7 +22,7 @@ describe "Refund Reasons", type: :feature do
end
context "when creating a new refund reason" do
let(:query) { "?page=1&q%5Bname_or_description_cont%5D=Ret" }
let(:query) { "?page=1&q%5Bname_or_code_cont%5D=Ret" }
before do
visit "/admin/refund_reasons/#{query}"
@ -44,10 +44,13 @@ describe "Refund Reasons", type: :feature do
context "with valid data" do
it "successfully creates a new refund reason, keeping page and q params" do
fill_in "Name", with: "Return process"
uncheck "Active"
click_on "Add Refund Reason"
expect(page).to have_content("Refund reason was successfully created.")
click_on "Return process"
expect(checkbox("Active")).not_to be_checked
expect(Spree::RefundReason.find_by(name: "Return process")).to be_present
expect(page.current_url).to include(query)
end
@ -64,10 +67,10 @@ describe "Refund Reasons", type: :feature do
end
context "when editing an existing refund reason" do
let(:query) { "?page=1&q%5Bname_or_description_cont%5D=Ret" }
let(:query) { "?page=1&q%5Bname_or_code_cont%5D=Ret" }
before do
Spree::RefundReason.create(name: "Return process")
Spree::RefundReason.create(name: "Return process", active: false)
visit "/admin/refund_reasons#{query}"
click_on "Return process"
expect(page).to have_css("dialog")
@ -84,15 +87,22 @@ describe "Refund Reasons", type: :feature do
expect(page.current_url).to include(query)
end
it "successfully updates the existing refund reason" do
it "successfully updates the existing refund reason", :js do
fill_in "Name", with: "Customer complaint"
check "Active"
click_on "Update Refund Reason"
expect(page.current_url).to include(query)
expect(page).to have_content("Refund reason was successfully updated.")
expect(page).to have_content("No Refund Reasons found") # search query still applied, filters out updated name
clear_search
expect(page).to have_content("Customer complaint")
expect(page).not_to have_content("Return process")
click_on "Customer complaint"
expect(checkbox("Active")).to be_checked
expect(Spree::RefundReason.find_by(name: "Customer complaint")).to be_present
expect(page.current_url).to include(query)
end
end
end

View File

@ -46,10 +46,13 @@ describe "Tax categories", type: :feature do
context "with valid data" do
it "successfully creates a new tax category, keeping page and q params" do
fill_in "Name", with: "Clothing"
check "Default"
click_on "Add Tax Category"
expect(page).to have_content("Tax category was successfully created.")
click_on "Clothing"
expect(checkbox("Default")).to be_checked
expect(Spree::TaxCategory.find_by(name: "Clothing")).to be_present
expect(page.current_url).to include(query)
end
@ -64,4 +67,39 @@ describe "Tax categories", type: :feature do
end
end
end
context "when editing an existing tax category" do
let(:query) { "?page=1&q%5Bname_or_description_cont%5D=Cloth" }
before do
Spree::TaxCategory.create(name: "Clothing", is_default: true)
visit "/admin/tax_categories#{query}"
click_on "Clothing"
expect(page).to have_css("dialog")
expect(page).to have_content("Edit Tax Category")
expect(page).to be_axe_clean
end
it "closing the modal keeps query params", :js do
within("dialog") { click_on "Cancel" }
expect(page).not_to have_selector("dialog")
expect(page.current_url).to include(query)
end
it "successfully updates the existing tax category", :js do
fill_in "Name", with: "Other"
uncheck "Default"
click_on "Update Tax Category"
expect(page.current_url).to include(query)
expect(page).to have_content("Tax category was successfully updated.")
expect(page).to have_content("No Tax Categories found") # search query still applied, filters out updated name
clear_search
expect(page).to have_content("Other")
expect(page).not_to have_content("Clothing")
click_on "Other"
expect(checkbox("Default")).not_to be_checked
end
end
end