Repository navigation
Add composable cart parts - #1509
Merged
wcole1-godaddy merged 3 commits intoOct 19, 2026
Merged
Conversation
CartDrawerRoot builds a custom cart drawer and CartRoot a cart page from the same parts: title, description, close, notices, items, empty state, summary, subtotal, checkout note and checkout. CartItems renders its child as the item for every line, built from CartItemRoot and the image, name, options, price, quantity and remove parts. useCart reads the cart anywhere inside the provider and useCartItem reads one line. CartDrawer is now composed from these parts; the dividers it always declared between lines now show. AddToCartButton says why it is disabled, and ProductDetailsQuantity shows the quantity message under the field. Catalog's title, description and showHeader props are deprecated, and catalog loading placeholders render inside the host's grid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 96c7380 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
- Oversold SKUs (negative available count) count as sold out again, as on main, in AddToCartButton, ProductDetailsAvailability and the quantity. - CartEmpty falls back to its default content when its children render nothing, using the same check as CartItems and CatalogProducts. - Adds open the drawer only while a CartDrawerRoot is mounted, and CommerceProvider takes onItemAdded so a site with a cart page can confirm adds. Without a drawer, open stays false. - CartItems warns in development when its template has no CartItemRoot. - The checkout note's stable class is on its outer element only. - The cart context value and sample items are memoized. - The conflict test now renders CartNotices and CartEmpty. The README covers onItemAdded, once-per-drawer title and description, parts that render nothing in a host grid, and the reset limit on host markup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Move the cart page example's h1 outside CartRoot, as the README advises. - Give AddToCartButton its own reason when the store cannot be reached. - Warn in development when more than one cart drawer is mounted. - Drop the CartProvider memo, which never hit because the commerce value is new each render. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wcole1-godaddy
approved these changes
Oct 19, 2026
sbolinger-godaddy
marked this pull request as ready for review
October 19, 2026 18:14
wcole1-godaddy
pushed a commit
that referenced
this pull request
Oct 19, 2026
- Oversold SKUs (negative available count) count as sold out again, as on main, in AddToCartButton, ProductDetailsAvailability and the quantity. - CartEmpty falls back to its default content when its children render nothing, using the same check as CartItems and CatalogProducts. - Adds open the drawer only while a CartDrawerRoot is mounted, and CommerceProvider takes onItemAdded so a site with a cart page can confirm adds. Without a drawer, open stays false. - CartItems warns in development when its template has no CartItemRoot. - The checkout note's stable class is on its outer element only. - The cart context value and sample items are memoized. - The conflict test now renders CartNotices and CartEmpty. The README covers onItemAdded, once-per-drawer title and description, parts that render nothing in a host grid, and the reset limit on host markup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Why
This is PR 3 of the compound components work, after #1502 (
ProductDetails) and #1506 (CatalogandProductCard). It does the same for the cart, so the packaged components and their parts are complete.AAB and other agents should keep using the packaged components,
<CartDrawer />via<CommerceStorefront />. The parts are an opt-in for layouts that need more customization.The parts can't produce impossible states. Cart and item parts take no item, quantity or price props. Every change still goes through the shared cart queue in the provider, and sample items never reach the store.
What
CartDrawerRootis the dialog, andCartRootis for a page such as/cart. Both use the same parts:CartTitle,CartDescription,CartClose(nothing outside the drawer),CartNotices,CartItems,CartEmpty,CartSummary,CartSubtotal,CartCheckoutNoteandCartCheckout.CartItemsis the item, rendered once per line. It's built fromCartItemRoot,CartItemImage,CartItemName,CartItemOptions,CartItemPrice,CartItemQuantityandCartItemRemove.useCart()works anywhere inside the provider, for example for a custom cart button.useCartItem()reads one line.CommerceProvider(andCommerceStorefront) take an optionalonItemAdded({ name, quantity, isSample }), so a site without a drawer can confirm adds. An add only opens the drawer while aCartDrawerorCartDrawerRootis mounted; without one,openstaysfalse.CartDraweris now composed from these parts. The README has a "Custom cart layouts" section with the drawer written out and a cart page example.AddToCartButtonsays why it's disabled through its accessible description: over stock, a quantity that isn't a whole number of 1 or more, the cart loading, the store connecting, or no store.ProductDetailsQuantityshows the quantity message under the field and marks the field invalid.Catalog'stitle,descriptionandshowHeaderprops are deprecated but still work. The loading placeholders now render inside theCatalogProductselement, so they follow the host's grid.Not repeating the #1502 and #1506 fixes
classNameon acommerce-partwrapper the CSS reset skips. The panel's position is on an outer element, so host width and margin classes onCartDrawerRootwork, andmr-autoopens it from the left. The shipped-CSS conflict check covers every cart part.role="list", not a<ul>: a<ul>needs a package margin and padding reset, and the conflict check showed that reset would beat host classes onCartItems.Cart*orCartItem*).<CartDrawer />.Visual changes
divide-ybetween lines, but the CSS reset outranked it, so 0.4.0 never showed them. Lines are now part elements the reset skips, so the 1px divider appears. Easy to drop if we'd rather stay pixel-identical.lg: 32px between cards and 40px above and below, instead of 24px and 32px.<div>, the line list isrole="list"withrole="listitem"lines, and the drawer panel sits inside a positioning element. Thecommerce-cart-checkout-adjustments-noteclass moved from the note's paragraph to its outer element, so hiding it also removes its spacing.Notes
Radix 1.1 and 1.3: the package allows
@radix-ui/react-dialog ^1.1.6. The workspace resolves 1.1.15 and a fresh install resolves 1.3.1, and the two link the dialog description differently.CartDrawerRootturns the link off only while noCartDescriptionis mounted, which works on both. The unit tests run on 1.1.15 and the test app on 1.3.1.Host styles on plain elements inside a root lose: the reset sets
color: inheritand removes borders on every non-part element, and because it's unlayered it beats a host's layered CSS. Host heading and paragraph styles lose their size and margin too. This applies to every root, not just the cart. The workaround is to put the class on a part, whose color reaches its children. The utility prefix follow-up could limit the reset to the package's own elements.Review fixes (second commit): an independent review and a restyled "boutique" test site found:
main.CartEmptyignored empty children ({custom && …}); it now falls back likeCartItemsdoes.onItemAddedis for.CartItemRootrendered an empty list silently; it now warns in development.useCart's memo.A third commit fixes four more review points:
<h1>insideCartRoot, against the README's own advice.CommerceStorefrontplus a customCartDrawerRoot) opened both silently; development builds now warn.CartProvidermemo that never hit was removed, along with its misleading comment.The README now also covers parts that render nothing in a host grid, and the reset limit on host markup (next note).
Next: prefixing the package utilities (known issue 1), then the AAB
components.mdandskill.mdupdates.components.mdstill uses<Catalog showHeader={false} />, which this PR deprecates.Testing
Package checks: typecheck, lint, build and all 86 storefront tests pass, 24 of them new. They cover:
CartDescriptionis rendereduseCartcounts for a custom buttonCartEmptyfallbackCartItemRootwarningonItemAddedfor live and sample addsTest app (
commerce-component-test-app, installs thepnpm packtarball the way a host would, with Radix dialog 1.3.1):main(0.4.0).<CartDrawer />pixel for pixel with live items, sample items and an empty cartuseCart"Bag" button anduseCartItemline prices)useProductDetailsOption, a cart page with no drawer, and a toast fromonItemAdded. Its 4 screen checks pass at desktop and phone widths. The site's own headings and buttons inside a root needed the workarounds the README now describes; the reset fix is planned with the utility prefix PR.main: 17 of 19 screenshots are identical. In the other two, the live and sample drawers, the only difference is the new 1px dividers, with each line below shifted down by them.Screenshots
Packaged drawer with the new dividers, and a customized drawer built from the parts:
A cart page built from the same parts, and the quantity message on a product page:
🤖 Generated with Claude Code