feat(CodeLookup): coding-system filter — ICD-10/SNOMED mode, ICD-11-ready#336
feat(CodeLookup): coding-system filter — ICD-10/SNOMED mode, ICD-11-ready#336horner wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a coding-system (“codetype”) filter to CodeLookup at both the engine and UI levels, enabling ICD-10-only / SNOMED-only (and future ICD-11) modes while keeping drill-down results consistent with the active filter.
Changes:
- Engine: adds
SearchOptions.codetypesand applies it to both label and code-prefix matching, skipping shards with no matching codetypes. - Worker + component: plumbs
codetypesthrough the worker and exposessearchCodetypes(fixed) +codetypeOptions(user toggle) inCodeLookup. - Stories/tests: updates Storybook stories for conditions and adds engine tests covering codetype filtering and “no matching systems” behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/components/CodeLookup/index.ts | Re-exports CodetypeOption from CodeLookup. |
| src/components/CodeLookup/engine.ts | Implements query-time codetype filtering for both label search and code-prefix search; skips non-matching shards. |
| src/components/CodeLookup/engine.test.ts | Adds coverage for codetype filtering across label and code searches, including the “no matching systems” case. |
| src/components/CodeLookup/codify.worker.ts | Passes codetypes through from worker messages into engine search options. |
| src/components/CodeLookup/CodeLookup.tsx | Adds searchCodetypes + codetypeOptions props, renders a segmented-control-style toggle, and applies the active filter to search and drill-down. |
| src/components/CodeLookup/CodeLookup.stories.tsx | Updates Conditions story to include All/ICD-10/SNOMED toggle and adds a fixed ICD-10-only story. |
Comments suppressed due to low confidence (2)
src/components/CodeLookup/CodeLookup.tsx:512
- Using
opt.labelas the React key can cause duplicate keys if consumers provide repeated labels (or if labels change), which can lead to incorrect button state reuse. Prefer a key that’s guaranteed unique/stable for the option (e.g., label + codetypes, plus index as a tie-breaker).
<button
key={opt.label}
type="button"
src/components/CodeLookup/CodeLookup.tsx:536
- There’s an extra JSX whitespace node (
{' '}) inside the search box container. It’s unnecessary and can introduce subtle layout/spacing differences; it should be removed.
<div className="relative" ref={anchorRef}>
{' '}
<SearchIcon
Deploying ui with
|
| Latest commit: |
304aed7
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://9fd3707b.ui-6d0.pages.dev |
| Branch Preview URL: | https://codelookup-codetype-filter.ui-6d0.pages.dev |
…d pill - empty codetypeOptions no longer overrides searchCodetypes (was truthy []) - clamp active index so a shrunk options list can't lose its selection - selected pill uses bg-primary-800 (was 600) to pass WCAG AA contrast Addresses PR #336 review (Copilot) and test:storybook a11y failure.
- arrow/Home/End keys move selection with roving tabIndex (only the checked radio is tabbable), matching the ARIA radiogroup pattern - key by index instead of consumer-provided label (may be non-unique/locale- dependent) Addresses PR #336 review (Copilot).
…d pill - empty codetypeOptions no longer overrides searchCodetypes (was truthy []) - clamp active index so a shrunk options list can't lose its selection - selected pill uses bg-primary-800 (was 600) to pass WCAG AA contrast Addresses PR #336 review (Copilot) and test:storybook a11y failure.
- arrow/Home/End keys move selection with roving tabIndex (only the checked radio is tabbable), matching the ARIA radiogroup pattern - key by index instead of consumer-provided label (may be non-unique/locale- dependent) Addresses PR #336 review (Copilot).
1ec7175 to
1cc31fb
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/components/CodeLookup/CodeLookup.tsx:268
activeCodetypescan legitimately be an empty array (e.g.searchCodetypes={[]}or an option withcodetypes: []). Because[]is truthy,codetypesKeybecomes'',keyToDomains('')yields[], and the engine treats that as a real filter that matches no codetypes (empty allowed set), returning no results and also breaking drill-down (codetypesRef.currentbecomes[]). Normalize empty arrays toundefinedso they behave like “no filter / all systems”.
const activeCodetypes = hasCodetypeOptions
? codetypeOptions![activeCodetypeIdx]?.codetypes
: searchCodetypes;
const codetypesKey = activeCodetypes ? activeCodetypes.join(',') : null;
/** active codetypes for the stable openDrill callback */
| s: CodifyShard, | ||
| opts?: SearchOptions | ||
| ): Set<number> | null { | ||
| if (!opts?.codetypes) return null; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/components/CodeLookup/engine.ts:463
SearchOptions.codetypestreats an empty array as a hard filter to “no codetypes” (allowed set becomes empty, and the shard gets skipped). That makescodetypes: []behave very differently fromboostCodetypes: [](which is effectively a no-op) and is easy to hit accidentally via array-to-key encoding. Consider treating an empty codetypes list as “no filter” (same as undefined) to avoid surprising “no results” behavior.
/** Shard-local codetype indices allowed by opts.codetypes (null = no filter;
* an empty set means the shard has none of the requested systems). */
function allowedCodetypes(
s: CodifyShard,
opts?: SearchOptions
): Set<number> | null {
if (!opts?.codetypes) return null;
return new Set(
opts.codetypes.map((ct) => s.codetypes.indexOf(ct)).filter((i) => i >= 0)
);
| const activeCodetypes = hasCodetypeOptions | ||
| ? codetypeOptions![activeCodetypeIdx]?.codetypes | ||
| : searchCodetypes; | ||
| const codetypesKey = activeCodetypes ? activeCodetypes.join(',') : null; | ||
| /** active codetypes for the stable openDrill callback */ |
…eady (#335) - engine SearchOptions.codetypes filters label + code matches at query time - searchCodetypes prop: programmer-fixed system; applies to drill-down too - codetypeOptions prop: user-facing segmented control (radiogroup) - ConditionsOnly story: All | ICD-10 | SNOMED toggle; new ICD-10-only story
…d pill - empty codetypeOptions no longer overrides searchCodetypes (was truthy []) - clamp active index so a shrunk options list can't lose its selection - selected pill uses bg-primary-800 (was 600) to pass WCAG AA contrast Addresses PR #336 review (Copilot) and test:storybook a11y failure.
- arrow/Home/End keys move selection with roving tabIndex (only the checked radio is tabbable), matching the ARIA radiogroup pattern - key by index instead of consumer-provided label (may be non-unique/locale- dependent) Addresses PR #336 review (Copilot).
1cc31fb to
304aed7
Compare
| 'rounded-full border px-2.5 py-0.5 text-xs transition-colors', | ||
| 'focus:ring-ring focus:ring-2 focus:outline-none', | ||
| i === activeCodetypeIdx | ||
| ? 'border-primary-800 bg-primary-800 dark:border-primary-400 dark:bg-primary-400 dark:text-primary-950 text-white' | ||
| : 'border-border text-muted-foreground hover:text-foreground' |
Adds a coding-system mode to CodeLookup — the programmer can fix it, or let the user decide. Groundwork for #335 (ICD-11): once the shards carry
ICD11rows, the mode is justcodetypes: ['ICD11'].What
engine.ts):SearchOptions.codetypes— query-time filter applied to both label matches and code-prefix matches; shards containing none of the requested systems are skipped entirely.codify.worker.ts): passescodetypesthrough.CodeLookup.tsx):searchCodetypes?: string[]— programmer-fixed system (e.g.['ICD10']); also applies to the → drill-down.codetypeOptions?: CodetypeOption[]— user-facing segmented control (accessibleradiogroup) above the search box; the user's pick overridessearchCodetypes.Why no ICD-11 option yet
The source dataset (
rxdb_utf8.MedicalCodify_search) has no ICD-11 rows — ingesting the WHO ICD-11 MMS linearization is tracked in #335. This PR makes the UI/engine side a one-line change when that lands.Testing
pnpm vitest run src/components/CodeLookup/engine.test.ts— 27/27pnpm typecheck,pnpm lint,pnpm format:fix— clean