Skip to content
Draft
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
45 changes: 45 additions & 0 deletions app/helpers/button_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module ButtonHelper
# Single source of truth for button styling, replacing the @apply-based .btn
# component classes (Evil Martians best practice #4: keep the utilities in the
# markup via a helper rather than extracting them into @apply CSS). Tailwind
# scans this file (see the @source in application.tailwind.css), so the class
# strings below generate exactly like inline utilities.
BUTTON_BASE = "inline-flex items-center gap-2 rounded-lg font-medium shadow-sm " \
"transition-colors duration-200 focus:outline-none focus:ring-2 " \
"focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed".freeze

# Size sets the padding + text scale. Pass `size: nil` when the call site
# supplies its own padding/text-size via `extra:` so the two don't collide
# (conflicting utilities in one class list resolve by Tailwind's generation
# order, not markup order β€” unlike the old component/utility layer split).
BUTTON_SIZES = {
md: "px-4 py-2 text-sm",
sm: "px-3 py-1 text-xs"
}.freeze

BUTTON_VARIANTS = {
primary: "border border-primary bg-primary text-white hover:bg-white hover:text-primary",
accent: "border-2 border-accent bg-accent text-white hover:bg-white hover:text-accent",
success: "border-2 border-success bg-success text-white hover:bg-white hover:text-success",
secondary: "border border-secondary bg-secondary text-white hover:bg-white hover:text-secondary",
info: "border border-info bg-info text-white hover:bg-white hover:text-info",
warning: "border border-warning bg-warning text-white hover:bg-white hover:text-warning",
danger: "border border-danger bg-danger text-white hover:bg-white hover:text-danger",
utility: "border border-gray-200 bg-gray-200 text-gray-800 hover:bg-white hover:text-gray-600",
primary_outline: "border border-primary text-primary hover:bg-primary hover:text-white",
accent_outline: "border-2 border-accent text-accent hover:bg-accent hover:text-white",
success_outline: "border-2 border-success text-success hover:bg-success hover:text-white",
secondary_outline: "border border-secondary text-secondary hover:bg-secondary hover:text-white",
info_outline: "border border-info text-info hover:bg-info hover:text-white",
warning_outline: "border border-warning text-warning hover:bg-warning hover:text-white",
danger_outline: "border border-danger text-danger hover:bg-danger hover:text-white",
utility_outline: "border border-gray-200 text-gray-600 hover:bg-gray-200 hover:text-gray-800"
}.freeze

def button_classes(variant = :primary, size: :md, extra: nil)
tokens = [ BUTTON_BASE, BUTTON_VARIANTS.fetch(variant) ]
tokens << BUTTON_SIZES.fetch(size) if size
tokens << extra if extra.present?
tokens.join(" ")
end
end
8 changes: 4 additions & 4 deletions app/views/events/_card.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
<% if registration&.slug.present? %>
<%= link_to "View ticket", registration_ticket_path(registration.slug),
data: { turbo_frame: "_top" },
class: "btn btn-success px-3 py-2 text-xs uppercase leading-tight font-telefon" %>
class: button_classes(:success, size: nil, extra: "px-3 py-2 text-xs uppercase leading-tight font-telefon") %>
<% end %>
<% elsif event.ended? %>
<span class="text-sm text-gray-500 italic">Event ended</span>
Expand All @@ -67,18 +67,18 @@
<%= button_to "Register",
event_registrant_registration_path(event_id: event),
data: { turbo: !event.object.cost_cents.to_i.positive? },
class: "btn btn-accent-outline px-3 py-2 text-xs uppercase leading-tight font-telefon" %>
class: button_classes(:accent_outline, size: nil, extra: "px-3 py-2 text-xs uppercase leading-tight font-telefon") %>
<% else %>
<%= link_to "Register",
new_event_public_registration_path(event),
data: { turbo_frame: "_top" },
class: "btn btn-accent-outline px-3 py-2 text-xs uppercase leading-tight font-telefon" %>
class: button_classes(:accent_outline, size: nil, extra: "px-3 py-2 text-xs uppercase leading-tight font-telefon") %>
<% end %>
<% elsif event.object.public_registration_enabled? && event.object.event_forms.registration.exists? %>
<%= link_to "Register",
new_event_public_registration_path(event),
data: { turbo_frame: "_top" },
class: "btn btn-accent-outline px-3 py-2 text-xs uppercase leading-tight font-telefon" %>
class: button_classes(:accent_outline, size: nil, extra: "px-3 py-2 text-xs uppercase leading-tight font-telefon") %>
<% end %>
<% else %>
<span class="text-sm text-gray-500 italic">Registration closed</span>
Expand Down
12 changes: 6 additions & 6 deletions app/views/events/_registration_section.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<% if allowed_to?(:manage?, event) %>
<%= button_to "Register",
event_registrant_registration_path(event_id: event),
class: "admin-only bg-blue-100 btn btn-primary-outline" %>
class: button_classes(:primary_outline, extra: "admin-only bg-blue-100") %>
<% end %>
<% elsif !registered && !slug_registered %>
<% if slug_cancelled && event.registerable? %>
Expand All @@ -25,18 +25,18 @@
<%= button_to button_text,
event_registrant_registration_path(event_id: event),
data: { turbo: !event.object.cost_cents.to_i.positive? },
class: "btn btn-accent px-10 py-2 text-2xl uppercase",
class: button_classes(:accent, size: nil, extra: "px-10 py-2 text-2xl uppercase"),
style: "font-family: 'Telefon Bold', sans-serif;" %>
<% else %>
<%= link_to button_text,
new_event_public_registration_path(event),
class: "btn btn-accent px-10 py-2 text-2xl uppercase",
class: button_classes(:accent, size: nil, extra: "px-10 py-2 text-2xl uppercase"),
style: "font-family: 'Telefon Bold', sans-serif;" %>
<% end %>
<% elsif event.object.public_registration_enabled? && event.object.event_forms.registration.exists? %>
<%= link_to button_text,
new_event_public_registration_path(event),
class: "btn btn-accent px-10 py-2 text-2xl uppercase",
class: button_classes(:accent, size: nil, extra: "px-10 py-2 text-2xl uppercase"),
style: "font-family: 'Telefon Bold', sans-serif;" %>
<% end %>
<% else %>
Expand All @@ -47,11 +47,11 @@
<% if registered %>
<% registration = event.active_registration_for(current_user&.person) %>
<%= link_to "View ticket", registration_ticket_path(registration.slug),
class: "btn btn-success px-10 py-2 text-2xl uppercase",
class: button_classes(:success, size: nil, extra: "px-10 py-2 text-2xl uppercase"),
style: "font-family: 'Telefon Bold', sans-serif;" %>
<% elsif slug_registered %>
<%= link_to "View ticket", registration_ticket_path(slug_registration.slug),
class: "btn btn-success px-10 py-2 text-2xl uppercase",
class: button_classes(:success, size: nil, extra: "px-10 py-2 text-2xl uppercase"),
style: "font-family: 'Telefon Bold', sans-serif;" %>
<% end %>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/views/events/callouts/scholarship.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@
<%# Native <details>; while it's open :has() hides Agree so the decline form stands alone. %>
<div class="group mt-3 flex flex-wrap items-center gap-3">
<%= form_with url: registration_scholarship_agreement_path(@event_registration.slug), method: :post, class: "group-has-[[open]]:hidden" do %>
<button type="submit" name="agreement" value="yes" class="btn btn-success">
<button type="submit" name="agreement" value="yes" class="<%= button_classes(:success) %>">
<i class="fa-solid fa-check"></i> Agree
</button>
<% end %>

<details class="group/details">
<summary class="btn btn-secondary-outline cursor-pointer list-none [&::-webkit-details-marker]:hidden">
<summary class="<%= button_classes(:secondary_outline, extra: "cursor-pointer list-none [&::-webkit-details-marker]:hidden") %>">
<i class="fa-solid fa-xmark"></i> Decline
</summary>
<%= form_with url: registration_scholarship_decline_path(@event_registration.slug), method: :post, class: "mt-3 w-full max-w-md" do %>
Expand Down
6 changes: 3 additions & 3 deletions app/views/events/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<% if @preview %>
<div class="bg-amber-50 border border-amber-300 text-amber-800 rounded-lg px-4 py-3 mb-4 flex items-center justify-between">
<span><span class="font-medium">Preview</span> β€” unsaved changes</span>
<button onclick="window.close()" class="btn btn-secondary-outline btn-sm">Close preview</button>
<button onclick="window.close()" class="<%= button_classes(:secondary_outline, size: :sm) %>">Close preview</button>
</div>
<% else %>
<!-- Top Right Actions -->
Expand Down Expand Up @@ -110,7 +110,7 @@
<div class="mt-4">
<%= link_to Form::BULK_PAYMENT_PUBLIC_NAME,
new_event_bulk_payment_path(@event),
class: "btn btn-success px-10 py-2 text-2xl uppercase",
class: button_classes(:success, size: nil, extra: "px-10 py-2 text-2xl uppercase"),
style: "font-family: 'Telefon Bold', sans-serif;" %>
</div>
<% end %>
Expand All @@ -131,6 +131,6 @@
<% if @preview %>
<div class="bg-amber-50 border border-amber-300 text-amber-800 rounded-lg px-4 py-3 mt-4 flex items-center justify-between">
<span><span class="font-medium">Preview</span> β€” unsaved changes</span>
<button onclick="window.close()" class="btn btn-secondary-outline btn-sm">Close preview</button>
<button onclick="window.close()" class="<%= button_classes(:secondary_outline, size: :sm) %>">Close preview</button>
</div>
<% end %>
39 changes: 39 additions & 0 deletions spec/helpers/button_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require "rails_helper"

RSpec.describe ButtonHelper, type: :helper do
describe "#button_classes" do
it "combines the base, default size, and the requested variant" do
result = helper.button_classes(:primary)

expect(result).to include("inline-flex", "rounded-lg", "shadow-sm")
expect(result).to include("px-4", "py-2", "text-sm")
expect(result).to include("bg-primary", "hover:text-primary")
end

it "defaults to the primary variant" do
expect(helper.button_classes).to eq(helper.button_classes(:primary))
end

it "swaps in the compact size" do
result = helper.button_classes(:secondary_outline, size: :sm)

expect(result).to include("px-3", "py-1", "text-xs")
expect(result).not_to include("px-4", "py-2", "text-sm")
end

it "omits size utilities when size is nil so a call site can supply its own" do
result = helper.button_classes(:success, size: nil, extra: "px-10 py-2 text-2xl")

expect(result).not_to include("px-4", "text-sm")
expect(result).to include("px-10", "text-2xl")
end

it "appends extra classes" do
expect(helper.button_classes(:primary, extra: "whitespace-nowrap")).to include("whitespace-nowrap")
end

it "raises for an unknown variant so typos fail loudly" do
expect { helper.button_classes(:nope) }.to raise_error(KeyError)
end
end
end