Skip to content

Leave out attribute values no active SKU has on the product page - #1521

Merged
sbolinger-godaddy merged 2 commits into
mainfrom
fix/storefront-hide-unpurchasable-attribute-values
Oct 29, 2026
Merged

sbolinger-godaddy merged 2 commits into
mainfrom
fix/storefront-hide-unpurchasable-attribute-values

Conversation

@sbolinger-godaddy

@sbolinger-godaddy sbolinger-godaddy commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Problem. ProductDetails builds each option control from every value on the attribute, whether or not an active SKU has that value. A merchant may define more values than they stock. For example, a product might define sizes XS to XL but have active SKUs only in XS. Shoppers then see every size. Choosing one with no SKU shows "This combination is unavailable. Choose different options." and disables Add to cart, even though the page offered that choice. We saw this on a live store: 5 of its 8 products offered sizes they couldn't sell.

Change. The product response already includes every active SKU with its attribute value names (gd-commerce-server 0.4.0+, up to 50 SKUs). When it does, the page keeps only the values that some SKU has:

  • getOfferedAttributes() filters each attribute's values to those found on an active SKU. It uses the same completeness check as sold-out marking: no hasNextPage, totalCount equal to the loaded SKUs, and every SKU carrying attributeValues. That check now lives in a shared getSkuAttributeValues() helper, so the two can't drift apart.
  • A URL selection that names a value outside the offered values counts as not chosen. For example, an old link with ?option.size=s asks the shopper to pick a size instead of showing "unavailable."
  • These cases keep every value, as before:
    • the SKU list is truncated
    • the server is older and returns no SKU attributeValues
    • no SKU has a value for the attribute, so the control still renders
  • Sold-out marking doesn't change. A value whose SKUs are all sold out is still shown, crossed out.

useProductDetailsOption() and ProductDetailsOption read the filtered values, so custom option controls get the same behavior.

Changeset

  • Changeset added: @godaddy/gd-commerce-storefront minor. The README and docs/server-api.md are updated to match.

Test Plan

New tests in default-design.test.tsx use a fixture product with five sizes and SKUs in only two:

  • offers only the values some active SKU has, so every choice can be bought: only xs and l render. Choosing L enables "Add to cart, $25.00", and "unavailable" never appears.
  • treats a shared link to a value with no active SKU as not yet chosen: ?option.size=s renders no checked size and a "Select size" button, with no "unavailable" message.
  • keeps every value when the SKU list is incomplete: with hasNextPage: true, all five sizes render.

The first two tests fail against main's product-details-context.tsx (2 failed, 13 passed). They pass with this change. The third passes on both, as expected, because it covers unchanged behavior.

pnpm --filter @godaddy/gd-commerce-storefront build, lint, typecheck, and test (9 files, 133 tests) all pass.

Review verification

  • build, typecheck, lint, test pass on Node 24 (133 tests). With main's product-details-context.tsx, the two new tests fail.
  • Confirmed commerce-server's product query returns only ACTIVE SKUs (up to 50) with value names, so the filter matches what a shopper can buy.
  • Packed main and this PR into commerce-component-test-app: smoke (19 checks), parts, walkthrough and contract all pass, and all 21 smoke screenshots are pixel-identical, so products where every value has a SKU are unchanged.
  • Added three mock products and drove them in Playwright:
    • Sizes XS–XL with SKUs only in XS and L: main shows all five, this PR shows XS and L.
    • ?option.size=s and a color with no SKU: main shows "unavailable", this PR asks for the option.
    • 60 SKUs (over the limit): all values still show.
    • An attribute no SKU carries: all values still show.
    • Sold-out values and nonexistent combinations behave as on main.

🤖 Generated with Claude Code

ProductDetails built each option control from every value on the
attribute. A product with five sizes but SKUs in only two offered all
five, and choosing one of the other three showed "This combination is
unavailable. Choose different options."

When the product response includes every SKU with its attribute values,
keep only values some SKU has. A URL selection outside the offered
values counts as not chosen. A truncated SKU list, an older server, or
an attribute no SKU uses keeps every value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 56ae336

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@godaddy/gd-commerce-storefront Minor

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

@sbolinger-godaddy
sbolinger-godaddy marked this pull request as ready for review October 29, 2026 17:35
@sbolinger-godaddy
sbolinger-godaddy requested a review from a team as a code owner October 29, 2026 17:35
@sbolinger-godaddy
sbolinger-godaddy merged commit 48258d3 into main Oct 29, 2026
3 checks passed
@sbolinger-godaddy
sbolinger-godaddy deleted the fix/storefront-hide-unpurchasable-attribute-values branch October 29, 2026 17:47
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