Replace Members inline filters with a Filter drawer, faceted counts, and job-title normalization - #39
Open
Bjjj834 wants to merge 2 commits into
Open
Replace Members inline filters with a Filter drawer, faceted counts, and job-title normalization#39Bjjj834 wants to merge 2 commits into
Bjjj834 wants to merge 2 commits into
Conversation
jest-dom was already a devDependency but no setupTests.js imported it, so matchers like toBeInTheDocument/toHaveValue were unavailable to any test in the suite.
Reworks the Members page filtering UX per feedback: the page now shows
only a search bar and a single "Filter" button (with an active-filter
count badge), instead of separate Company/Job Title dropdowns inline.
Clicking Filter opens a right-side drawer (near-full-width on mobile)
with two collapsible, searchable, multi-select checkbox sections.
Since the full ~993-member dataset is already loaded client-side (see
prior investigation), all of this — filtering, pagination, and facet
counts — operates on the complete dataset, not just the visible page.
No backend changes were needed or made.
- jobTitleNormalization.js: explicit, documented canonical-mapping
utility that merges job titles differing only in separator/formatting
(e.g. "Founder & CEO" / "Founder + CEO" / "Founder and CEO" / "Founder
CEO" -> "Founder / CEO"), while never merging distinct roles or
seniority levels (e.g. "Product Manager" vs "Senior Product Manager",
"CEO" vs "Founder / CEO"). A member's original stored title is never
overwritten — normalization is used only for grouping/filtering.
- memberFacets.js: filtering (AND across Company/Job Title sections, OR
within a section, combined with general search) plus true faceted
counts — Company counts respect the current search + selected Job
Titles, Job Title counts respect search + selected Companies. A
selected option stays visible (count 0) even if another filter zeroes
it out. Blank/null/placeholder values ("N/A", "-", etc.) are excluded
from filter options entirely.
- FilterSection.js / FilterDrawer.js: collapsible search-within-section
checkbox lists, backdrop + Escape-to-close + manual focus trap
(useFocusTrap.js), "Clear all" and "Done" actions.
- ArmorMembers.js: wires it all together — active-filter chips in the
main area, empty state, search/company/jobTitle/page synced to URL
query params (comma-separated multi-values, empty params omitted),
pagination resets to page 1 on filter change but not on initial
URL-hydrated load, and debounces the free-text search box.
Verified manually against the live ~993-row dataset in a browser
(desktop + mobile viewports): faceted counts, cross-filtering, URL
refresh/share, and the "N/A"/"NA" placeholder exclusion all behave
correctly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Members page (previous state, PR #37 still unmerged) had Company and Job Title as separate dropdowns inline next to the search bar. This PR replaces that with the requested UX: a single search bar + one "Filter" button that opens a drawer, plus faceted counts and job-title normalization. Built fresh off
mainsince #37 hasn't merged yet.Investigation (done before writing code)
UX design
Filterbutton, showing an active-filter count badge (Filter (2)) when applicable (search itself doesn't count).label — count.Company: Anote ×) and can be cleared individually or all at once (drawer's "Clear all").useFocusTrap.js).Filtering logic
memberFacets.js.N/A,NA,-,none, etc.) are excluded from filter options entirely (found and fixed during manual QA — real data had "N/A"/"NA" showing up as selectable "companies").Job-title normalization rules (
jobTitleNormalization.js)Documented in the file itself. Summary:
cleanTitle— trims/collapses whitespace only.normalizeSeparators— rewrites&,+,,, and the word "and" into a single canonical" / "separator. Safe to apply unconditionally: it only changes how words are joined, never which words are present, so it can't accidentally merge titles with different words (e.g. "Senior Product Manager" vs "Product Manager" are untouched — different word sets).jobTitleGroupKey— lowercases the separator-normalized string; this is the identity used for grouping/matching (so "CEO" vs "ceo" collapse, but "CEO" alone and "Founder / CEO" never do — different token sets).CANONICAL_JOB_TITLE_MAP— an explicit, hand-maintained table for cases a generic separator swap can't infer alone (e.g. "Founder CEO" with no punctuation at all). Each entry is documented with why it's an equivalence, and new mappings can be added here as new variants are discovered in the data.A member's original
titlefield is never overwritten — normalization is used only for building filter options and matching, never for display in the table.Count behavior
Backend/API changes
None. Confirmed via investigation this repo doesn't need any — see above.
Manual verification (real ~993-row dataset, not mocks)
Filter (2), and URL (?company=Anote&jobTitle=Founder+%2F+CEO) all correct.Tests completed
45 tests across 4 suites (
npm test):jobTitleNormalization.test.js— separator merging, case-insensitivity, placeholder exclusion, and explicit non-merge cases (seniority levels, related-but-distinct roles, single-role vs combined-role).memberFacets.test.js— facet building/counts across the full dataset, blank/placeholder exclusion, AND/OR filter logic, faceted cross-filtering, "selected option stays visible at zero count."ArmorMembers.test.js— drawer open/close (click, Escape, close button), full-dataset facet counts and options, search-within-section, multi-select OR, AND between sections, combining with general search, page-reset-on-filter-change, filter persistence across pagination, clear all, individual chip removal, active-filter count badge, empty state, URL hydration/sync, loading/error states, and an explicit "full dataset, not just the visible page" regression test.npm run lint: 0 errors (160 pre-existing warnings elsewhere in the repo, none introduced here).npm run build(CI=false, matching this repo's ownci.yml/deploy.ymlconvention): succeeds.Known limitations
ArmorMembers.jsstill calls the Google Sheets API with a hardcoded API key committed in source (pre-existing, unrelated to this PR).maindirectly rather than stacked on the still-open Add Company and Job Title filters to Members page #37, since the two implement different (and now superseded, for Add Company and Job Title filters to Members page #37) UX approaches to the same page — recommend closing/superseding Add Company and Job Title filters to Members page #37 once this merges, or reconciling separately if any of its non-filtering commits are still wanted.