feat(ep-commerce): add EPSearchEmpty for zero-result state#329
Open
field123 wants to merge 1 commit into
Open
Conversation
Closes the no-results UX gap from #304 / Suggested-Sequencing #1. Designers drop EPSearchEmpty as a sibling of EPSearchHits and it renders heading + body copy when (and only when) a real InstantSearch response lands with zero hits. Visibility gate is `useInstantSearch().results !== null && results.nbHits === 0` — deliberately stricter than `useStats().nbHits`. It stays silent during the SSR-to-first-response window (`results` is null until the first response) and during search-time errors (`results` keeps the last successful value), so designers never see Empty flash on a fresh page or hide a real error behind a "no results" message. Adapter-init failures remain the Provider's `errorContent` concern. Component splits into Mock + Inner per L7: editor branch renders the wrapper unconditionally without ever calling `useInstantSearch`, runtime branch gates and shares the same JSX wrapper so the headless-styling contract sees structurally equivalent leaves either way. Wrapper carries `role="status"`, which is implicitly `aria-live="polite"` per the ARIA spec, so screen readers announce the slot's content when results flip from N hits to 0. `data-ep-search-empty` is the documented leaf for designer className forwarding and the headless `:where()` block (width: 100%; align-self: stretch; text-align: center). Default slot ships heading + body in a centered vbox — recognisable "no results" shape on fresh drop, all values overridable in Studio. Sibling visibility (Stats / Pagination) is the designer's call via `dataCond: $ctx.searchStatsData.nbHits > 0`; Empty stays single-purpose and does not publish a redundant `searchIsEmpty` flag. Tests: 16 new cases covering visibility gate, role/data attributes, mock-vs-runtime split (Inner never mounts in canvas), preview-state overrides, lifecycle flips (5 → 0 mounts, 0 → 5 unmounts), default-slot shape, registration. 193/193 catalog-search jest tests pass.
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.
Part of #304 — ships item 9 from the catalog-search coverage gaps roadmap (Suggested Sequencing #1).
Summary
Designers drop
EPSearchEmptyas a sibling ofEPSearchHitsand it renders heading + body copy when (and only when) a real InstantSearch response lands with zero hits.useInstantSearch().results !== null && results.nbHits === 0— deliberately stricter thanuseStats().nbHits. Stays silent during the SSR-to-first-response window (resultsis null) and during search-time errors (resultskeeps last successful value). Adapter-init failures remain the Provider'serrorContentconcern.useInstantSearch; runtime branch gates and shares the same JSX wrapper so the headless-styling contract sees structurally equivalent leaves either way.role=\"status\"on the wrapper — implicitlyaria-live=\"polite\"per ARIA spec — so screen readers announce the slot's content when results flip from N hits to 0.data-ep-search-emptyis the documented leaf for designer className forwarding and the headless:where()block (width: 100%; align-self: stretch; text-align: center).h2: \"No results found\",p: \"Try clearing your filters or searching for something else.\") — recognisable shape on fresh drop, all values overridable in Studio.dataCond: $ctx.searchStatsData.nbHits > 0; Empty stays single-purpose and does not publish a redundantsearchIsEmptyflag.Design decisions resolved (
/grill-mewalkthrough)results !== null && results.nbHits === 0(no flicker, no error masking)errorContenthandles themsearchStatsData.nbHitscontext<div data-ep-search-empty role=\"status\">\"auto\" | \"withData\"with Mock + Inner splitrole=\"status\"baked, no explicit aria-live (implied)width: 100%; align-self: stretch; text-align: centerchildren,className,previewState; parent enforcedTest plan
yarn test plasmicpkgs/commerce-providers/elastic-path/src/catalog-search/__tests__/)previewState=\"withData\"override, lifecycle flips (5→0 mounts, 0→5 unmounts cleanly), default-slot shape, registrationEP Search Emptynext toEP Search Hitson a search page, verify it renders only on zero-hit responses and shows the default heading + bodyrole=\"status\"is announced by VoiceOver / NVDA when filters narrow to zeroWhat's next from #304
Per the issue's Suggested Sequencing, after this lands the next item is #7 EPToggleRefinement (single-slot wrapper-onClick for boolean filters like "In stock only").