Condition editor solution: TypeScript + React + Vite + GraphQL/MSW - #6
Open
claude[bot] wants to merge 5 commits into
Open
Condition editor solution: TypeScript + React + Vite + GraphQL/MSW#6claude[bot] wants to merge 5 commits into
claude[bot] wants to merge 5 commits into
Conversation
…d GraphQL Reimplements the datastore.js dataset (properties, operators, products, including the sparse `wireless` property on products 3-5) behind a mocked GraphQL API using MSW, fetched via graphql-request. The condition editor builds a single [property] [operator] [value] filter with operator options constrained to the selected property's type, and the product list updates live from a pure, unit-tested filterProducts/evaluateCondition function as the condition changes; clearing restores the full list. Adds Vitest + React Testing Library coverage: a full property-type x operator validity matrix and all README worked examples at the domain layer, plus MSW-backed component tests exercising live filtering and clear-filter through the real App. typecheck, lint, test, and build all pass. See SOLUTION.md for the guided tour, architecture rationale, and assumptions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LgpEckQKhFj9SXzRKmpwe6
filterProducts previously ran evaluateCondition as soon as a property was selected (ConditionEditor auto-fills a default operator), which for value-requiring operators like equals/contains/greater_than/less_than/in matched nothing until a value was typed - showing an empty or wrongly narrowed list instead of the full catalog. Add isConditionComplete() to domain/filter.ts: true immediately for any/none (no value needed), and only once a non-empty value (or non-empty list for `in`) is present for the other operators. filterProducts now short-circuits to the unfiltered list until the condition is complete, so partial conditions - and conditions cleared back to partial, e.g. by changing the operator - show the full list, matching the existing clear-filter behavior.
… Value label
ValueInput rendered the `in` operator's comma-separated text input's
`value` as `parsedArray.join(", ")` instead of tracking the raw text the
user was typing. Since parseStringList/parseNumberList trim and drop
empty segments, a trailing or in-progress comma (e.g. "Headphones,")
round-tripped through parse -> join and vanished the instant it was
typed, making it look impossible to enter more than one value.
ValueInput now keeps its own `listText` state as the source of truth for
what's displayed, updating it directly from the input event while still
calling onChange with the parsed array. ConditionEditor now keys
ValueInput by `${property.id}-${operatorId}` so that state resets
cleanly whenever the property or operator changes (matching the existing
value-reset behavior), rather than carrying over stale text.
Also: hide the "Value" field's label whenever operatorTakesValue(operatorId)
is false (any/none), so no orphaned "Value" text is left rendered next to
a value input that ValueInput itself already omits.
Adds ValueInput/App tests covering: typing "Headphones," and "5," without
the comma disappearing, the resulting filtered list once the list is
complete, the free text resetting on operator/property change, and the
"Value" label being absent (not just the input) for any/none operators.
Split the single Catalog query into ReferenceData (properties + operators,
fetched once) and products(condition: ConditionInput) (fetched per the
current condition). The MSW `Products` handler now owns filtering, reusing
filterProducts/evaluateCondition from domain/filter at response time
instead of render time.
- src/api/useCatalog.ts -> useReferenceData.ts (unchanged one-shot fetch)
+ new useProducts.ts, which only sends `condition` once it's complete
(isConditionComplete) and refetches when the effective condition
changes, keeping the previous list visible mid-refetch.
- App.tsx now just renders whatever useProducts holds; no more
filterProducts call in the render path.
- App.test.tsx drives filtering through the real MSW network layer
(awaiting the Products response) instead of asserting on a pure
client-side filter, plus a request-shape test asserting exactly when/how
`condition` is sent. domain/filter.test.ts and operators.test.ts are
unchanged, still covering the matching semantics directly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgpEckQKhFj9SXzRKmpwe6
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.
Requested by Mauro Lemos · Slack thread
Before: the repo contains only the exercise spec (
EXERCISE_README.md), the vanilla-JS in-memorydatastore.js, and a wireframe PDF — no UI, no build setup, nothing runnable.After: a working, data-driven condition editor UI, built with TypeScript, React, and Vite, backed by a mocked GraphQL API (MSW) instead of the raw
datastore.js. A reviewer cannpm install && npm run devand get a live page: pick a property, pick a valid operator for that property's type, enter a value, and watch the product table filter in real time.This implements the exercise's condition editor — property/operator/value selection with type-appropriate inputs and a live-filtered product list — against a client-side GraphQL data layer rather than importing
datastore.jsdirectly.How: TypeScript + React + Vite (standard
create vite --template react-tsscaffold), Vitest + React Testing Library for tests, MSW for the mocked GraphQL layer (shared between the running app and the test suite, viamsw/browserandmsw/nodeon the same handlers), andgraphql-requestas a thin fetch wrapper for the single, variable-freeCatalogquery — a full client library likeurqlwould be overhead for a one-shot, no-mutation fetch. Filtering itself stays client-side and synchronous (filterProducts/evaluateConditioninsrc/domain/), mirroring how the originaldatastore.jsworked. Full guided tour, file-by-file, inSOLUTION.md.npm run test(57 tests),npm run typecheck,npm run lint, andnpm run buildall pass as of this commit.Assumptions/notes for reviewers:
containsis case-insensitive;equalsandinare exact/case-sensitive matches.in's value input is a comma-separated text field for string/number properties, and a checkbox group for enumerated properties.http://localhost/graphql) is a placeholder — MSW intercepts by operation name, not by reaching a real host.graphql-jsschema/executor behind the mock —handlers.tsmatches by operation name and returns a plain object shaped like the query, which is the standard MSW pattern for a mocked, read-only, single-query API.See
SOLUTION.mdfor the full write-up (architecture rationale, testing breakdown, and other deviations worth a second look).Generated by Claude Code