@vtex/faststore 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/catalog.json +67 -1
- package/dist/components.d.ts +4 -1
- package/dist/components.js +5 -2
- package/dist/content-types.json +73 -0
- package/dist/docs/account-access-gate.md +153 -0
- package/dist/docs/account-entry-point.md +1 -2
- package/dist/docs/account-menu.md +164 -0
- package/dist/docs/buy-button.md +1 -1
- package/dist/docs/cart.md +1 -1
- package/dist/docs/session.md +1 -1
- package/dist/docs/sign-in-redirect.md +1 -1
- package/dist/matter/account-access-gate/account-access-gate.d.ts +86 -0
- package/dist/matter/account-access-gate/account-access-gate.js +283 -0
- package/dist/matter/account-access-gate/account-access-gate.js.map +1 -0
- package/dist/matter/account-access-gate/sign-out-intent.d.ts +17 -0
- package/dist/matter/account-access-gate/sign-out-intent.js +62 -0
- package/dist/matter/account-access-gate/sign-out-intent.js.map +1 -0
- package/dist/matter/account-entry-point/account-entry-point.js +2 -2
- package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
- package/dist/matter/account-menu/account-menu.d.ts +88 -0
- package/dist/matter/account-menu/account-menu.js +246 -0
- package/dist/matter/account-menu/account-menu.js.map +1 -0
- package/dist/matter/cart/cart.d.ts +1 -1
- package/dist/matter/cart/cart.js +2 -2
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/delivery-options/delivery-options.js.map +1 -1
- package/dist/matter/my-account-profile/my-account-profile.js +1 -1
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/shipping-address/shipping-address.js +1 -1
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/particles/footer-social/footer-social.js +1 -1
- package/dist/particles/incentives/incentives.js +1 -1
- package/dist/sdk/actions/cart-action-provider.d.ts +1 -1
- package/dist/sdk/actions/cart-action-provider.js +1 -1
- package/dist/sdk/actions/cart-action-provider.js.map +1 -1
- package/dist/sdk/actions/session-action-provider.d.ts +1 -1
- package/dist/sdk/actions/session-action-provider.js +1 -1
- package/dist/sdk/actions/session-action-provider.js.map +1 -1
- package/dist/sdk/actions/shipping-action-provider.d.ts +1 -1
- package/dist/sdk/actions/shipping-action-provider.js +1 -1
- package/dist/sdk/actions/shipping-action-provider.js.map +1 -1
- package/dist/sdk/cache/storefront-cache.js +1 -1
- package/dist/sdk/cache/storefront-cache.js.map +1 -1
- package/dist/sdk/cart/operations.d.ts +4 -4
- package/dist/sdk/cart/operations.js +1 -1
- package/dist/sdk/cart/operations.js.map +1 -1
- package/dist/sdk/cart/optimistic.js +1 -1
- package/dist/sdk/cart/optimistic.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.d.ts +1 -1
- package/dist/sdk/cart/use-cart-actions.js +3 -3
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js.map +1 -1
- package/dist/sdk/cart/write-gate.js +1 -1
- package/dist/sdk/cart/write-gate.js.map +1 -1
- package/dist/sdk/data/page-reactive-data-root.d.ts +2 -2
- package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
- package/dist/sdk/data/reactive-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-data-provider.js +1 -1
- package/dist/sdk/data/reactive-data-provider.js.map +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
- package/dist/sdk/data/use-reactive-entity.js +1 -1
- package/dist/sdk/data/use-reactive-entity.js.map +1 -1
- package/dist/sdk/generated/gql.js +2 -1
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.js +85 -1
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/identity/operations.js.map +1 -1
- package/dist/sdk/identity/use-identity-actions.d.ts +1 -1
- package/dist/sdk/identity/use-identity-actions.js.map +1 -1
- package/dist/sdk/session/auth-operations.d.ts +1 -1
- package/dist/sdk/session/auth-operations.js.map +1 -1
- package/dist/sdk/session/use-session-actions.d.ts +1 -1
- package/dist/sdk/session/use-session-actions.js.map +1 -1
- package/dist/sdk/session/use-session-revalidation.js +65 -0
- package/dist/sdk/session/use-session-revalidation.js.map +1 -0
- package/dist/sdk/shipping/option-select-operations.d.ts +1 -1
- package/dist/sdk/shipping/option-select-operations.js.map +1 -1
- package/dist/sdk/shipping/request-timeout.js +1 -1
- package/dist/sdk/shipping/request-timeout.js.map +1 -1
- package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
- package/dist/sdk/shipping/shipping-operations.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-actions.d.ts +2 -2
- package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
- package/dist/sdk/state/cart.d.ts +3 -4
- package/dist/sdk/state/cart.js +6 -7
- package/dist/sdk/state/cart.js.map +1 -1
- package/dist/utils/safe-href.js +3 -3
- package/dist/utils/safe-href.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# AccountAccessGate
|
|
2
|
+
|
|
3
|
+
**Category:** identity
|
|
4
|
+
**Purpose:** The account's single access decision — renders the account only for a signed-in shopper, and decides what happens to everyone else.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Visual cues (how to identify in an image)
|
|
9
|
+
|
|
10
|
+
**None, deliberately.** This component has no visual identity of its own and must never be selected from a screenshot. It is a rule about who may read a page, not a block on one. What it draws — a waiting message, or a short notice saying the session ended — replaces the account rather than sitting beside it, so neither appears in a design of the account itself.
|
|
11
|
+
|
|
12
|
+
A screenshot of an account page shows the components the account is made of. This one is the wrapper around them, and it is authored in the layout rather than in the page.
|
|
13
|
+
|
|
14
|
+
The image is the source of truth for layout, and this component is not in any layout an image can show.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- Once, in the account layout, wrapping the account body — so every page declaring that layout inherits the decision.
|
|
19
|
+
|
|
20
|
+
## When NOT to use
|
|
21
|
+
|
|
22
|
+
- On an individual account section, to repeat the rule → the layout already made it. A second gate is a second decision to keep in agreement with the first.
|
|
23
|
+
- To show or hide a subtree by session state, with nothing else riding on it → use `SessionGuard`, which is the declarative show/hide. This component navigates, remembers, and is meant to own a whole page.
|
|
24
|
+
- On the `/login` route → that route is [[sign-in-redirect]], and this component redirects *to* it. Gating it closes a loop the shopper cannot leave.
|
|
25
|
+
- To decide whether a shopper may read *this particular order* or *that address* → the gate answers "is anyone signed in", never "is this record theirs". Record-level authorization belongs to the resolver.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Composition
|
|
30
|
+
|
|
31
|
+
`AccountAccessGate` is the account body's parent in the PageSchema: everything it should protect is in its `children` array, in visual order.
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
AccountAccessGate
|
|
35
|
+
├── AccountMenu ← the account's own composition, in visual order
|
|
36
|
+
└── OrderList
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
It declares `requiresContext: 'session'` and must be a descendant of `SessionRoot`, which the same layout hosts.
|
|
40
|
+
|
|
41
|
+
`AccountAccessGateLoading` and `AccountAccessGateNotice` are the parts it renders in place of the account. They are exported so a store can style and demonstrate them, and they are never authored in a schema.
|
|
42
|
+
|
|
43
|
+
## Components
|
|
44
|
+
|
|
45
|
+
### AccountAccessGate
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Default | Notes |
|
|
48
|
+
|------|------|---------|-------|
|
|
49
|
+
| `loadingLabel` | string | — | Announced politely while the session resolves. Absent renders the waiting element with no text. |
|
|
50
|
+
| `expiredMessage` | string | — | What a shopper whose session ended mid-visit reads. Also what a session the gate could not re-read resolves to. |
|
|
51
|
+
| `signInIncompleteMessage` | string | — | What a shopper who came back from sign-in still signed out reads. |
|
|
52
|
+
| `signInLabel` | string | — | Label of the link out of either notice. Absent renders no link at all. |
|
|
53
|
+
|
|
54
|
+
Content model (`account-access-gate`) fields, edited in the CMS — the same four, each winning over the authored prop when published:
|
|
55
|
+
|
|
56
|
+
| Field | Type | Required | Notes |
|
|
57
|
+
|-------|------|----------|-------|
|
|
58
|
+
| `loadingLabel` | string | no | Cleared or absent keeps the authored prop |
|
|
59
|
+
| `expiredMessage` | string | no | Cleared or absent keeps the authored prop |
|
|
60
|
+
| `signInIncompleteMessage` | string | no | Cleared or absent keeps the authored prop |
|
|
61
|
+
| `signInLabel` | string | no | Cleared or absent keeps the authored prop |
|
|
62
|
+
|
|
63
|
+
**Five outcomes, decided in order.** A shopper who asked to sign out sees nothing while the sign-out navigates. An unresolved session — including one being re-read — shows the waiting part. A signed-in shopper gets the children. A shopper who *was* signed in and no longer is gets the expired notice. Everyone else is sent to sign in.
|
|
64
|
+
|
|
65
|
+
**`identified` counts as signed out.** A shopper the platform recognizes but has not authenticated cannot reach their account, which is the same reading [[account-entry-point]] applies to its own label.
|
|
66
|
+
|
|
67
|
+
**A visitor who arrived signed out is sent, not told.** The redirect replaces the history entry rather than pushing one, carrying the page they were trying to reach as the return, query string included — so back from the platform's login reaches the page they were reading rather than the first page of it, and never bounces off the gate again.
|
|
68
|
+
|
|
69
|
+
**A session that ended mid-visit is told, not sent.** Past a redirect there is no surface left to say anything on, so the gate keeps the page and renders the notice with a sign-in link the shopper may take. This is a deliberate departure from "redirect whenever signed out", and it applies only when the gate has already rendered the account for this shopper.
|
|
70
|
+
|
|
71
|
+
**One automatic hop, then the shopper's choice.** The gate records the hop before redirecting and does not redirect a second time: a shopper who comes back from sign-in still signed out reads the `signInIncompleteMessage` and a link rather than bouncing again. Every other outcome returns the hop, so a shopper who signs in, browses, and comes back later is an arrival again.
|
|
72
|
+
|
|
73
|
+
**A restored document re-decides.** Coming back with the browser's back button can restore a page whose heap already holds an answer — including a painted account. The gate takes the account down on the way into the cache, so the document the browser stores does not hold it, and on the way back it discards the session it was restored with and re-reads it. A re-read that cannot answer resolves to a notice, keeping the reason the gate had already earned; the gate never sits in a wait nothing is coming to end.
|
|
74
|
+
|
|
75
|
+
### AccountAccessGateLoading
|
|
76
|
+
|
|
77
|
+
The waiting look. Renders an `output` announcing its label politely.
|
|
78
|
+
|
|
79
|
+
### AccountAccessGateNotice
|
|
80
|
+
|
|
81
|
+
What renders in the account's place when it will not open: the message for this reason, and the sign-in link.
|
|
82
|
+
|
|
83
|
+
| Prop | Type | Default | Notes |
|
|
84
|
+
|------|------|---------|-------|
|
|
85
|
+
| `reason` | `'expired' \| 'sign-in-incomplete'` | — | Exposed as a data attribute, so a store can style the two cases apart |
|
|
86
|
+
| `message` | string | — | Absent renders nothing in its place |
|
|
87
|
+
| `signInHref` | string | — | Built by the gate from the page the shopper is on |
|
|
88
|
+
| `signInLabel` | string | — | No label, no link |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Canonical schema example
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"account-gate": {
|
|
97
|
+
"type": "AccountAccessGate",
|
|
98
|
+
"props": {
|
|
99
|
+
"loadingLabel": "Carregando sua conta…",
|
|
100
|
+
"expiredMessage": "Sua sessão expirou. Entre novamente para ver seus pedidos.",
|
|
101
|
+
"signInIncompleteMessage": "Não conseguimos entrar na sua conta.",
|
|
102
|
+
"signInLabel": "Entrar"
|
|
103
|
+
},
|
|
104
|
+
"style": ["$account-access-gate"],
|
|
105
|
+
"children": ["account-orders"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Bound to a CMS entry, the element authors no messages at all — the entry supplies them.
|
|
111
|
+
|
|
112
|
+
## Common mistakes
|
|
113
|
+
|
|
114
|
+
- **`AccountGate`, `AuthGuard`, `RequireLogin`, `ProtectedRoute`.** The correct name is `AccountAccessGate`.
|
|
115
|
+
- **Repeating it per account section.** It belongs once in the layout, wrapping the body. Two gates are two decisions that can disagree.
|
|
116
|
+
- **Putting [[sign-in-redirect]] under it.** The gate redirects to `/login`; a gated `/login` redirects to itself.
|
|
117
|
+
- **Placing it outside `SessionRoot`.** It declares `requiresContext: 'session'` and has nothing to read without one.
|
|
118
|
+
- **Reaching for `SessionGuard when={['loggedIn']}` instead.** That expresses one of the five outcomes. It cannot redirect, cannot carry a return, cannot tell an arrival from an expiry, and renders nothing while the session is still resolving.
|
|
119
|
+
- **Expecting it to redirect an expired session.** It renders a notice on purpose — see above.
|
|
120
|
+
- **Authoring `$content` in the schema.** `$content` is compiler-injected from the layout's content entry; naming it in `props` fails the build.
|
|
121
|
+
- **Selecting it from a screenshot.** It has no visual identity; the account page in the image is its children.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Styling
|
|
126
|
+
|
|
127
|
+
| Component | What you style | Plausible classes |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `AccountAccessGateLoading` | The waiting message that stands in for the whole account. Style it as a centered page state rather than an inline label | `flex min-h-[420px] items-center justify-center text-[14px] font-medium text-[#6B7280]` |
|
|
130
|
+
| `AccountAccessGateNotice` | The message and its link, stacked and centered in the account's place | `flex min-h-[420px] flex-col items-center justify-center gap-[12px] text-[14px] text-[#374151]` |
|
|
131
|
+
|
|
132
|
+
The gate itself renders no element of its own: whichever part it renders carries `data-fs-account-access-gate` alongside its own hook, so `[data-fs-account-access-gate]` reaches the current state and the part hooks reach one state each. `data-fs-account-access-gate-notice-reason` separates the two notices, and `data-fs-account-access-gate-notice-action` is the link inside one.
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"AccountAccessGate": {
|
|
137
|
+
"base": ["flex", "min-h-[420px]", "flex-col", "items-center", "justify-center", "gap-[12px]"],
|
|
138
|
+
"parts": {
|
|
139
|
+
"notice-action": ["rounded-[6px]", "bg-[#111827]", "px-[16px]", "py-[8px]", "font-medium", "text-[#FFFFFF]"]
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Styling the gate's swatch reaches the loading part and both notices, which is usually what a store wants: they are the same page state wearing different text. Target `data-fs-account-access-gate-notice-reason="expired"` when the two notices need to read differently.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Related
|
|
150
|
+
|
|
151
|
+
- [[sign-in-redirect]] — the `/login` route this component sends a signed-out visitor to, and which reads back the return this one encoded. Never place it under the gate.
|
|
152
|
+
- [[account-entry-point]] — the header control that offers sign-in to a shopper who chose to go there. It asks; this one insists.
|
|
153
|
+
- [[session]] — the context both read. `SessionGuard`, in the same group, is the declarative show/hide for a subtree with no navigation, no memory, and no loading state of its own.
|
|
@@ -68,7 +68,7 @@ Content model (`account-entry-point`) fields, edited in the CMS:
|
|
|
68
68
|
|
|
69
69
|
The placeholder does not know which state is coming, so it renders for the whole wait either way. A control that authors only `signedOutLabel` therefore shows the placeholder to a logged-in shopper and then removes it, reflowing the header once. Author both labels to avoid it — the half-authored control is supported, not recommended.
|
|
70
70
|
|
|
71
|
-
**The resolving state is announced, not hidden.** The component renders a polite live region that stays mounted through the whole exchange: it is empty on the first pass, receives `loadingLabel` once mounted, and is cleared again when the session resolves. The order matters and is the entire reason the region is not part of the placeholder. Assistive technology reports changes *within* a region already in the tree, so a region that appears with its text already inside says nothing, and one that is removed along with the placeholder can never report that the wait ended. The visible bar beside it is `aria-hidden`, so the wait is spoken once rather than twice.
|
|
71
|
+
**The resolving state is announced, not hidden.** The component renders a polite live region that stays mounted through the whole exchange: it is empty on the first pass, receives `loadingLabel` once mounted, and is cleared again when the session resolves. The order matters and is the entire reason the region is not part of the placeholder. Assistive technology reports changes *within* a region already in the tree, so a region that appears with its text already inside says nothing, and one that is removed along with the placeholder can never report that the wait ended. The visible bar beside it is `aria-hidden`, so the wait is spoken once rather than twice. A page-hosted `SessionRoot` is seeded server-side with the shopper cookie, so it resolves on the first paint and may skip the loading state.
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
@@ -100,7 +100,6 @@ Bound to a CMS entry, the element authors no content props at all — the entry
|
|
|
100
100
|
- **Expecting a default glyph.** The base icon dialect has no account icon, so a store that authors no `icon` gets a label-only link. Author one from the store's own collection.
|
|
101
101
|
- **Letting a merchant pick the icon without declaring it.** An `icon` supplied by a CMS entry renders nothing at all unless the store lists the name in `global.json → icons.include`: the compiler builds each route's sprite from the schemas, and a runtime value never reaches that pass.
|
|
102
102
|
- **Hiding the announced text with `display:none`.** That removes it from the accessibility tree and defeats the announcement; use a clipping technique instead.
|
|
103
|
-
- **Hosting the `SessionRoot` on the page.** The session then resolves server-side without the shopper cookie, and every shopper sees the signed-out label.
|
|
104
103
|
|
|
105
104
|
---
|
|
106
105
|
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# AccountMenu
|
|
2
|
+
|
|
3
|
+
**Category:** navigation
|
|
4
|
+
**Purpose:** The account's section list — a navigation landmark listing the sections a merchant configured, with the one the shopper is reading announced.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Visual cues (how to identify in an image)
|
|
9
|
+
|
|
10
|
+
- A column of text links down the left of an account page, beside the section's own content.
|
|
11
|
+
- One entry stands out — heavier weight, a filled row, a rule down one edge — and it is the section the page is showing.
|
|
12
|
+
- On a narrow screen the same entries read as a single horizontal row of pills above the content, scrolling sideways rather than collapsing behind a control.
|
|
13
|
+
- Five entries is the untouched default; a different count or a different order is the merchant's configuration, not a different component.
|
|
14
|
+
|
|
15
|
+
The image is the source of truth for layout.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
- For the account's persistent section navigation, composed once in the `my-account` layout.
|
|
20
|
+
|
|
21
|
+
## When NOT to use
|
|
22
|
+
|
|
23
|
+
- For a general navigation list with no current-section marking → use [[navigation-link-list]].
|
|
24
|
+
- For the trail down to a deep page → use `BreadcrumbRoot`, which keeps the "current page" claim.
|
|
25
|
+
- For the header's sign-in / account control → use [[account-entry-point]].
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Composition
|
|
30
|
+
|
|
31
|
+
Two parts, and the split is the point. `AccountMenuRoot` is the provider and `AccountMenuList` is the landmark.
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
AccountMenuRoot
|
|
35
|
+
├── AccountMenuList
|
|
36
|
+
└── Slot ← the section's own page
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**The Root wraps the section's content; the landmark does not.** The Root's context has to reach consumers that live inside the slot — the account root's redirect, the sibling breadcrumb — and a context reaches only its descendants. A `<nav>` stretched that far would put every section's content inside a navigation region: a screen reader would announce the page as navigation and "skip to navigation" would skip nothing. So the Root wraps, and the `<nav>` stays around the items alone.
|
|
40
|
+
|
|
41
|
+
The sidebar still renders visually beside the section. That is the swatches' business, not the tree's.
|
|
42
|
+
|
|
43
|
+
## Components
|
|
44
|
+
|
|
45
|
+
### AccountMenuRoot
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Default | Notes |
|
|
48
|
+
|------|------|---------|-------|
|
|
49
|
+
| `items` | `{ label, href, external? }[]` | the library's five | Authored fallback, used when no CMS entry supplies a list. |
|
|
50
|
+
| `children` | ReactNode | — | The landmark and the section's slot. |
|
|
51
|
+
|
|
52
|
+
Content model (`account-menu`) fields, edited in the CMS:
|
|
53
|
+
|
|
54
|
+
| Field | Type | Required | Notes |
|
|
55
|
+
|-------|------|----------|-------|
|
|
56
|
+
| `items` | array | no | One entry per section, in the order they should appear |
|
|
57
|
+
| `items[].label` | string | yes | The section's name |
|
|
58
|
+
| `items[].href` | string | yes | Its destination; `http(s)`, `mailto:`, `tel:`, or a site-relative path |
|
|
59
|
+
| `items[].external` | boolean | no | The entry leaves the account; it is never marked current |
|
|
60
|
+
|
|
61
|
+
**The list comes from three channels, in order: a published entry, the authored `items` prop, then the library's five.** The default is Profile, Orders, Address, Payments, and Security, at `/account/profile` and its siblings — the set the PRD names. Presence decides, not emptiness: a channel supplying an empty list resolves to empty rather than falling through, because a merchant who cleared the list cleared it and the defaults must not override a deliberate publication.
|
|
62
|
+
|
|
63
|
+
**The current section is derived, never authored.** The component reads the current path and matches each entry's destination on segment boundaries. At most one entry is current: among the matches, the one with the longest destination wins, which makes an exact match beat every ancestor without a second rule. `/account/order` is not a match for `/account/orders` — a prefix that does not land on a segment boundary is not a match at all.
|
|
64
|
+
|
|
65
|
+
**An exact match announces `aria-current="page"`; an ancestor match announces `aria-current="true"`.** Inside `/account/orders/123`, Orders is the current item of its set but not the current page. That claim belongs to the breadcrumb's deep entry, and two surfaces announcing "current page" for two different things is worse than the menu claiming only its set.
|
|
66
|
+
|
|
67
|
+
Two kinds of entry are excluded from the match entirely: one marked `external`, and one whose destination is not site-relative. A destination of `/` is excluded from ancestor matching alone — a root destination is an ancestor of every account path, so a merchant listing the storefront's home beside the sections would otherwise see it current on every page. It still matches the path `/` exactly.
|
|
68
|
+
|
|
69
|
+
**V0 assumes the store's public paths carry no binding prefix.** The generated tree lives under a binding segment and a rewrite carries the unprefixed path inward, so the path the component reads matches the destination the merchant published. A store whose public base path *is* its binding id makes the prefixed URL canonical, and then no entry meets a segment boundary and nothing is current. The shipped `AccountEntryPoint` has the same exposure, so this is pre-existing rather than introduced here.
|
|
70
|
+
|
|
71
|
+
### AccountMenuList
|
|
72
|
+
|
|
73
|
+
| Prop | Type | Default | Notes |
|
|
74
|
+
|------|------|---------|-------|
|
|
75
|
+
| `label` | string | `'Account'` | Accessible name of the navigation landmark. |
|
|
76
|
+
|
|
77
|
+
It takes no children: one `<li>` per resolved section is what it renders. With no sections to render it renders nothing at all, rather than an empty landmark for a screen-reader user to land in.
|
|
78
|
+
|
|
79
|
+
**The current item is brought into view when the list scrolls.** A horizontally scrolling row can start with the current item outside the viewport — the fifth section of five leaves the shopper opening the page with no idea which one they are in. So on mount and on every route change, a current item in an overflowing list is scrolled into view. It is a no-op when the item is already visible, and it does not run at all when the list fits, since an unconditional scroll would move the page instead.
|
|
80
|
+
|
|
81
|
+
A refused destination keeps its label and loses its anchor, rendering as text under `data-fs-account-menu-text`. A blank label drops the entry outright: an anchor with no accessible name is a WCAG failure.
|
|
82
|
+
|
|
83
|
+
### useAccountMenu()
|
|
84
|
+
|
|
85
|
+
Returns `{ items, current }` — the resolved sections and the current one as `{ index, match }`. It throws outside an `AccountMenuRoot`. This is the contract the account root's redirect and the sibling breadcrumb read, so the two surfaces cannot disagree about which section is current.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Canonical schema example
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"account-menu": {
|
|
94
|
+
"type": "AccountMenuRoot",
|
|
95
|
+
"children": ["account-menu-list", "account-slot"],
|
|
96
|
+
"style": ["$account-menu"]
|
|
97
|
+
},
|
|
98
|
+
"account-menu-list": {
|
|
99
|
+
"type": "AccountMenuList",
|
|
100
|
+
"props": { "label": "Minha conta" },
|
|
101
|
+
"style": ["$account-menu-list"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Bound to a CMS entry, the Root authors no `items` at all — the entry supplies the whole list.
|
|
107
|
+
|
|
108
|
+
## Common mistakes
|
|
109
|
+
|
|
110
|
+
- **Placing the Root beside the slot instead of around it.** The layout's own consumers would be satisfied and the page's would not: a context reaches only descendants. Context-constraint validation reports it as a build error rather than as an empty menu at runtime.
|
|
111
|
+
- **Putting the `<nav>` around the section's content.** That is what the two-part split exists to prevent; do not wrap the slot in `AccountMenuList`.
|
|
112
|
+
- **Leaving `label` unauthored.** The default is `Account`; a store with its own wording should say so, because a landmark's name is how a screen-reader user tells it from every other one.
|
|
113
|
+
- **Styling the current item and nothing else.** The hook exists beside `aria-current`, never instead of it. Removing the programmatic signal leaves position conveyed by appearance alone.
|
|
114
|
+
- **Publishing an empty list and expecting the defaults.** Empty is a configuration, not an absence. The account's root then has no section to open.
|
|
115
|
+
- **Authoring `$content` in the schema.** `$content` is compiler-injected from the page's or layout's content entry; naming it in `props` fails the build.
|
|
116
|
+
- **Expecting the default five to resolve.** Their routes exist only in a store that authored the account's route family; until then the default is correct in the example store alone.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Styling
|
|
121
|
+
|
|
122
|
+
| Component | What you style | Plausible classes |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `AccountMenuRoot` | `data-fs-account-menu` is the wrapper holding the sidebar and the section side by side | `flex gap-8 md:flex-row flex-col` |
|
|
125
|
+
| `AccountMenuList` | `data-fs-account-menu-list` is the landmark; size the sidebar here | `w-56 shrink-0` |
|
|
126
|
+
| Items | `data-fs-account-menu-items` is the `<ul>`, and the scroll container at narrow viewports | `flex flex-col gap-1` |
|
|
127
|
+
| Item | `data-fs-account-menu-item` is one row | `list-none` |
|
|
128
|
+
| Current item | `data-fs-account-menu-item-current` marks the current row | `font-semibold` |
|
|
129
|
+
| Link | `data-fs-account-menu-link` is the anchor | `block rounded-md px-3 py-2 text-sm` |
|
|
130
|
+
| Refused text | `data-fs-account-menu-text` replaces the anchor for a refused destination | `block px-3 py-2 text-gray-400` |
|
|
131
|
+
|
|
132
|
+
The vertical-to-horizontal switch and the scroll affordance are one DOM styled two ways, authored as the `AccountMenu` swatch set in `global.json → styles`:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"AccountMenuRoot": {
|
|
137
|
+
"account-menu": [
|
|
138
|
+
"flex",
|
|
139
|
+
"flex-col",
|
|
140
|
+
"gap-6",
|
|
141
|
+
"md:flex-row",
|
|
142
|
+
"md:gap-8",
|
|
143
|
+
"[&_[data-fs-account-menu-items]]:flex",
|
|
144
|
+
"[&_[data-fs-account-menu-items]]:gap-2",
|
|
145
|
+
"[&_[data-fs-account-menu-items]]:overflow-x-auto",
|
|
146
|
+
"md:[&_[data-fs-account-menu-items]]:flex-col",
|
|
147
|
+
"md:[&_[data-fs-account-menu-items]]:overflow-visible",
|
|
148
|
+
"[&_[data-fs-account-menu-item-current]_[data-fs-account-menu-link]]:bg-gray-100",
|
|
149
|
+
"[&_[data-fs-account-menu-item-current]_[data-fs-account-menu-link]]:font-semibold"
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The current-item look reaches the anchor through the row, since the hook is on the `<li>` and the anchor is its child.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Related
|
|
160
|
+
|
|
161
|
+
- [[account-entry-point]] — the header control that links into the account.
|
|
162
|
+
- [[navigation-link]] — the client-side link each item composes.
|
|
163
|
+
- [[navigation-link-list]] — the general-purpose CMS-driven list this one is patterned on.
|
|
164
|
+
- `BreadcrumbRoot` — the sibling trail that keeps the "current page" claim for the deep page.
|
package/dist/docs/buy-button.md
CHANGED
|
@@ -35,7 +35,7 @@ BuyButtonRoot
|
|
|
35
35
|
└── BuyButtonButton
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
`BuyButtonButton` dispatches the cart `addItem` action through the compiler-injected `$actions.cart.addItem` prop. The add-to-cart write is **context-free** — it reads and writes the Apollo cache directly — so `BuyButton` needs **no `CartRoot` ancestor** (
|
|
38
|
+
`BuyButtonButton` dispatches the cart `addItem` action through the compiler-injected `$actions.cart.addItem` prop. The add-to-cart write is **context-free** — it reads and writes the Apollo cache directly — so `BuyButton` needs **no `CartRoot` ancestor** (docs/storefront-core/reference-free-particle-model.md § Write side — actions, ADR-0010). Drop it on a PDP, a product card, a shelf, or the header alike.
|
|
39
39
|
|
|
40
40
|
Child order reflects visual order.
|
|
41
41
|
|
package/dist/docs/cart.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Category:** commerce
|
|
4
4
|
**Purpose:** Cart **read** layer for the store. `CartRoot` loads the active cart and exposes it (and the cart query error) to descendants, keeping them in sync as the cart changes. It is **hosted by the layout** (shared shell), wrapping the page slot — so every page, including PDPs, consumes its context without declaring it.
|
|
5
5
|
|
|
6
|
-
Cart **writes** (add / remove / quantity) no longer flow through `CartRoot`. A component declares the cart methods it needs as an `actions: { cart: [...] }` selector on its definition and dispatches through the compiler-injected `$actions.cart.*` prop — the write-side mirror of `fragment` → `$data`. Cart writes are **context-free**: an add-to-cart button works on a PDP, a product card, a search result, or the header with **no `CartRoot` ancestor** (
|
|
6
|
+
Cart **writes** (add / remove / quantity) no longer flow through `CartRoot`. A component declares the cart methods it needs as an `actions: { cart: [...] }` selector on its definition and dispatches through the compiler-injected `$actions.cart.*` prop — the write-side mirror of `fragment` → `$data`. Cart writes are **context-free**: an add-to-cart button works on a PDP, a product card, a search result, or the header with **no `CartRoot` ancestor** (docs/storefront-core/reference-free-particle-model.md § Write side — actions, ADR-0010).
|
|
7
7
|
|
|
8
8
|
This doc covers `CartRoot` — hosted in the layout, never emitted in page schemas — plus the **mini-cart footer checkout CTA** ([below](#mini-cart-footer-checkout-cta)), the composition of `CartCheckoutGuard` → (`CartTotal` + `CartGoCheckout`). The remaining cart line-item and summary particles (`CartList`, `CartItem*`, `CartCount`, `CartError`) exist in the catalog and will be documented when the full cart page enters scope.
|
|
9
9
|
|
package/dist/docs/session.md
CHANGED
|
@@ -34,7 +34,7 @@ Exactly one `SessionRoot` per rendered tree. A layout-hosted provider already co
|
|
|
34
34
|
## Layout hosting vs. page hosting
|
|
35
35
|
|
|
36
36
|
Layout and page scope still fetch differently, even though both now see the shopper's cookie
|
|
37
|
-
([
|
|
37
|
+
([SSR session cookie forwarding](../../../../../docs/storefront-core/reference-ssr-session-cookie-forwarding.md)):
|
|
38
38
|
|
|
39
39
|
- **Layout scope** emits `LAYOUT_QUERY` inside a client component with no SSR seed — no `serverVariables`, no
|
|
40
40
|
`initialData`. The read happens once in the browser and is reused from cache across client-side navigation.
|
|
@@ -134,4 +134,4 @@ A spinner beside the text is a decoration the component does not render. Draw it
|
|
|
134
134
|
|
|
135
135
|
- [[account-entry-point]] — the header control that produces one of this route's inbound URLs, encoding the `returnUrl` this component revalidates. It is what a design showing a "Sign In" control actually calls for.
|
|
136
136
|
- [[navigation-link]] — an ordinary link the shopper chooses to follow, for any destination they read on arrival. This route is passed through, never read.
|
|
137
|
-
-
|
|
137
|
+
- [[account-access-gate]] — the other producer of inbound URLs, redirecting an unauthenticated visitor here with the account page they were trying to reach as the return. Never place this route under it.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ContentValue } from "../../core/dist/define-content-type.js";
|
|
2
|
+
import { ComponentDefinition } from "../../core/dist/define-component.js";
|
|
3
|
+
import "../../core/dist/index.js";
|
|
4
|
+
import { ComponentPropsWithRef, ReactNode } from "react";
|
|
5
|
+
//#region src/matter/account-access-gate/account-access-gate.d.ts
|
|
6
|
+
export declare const AccountAccessGateContent: {
|
|
7
|
+
readonly id: "account-access-gate";
|
|
8
|
+
readonly title: "Account Access Gate";
|
|
9
|
+
readonly description: "What the shopper is told while the account resolves and when it will not open for them.";
|
|
10
|
+
readonly schema: {
|
|
11
|
+
readonly type: "object";
|
|
12
|
+
readonly additionalProperties: false;
|
|
13
|
+
readonly properties: {
|
|
14
|
+
readonly loadingLabel: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly minLength: 1;
|
|
17
|
+
readonly title: "Loading announcement";
|
|
18
|
+
};
|
|
19
|
+
readonly expiredMessage: {
|
|
20
|
+
readonly type: "string";
|
|
21
|
+
readonly minLength: 1;
|
|
22
|
+
readonly title: "Session ended message";
|
|
23
|
+
};
|
|
24
|
+
readonly signInIncompleteMessage: {
|
|
25
|
+
readonly type: "string";
|
|
26
|
+
readonly minLength: 1;
|
|
27
|
+
readonly title: "Sign-in did not complete message";
|
|
28
|
+
};
|
|
29
|
+
readonly signInLabel: {
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
readonly minLength: 1;
|
|
32
|
+
readonly title: "Sign-in link label";
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/** Why the account will not open, as a bounded value: a style target and the choice of message. */
|
|
38
|
+
export type AccountAccessGateNoticeReason = 'expired' | 'sign-in-incomplete';
|
|
39
|
+
/** Props for {@link AccountAccessGate}. It renders a different element per outcome and forwards no
|
|
40
|
+
* element props of its own; each rendered part carries its own. */
|
|
41
|
+
export type AccountAccessGateProps = {
|
|
42
|
+
/** Text announced while the session resolves. */
|
|
43
|
+
loadingLabel?: string;
|
|
44
|
+
/** What a shopper whose session ended mid-visit is told. */
|
|
45
|
+
expiredMessage?: string;
|
|
46
|
+
/** What a shopper who came back from sign-in still signed out is told. */
|
|
47
|
+
signInIncompleteMessage?: string;
|
|
48
|
+
/** Label of the link out of either notice. Without it no link renders — a focusable link a screen
|
|
49
|
+
* reader announces as its bare URL is worse than no control at all. */
|
|
50
|
+
signInLabel?: string;
|
|
51
|
+
/** Merchant-authored fields, sliced from the layout content entry; win over the props field by field
|
|
52
|
+
* when present. Absent in a non-CMS store — the props are the fallback. */
|
|
53
|
+
$content?: ContentValue<typeof AccountAccessGateContent>;
|
|
54
|
+
/** The account. Rendered only for a signed-in shopper. */
|
|
55
|
+
children?: ReactNode;
|
|
56
|
+
};
|
|
57
|
+
/** Props for {@link AccountAccessGateLoading}. Extends native `<output>` attributes. */
|
|
58
|
+
export type AccountAccessGateLoadingProps = ComponentPropsWithRef<'output'>;
|
|
59
|
+
/** Props for {@link AccountAccessGateNotice}. Extends native `<output>` attributes. */
|
|
60
|
+
export type AccountAccessGateNoticeProps = Omit<ComponentPropsWithRef<'output'>, 'children'> & {
|
|
61
|
+
reason: AccountAccessGateNoticeReason;
|
|
62
|
+
/** The message for this reason. Nothing renders in its place when unauthored. */
|
|
63
|
+
message?: string;
|
|
64
|
+
/** Where the link out leads, built by the gate from the page the shopper is on. */
|
|
65
|
+
signInHref: string;
|
|
66
|
+
/** Label of the link out. No label, no link. */
|
|
67
|
+
signInLabel?: string;
|
|
68
|
+
};
|
|
69
|
+
export declare const AccountAccessGateDefinition: ComponentDefinition<AccountAccessGateProps>;
|
|
70
|
+
export declare function AccountAccessGate({ loadingLabel, expiredMessage, signInIncompleteMessage, signInLabel, $content, children }: Readonly<AccountAccessGateProps>): import("react/jsx-runtime").JSX.Element | null;
|
|
71
|
+
/**
|
|
72
|
+
* The look of a session that has not answered yet. A real part with its own hook rather than an ad hoc
|
|
73
|
+
* spinner, so a store can style it and a story can render it.
|
|
74
|
+
*/
|
|
75
|
+
export declare function AccountAccessGateLoading({ className, children, ...props }: AccountAccessGateLoadingProps): import("react/jsx-runtime").JSX.Element;
|
|
76
|
+
/**
|
|
77
|
+
* What renders in the account's place when it will not open: the message for this reason, and a link
|
|
78
|
+
* the shopper may take back into sign-in.
|
|
79
|
+
*
|
|
80
|
+
* The link is the whole of the escape from a sign-in loop. The gate stops redirecting after one
|
|
81
|
+
* automatic hop, so the next attempt is a click rather than a bounce — and a shopper who keeps landing
|
|
82
|
+
* here at least lands on a page that says so.
|
|
83
|
+
*/
|
|
84
|
+
export declare function AccountAccessGateNotice({ reason, message, signInHref, signInLabel, className, ...props }: AccountAccessGateNoticeProps): import("react/jsx-runtime").JSX.Element;
|
|
85
|
+
//#endregion
|
|
86
|
+
//# sourceMappingURL=account-access-gate.d.ts.map
|