Skip to content

feat(table): collapse the filter button group into a dropdown on small screens - #2137

Merged
markdumay merged 2 commits into
mainfrom
feat/filter-responsive-dropdown
Aug 23, 2026
Merged

feat(table): collapse the filter button group into a dropdown on small screens#2137
markdumay merged 2 commits into
mainfrom
feat/filter-responsive-dropdown

Conversation

@markdumay

Copy link
Copy Markdown
Collaborator

Summary

Adds filter-responsive to the table shortcode: below the site's main breakpoint the filter button group is replaced by a dropdown, for groups with more categories than a narrow viewport can hold.

Off by default, so every existing filtered table renders its button group at all widths, exactly as before.

Why

A filter group of more than a few categories overflows its row on a phone. The nav component already solves this shape with a companion dropdown, so this reuses that mechanism rather than inventing a second one.

How it works

The dropdown carries no behavior of its own. Each item names a filter button through data-link, and nav.js replays the click on it — so filtering still runs through the single existing handler and keeps composing with sorting, paging and search. No new filter JavaScript.

Three supporting changes:

  • assets/panel-dropdown.htmlnav.html's inline nav-dropdown extracted into a shared partial so both callers use it. Every tag, class and attribute is carried over unchanged (including the double space in dropdown-item text-nowrap, kept so the extraction reads as a move rather than a rewrite). Only the emitted indentation differs, since the markup no longer sits nested inside nav.html — verified by diffing a full example-site build against main.
  • nav.js — the companion sync now delegates from the [data-companion] group and resolves the control with closest, instead of assuming a nav's button > li > ul nesting. A nav and a button group both reach their dropdown, so neither strands a stale label when the viewport crosses the breakpoint.
  • _shortcodes/table.html — forwards the new argument, which the shortcode's explicit allowlist would otherwise drop.

filter-responsive is defined inline in data/structures/table.yml rather than in mod-utils, so this needs no module release. Note the global responsive argument was not reused: it defaults to true, which would have flipped every existing table into dropdown mode, and table.yml already uses "responsive" to describe the horizontal-scroll wrapper.

A no-op at xs, which has no Bootstrap display-utility infix to hide the group with — the same guard wrap already applies.

Bug fix included

fix(nav): start the companion dropdown on the nav's own active entry

panel-dropdown hardcoded the first entry as active, so a nav using nav-show to open on a later tab advertised the wrong one — the toggle read "Tab 1" while Tab 3 was the tab actually showing. Selecting a tab corrected it, so the mismatch only showed on first paint, which is when a reader looks at it.

Pre-existing; the extraction only made it easy to see. nav-show names its entry by item id, so nav.html resolves it to an index using the same {args.id}-btn-{index} expression its own buttons use, and passes it as active.

Testing

New fixture in exampleSite/content/en/table-demo.md (fixture-filter-responsive), with enough categories to overflow a narrow viewport.

Driven in a browser at 390px and 1200px:

  • Dropdown item → data-link → real button → table filters. Rows went [widget, gadget, doohickey, thingamajig, whatsit][doohickey]; the real button took active and the toggle relabeled.
  • At 1200px, clicking a real button updated the hidden dropdown's label and active item, so crossing the breakpoint doesn't strand a stale label.
  • The three pre-existing filter fixtures render a plain btn-group with no companion and no dropdown at 390px — default behavior unchanged.
  • Nav regression: forward (dropdown → tab + pane switch) and reverse (tab button → dropdown label) both still work.
  • The nav-show fixture's dropdown now renders "Tab 3", matching its active tab.

pnpm lint clean, pnpm test:templates passes, example site builds clean.

Follow-ups (not in this PR)

  • mod-blocks' list component forwards filter / filter_col / wrap but not filter-responsive, so component-rendered tables can't opt in until it does.
  • Docs live in gethinode/mod-docs — see the companion PR. That PR should not merge before this ships in v3.22.0, or its example would pass an argument the theme doesn't know and raise an "Invalid arguments" warning.

markdumay and others added 2 commits August 23, 2026 10:30
…l screens

A filter group of more than a few categories outgrows a narrow viewport and
overflows its row. `filter-responsive` swaps it for a dropdown below the site's
main breakpoint. Off by default, so every existing table keeps its button group
at all widths, and a no-op at `xs`, which has no display-utility infix to hide
the group with — the same guard `wrap` already applies.

The dropdown carries no behavior of its own. Each item names a filter button
through `data-link`, and nav.js replays the click on it, so filtering still runs
through the one handler and keeps composing with sorting, paging and search.

Extracts nav.html's inline nav-dropdown into assets/panel-dropdown.html so both
callers share it. Every tag, class and attribute is carried over unchanged; only
the emitted indentation differs, since the markup no longer sits nested inside
nav.html.

Generalizes nav.js's companion sync to delegate from the `[data-companion]`
group and resolve the control with `closest`, rather than assuming a nav's
`button > li > ul` nesting. A nav and a button group now both reach their
dropdown, so neither strands a stale label when the viewport crosses the
breakpoint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The dropdown hardcoded the first entry as active, so a nav using `nav-show` to
open on a later tab advertised the wrong one: the toggle read "Tab 1" while
Tab 3 was the tab actually showing, and the dropdown marked "Tab 1" as active.
Selecting a tab fixed the label, so the mismatch only showed on first paint —
which is exactly when a reader looks at it.

`nav-show` names its entry by item id rather than position, so nav.html resolves
it to an index using the same `{args.id}-btn-{index}` expression its own buttons
use, and passes it to the dropdown as `active`. Falls back to the first entry
when `nav-show` is unset or names an item that does not exist.

Pre-existing, and carried over unchanged when the inline nav-dropdown was
extracted; the extraction only made it easy to see. The table filter group has
no equivalent case, since its "All" button is always the initial one.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@netlify

netlify Bot commented Aug 23, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit 71b0e08
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a8ab28434218c00086d5f0a
😎 Deploy Preview https://deploy-preview-2137--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@markdumay

Copy link
Copy Markdown
Collaborator Author

Docs companion: gethinode/mod-docs#139 — hold that one until this ships in v3.22.0, or its example will pass an argument the released theme does not recognize.

@markdumay
markdumay merged commit e1bfc5e into main Aug 23, 2026
17 checks passed
@markdumay
markdumay deleted the feat/filter-responsive-dropdown branch August 23, 2026 09:31
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.22.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant