Repository navigation
fix(web): make the entity picker read as a search field and focus it on open - #736
Conversation
…on open The open NamedEntityPicker input shared surface, hairline, radius and type with the listbox rows beneath it, so it rendered as a highlighted first row carrying the committed name; and opening from the trigger neither focused the input nor selected its text, so nothing suggested typing. Affects all 11 picker instances across 7 screens. - Effect keyed on `open`: focus the input and select its text, declared after the open effect so the input exists in the same commit. - CSS: inset `--surface-2` background, leading magnifier glyph, brand border while `aria-expanded="true"`, slightly wider gap to the listbox. Trigger, placeholder, ARIA and `type` unchanged. - Test written red first (activeElement was `body`), green after. Closes #735
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe named picker now focuses and selects its committed name when opened. New tests verify this behavior. CSS changes distinguish the input from listbox options with search-field styling and expanded-state branding. ChangesNamed picker improvements
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~15–30 minutes Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to Named entity pickers now focus and select the committed value when opened and have clearer search-field styling. The change is covered by an open-focus regression test, with no remaining merge-readiness risk identified. Sequence Diagram(s)sequenceDiagram
participant TriggerButton
participant NamedEntityPickerEngine
participant ComboboxInput
TriggerButton->>NamedEntityPickerEngine: Open picker
NamedEntityPickerEngine->>ComboboxInput: Focus input
NamedEntityPickerEngine->>ComboboxInput: Select committed name
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Linked Issues checkExplanation The implementation addresses the shared picker styling and open-focus behavior in issue [ Full details: Docstring CoverageExplanation Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (1 skipped: 1 unsupported.) ✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
The API serves the SPA under `img-src 'self' blob:` (SecurityHeaders.cs), and CSP applies img-src to CSS background images, so the data-URI glyph from the previous commit painted on the Vite dev server and vanished in production — reproduced by capturing under that directive: `Loading the image 'data:image/svg+xml,…' violates the following CSP`. - The magnifier is now a bordered circle and a rotated bar on the control's ::before/::after, coloured by --muted so it follows the theme. - styles.csp.test.ts walks every url() in styles.css and admits only same-origin paths or fragments; red on the data-URI, green now. Found by the codex review of #736.
|
External review finding (codex, gpt-5.6-sol), addressed in 9d788dc. Finding: the magnifier was a Fix: the glyph is now drawn with Verification on 9d788dc: 121 files / 2708 tests green, typecheck OK, coverage floor unchanged; re-capture under the |
|
@coderabbitai review |
|
|
@coderabbitai review |
|
…url() guard Codex review of #736, round 2, against the previous fix: - The glyph's vertical offsets were fixed rem values measured on the desktop field; the phone breakpoint raises `.entry-context input` to `min-height: 2.75rem`, which put the lens ~4px above centre on Daily entry. Both pseudo-elements now key off `top: 50%` of the control, whose only in-flow child is the input. Probed in Chromium at 1280 and 390px: lens offset from the input's centre is 0 at 38.6px and 44px heights. - The disabled input dims itself on top of the whole-picker dim; the glyph sits outside the input and took only one step, so it read darker than its field. `:has(input:disabled)` gives it the same second step. - `styles.csp.test.ts` matched only lowercase `url(` — CSS function names are case-insensitive, so `URL("data:…")` slipped through — and keyed same-origin on a leading slash, which admitted protocol-relative `//host/x.svg` and rejected a bare relative path. It now matches /i and classifies by scheme/protocol-relative prefix. Mutation set: uppercase data:, protocol-relative, uppercase scheme, unquoted blob: all red; bare relative, root-relative and #fragment stay green. - docs/images/reviews/735-picker-before-after.png: the 1:1 before/after montage (night + day) the #662 rule asks a visual PR to carry.
|
Codex review, round 2 (gpt-5.6-sol) against
Verification on f71fb29: 121 files / 2708 tests green, typecheck OK, coverage floor holds; captures under the production Before/after (1:1, night + day): https://raw.githubusercontent.com/mforce/cluckwork/f71fb29e5131871bc95a8998fb4aa2697c4e57db/docs/images/reviews/735-picker-before-after.png |
…border out of rem Codex review of #736, round 3, against the previous fix: - The guard classified raw CSS text: `url(d\61 ta:x)` and `u\72l(data:x)` are the same blocked request once the tokenizer decodes them, but passed as written. Escapes are now decoded before matching. Mutants hex-escaped data:, escaped function name and six-digit hex all red; an escaped colon inside a same-origin path stays green. - The handle's start point folded the lens's 2px border into 0.425rem, which only holds at a 16px root. The offsets now keep px and rem apart; probed against the stylesheet in Chromium at 16px and 24px root, on the desktop height, the phone 2.75rem min-height and a line-height of 2: lens offset from the field centre 0, handle gap from the lens 0. - The disabled glyph dim is keyed on `.named-picker.disabled` instead of `:has(input:disabled)`; both come from the one `disabled` prop, and the ancestor form needs no `:has()` support. Probed: input and glyph both at 0.6 inside the 0.6 ancestor.
|
Codex review, round 3 (gpt-5.6-sol) against
Verification on 10236c0: 121 files / 2708 tests green, typecheck OK, coverage floor holds. Round tally: r1 = 1 product defect (CSP), r2 = 2 product + 2 guard, r3 = 0 product at defaults + 1 guard. |
A one-paragraph rule in `AGENTS.md`'s **Git / PR workflow** section, beside the existing PR-title rule — same subject, title versus body. ## Why PR #742 carried `(#722)` in its title and no closing keyword in its body. GitHub treats a title reference as a reference: **the issue would have survived the merge.** The repo's convention already is the keyword — #733 opens `Closes #732`, #736 opens `Closes #735` — but nothing in `AGENTS.md` said so, so a driver-authored runbook that specified the PR title verbatim and the body's required paragraph verbatim simply never mentioned it. Caught by the owner reading the PR one step from the merge ask. The note also records two mechanics that cost time to rediscover: verify the linkage with the `closingIssuesReferences` GraphQL query rather than by re-reading the body, and patch a PR body with `gh api -X PATCH` because `gh pr edit` fails on this repo with a Projects-classic deprecation error. ## Scope One line replaced by two in `AGENTS.md`. No code, no behaviour change. Split out of #742 at the owner's direction rather than riding along, so #742 stays at the exact head its two review rounds and its full verification ran on. Co-authored-by: mforce <mforce@users.noreply.github.com>
🤖 I have created a release *beep* *boop* --- ## [0.1.0](v0.0.4...v0.1.0) (2026-09-12) ### ⚠ BREAKING CHANGES * log in by farm code, with per-account email identity ([#532](#532)) (#564) ### Features * **accounts:** add Account.Slug (farm code), suspend/reactivate, list-accounts verb ([#531](#531)) ([3fe9754](3fe9754)) * **accounts:** provision additional farms ([#581](#581)) ([006f298](006f298)) * add Aspire local development AppHost ([#567](#567)) ([2c9e6b9](2c9e6b9)) * add configurable worker sale allocation ([#619](#619)) ([0955095](0955095)) * add searchable entity pickers ([#642](#642)) ([60d2053](60d2053)) * **api:** provision-account takes an optional --timezone at creation ([#603](#603)) ([#694](#694)) ([a0aee39](a0aee39)) * **audit:** show the sales-line audit payload as a readable Details column ([#745](#745)) ([#749](#749)) ([d26d389](d26d389)) * **auth:** add ApplicationUser.StepUpLogoutEpoch column ([#338](#338)) ([#554](#554)) ([18306ee](18306ee)) * certify over-cap simulation fixture bands ([#633](#633)) ([a67b2e1](a67b2e1)), closes [#627](#627) * **cli:** rename-account verb to change a farm code ([#732](#732)) ([#733](#733)) ([4b70559](4b70559)) * **customers:** edit existing customer details ([#625](#625)) ([#626](#626)) ([062a55c](062a55c)) * **jobs:** single-runner leader gate for the durable job worker ([#271](#271)) ([#555](#555)) ([4148f9b](4148f9b)) * let owners change user email addresses ([#605](#605)) ([842347b](842347b)) * log in by farm code, with per-account email identity ([#532](#532)) ([#564](#564)) ([68adb62](68adb62)) * **ratelimit:** distributed IP-keyed auth limiters ([#544](#544)) ([#558](#558)) ([ec14972](ec14972)) * **ratelimit:** distributed per-account report concurrency cap with local-ceiling fallback ([#545](#545)) ([#559](#559)) ([1522e4e](1522e4e)) * **sales:** mark discounted lines, total the discount, and show it in the Orders list ([#723](#723), [#724](#724)) ([#741](#741)) ([1a07441](1a07441)) * **sales:** record list, old and new price in the order-line audit payload ([#722](#722)) ([#742](#742)) ([97c866f](97c866f)) * **sales:** refuse an over-ceiling confirm from a Sales user ([#727](#727)) ([#766](#766)) ([8c0792a](8c0792a)) * **sales:** show what each order still owes, and filter the list to unpaid ([#771](#771)) ([ca59d68](ca59d68)) * **sales:** snapshot the list price on the order line and show the discount ([#734](#734)) ([cffed5e](cffed5e)) * **sales:** snapshot the product name and unit in the order-line audit payload ([#747](#747)) ([#748](#748)) ([0481c06](0481c06)) * scope Worker reads to assigned flocks ([#388](#388)) ([#611](#611)) ([5884a9a](5884a9a)) * shared-state ports with Redis + in-process fallback ([#543](#543)) ([#552](#552)) ([f767fa9](f767fa9)) * suspend-account / reactivate-account operator verbs ([#534](#534)) ([#573](#573)) ([d0be26c](d0be26c)) * **tenancy:** write-side tenant guard + single-assignment TenantContext ([#546](#546)) ([#561](#561)) ([f371f1d](f371f1d)) * **web:** dashboard rework — capture-status tiles, 14-day trend, stock as a stacked bar ([#654](#654)) ([396ba23](396ba23)) * **web:** date-range filters on audit and expenses, and the stock lot filter gets its bounded toolbar ([#666](#666), [#667](#667), [#653](#653)) ([94b188f](94b188f)) * **web:** elevation hierarchy and sentence-case labels ([#651](#651), [#652](#652)) ([#661](#661)) ([28db4c7](28db4c7)) * **web:** Expenses and Audit keep a clear-filters control while rows are still showing ([#679](#679)) ([#697](#697)) ([b859982](b859982)) * **web:** expenses filters by a date range like its sibling screens ([#667](#667)) ([f13858f](f13858f)) * **web:** key the farm brand palette per farm ([#586](#586)) ([#600](#600)) ([7183a43](7183a43)) * **web:** let operators forget remembered farms ([#598](#598)) ([577d94e](577d94e)) * **web:** one-line provenance, bounded date filters, and empty states that invite action ([#653](#653), [#655](#655)) ([#668](#668)) ([80b53f4](80b53f4)) * **web:** prefill the farm code from ?farm= and remember it ([#535](#535)) ([#588](#588)) ([b7f5cc6](b7f5cc6)) * **web:** split authenticated routes into lazy chunks ([#620](#620)) ([5089271](5089271)) * **web:** the audit log filters by a date range, and says which window is empty ([#666](#666)) ([63027e0](63027e0)) * **web:** typeset numbers as numbers and refresh the Help glossary ([#650](#650), [#657](#657)) ([af4fe11](af4fe11)) ### Bug fixes * **api:** order same-instant audit events by a durable monotonic key ([#700](#700)) ([8fcf084](8fcf084)) * **api:** print the farm code from bootstrap-admin ([#589](#589)) ([#594](#594)) ([34032ac](34032ac)) * **audit:** show the price a line sold for, not its list price ([#759](#759)) ([e6b37d0](e6b37d0)) * **audit:** store catalog enums by name and guard the add-item transaction shape ([#751](#751)) ([23609ff](23609ff)) * **auth:** reject invalid account claims ([#622](#622)) ([8d6c7fe](8d6c7fe)) * **auth:** require step-up for durable user access ([#360](#360)) ([#607](#607)) ([f767dce](f767dce)) * **ci:** bound the npm audit calls and give the web job room to finish ([#686](#686)) ([153b7a8](153b7a8)) * **ci:** escalate the audit bound to SIGKILL, so it actually bounds ([#686](#686)) ([a0c8f4e](a0c8f4e)) * **ci:** fail closed on invalid vulnerability config ([#621](#621)) ([1690db8](1690db8)) * **ci:** lockfix covers the two AppHost lock files, derived from the sln ([efb05e6](efb05e6)) * **ci:** lockfix covers the two AppHost lock files, derived from the sln ([8986d77](8986d77)) * **ci:** remove invalid XML comment from nuget.lockfix.config ([#541](#541)) ([5f1bc0a](5f1bc0a)) * **ci:** the advisory vuln gate no longer blocks on an unusable report ([#686](#686)) ([aaf6934](aaf6934)) * **ci:** the advisory vuln gate no longer blocks on an unusable report ([#686](#686)) ([64f1f53](64f1f53)) * **i18n:** tl help text names the saleable flag and unit-system setting what their labels call them ([#688](#688)) ([#696](#696)) ([bfd24d7](bfd24d7)) * **infra:** AccountId must be a non-nullable Guid or both tenant write layers refuse ([#673](#673)) ([#695](#695)) ([2470c4e](2470c4e)) * require step-up for flock scope changes ([#609](#609)) ([4151f89](4151f89)) * **sales:** keep a line's discount markers agreeing while its price is edited ([#752](#752)) ([#753](#753)) ([c159b4b](c159b4b)) * **sales:** say which kind of missing list price a line has ([#774](#774)) ([489180e](489180e)) * scope legacy logout to selected farm ([#624](#624)) ([fae8d82](fae8d82)) * **seed:** drain the daily-entry lock sweep so deep simulation fixtures validate ([#644](#644)) ([730fa23](730fa23)), closes [#638](#638) * **tenancy:** AccountId is a concurrency token, so the database refuses a detached cross-tenant write ([#562](#562)) ([4d1dfa3](4d1dfa3)) * **tenancy:** AspNetUserRoles carries a tenant column, so a role write naming another farm's user is refused ([#670](#670)) ([fc0552a](fc0552a)) * **tests:** bump the image-pin allow-list counts for the AppHost LocalPorts tests ([#593](#593)) ([58d3056](58d3056)) * **tests:** the OTLP collector survives a lost port race and ignores traffic that is not an export ([#672](#672), [#676](#676)) ([#677](#677)) ([965c737](965c737)) * **web:** a scoped audit view filtered to nothing names both the record and the range ([#666](#666)) ([41bbfe1](41bbfe1)) * **web:** an abandoned dialog attempt's success no longer hijacks the replacement on Customers, Daily Entry, Flocks, Grades and Products ([#703](#703)) ([#705](#705)) ([85605db](85605db)) * **web:** an abandoned dialog attempt's success no longer hijacks the replacement on Inventory, Expenses, History and Stock ([#703](#703)) ([#706](#706)) ([60a4997](60a4997)) * **web:** an abandoned edit's success no longer hijacks the dialog that replaced it on Users ([#703](#703)) ([#710](#710)) ([778faab](778faab)) * **web:** an abandoned order attempt's success no longer hijacks the dialog that replaced it ([#702](#702)) ([522c699](522c699)) * **web:** capture screens open on the flock you last used, and assigning one no longer guesses ([#646](#646)) ([#699](#699)) ([7f8f317](7f8f317)) * **web:** constrain dialog session helpers to declared scopes ([#715](#715)) ([389e3c8](389e3c8)) * **web:** date validation gets one boundary table instead of one case per review round ([#666](#666)) ([215f830](215f830)) * **web:** keep a paged window and an item panel on the user's newest intent ([#645](#645)) ([d81bccf](d81bccf)) * **web:** keep Sales order panels closed after pending writes ([#711](#711)) ([f0f7492](f0f7492)) * **web:** keep Sales panels closed after pending Open reads ([#716](#716)) ([620411f](620411f)) * **web:** make login take the cross-tab cookie lock so a racing refresh cannot restore the wrong session ([#648](#648)) ([ff18beb](ff18beb)) * **web:** make the entity picker read as a search field and focus it on open ([#736](#736)) ([66ef667](66ef667)), closes [#735](#735) * **web:** page truncated customer and movement tables with usePagedList ([7cfe4d6](7cfe4d6)) * **web:** reconcile Sales line edits with refreshed orders ([#717](#717)) ([d7dd2c9](d7dd2c9)) * **web:** the audit date filter accepts low-numbered years, and its empty state covers every narrowing ([#666](#666)) ([af52d25](af52d25)) * **web:** the audit date filter rejects impossible dates, and its history guard actually guards ([#666](#666)) ([8d51846](8d51846)) * **web:** the expense range bounds are not capped at today, which the month-end default exceeds ([#667](#667)) ([7e01864](7e01864)) * **web:** the help text calls the expiry field what the field calls itself ([#666](#666)) ([2fd1f3c](2fd1f3c)) * **web:** the stock lot date range sits in the bounded toolbar ([#653](#653)) ([43dec5e](43dec5e)) ### Refactoring * **web:** extract SalesPage's dialog-write wrapper into a shared useDialogAction hook ([#703](#703)) ([#704](#704)) ([60ee9d9](60ee9d9)) ### Documentation * add k6 preparation steps to the dev-database fixture runbook ([#643](#643)) ([a4f1f09](a4f1f09)) * add runbook for loading the simulation fixture into a dev database ([#639](#639)) ([2d143b8](2d143b8)) * **agents:** a PR closes its issue from the body, not the title ([#744](#744)) ([39be13c](39be13c)) * **agents:** drop the commit and push gate, and require screenshots on UI changes ([#757](#757)) ([6225172](6225172)) * **agents:** find guards by grepping registry readers; amend issues a PR overtakes ([#580](#580)) ([fe3fde8](fe3fde8)) * **agents:** the Playwright specs have been in CI since 2026-08-08 ([#768](#768)) ([68ee612](68ee612)) * **aspire:** record the second local database and pin the AppHost dashboard ports ([#623](#623)) ([713b941](713b941)) * compress AGENTS.md to one paragraph per rule, and draw the two orders that matter ([#551](#551)) ([997ae8a](997ae8a)) * item 7 names each screen's actual initial filter value ([#666](#666)) ([70a53d8](70a53d8)) * multi-farm tenancy decision record and AGENTS/GLOSSARY sync ([#537](#537)) ([#601](#601)) ([2c34771](2c34771)) * name the scoped filtered-empty key and state the [#653](#653) relationship plainly ([#666](#666)) ([0e93dac](0e93dac)) * note that a PackageReference in Directory.Build.props is invisible to the dependency graph ([4845724](4845724)) * **plans:** commit the [#722](#722) and [#745](#745) design records ([#754](#754)) ([c942fcd](c942fcd)) * record [#579](#579) as won't-fix — suspension is immediate for use, not issuance ([#582](#582)) ([7a3be40](7a3be40)) * record the [#508](#508) audit ordering key and the tracked-file guard lesson ([#701](#701)) ([08964e9](08964e9)) * **runbooks:** add procedure to rename the default farm's code after upgrade ([#731](#731)) ([2f6e242](2f6e242)) * screenshots of the running SPA in the README ([#550](#550)) ([711488a](711488a)) * **sim:** commit the dashboard screenshot, capture the palette matrix, and record the [#651](https://github.com/mforce/cluckwork/issues/651)/[#652](https://github.com/mforce/cluckwork/issues/652) conventions ([#660](#660), [#662](#662), [#663](#663), [#664](#664)) ([#665](#665)) ([930ea30](930ea30)) * specify searchable entity picker ([#641](#641)) ([91d4300](91d4300)) * split the README into audience-scoped docs and adopt repo-template scaffolding ([#548](#548)) ([b3f3fcf](b3f3fcf)) * surface Aspire local development workflow ([#568](#568)) ([a343baa](a343baa)) * **web:** record the per-screen idempotency-key policies and runWrite's refresh contract ([#703](#703)) ([#707](#707)) ([8bee651](8bee651)) * **web:** the date-cap help text covers every stocked item, not only feed ([#666](#666), [#667](#667)) ([c8433c5](c8433c5)) * **web:** the help text claims only what is true of recording, and says nothing about filter caps ([#666](#666), [#667](#667)) ([e2f63d1](e2f63d1)) * **web:** the help text describes the date-range filters that shipped ([#666](#666), [#667](#667)) ([c3275b7](c3275b7)) * **web:** the help text stops describing a cap the filters no longer have ([#666](#666), [#667](#667)) ([49654cd](49654cd)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: cluckwork-lockfix[bot] <309265648+cluckwork-lockfix[bot]@users.noreply.github.com>
Closes #735
What
The searchable entity picker (#512) did not read as a search box once open, on every one of its 11 instances across 7 screens.
.named-picker-control inputand.named-picker-listboxsharedbackground,border,border-radiusand near-identical type, and the input's value is the committed name, so it rendered as one more "Sim House A" row above the list.focus()calls were the two Retry paths. A pointer open left the input blurred with the old name in it.Change
NamedEntityPicker.tsx: an effect keyed onopenfocuses the input and selects its text. Declared after the open effect so the input exists in the same commit; Retry paths untouched.styles.css: inset--surface-2background, a leading magnifier drawn with::before/::after(coloured by--muted), brand border whilearia-expanded="true", listbox gap 0.25→0.4rem. Closed trigger, placeholder (spec FR-018), ARIA andtype="text"unchanged.styles.csp.test.ts: guard that walks everyurl()instyles.cssand admits only same-origin paths or fragments — the API serves the sheet underimg-src 'self' blob:, which the dev server does not enforce (codex review finding; see the PR comment).NamedEntityPicker.openFocus.test.tsx: written red first (document.activeElementwasbody), green after.Verification
npx vitest run: 121 files / 2708 tests green.npm run typecheckOK. Coverage floor holds (91.0% lines, 87.3% branches).focused:false, selectionStart 11, selectionEnd 11focused:true, selectionStart 0, selectionEnd 11img-srcdirective injected on the document: glyph present in both themes,cspViolations=0.Not in scope
Help page / glossary (no concept change). Native
<datalist>on Settings (browser-drawn). Help page search already has the icon+placeholder pattern this matches.Summary by CodeRabbit
Bug Fixes
Style
Before / after (1:1, desktop, night + day)
Phone width (390px) after, under the production
img-srcdirective: glyph centred at the 2.75rem field height.