diff --git a/app/components/DS/pill.rb b/app/components/DS/pill.rb index a868d8baa..b04ef61d6 100644 --- a/app/components/DS/pill.rb +++ b/app/components/DS/pill.rb @@ -32,6 +32,13 @@ class DS::Pill < DesignSystemComponent # # Other options: # + # - `show_dot:` defaults per mode. Stage markers (`marker: true`) keep + # their dot; status / category badges (`marker: false`) are clean by + # default — the pill shape + tone + label already carry the signal, so + # a leading dot is usually redundant and noisy in dense lists. Pass + # `show_dot: true` to opt a badge back in where the dot is genuinely + # additive: live / temporal status ("Syncing", "Active"), or a single + # sparse pill where the dot anchors it as a discrete element. # - `dot_only: true` renders only the colored dot (no label, no border). # Use on the collapsed sidebar nav, where there's no room for the label. # - `icon:` overrides the dot with a Lucide icon (sized xs, current color). @@ -44,13 +51,15 @@ class DS::Pill < DesignSystemComponent # - Sure has full violet / indigo / fuchsia / amber / green / gray / # red ramps in the design system; this component picks named tokens # at render time. No raw hex. - def initialize(label: nil, tone: :violet, style: :soft, size: :sm, show_dot: true, dot_only: false, title: nil, icon: nil, marker: true, custom_color: nil) + def initialize(label: nil, tone: :violet, style: :soft, size: :sm, show_dot: nil, dot_only: false, title: nil, icon: nil, marker: true, custom_color: nil) resolved_tone = SEMANTIC_TONE_ALIASES.fetch(tone.to_sym, tone.to_sym) @label = label || I18n.t("ds.pill.default_label", default: "Beta") @tone = TONES.include?(resolved_tone) ? resolved_tone : :violet @style = STYLES.include?(style.to_sym) ? style.to_sym : :soft @size = SIZES.include?(size.to_sym) ? size.to_sym : :sm - @show_dot = show_dot + # Default per mode: markers keep their dot, badges are dot-less. An + # explicit show_dot: true/false always wins. + @show_dot = show_dot.nil? ? marker : show_dot @dot_only = dot_only @title = title @icon = icon diff --git a/app/views/settings/providers/_status_pill.html.erb b/app/views/settings/providers/_status_pill.html.erb index efabfd989..e0fefc018 100644 --- a/app/views/settings/providers/_status_pill.html.erb +++ b/app/views/settings/providers/_status_pill.html.erb @@ -7,9 +7,12 @@ else :neutral end %> +<%# Provider connection state is genuine live status — keep the indicator dot + (badge mode is dot-less by default; this is a deliberate opt-in). %> <%= render DS::Pill.new( label: t("settings.providers.status.#{status}"), tone: tone, marker: false, + show_dot: true, size: :sm ) %> diff --git a/test/components/DS/pill_test.rb b/test/components/DS/pill_test.rb index a066e6e94..b66b0a200 100644 --- a/test/components/DS/pill_test.rb +++ b/test/components/DS/pill_test.rb @@ -51,6 +51,26 @@ class DS::PillTest < ViewComponent::TestCase assert_includes pill.palette[:bg], "color-red-50" end + test "marker mode shows the dot by default" do + render_inline(DS::Pill.new(label: "Beta", tone: :violet)) + assert_selector "span.inline-block.rounded-full" + end + + test "badge mode (marker: false) is dot-less by default" do + render_inline(DS::Pill.new(label: "Member", tone: :neutral, marker: false)) + assert_no_selector "span.inline-block.rounded-full" + end + + test "badge mode opts back into the dot with show_dot: true" do + render_inline(DS::Pill.new(label: "Active", tone: :success, marker: false, show_dot: true)) + assert_selector "span.inline-block.rounded-full" + end + + test "marker mode can drop the dot with show_dot: false" do + render_inline(DS::Pill.new(label: "Beta", tone: :violet, show_dot: false)) + assert_no_selector "span.inline-block.rounded-full" + end + test "custom color renders dynamic badge styles" do render_inline(DS::Pill.new(label: "Groceries", marker: false, custom_color: "#f97316")) diff --git a/test/components/previews/pill_component_preview.rb b/test/components/previews/pill_component_preview.rb index ae036179c..fce55a602 100644 --- a/test/components/previews/pill_component_preview.rb +++ b/test/components/previews/pill_component_preview.rb @@ -39,8 +39,12 @@ class PillComponentPreview < ViewComponent::Preview # @!endgroup # @!group Status badges (marker: false, semantic tones) + # Badge mode is dot-less by default — tone + label carry the signal. Opt the + # dot back in with show_dot: true only where it's genuinely additive (live / + # temporal status, or a single sparse pill). status_active below shows the + # opt-in; status_pending / status_archived show the clean default. def status_active - render DS::Pill.new(label: "Active", tone: :success, marker: false) + render DS::Pill.new(label: "Active", tone: :success, marker: false, show_dot: true) end def status_pending