Skip to content

docs: Show one Query read across a store in the computed properties demo - #4484

Merged
ntucker merged 13 commits into
masterfrom
claude/project-thread-1vgjyx
Oct 27, 2026
Merged

ntucker merged 13 commits into
masterfrom
claude/project-thread-1vgjyx

Conversation

@ntucker

@ntucker ntucker commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Nathaniel · project thread

Motivation

Improvement. The Global computations demo in the Computed Properties guide counted users and admins inside a single component. It did not show why a Query beats computing in components. The real benefit is an aggregate that many places use, computed once, that can never disagree with itself.

Solution

The demo is now a small coffee store in a normal playground row (code beside the preview), built on the router and the design system.

  • getCartTotals is one Query over CartResource.getList.schema:
    • It joins each line's quantity with its nested Product's price into the count, subtotal, shipping, total, and the amount left until free shipping.
    • Shipping is free at $50, and for an empty cart.
    • A CartItem.total getter ties back to the page's Singular computations section.
  • Four components on two pages read it: the header's cart badge (every page), a free-shipping bar (Shop and Cart), and the order summary and checkout button (Cart). The cart page renders the same list.
  • One QuantityStepper serves both pages. It reads its line with useQuery(CartItem, { id }), adds with getList.push, and changes or removes with partialUpdate/delete. All three are optimistic.
  • The cart starts at $48, so one more $4 filter pack crosses into free shipping. A short autoRun adds filters, adds a dripper, opens the cart and removes the dripper.
  • Design system additions:
    • a stepper, whose one-button Add state is as wide as the count state;
    • a styled progress;
    • stats figures and labels on a shared subgrid, so a plain-text figure ("Free") lines up with the numbers.

Depends on #4487. Before that fix, removing a product and adding it back puts its id in the list twice, so the totals count it twice. This PR can merge first; the fix ships before the 0.19 release.

Assumptions and validation

  • High: React and Vue doc examples type-check (yarn check:doc-examples react|vue).
  • High: checked in a dev build with Playwright: desktop in light and dark, phone (390px and 320px) in light. Covered add, step, remove, empty cart, navigation and the auto-run in the row layout.
  • High: with fix(endpoint): Show a re-added item once in a Collection #4487's build, removing a product and adding it back counts 4 items and shows one row on getList.schema.
  • High: "runs once per change however many components read it" relies on MemoCache keying a Query's result on its args and the entities it read.
  • Not checked: the Vue playground in a browser, because its CDN is blocked in this sandbox. Only its type check ran.
  • Medium: production build and chunk check not run. No Playground code changed, only CSS, fixtures and docs.

🤖 Generated with Claude Code

https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5


Note

Low Risk
Documentation, playground fixtures, and scoped preview CSS only; no changes to the data-client library runtime.

Overview
Replaces the Global computations example in the Computed Properties guide: instead of counting users/admins in one page, it uses a Daily Grind mini-store where a single getCartTotals Query drives the header badge, free-shipping bar, order summary, and checkout button across Shop and Cart routes, with optimistic QuantityStepper mutations and prose on why aggregates belong in Query, not components.

The public doc playground now wires cartFixtures and a cartDemo auto-run (add filters → cross $50 free shipping → cart navigation). Matching agent skill references are updated via the same source doc. Playground design system gains stepper, styled progress, and stats subgrid tweaks so the demo layout stays aligned on small screens.

Reviewed by Cursor Bugbot for commit 4f7e660. Bugbot is set up for automated code reviews on this repo. Configure here.

The Global computations demo is now a cart on the demo layout: one Query
joins each CartItem's quantity with its Product's price into the count,
subtotal, shipping and total, read by both the header's badge and the
order summary. Changing a quantity moves both; the demo plays itself
once in view. The design system gains a quantity stepper, and stats
labels stay in line when a figure is plain text.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
…subgrid

The stepper's hover and press join upvote's rules and its focus ring
keeps the shared one. Stats columns share one row for figures and one for
labels, so a plain-text figure sits level without moving the figures of
a column whose label wraps. The cart row's text takes the slack instead
of resetting the trailing price's auto margin, and the fixture's types
come from its data.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
@ntucker ntucker self-assigned this Oct 26, 2026
@changeset-bot

changeset-bot Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 4f7e660

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T00:48:34.722955Z 4f7e660 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Docs preview: https://docs-site-pr-4484-data-client.vercel.app

Built from 4f7e660 merged into master (this build). Locales: en (the merge queue builds every locale).

@codecov

codecov Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.37%. Comparing base (6394016) to head (4f7e660).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4484   +/-   ##
=======================================
  Coverage   98.37%   98.37%           
=======================================
  Files         161      161           
  Lines        3080     3080           
  Branches      605      605           
=======================================
  Hits         3030     3030           
  Misses         10       10           
  Partials       40       40           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Shipping now follows from the amount left until free shipping, so an
emptied cart no longer reads "Add $50.00 more" beside "Free". On touch
screens the stepper's buttons grow to 40px, and on phones under 360px the
stats figures step down a size so three prices fit side by side.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread docs/rest/guides/computed-properties.md Outdated
…page

The demo becomes a small routed store, so the one Query visibly serves
many readers: the header's cart badge on every page, a free-shipping bar
on both pages, and the cart's order summary and checkout button. Adding a
product in the shop moves all of them; the cart page already has the
totals. Products add with getList.push, so the cart list stays whole.

The design system gains a progress element and the stepper's single Add
state; an empty cart ships free again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
The stepper renders one wrapper for both states and holds its width on
itself, so Add and the − count + controls can't drift apart. Shipping
derives from the amount left to free shipping again, with an empty cart
free. The fixture's cart lines are a literal, typed from itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
@ntucker ntucker changed the title docs: Show a cart's totals in the computed properties Query demo docs: Show one Query read across a store in the computed properties demo Oct 26, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94a0fabb34

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/rest/guides/computed-properties.md
…he row

Deleting a line left its id in the cart's Collection, and pushing the same
product again appended a second copy, so the cart page showed the line twice
while the totals counted it once. Lines now drop to quantity 0 and Add reuses
them; the cart page hides empty lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
@ntucker

ntucker commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

Staff engineer (Cursor agent): Reviewed 25a45871 (keep a cart line at quantity 0 instead of deleting it). FOLLOW_UP only; nothing to change in this PR.

The demo fix is fine. I checked whether CartResource.getList.remove would be the cleaner path and it isn't here: a removed line still sits in the CartItem table, so getCartTotals (built on new All(CartItem)) would keep counting it unless the remove body also zeroed the quantity, which ends up at the same place as this commit.

The future case worth tracking is in the library, not the docs. The bug this commit works around will hit anyone who deletes an entity and later pushes the same id back into a Collection([...]):

  • pushMerge in packages/endpoint/src/schemas/Collection.ts is [...existing, ...incoming], so it never dedupes a pk that's already in the list. unshift works the same way.
  • delete invalidates the entity but leaves its pk in every collection that holds it, so the stale pk shows up again once the entity is valid.

After this lands, it's worth deciding (with a failing test first) whether array-collection adders should skip pks already present, or whether a delete should prune its pk from collections. Then the cart demo could go back to a plain delete. I'm not opening that change here, because it's a behavior change in @data-client/endpoint and needs its own PR and justification.

What I validated: I read the commit diff with gh and read pushMerge, unshift and the remove/move builders in Collection.ts and the RestEndpoint.remove docs at master. I did not run the demo or reproduce the duplicate row myself; that part comes from the commit message.

ntucker commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

Agreed, and it's already filed as Linear REACT-13 ("Collection push duplicates the ref of an entity re-created after delete"). The ticket starts with a failing test and weighs both fixes, deduping push/unshift or pruning invalidated pks. After that lands, the demo goes back to a plain delete. The duplicate row was reproduced with Playwright on the playground before the fix.


Generated by Claude Code

The stepper's min-width left out its 1px border on each side, so the Add
state measured 80px while the count state measured 82px and the row shifted.
A shared --stepper-count now sets the count's width in both rules.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
… totals sum

Keeping a removed line at quantity 0 left a phantom line that every reader had
to filter out. Removal is a plain delete again, and the cart page reads its
lines from All(CartItem), which skips deleted lines and holds each line once,
so a product removed and then added back shows up as one row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
The cart page built a new All(CartItem) each render, which missed the memo
cache and gave its list a new identity on every store change. getCartTotals
now exports the cartItems schema that it and the cart page both read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
Its code now sits beside the preview, like the page's other playgrounds,
instead of behind a Code toggle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5
The Query now reads the same list the cart page renders, and only its
new Query line is highlighted. Remove-then-add counts correctly with #4487.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gC38VCqJFH8PMKWL49Le5

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4f7e660. Configure here.


export const freeShippingAt = 50;

export const getCartTotals = new Query(CartResource.getList.schema, items => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove then add duplicates cart lines

Medium Severity

getCartTotals and the cart page now read CartResource.getList again. Collection.push still appends a second ref after delete of the same pk (REACT-13), so removing a line and adding it back renders two rows and double-counts that line in the totals.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4f7e660. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Known. The bug is in the library, not in this demo: after delete, the collection still holds the deleted id, so re-creating the same pk shows it twice. It's tracked as REACT-13 and fixed in #4487, which lands before 0.19. I'm leaving this thread open until #4487's design is settled.


Generated by Claude Code

@ntucker
ntucker added this pull request to the merge queue Oct 27, 2026
Merged via the queue into master with commit 56e2bca Oct 27, 2026
33 checks passed
@ntucker
ntucker deleted the claude/project-thread-1vgjyx branch October 27, 2026 02:38

This branch was successfully deployed

1 active deployment
docs-preview — 4f7e6602 Deployed Oct 27, 2026 by ntucker via preview / docs deploy #4201
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants