From 8e444ff98b7c5a6e45ecfede7b80efd961d06c66 Mon Sep 17 00:00:00 2001 From: Guillem Arias Fauste Date: Thu, 21 May 2026 12:54:54 +0200 Subject: [PATCH 1/3] =?UTF-8?q?feat(design-system):=20add=20DS::SearchInpu?= =?UTF-8?q?t=20primitive=20(closes=20#1715=20=C2=A73)=20(#1853)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(design-system): add DS::SearchInput + migrate 2 broken-focus callsites Resolves #1715 §3. Two standalone search-field callsites — `/settings/preferences` currency filter and `/settings/providers` filter row — had a hand- rolled markup that ended in `focus:ring-gray-500`. That utility has no backing token in the design system (`ring-gray-500` isn't in Tailwind's default + Sure doesn't register a gray ring color), so the input rendered with zero focus indicator on a bordered bg-container surface. Keyboard users couldn't tell when the field was focused. Introduce `DS::SearchInput` — icon-on-left, bordered, token-backed focus ring matching the DS::Button pattern landing in #1840 (`outline-2 outline-offset-2 outline-gray-900` with the dark-mode override). API: DS::SearchInput.new( name: "...", placeholder: "...", value: ..., aria_label: "...", # defaults to placeholder class: "...", # passed to the wrapper **opts # spread onto the , e.g. data-* ) Migrate the two broken callsites. Three other "search" patterns stay as-is (out of scope for this PR): - `form.search_field :search` inside `styled_form_with` blocks (accounts/show/_activity.html.erb, UI::Account::ActivityFeed) — already routes through StyledFormBuilder's form-field CSS. - Embedded-dropdown search input inside DS::Select, DS::Menu, and the splits/category-select panels — uses a different shape (no border, no ring) because the parent panel provides the chrome. - Category dropdown's combobox search input (app/views/category/dropdowns/show.html.erb) — has a custom `role=combobox` flow and stays intentionally distinct. * feat(design-system): add embedded variant to DS::SearchInput, migrate 2 more callsites Adds `variant: :embedded` to `DS::SearchInput` for search inputs that live *inside* another DS panel (DS::Select dropdown, splits category filter, future DS::Popover-hosted filters). No own border / no own focus ring — the parent panel provides the chrome, so adding ring + outline competes with its `focus-within` state. API: DS::SearchInput.new(variant: :embedded, placeholder: "...", data: {...}) The `:standalone` default (from the previous commit) stays unchanged and remains the right choice for top-of-list filter inputs. Migrated: - `app/components/DS/select.html.erb` — the in-dropdown search input for `DS::Select.new(searchable: true)`. Was the only remaining internal raw markup in the component. - `app/views/splits/_category_select.html.erb` — split-transaction category picker filter. Same shape as DS::Select's search but hand-rolled because the picker isn't a vanilla DS::Select. Three other search patterns stay out of scope (intentionally, per the previous commit): - `form.search_field :search` inside `styled_form_with` — uses form-field CSS, different visual contract. - `app/views/category/dropdowns/show.html.erb` — bespoke `role="combobox"` flow with `aria-expanded` / `aria-autocomplete` semantics that don't belong in this primitive. * fix(review): mobile font + embedded variant focus-within ring - DS::SearchInput: switch text-sm -> text-base sm:text-sm on both variants so the input keeps its 16px base size on mobile. iOS Safari zooms the viewport when a focused input is below 16px, which the unconditional text-sm was triggering on the Settings Preferences currency search and Settings Bank Sync provider search. - DS::Select (searchable variant) + splits/_category_select: add focus-within:ring-4 focus-within:ring-alpha-black-200 (with theme-dark variant) on the wrapper around the embedded search input. The embedded variant intentionally has no own focus ring so it inherits chrome from its parent panel — but the two current parent panels were not providing one, so keyboard focus on the dropdown search box rendered with no visible indicator. Ring matches the .form-field token used across the design system. * fix(merge): repair DS::Select search input merge resolution The previous merge of main left invalid Ruby inside the DS::SearchInput `data:` hash: aria-label="<%= t("helpers.select.search_placeholder") %>" This is an ERB string assignment masquerading as a hash entry — it does not parse and would have raised SyntaxError at render. Two follow-ups: - Drop the `aria-label` entry entirely. `DS::SearchInput` already defaults `aria_label` to `placeholder`, and `placeholder` is set on the call, so the resulting already carries `aria-label="<%= t(...) %>"`. - Restore the `input->select#syncTabindex` action that main #1848 added on the embedded search input. It keeps the roving tabindex on the listbox in sync as filtered results change. Original PR branch had only `list-filter#filter`; reintegrate both with explicit `input->` event prefixes for parity with main. --------- Signed-off-by: Juan José Mata Co-authored-by: Juan José Mata --- app/components/DS/search_input.html.erb | 17 ++++ app/components/DS/search_input.rb | 78 +++++++++++++++++++ app/components/DS/select.html.erb | 18 ++--- app/views/settings/preferences/show.html.erb | 19 ++--- .../providers/_search_filters.html.erb | 21 +++-- app/views/splits/_category_select.html.erb | 17 ++-- 6 files changed, 129 insertions(+), 41 deletions(-) create mode 100644 app/components/DS/search_input.html.erb create mode 100644 app/components/DS/search_input.rb diff --git a/app/components/DS/search_input.html.erb b/app/components/DS/search_input.html.erb new file mode 100644 index 000000000..e79fd6715 --- /dev/null +++ b/app/components/DS/search_input.html.erb @@ -0,0 +1,17 @@ +<%= tag.div class: container_classes do %> + <%= tag.input type: "search", + name: name, + value: value, + placeholder: placeholder, + "aria-label": aria_label, + autocomplete: "off", + class: input_classes, + **opts %> + <% if variant == :embedded %> + <%= helpers.icon("search", class: "absolute inset-0 ml-2 transform top-1/2 -translate-y-1/2") %> + <% else %> +
+ <%= helpers.icon("search", class: "text-secondary") %> +
+ <% end %> +<% end %> diff --git a/app/components/DS/search_input.rb b/app/components/DS/search_input.rb new file mode 100644 index 000000000..51c3dc9ca --- /dev/null +++ b/app/components/DS/search_input.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +# `DS::SearchInput` is the search-field primitive. +# +# Two variants: +# +# - `:standalone` (default) — top-of-list filter inputs (Preferences +# currency search, Settings/Bank Sync provider filter). Bordered +# bg-container surface, icon-on-left, full token-backed focus ring. +# +# - `:embedded` — search-inside-a-panel (DS::Select internal search, +# splits category filter, any future DS::Popover that hosts a filter). +# No border / no own focus ring — the parent panel provides the +# chrome, so adding ring + outline here would compete with the +# parent's focus-within state. +# +# For `form.search_field :foo` inside a `styled_form_with` block, +# keep using the form helper — it routes through `StyledFormBuilder`'s +# form-field CSS, which is a different visual contract. +class DS::SearchInput < DesignSystemComponent + VARIANTS = %i[standalone embedded].freeze + + attr_reader :variant, :name, :placeholder, :value, :aria_label, :extra_classes, :opts + + def initialize(variant: :standalone, name: nil, placeholder: nil, value: nil, aria_label: nil, class: nil, **opts) + @variant = variant.to_sym + @name = name + @placeholder = placeholder + @value = value + @aria_label = aria_label || placeholder + @extra_classes = binding.local_variable_get(:class) + @opts = opts + + raise ArgumentError, "Invalid variant: #{@variant}. Must be one of #{VARIANTS.inspect}" unless VARIANTS.include?(@variant) + end + + def container_classes + class_names("relative", extra_classes) + end + + def input_classes + # `text-base sm:text-sm` — keep the base font at 16px so iOS Safari + # does not zoom the viewport when the input is focused. Shrink to + # 14px from `sm:` upward. The previous unconditional `text-sm` + # triggered the mobile zoom regression. + case variant + when :embedded + # No own focus ring — the parent panel handles focus chrome via + # `focus-within`. `focus:outline-hidden focus:ring-0` neutralizes + # the browser default so it doesn't compete with the panel's + # state. + "bg-container text-primary text-base sm:text-sm placeholder:text-secondary font-normal " \ + "h-10 pl-10 w-full border-none rounded-lg " \ + "focus:outline-hidden focus:ring-0" + else + # `focus-visible:outline-*` matches the focus-ring pattern from + # DS::Button (base.css) so every interactive surface in the design + # system uses the same ring token. Replaces the broken + # `focus:ring-gray-500` from the inline callsites — that utility + # had no backing token and rendered invisibly on the bordered + # bg-container surface. + "block w-full border border-secondary rounded-md py-2.5 pl-10 pr-3 bg-container text-base sm:text-sm " \ + "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900 " \ + "theme-dark:focus-visible:outline-white" + end + end + + def icon_classes + variant == :embedded ? "absolute inset-0 ml-2 transform top-1/2 -translate-y-1/2" : "text-secondary" + end + + def icon_wrapper_classes + # Standalone variant wraps the icon in a positioned div; embedded + # places the icon as an absolutely-positioned sibling so the parent + # panel can stay in control of vertical alignment. + variant == :embedded ? nil : "absolute inset-0 ml-2 top-1/2 -translate-y-1/2 pointer-events-none" + end +end diff --git a/app/components/DS/select.html.erb b/app/components/DS/select.html.erb index 6c06d83f1..1ac9b027f 100644 --- a/app/components/DS/select.html.erb +++ b/app/components/DS/select.html.erb @@ -33,15 +33,15 @@ + <% end %> <% unless snaptrade_item.scheduled_for_deletion? %>
@@ -155,5 +157,5 @@ <% end %>
<% end %> - + <% end %> <% end %> diff --git a/app/views/sophtron_items/_sophtron_item.html.erb b/app/views/sophtron_items/_sophtron_item.html.erb index d91450a86..e8f64bb6e 100644 --- a/app/views/sophtron_items/_sophtron_item.html.erb +++ b/app/views/sophtron_items/_sophtron_item.html.erb @@ -4,105 +4,107 @@ <% provider_display_name = sophtron_item.provider_display_name %> <% manual_sync_required = sophtron_item.manual_sync_required? %> <% connected_institution_options = sophtron_item.connected_institution_options %> -
- -
- <%= icon "chevron-right", class: "group-open:transform group-open:rotate-90" %> + <%= render DS::Disclosure.new(variant: :card, open: true) do |disclosure| %> + <% disclosure.with_summary_content do %> +
+
+ <%= icon "chevron-right", class: "group-open:rotate-90 motion-safe:transition-transform motion-safe:duration-150" %> -
- <% if sophtron_item.logo.attached? %> - <%= image_tag sophtron_item.logo, class: "rounded-full h-full w-full", loading: "lazy" %> - <% else %> -
- <%= tag.p provider_display_name.first.upcase, class: "text-orange-600 text-xs font-medium" %> -
- <% end %> -
- -
-
- <%= tag.p provider_display_name, class: "font-medium text-primary" %> - <% if manual_sync_required %> - <%= t(".manual_sync") %> - <% end %> - <% if sophtron_item.scheduled_for_deletion? %> -

<%= t(".deletion_in_progress") %>

+
+ <% if sophtron_item.logo.attached? %> + <%= image_tag sophtron_item.logo, class: "rounded-full h-full w-full", loading: "lazy" %> + <% else %> +
+ <%= tag.p provider_display_name.first.upcase, class: "text-orange-600 text-xs font-medium" %> +
<% end %>
- <% if sophtron_item.accounts.any? %> -

- <%= sophtron_item.institution_summary %> -

- <% end %> - <% if sophtron_item.syncing? %> -
- <%= icon "loader", size: "sm", class: "animate-spin" %> - <%= tag.span t(".syncing") %> -
- <% elsif sophtron_item.sync_error.present? %> -
- <%= render DS::Tooltip.new(text: sophtron_item.sync_error, icon: "alert-circle", size: "sm", color: "destructive", as: :span) %> - <%= tag.span t(".error"), class: "text-destructive" %> -
- <% else %> -

- <% if sophtron_item.last_synced_at %> - <% if sophtron_item.sync_status_summary %> - <%= t(".status_with_summary", timestamp: time_ago_in_words(sophtron_item.last_synced_at), summary: sophtron_item.sync_status_summary) %> - <% else %> - <%= t(".status", timestamp: time_ago_in_words(sophtron_item.last_synced_at)) %> - <% end %> - <% else %> - <%= t(".status_never") %> + +

+
+ <%= tag.p provider_display_name, class: "font-medium text-primary" %> + <% if manual_sync_required %> + <%= t(".manual_sync") %> <% end %> -

- <% end %> + <% if sophtron_item.scheduled_for_deletion? %> +

<%= t(".deletion_in_progress") %>

+ <% end %> +
+ <% if sophtron_item.accounts.any? %> +

+ <%= sophtron_item.institution_summary %> +

+ <% end %> + <% if sophtron_item.syncing? %> +
+ <%= icon "loader", size: "sm", class: "animate-spin" %> + <%= tag.span t(".syncing") %> +
+ <% elsif sophtron_item.sync_error.present? %> +
+ <%= render DS::Tooltip.new(text: sophtron_item.sync_error, icon: "alert-circle", size: "sm", color: "destructive", as: :span) %> + <%= tag.span t(".error"), class: "text-destructive" %> +
+ <% else %> +

+ <% if sophtron_item.last_synced_at %> + <% if sophtron_item.sync_status_summary %> + <%= t(".status_with_summary", timestamp: time_ago_in_words(sophtron_item.last_synced_at), summary: sophtron_item.sync_status_summary) %> + <% else %> + <%= t(".status", timestamp: time_ago_in_words(sophtron_item.last_synced_at)) %> + <% end %> + <% else %> + <%= t(".status_never") %> + <% end %> +

+ <% end %> +
-
-
- <% if manual_sync_required || Rails.env.development? %> - <%= render DS::Button.new( - variant: :icon, - icon: "refresh-cw", - href: sync_sophtron_item_path(sophtron_item), - frame: (manual_sync_required ? "modal" : nil), - title: t(".sync_now") - ) %> - <% end %> +
+ <% if manual_sync_required || Rails.env.development? %> + <%= render DS::Button.new( + variant: :icon, + icon: "refresh-cw", + href: sync_sophtron_item_path(sophtron_item), + frame: (manual_sync_required ? "modal" : nil), + title: t(".sync_now") + ) %> + <% end %> - <%= render DS::Menu.new do |menu| %> - <% if connected_institution_options.many? %> - <% connected_institution_options.each do |institution| %> - <% institution_manual_sync_required = sophtron_item.manual_sync_required_for_institution?(institution[:institution_key]) %> + <%= render DS::Menu.new do |menu| %> + <% if connected_institution_options.many? %> + <% connected_institution_options.each do |institution| %> + <% institution_manual_sync_required = sophtron_item.manual_sync_required_for_institution?(institution[:institution_key]) %> + <% menu.with_item( + variant: "button", + text: t(institution_manual_sync_required ? ".automatic_sync_for" : ".manual_sync_action_for", institution: institution[:name]), + icon: institution_manual_sync_required ? "refresh-cw" : "pause-circle", + href: toggle_manual_sync_sophtron_item_path(sophtron_item, institution_key: institution[:institution_key]), + method: :post + ) %> + <% end %> + <% else %> <% menu.with_item( variant: "button", - text: t(institution_manual_sync_required ? ".automatic_sync_for" : ".manual_sync_action_for", institution: institution[:name]), - icon: institution_manual_sync_required ? "refresh-cw" : "pause-circle", - href: toggle_manual_sync_sophtron_item_path(sophtron_item, institution_key: institution[:institution_key]), + text: t(manual_sync_required ? ".automatic_sync" : ".manual_sync_action"), + icon: manual_sync_required ? "refresh-cw" : "pause-circle", + href: toggle_manual_sync_sophtron_item_path(sophtron_item), method: :post ) %> <% end %> - <% else %> <% menu.with_item( variant: "button", - text: t(manual_sync_required ? ".automatic_sync" : ".manual_sync_action"), - icon: manual_sync_required ? "refresh-cw" : "pause-circle", - href: toggle_manual_sync_sophtron_item_path(sophtron_item), - method: :post + text: t(".delete"), + icon: "trash-2", + href: sophtron_item_path(sophtron_item), + method: :delete, + confirm: CustomConfirm.for_resource_deletion(provider_display_name, high_severity: true) ) %> <% end %> - <% menu.with_item( - variant: "button", - text: t(".delete"), - icon: "trash-2", - href: sophtron_item_path(sophtron_item), - method: :delete, - confirm: CustomConfirm.for_resource_deletion(provider_display_name, high_severity: true) - ) %> - <% end %> +
-
+ <% end %> <% unless sophtron_item.scheduled_for_deletion? %>
@@ -154,5 +156,5 @@ <% end %>
<% end %> -
+ <% end %> <% end %> From 547dd21bf281651686e05bfd841b772c31ec5016 Mon Sep 17 00:00:00 2001 From: Guillem Arias Fauste Date: Thu, 21 May 2026 12:57:26 +0200 Subject: [PATCH 3/3] =?UTF-8?q?feat(design-system):=20migrate=203=20provid?= =?UTF-8?q?er=20panels=20to=20DS::Disclosure=20:card=20(#1715=20=C2=A76)?= =?UTF-8?q?=20(#1856)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(design-system): DS::Disclosure :card variant + migrate 14 provider items Resolves part of #1715 §6. The provider-item view templates (binance, brex, coinbase, coinstats, enable_banking, ibkr, indexa_capital, kraken, lunchflow, mercury, plaid, simplefin, snaptrade, sophtron — 14 in total) all hand-rolled the same `
` shell with a custom summary inside and content below. Extend `DS::Disclosure` with a `:card` variant that bakes the card chrome onto the `
` element itself; the summary becomes slot-driven via the existing `summary_content` slot. Provider items keep their custom summary content (logos, brand colors, status copy) unchanged — they just hand it to the slot instead of writing it between `` tags. API: DS::Disclosure.new(variant: :card, open: true) do |d| d.with_summary_content do
chevron + custom summary markup
end body content end While here: - Drop the no-op `group-open:transform` from the default chevron (Tailwind v4 applies `rotate-90` directly). - Add `motion-safe:transition-transform motion-safe:duration-150` to chevron rotation for reduced-motion respect (matches the pattern landing in #1841). - Extract `summary_classes` / `details_classes` helpers so the default and card surfaces stay readable side-by-side. Note: this PR touches `DS::Disclosure` and will textually conflict with #1841 (focus-ring + reduced-motion polish). Both changes are compatible — when #1841 merges first, the resolution is just preserving both edits (the focus-ring classes are already merged into `summary_classes` here). * feat(design-system): migrate 3 provider panels to DS::Disclosure :card variant Resolves the panel slice of #1715 §6. Continuation of the DS::Disclosure :card variant work — same migration pattern, applied to the 3 provider-PANEL templates that share the card shape with the provider-item templates landing on the parent branch. Migrated `
` → `DS::Disclosure.new(variant: :card)` in: - `app/views/settings/providers/_kraken_panel.html.erb` — 1 details in the items-each loop. - `app/views/settings/providers/_mercury_panel.html.erb` — 1 details in the items-each loop. - `app/views/settings/providers/_brex_panel.html.erb` — 2 details: one in the items-each loop, one standalone "add connection" panel that opened by default when no active items existed. The conditional `<%= "open" unless active_items.any? %>` becomes `open: active_items.none?` on the `:card` disclosure. Panels do NOT show a chevron in their summary (different UX from the per-item rows in #1855), so the migration preserves that — no chevron inserted. NOT migrated (intentionally — different shapes): - `_ibkr_panel.html.erb` — `
`. Uses bg-surface-inset, not bg-container — needs a `:card-inset` variant we haven't built. Deferred. - `_indexa_capital_panel.html.erb` — `
` with no card chrome. Inline expander; doesn't fit either disclosure variant. - `_snaptrade_panel.html.erb` — same inline pattern as indexa_capital. * fix(review): use ring-alpha-black-300 focus token in DS::Disclosure CodeRabbit P2: switch the focus-visible outline from raw gray-900/white palette values to the alpha-black-300 ring token, matching the established focus pattern on settings/provider_card.html.erb. This keeps theme behavior centralized in the design system tokens instead of branching on theme-dark: in the component. Applies to both :default and :card summary variants. * fix(review): stretch DS::Disclosure summary_content to full width Codex P2 follow-up on the disclosure-migration stack: \`\` is \`display: list-item\`, so a flex inner div inside the slot shrink-wraps to content width — any \`justify-between\` the caller adds has nothing to distribute, and the right-side admin actions collapse toward the title across every provider-item partial migrated to \`DS::Disclosure variant: :card\` in #1855 (and the panels in #1856 / #1857 / #1858 that inherit this component). Wrap the slot in \`
\` so caller-supplied flex rows stretch across the card. \`:default\` variant is unchanged (it never uses \`summary_content\`). * fix(review): stretch :card summary flex row to full width Codex P2 follow-up on #1856: the migrated kraken / mercury / brex panel summary rows wrap their content in \`
\`, but a flex container inside \`\` (\`display: list-item\`) shrink-wraps to content size, so \`justify-between\` had nothing to distribute and the right-side admin actions collapsed toward the title. Add \`w-full\` so the flex row stretches across the card. The deeper component-level fix lands in #1855 (wraps \`summary_content\` in a \`w-full\` block); this commit makes #1856 self-contained against the merge order. --- .../settings/providers/_brex_panel.html.erb | 38 ++++++++++--------- .../settings/providers/_kraken_panel.html.erb | 36 +++++++++--------- .../providers/_mercury_panel.html.erb | 24 ++++++------ 3 files changed, 53 insertions(+), 45 deletions(-) diff --git a/app/views/settings/providers/_brex_panel.html.erb b/app/views/settings/providers/_brex_panel.html.erb index 5b87ad38e..545e01802 100644 --- a/app/views/settings/providers/_brex_panel.html.erb +++ b/app/views/settings/providers/_brex_panel.html.erb @@ -38,18 +38,20 @@ <% if active_items.any? %>
<% active_items.each do |item| %> -
- -
-
-

<%= item.name.to_s.first.to_s.upcase %>

-
-
-

<%= item.name %>

-

<%= item.sync_status_summary %>

+ <%= render DS::Disclosure.new(variant: :card) do |disclosure| %> + <% disclosure.with_summary_content do %> +
+
+
+

<%= item.name.to_s.first.to_s.upcase %>

+
+
+

<%= item.name %>

+

<%= item.sync_status_summary %>

+
-
+ <% end %>
@@ -102,16 +104,18 @@
<% end %>
-
+ <% end %> <% end %>
<% end %> -
class="group bg-container p-4 shadow-border-xs rounded-xl"> - - <%= icon "plus" %> - <%= t("brex_items.provider_panel.add_connection") %> - + <%= render DS::Disclosure.new(variant: :card, open: active_items.none?) do |disclosure| %> + <% disclosure.with_summary_content do %> +
+ <%= icon "plus" %> + <%= t("brex_items.provider_panel.add_connection") %> +
+ <% end %> <% brex_item = Current.family.brex_items.build(name: t("brex_items.provider_panel.default_connection_name")) %> <%= styled_form_with model: brex_item, @@ -138,7 +142,7 @@ <%= form.submit t("brex_items.provider_panel.add_connection") %>
<% end %> -
+ <% end %>
<% if credentialed_items.any? %> diff --git a/app/views/settings/providers/_kraken_panel.html.erb b/app/views/settings/providers/_kraken_panel.html.erb index 8490b1c49..39719602c 100644 --- a/app/views/settings/providers/_kraken_panel.html.erb +++ b/app/views/settings/providers/_kraken_panel.html.erb @@ -24,24 +24,26 @@ <% if items.any? %>
<% items.each do |item| %> -
- -
-
- <%= icon "waves", size: "sm", class: "text-primary" %> -
-
-

<%= item.name %>

-

- <% if item.syncing? %> - <%= t("settings.providers.kraken_panel.syncing") %> - <% else %> - <%= item.sync_status_summary %> - <% end %> -

+ <%= render DS::Disclosure.new(variant: :card) do |disclosure| %> + <% disclosure.with_summary_content do %> +
+
+
+ <%= icon "waves", size: "sm", class: "text-primary" %> +
+
+

<%= item.name %>

+

+ <% if item.syncing? %> + <%= t("settings.providers.kraken_panel.syncing") %> + <% else %> + <%= item.sync_status_summary %> + <% end %> +

+
-
+ <% end %>
@@ -97,7 +99,7 @@
<% end %>
-
+ <% end %> <% end %>
<% end %> diff --git a/app/views/settings/providers/_mercury_panel.html.erb b/app/views/settings/providers/_mercury_panel.html.erb index 4e711b4c7..c653ed66a 100644 --- a/app/views/settings/providers/_mercury_panel.html.erb +++ b/app/views/settings/providers/_mercury_panel.html.erb @@ -19,18 +19,20 @@ <% if active_items.any? %>
<% active_items.each do |item| %> -
- -
-
-

<%= item.name.to_s.first.to_s.upcase %>

-
-
-

<%= item.name %>

-

<%= item.sync_status_summary %>

+ <%= render DS::Disclosure.new(variant: :card) do |disclosure| %> + <% disclosure.with_summary_content do %> +
+
+
+

<%= item.name.to_s.first.to_s.upcase %>

+
+
+

<%= item.name %>

+

<%= item.sync_status_summary %>

+
-
+ <% end %>
@@ -82,7 +84,7 @@
<% end %>
-
+ <% end %> <% end %>
<% end %>