Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

The folded query summary never says the local-only filter is armed

Open Beginner friendly
#950 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
88/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript
Domain
frontend

Research direction

Start with src/components/features/JobQuerySummary.tsx:41-60 and src/lib/job-search/query-steps.ts:93-101, then run the focused tests in JobQuerySummary.test.ts and query-steps.test.ts. Update both summaries so an armed location-only filter with a location is represented while inactive or locationless states remain unchanged, and pin both directions in the tests.

Written by the indexing model from the issue text.

Description

gaal improvement ux:job-search

Problem

locationOnly (#809, shipped in #905) is the only control in the job-search lane that lets
location remove a posting. It is the one axis the folded query summary does not mention,
so the summary reads identically whether the hard filter is armed or not.

Both summarizers enumerate the query's axes field by field, and neither reads the flag:

  • src/components/features/JobQuerySummary.tsx:41-60 — summarizeQuery pushes titles,
    location, seniority, a skill count, an excluded count, the comp floor and a company count.
  • src/lib/job-search/query-steps.ts:93-101 — filtersSummary pushes location, the excluded
    count, the comp floor and a board count. This is the filters step summary in the query
    rail.

Why this is a contract violation, not just a missing nicety

JobQuerySummary.tsx's own docblock states the rule the omission breaks:

/jobs/ gives the results the full page width by FOLDING the query controls away once a
search has run (FindJobsPanel). Folding a form the user has just filled in is only safe if
what it contained stays legible — otherwise the collapsed state is amnesia... every axis
that can change the result set gets a segment, so the summary and the ranking can't silently
disagree.

It also states the reason silent axes are omitted — "printing it would imply a filter is doing
work when it isn't" — which is the exact inverse of this case: the filter is doing work and
nothing says so.

The lane's other hard filter already follows the rule. excludeTerms gets 1 excluded, and
the comp floor gets ≥ $185k. locationOnly is the third remover (the lane's CLAUDE.md
now names all three) and the only one absent from both summaries.

Failing scenario

  1. Search for a role with location: "Austin, TX", local-only off. Summary reads
    Frontend Engineer · Austin, TX · 3 skills.
  2. Tick Only jobs near Austin, TX in the results strip. Postings elsewhere are dropped.
  3. The summary still reads Frontend Engineer · Austin, TX · 3 skills — byte-identical.

The two states differ in what the result set contains, and the line whose stated job is to keep
the folded query legible cannot tell them apart. Collapsing the form and returning later — or
reading the rail's filters step — gives no indication that a hard filter is removing postings.

Scope of the impact

Bounded, which is why this is not a blocker on #905:

  • The strip's checkbox is rendered whenever the ranked set is non-empty, so the armed state is
    visible on screen while the user is looking at the results.
  • locationFilteredOut already states a count in a notice ("N postings hidden as too far away
    — untick the local-only filter above"), which is what satisfies #809's "stated as a count and
    recoverable" criterion.

What is missing is the summary's account of it: the one-line description of the query behind
the results, and the rail step that claims to carry the current value of its own fields.

Both summaries are pinned by tests that will need the new segment

  • src/components/features/JobQuerySummary.test.ts:43 asserts the whole array by equality
    (toEqual([...])), so a new segment must be added there deliberately.
  • src/lib/job-search/query-steps.test.ts:63 — "assembles the filters summary from only the
    axes that are set" — enumerates the axes and asserts the location-only query renders exactly
    "Remote".

That is a feature, not an obstacle: both tests are the place to state what the new segment says.

Proposed fix

One segment in each summarizer, rendered only when the flag is armed and a location is set
(the flag is inert without one, and refineSearchResult ignores it):

// JobQuerySummary.ts — replace the unconditional location push
const location = query.location?.trim();
parts.push(location ? location : "anywhere");
if (location && query.locationOnly) parts.push("local only");
// query-steps.ts — filtersSummary
if (query.location) parts.push(query.location);
if (query.location && query.locationOnly) parts.push("local only");

Wording is the open question, not the placement. local only matches the control's own name
("Only jobs near {location}") without repeating the location that already sits beside it, and
the lane's copy rule in JobSearchNotices.tsx — name the control the way the control names
itself — applies here too. An alternative worth considering is folding it into the location
segment (Austin, TX (only)), which costs a segment but reads closer to the toggle.

Acceptance criteria

  • With locationOnly armed and a location set, both summarizeQuery and filtersSummary
    carry a segment saying so.
  • With the flag off, or with no location set, both summaries are byte-identical to today —
    no empty segment, no "not local only".
  • JobQuerySummary.test.ts and query-steps.test.ts pin both directions.
  • The wording matches the checkbox's own label register, per the copy rule in
    JobSearchNotices.tsx.

Provenance

Found reviewing #905 (which introduced locationOnly). The flag is correct and the strip's
checkbox is the right control; only the two summarizers were not extended to read it. Both live
outside #905's diff, so this is filed rather than folded into that PR.

Dominant language
TypeScript
Stars
11
Forks
4
Avg merge
1d 5h
Merged PRs (30d)
66

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from offlinecv/OfflineCV

All issues in offlinecv/OfflineCV

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.