Project: workspace-doctolib · Branch: HEAD · Mode: ` · **Model:**claude-sonnet-4-6`
Stats: 1 user messages, 32 tool calls, tools used: Bash, Read.
Conversation
Turn 1 — User
Analyze test coverage for PR #3051 in doctolib/treatment-plan (branch fhir-2219_implement-multi-select-category-filter, base main).
The repo is at /Users/USER/workspace/doctolib/treatment-plan. The branch is checked out there.
Get the diff with: git diff main...HEAD from that directory.
What changed:
- NhrDocumentCategoryFilter.tsx: replaced BetaComboBox with custom multi-select (container with trigger button, optional clear button, Checkbox dropdown)
- createNhrDocumentCategoryFilterConfig.tsx: reads full typeCodes[], computes triggerLabel, handles toggle and clear
- New tests in .test.tsx files cover: label render, dropdown open/close, checkbox toggle (add/remove), clear button, Escape key, click-outside, triggerLabel computation for 0/1/N selections
Identify any gaps in test coverage — scenarios that are not currently tested but should be. Focus on edge cases and acceptance criteria from the FHIR-2219 ticket.
Turn 1 — Assistant
I'll analyze the test coverage for this PR by examining the diff, source files, and test files.
Tools used:
Bash:git diff main...HEAD --name-only
Tool results:
→ result (ok): packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.tsx packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.stories.tsx packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.test.tsx pa …(truncated, 124 more chars)
Tools used:
Bash:git log main...HEAD --oneline
Tool results:
→ result (ok): 755f86d2d feat(fhir-2219): implement multi-select for Document Category filter
Tools used:
Bash:git -C /Users/USER/workspace/doctolib/treatment-plan diff main...HEAD
Tool results:
→ result (ok): diff --git a/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx b/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx index cabf73685..6b94c7168 100644 --- a/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx +++ b/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocum …(truncated, 16855 more chars)
Now let me read the full test files and the source implementation to understand details:
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.test.tsx
Tool results:
→ result (ok): 1 import { setup } from '@doctolib/test-helpers'
2 import { screen } from '@testing-library/react'
3 import { describe, expect, it, vi } from 'vitest'
4
5 import { NhrDocumentCategoryFilter } from './NhrDocumentCategoryFilter'
6
7 const sampleOptions = [
8 { value: 'A', label: 'Report A' },
9 { value: 'B', label: 'Report B' },
10 ]
11
12 describe('NhrDocumentCategoryFilter', () => {
13 const renderComponent = (props: Partial<React.ComponentProps<typeof NhrDocumentCategoryFilter>> = {})
…(truncated, 3518 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx
Tool results:
→ result (ok): 1 import { setup } from '@doctolib/test-helpers'
2 import { screen } from '@testing-library/react'
3 import { describe, expect, it, vi } from 'vitest'
4
5 import { mockPatientInfo } from '../../../test-helpers'
6 import { createNhrDocumentCategoryFilterConfig } from './createNhrDocumentCategoryFilterConfig'
7
8 const ctx = { patient: mockPatientInfo }
9
10 const sampleOptions = [
11 { value: 'A', label: 'Report A' },
12 { value: 'B', label: 'Report B' },
13 { value: 'C', label: 'Report
…(truncated, 2524 more chars)
Now let me examine the source implementation more carefully to find untested code paths:
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.tsx
Tool results:
→ result (ok): 1 import { ChevronDownRegularIcon } from '@doctolib/design-system-icons/ChevronDownRegularIcon'
2 import { Checkbox } from '@doctolib/design-system/Checkbox/v2'
3 import type { Option } from '@doctolib/design-system/Option'
4 import { useEffect, useRef, useState } from 'react'
5
6 type NhrDocumentCategoryFilterProps = {
7 label: string
8 values: string[]
9 triggerLabel: string
10 options: Option[]
11 placeholder?: string
12 onToggle: (option: Option) => void
13 onClear: () => void
…(truncated, 2158 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.tsx
Tool results:
→ result (ok): 1 import type { Option } from '@doctolib/design-system/Option'
2
3 import { NhrDocumentCategoryFilter } from '../../../components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter'
4 import type { FilterConfig } from '../../../types'
5
6 export const createNhrDocumentCategoryFilterConfig = ({
7 options = [],
8 label = 'Document Category',
9 placeholder,
10 }: {
11 options?: Option[]
12 label?: string
13 placeholder?: string
14 } = {}): FilterConfig => ({
15 key:
…(truncated, 1141 more chars)
Now let me look at any existing related filter tests and check whether placeholder prop behavior is tested anywhere, and also scan for any accessibility expectations in the project:
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters -type f | sort
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDateSelectionFilterConfig.test.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDateSelectionFilterConfig.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx /Users/USER/workspa …(truncated, 785 more chars)
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src -name "*.test.tsx" | sort
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/columns/createNhrDocumentCategoryColumn.test.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/columns/createNhrDocumentNameColumn.test.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/columns/createNhrProcedureDateColumn.test.tsx /Users/USER/workspace/doctolib/tr …(truncated, 5397 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentStatusFilterConfig.test.tsx
Tool results:
→ result (ok): 1 import { setup } from '@doctolib/test-helpers'
2 import { screen } from '@testing-library/react'
3 import { describe, expect, it, vi } from 'vitest'
4
5 import { mockPatientInfo } from '../../../test-helpers'
6 import { createNhrDocumentStatusFilterConfig } from './createNhrDocumentStatusFilterConfig'
7
8 // ctx is required by FilterConfig.render but unused by this filter
9 const ctx = { patient: mockPatientInfo }
10
11 const sampleStatuses = [
12 { filterKey: 'includeArchived', label: 'A
…(truncated, 1866 more chars)
Tools used:
Bash:grep -n "placeholder\|aria-expanded\|aria-label\|isOpen\|toggle\|dropdown" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/Nhr …(truncated, 52 more chars)
Tool results:
→ result (ok): 11: placeholder?: string 24: const [isOpen, setIsOpen] = useState(false) 48: aria-label={label} 49: aria-expanded={isOpen} 51: onClick={() => setIsOpen(!isOpen)} 59: aria-label='Clear' 71: {isOpen && (
Tools used:
Bash:grep -n "placeholder" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.tsx /Users/clerton …(truncated, 515 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.tsx:11: placeholder?: string /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.tsx:9: placeholder, /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNh …(truncated, 447 more chars)
Tools used:
Bash:grep -n "aria-expanded\|aria-label\|aria-" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocume …(truncated, 25 more chars)
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:grep -n "typeCodes.*\['" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx
Tool results:
→ result (ok): 33: expect(onChange).toHaveBeenCalledWith({ typeCodes: ['A'] }) 38: const { user } = renderFilter(sampleOptions, { typeCodes: ['A'] }, onChange) 48: const { user } = renderFilter(sampleOptions, { typeCodes: ['A', 'B'] }, onChange) 63: renderFilter(sampleOptions, { typeCodes: ['A'] }) 69: renderFilter(sampleOptions, { typeCodes: ['A', 'B', 'C'] })
Tools used:
Bash:grep -rn "NhrDocumentCategoryFilter\|createNhrDocumentCategoryFilterConfig" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentListFil …(truncated, 247 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentListFilterPanel/NhrDocumentListFilterPanel.stories.tsx:5:import { createNhrDocumentCategoryFilterConfig } from '../../business/factories/filters/createNhrDocumentCategoryFilterConfig' /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentListFilterPanel/NhrDocumentListFilterPanel.stories.tsx:37: createNhrDocumentCategoryFilterC …(truncated, 7 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentListFilterPanel/NhrDocumentListFilterPanel.test.tsx
Tool results:
→ result (ok): 1 import { render, screen, waitFor } from '@testing-library/react'
2 // eslint-disable-next-line import/no-extraneous-dependencies, @doctolib-tpf/no-undeclared-dependencies
3 import { userEvent } from '@testing-library/user-event'
4 import { forwardRef } from 'react'
5 import { describe, expect, it, vi } from 'vitest'
6
7 import type { FilterConfig } from '../../types'
8 import { NhrDocumentListFilterPanel } from './NhrDocumentListFilterPanel'
9
10 // Mock design system components
11 vi.mock('
…(truncated, 9750 more chars)
Tools used:
Bash:grep -n "placeholder\|empty\|options.*\[\]" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterCon …(truncated, 195 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.test.tsx:72: it('should not show clear button when values array is empty', () => { /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx:46: it('should emit empty typeCodes array when onClear is called', async () => { / …(truncated, 247 more chars)
Tools used:
Bash:grep -n "selectedOptions\[0\].*label\|label.*selectedOptions\[0\]\|orphan\|stale\|not.*in.*options\|invalid.*value" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-reco …(truncated, 79 more chars)
Tool results:
→ result (ok): 24: ? (selectedOptions[0]?.label ?? '')
25: : ${selectedOptions[0]?.label ?? ''}, +${selectedValues.length - 1}
Tools used:
Bash:grep -n "toggle.*dropdown\|dropdown.*toggle\|isOpen.*false\|isOpen.*true\|setIsOpen" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocume …(truncated, 62 more chars)
Tool results:
→ result (ok): 24: const [isOpen, setIsOpen] = useState(false) 30: setIsOpen(false) 39: if (e.key === 'Escape') setIsOpen(false) 51: onClick={() => setIsOpen(!isOpen)}
Tools used:
Bash:grep -n "toggle\|close.*after\|remain.*open\|stay.*open" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFi …(truncated, 39 more chars)
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:grep -n "label.*prop\|custom.*label\|default.*label\|Document Category" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/create …(truncated, 40 more chars)
Tool results:
→ result (ok): 30: await user.click(screen.getByRole('button', { name: 'Document Category' })) 40: await user.click(screen.getByRole('button', { name: 'Document Category' })) 58: expect(screen.getByRole('button', { name: 'Document Category' })).toBeInTheDocument()
Tools used:
Bash:grep -rn "mousedown\|mouseDown" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryF …(truncated, 14 more chars)
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:grep -n "empty.*options\|options.*empty\|no.*options\|options=\{\[\]\}" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDoc …(truncated, 223 more chars)
Tool results:
→ result (ERROR): Exit code 2 ugrep: error: error at position 59 options.empty|no.options|options={[]} invalid repeat___/
Tools used:
Bash:grep -n "stopPropagation\|e\.stop\|clear.*toggle\|toggle.*clear" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCa …(truncated, 225 more chars)
Tool results:
→ result (ERROR): <tool_use_error>Cancelled: parallel tool call Bash(grep -n "empty.options|options.empty\…) errored</tool_use_error>
Tools used:
Bash:grep -n "options.*\[\]" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.te …(truncated, 6 more chars)
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:grep -n "stopPropagation" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter. …(truncated, 186 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryFilter/NhrDocumentCategoryFilter.tsx:62: e.stopPropagation()
Tools used:
Bash:grep -n "two.*select\|select.*two\|A.*B\|B.*A\|order\|append\|preserve" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/create …(truncated, 40 more chars)
Tool results:
→ result (ok): 31: await user.click(screen.getByRole('checkbox', { name: 'Report A' })) 33: expect(onChange).toHaveBeenCalledWith({ typeCodes: ['A'] }) 41: await user.click(screen.getByRole('checkbox', { name: 'Report A' })) 48: const { user } = renderFilter(sampleOptions, { typeCodes: ['A', 'B'] }, onChange) 59: expect(screen.queryByText('Report A')).not.toBeInTheDocument() 65: expect(screen.getByText('Report A')).toBeInTheDocument() 69: renderFilter(sampleOptions, { typeCodes: ['A', 'B', …(truncated, 76 more chars)
Tools used:
Bash:grep -n "trigger.*button\|button.*trigger\|aria-expanded" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocumentList/NhrDocumentCategoryF …(truncated, 40 more chars)
Tool results:
→ result (ok): 32: it('should show triggerLabel in the trigger button', () => { 38: it('should open dropdown when trigger button is clicked', async () => {
Tools used:
Bash:grep -rn "placeholder" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:grep -rn "two.*select\|multiple.*toggle\|second.*check\|check.*second\|select.*both\|both.*check" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/compone …(truncated, 80 more chars)
Tool results:
→ result (ok): (Bash completed with no output)
Now I have a thorough understanding of the code and tests. Let me compile my findings.
Tools used:
Bash:grep -rn "label.*custom\|custom.*label\|Document Category" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCa …(truncated, 27 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx:30: await user.click(screen.getByRole('button', { name: 'Document Category' })) /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/factories/filters/createNhrDocumentCategoryFilterConfig.test.tsx:40: await user.click(screen.getByRole('button', { name: 'Document Category' })) /Users …(truncated, 250 more chars)
Tools used:
Bash:grep -n "two.*select\|remain.*open\|stays.*open\|keep.*open\|still.*open\|not.*close" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/components/NhrDocum …(truncated, 68 more chars)
Tool results:
→ result (ok): (Bash completed with no output)
I now have a complete picture of the implementation and its test coverage. Here is my analysis.
Summary
The tests for this PR are well-structured and cover the core happy-path scenarios for both the component and the factory config. They correctly focus on behavioral contracts rather than implementation details, use RTL best practices, and the test descriptions are clear. However, there are meaningful gaps in several areas that could lead to silent regressions or user-facing bugs.
Critical Gaps
1. Dropdown does not close after a checkbox selection (criticality: 8/10)
The current implementation sets isOpen(!isOpen) only on the trigger button click. There is no code path that closes the dropdown when a checkbox is toggled. This is the defining UX difference between a multi-select and a single-select: the dropdown must stay open so the user can select multiple items without re-opening it. There is no test asserting that the dropdown remains open after a checkbox is clicked. If a future refactor accidentally adds setIsOpen(false) to onChange (a common mistake when adapting single-select components), no test would catch it.
The test to add in NhrDocumentCategoryFilter.test.tsx:
it('should keep the dropdown open after a checkbox is toggled')
Open the dropdown, toggle a checkbox, assert screen.getByRole('checkbox', { name: 'Report A' }) is still in the document.
This prevents the most common multi-select regression.
2. Clear button does not cause the dropdown to open (criticality: 7/10)
The clear button has e.stopPropagation() to prevent the click from bubbling up to the trigger button's toggle handler. There is no test verifying that clicking Clear while the dropdown is closed does not accidentally open it, nor that the dropdown stays closed after Clear. If stopPropagation is removed during a refactor, the button click would bubble to the toggle handler and open the dropdown — an unexpected UX behavior.
The test to add in NhrDocumentCategoryFilter.test.tsx:
it('should not open the dropdown when clear button is clicked')
Render with values={['A']}, assert dropdown is closed, click the Clear button, assert no checkboxes appear.
Important Improvements
3. Placeholder is used as triggerLabel when no selection and placeholder is provided (criticality: 6/10)
In createNhrDocumentCategoryFilterConfig.tsx line 22, placeholder ?? '' is the triggerLabel when nothing is selected. If placeholder is provided, it should appear in the trigger button. This branch is completely untested. If someone accidentally inverts the null-coalescing or changes the condition, the placeholder would silently stop appearing.
The test to add in createNhrDocumentCategoryFilterConfig.test.tsx:
it('should use placeholder as triggerLabel when no options are selected and placeholder is provided')
Call createNhrDocumentCategoryFilterConfig({ options: sampleOptions, placeholder: 'Ex. Laboratory report' }), render with empty filters, assert the trigger button shows 'Ex. Laboratory report'.
4. triggerLabel when selected value is not found in the options list (criticality: 6/10)
In createNhrDocumentCategoryFilterConfig.tsx line 24, selectedOptions[0]?.label ?? '' uses optional chaining as a guard for when a selected typeCode value has no matching option (e.g., stale filter state after options are reloaded). The ?? '' fallback exists precisely for this scenario. No test covers it. A user with a stale filter would see a blank trigger label rather than any indication of what is selected, which is arguably correct behavior — but it is currently untested, so if someone removes the optional chaining, the component would throw at runtime.
The test to add in createNhrDocumentCategoryFilterConfig.test.tsx:
it('should show empty triggerLabel when selected value does not match any option')
Render with { typeCodes: ['UNKNOWN'] } and sampleOptions that do not contain 'UNKNOWN'. Assert the trigger button label is empty or falls back gracefully.
5. aria-expanded reflects open/closed state (criticality: 5/10)
The trigger button sets aria-expanded={isOpen} (line 49 of NhrDocumentCategoryFilter.tsx). No test asserts this attribute changes. Screen readers rely on aria-expanded to announce whether the listbox is open. If someone removes the attribute or hardcodes it to false, no test would fail.
The test to add in NhrDocumentCategoryFilter.test.tsx:
it('should set aria-expanded to false when closed and true when open')
Assert toHaveAttribute('aria-expanded', 'false') initially, click the trigger, assert 'true'.
6. Selection order is preserved when toggling multiple options (criticality: 5/10)
The onToggle handler in createNhrDocumentCategoryFilterConfig.tsx (lines 35–39) appends new values with [...selectedValues, option.value]. There is no test verifying that toggling option B when A is already selected produces ['A', 'B'] (not ['B', 'A']). Order can matter if the consumer sorts by selection order or if the triggerLabel always shows the first selected item.
The test to add in createNhrDocumentCategoryFilterConfig.test.tsx:
it('should append new selections in the order they are toggled')
Start with { typeCodes: ['A'] }, toggle B, assert onChange was called with { typeCodes: ['A', 'B'] }.
Test Quality Issues
Tests against getByText for trigger label content
The test 'should compute the option label as triggerLabel when one option is selected' uses screen.getByText('Report A'). Because triggerLabel is rendered as plain text inside a <button>, getByText will find it. However, the project's testing guidelines prefer getByRole with a name option for interactive elements. This makes the assertion more brittle to layout changes (e.g., if the same text appears elsewhere on the page). Consider replacing with expect(screen.getByRole('button', { name: /Report A/i })).toBeInTheDocument() where the aria-label on the button is label rather than triggerLabel — though this requires assessing whether the button's accessible name is derived from aria-label (the label prop) or its text content (triggerLabel). Given the current implementation uses aria-label={label}, the button's accessible name is always the label prop, making getByText('Report A') the correct query to assert the displayed text — so this is a minor issue, not a blocker.
Escape key test fires the event while the trigger button is focused
The 'should close dropdown when Escape is pressed' test calls user.keyboard('{Escape}') after clicking a checkbox. The onKeyDown handler is attached only to the trigger button (line 52 of NhrDocumentCategoryFilter.tsx). If focus moves to a checkbox after the click, the Escape event would not be caught by the trigger's handler. The test may be relying on implementation-specific focus behavior. This could become a flaky or misleading test if focus handling is changed. Worth verifying that the Escape handler is attached at the right level, or that the test explicitly returns focus to the trigger.
Positive Observations
- The tests correctly test behavior contracts (what
onChangeis called with, what is rendered) rather than internal state likeisOpen. - Using
user.clickfrom@testing-library/user-eventthroughout is correct and consistent with the project's testing conventions. - The factory config tests are appropriately separated from the component tests, giving independent coverage at both the integration and unit levels.
- The
sampleOptionsfixture with three entries (A, B, C) is a good choice for testing the+Nsuffix intriggerLabel. - The
'should reflect checked state for selected values'test explicitly asserts both checked and unchecked states for different options, which is important for verifying the controlled checkbox behavior. - Th …(truncated, 800 more chars)