@vtex/faststore 0.0.3 → 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 +41 -0
- package/README.md +7 -3
- package/dist/catalog.json +337 -1
- package/dist/components.d.ts +5 -1
- package/dist/components.js +8 -4
- 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/my-account-profile.md +208 -0
- 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.d.ts +291 -0
- package/dist/matter/my-account-profile/my-account-profile.js +856 -0
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -0
- package/dist/matter/shipping-address/shipping-address.js +2 -2
- 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 -18
|
@@ -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
|
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# MyAccountProfile
|
|
2
|
+
|
|
3
|
+
**Category:** identity
|
|
4
|
+
**Purpose:** The data root, frame, and field Controls for the My Account > Profile page — the Root owns the shopper's saved profile, the draft over it, the view↔edit toggle, and the single batched Save; the field Controls (name, phone, tax ID, birth date, gender — SFS-3288/3289/3290) register with the Root instead of it enumerating them, so which fields the page shows is a composition decision, in both states, from one act of composing.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Visual cues (how to identify in an image)
|
|
9
|
+
|
|
10
|
+
- A My Account > Profile page showing saved shopper data (name, email, phone, tax ID, birth date, gender) with a single "Edit" action.
|
|
11
|
+
- Pressing Edit swaps the same fields into inputs, with "Save" and "Cancel" replacing "Edit".
|
|
12
|
+
- Email always renders read-only, in both states — never an input.
|
|
13
|
+
- Save success or failure surfaces as a toast (SFS-3366), not an inline line or banner near the fields.
|
|
14
|
+
|
|
15
|
+
Use `MyAccountProfile` (not a hand-assembled combination of ad-hoc state) whenever the image shows this whole view/edit/save profile block, composing its own field Controls (`FirstNameControl`, `LastNameControl`, `PhoneControl`, `TaxIdControl`, `BirthDateControl`, `GenderControl`) for whichever fields the page shows.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- The My Account > Profile page's frame: the view↔edit toggle, the batched Save, Cancel, the read-only email, and the Toast-based save feedback.
|
|
22
|
+
|
|
23
|
+
## When NOT to use
|
|
24
|
+
|
|
25
|
+
- Rendering an individual profile field's presentation or input directly on the Root — that is each field Control's job. This Root owns no field's formatting or masking; the Controls delegate that to the `Field`, `PhoneField`, `DocumentField`, `Input`, and `Select` particles.
|
|
26
|
+
- The signed-out prompt — compose the existing `SignInForm` alongside a `SessionGuard` in the store topology; this Root renders nothing for that audience.
|
|
27
|
+
- Editing the email — it is the login identifier and is read-only by construction; there is no edit-state rendering for it.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Composition
|
|
32
|
+
|
|
33
|
+
`MyAccountProfile` is a **compound**: `Root` owns the data + mechanics; the frame parts and every field Control are composed freely (any order, any subset). A field the schema author does not compose is absent from both the view state and the edit state, and cannot reach the write payload — there is no separate flag to keep it out.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
ToastProvider (required ancestor; SFS-3366 — hosts Save's success/error toasts)
|
|
37
|
+
└── SessionRoot (required ancestor; must resolve to loggedIn)
|
|
38
|
+
└── MyAccountProfile.Root (data-fs-my-account-profile; own fragment/data root; owns draft + Save)
|
|
39
|
+
├── MyAccountProfile.Email (read-only, both states)
|
|
40
|
+
├── MyAccountProfile.FirstNameControl / .LastNameControl (required; Field particle)
|
|
41
|
+
├── MyAccountProfile.PhoneControl (optional; compose up to twice via the `field` prop)
|
|
42
|
+
├── MyAccountProfile.TaxIdControl (optional; self-gates on activeDocumentType)
|
|
43
|
+
├── MyAccountProfile.BirthDateControl (optional; Field + Input type="date")
|
|
44
|
+
├── MyAccountProfile.GenderControl (optional; Select over a configurable options prop)
|
|
45
|
+
├── MyAccountProfile.EditButton (view state only)
|
|
46
|
+
├── MyAccountProfile.SaveButton (edit state only)
|
|
47
|
+
└── MyAccountProfile.CancelButton (edit state only)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A field Control is absent from the composition means absent from both states and never in the write payload — there is no separate `show*`/`visible` prop anywhere in this compound (compose-or-omit).
|
|
51
|
+
|
|
52
|
+
`Root` is self-contained: it declares the colocated fragment over `customerProfile` — the identical field set SFS-3286's `customerProfileUpdate` mutation returns — and self-gates on the session's `loggedIn` state. Every frame part and field Control `requiresContext: 'my-account-profile'`, except `MyAccountProfileSkeleton`: it renders directly from the Root, before any Provider exists, so it declares no context requirement (mirrors `ProductShelfSkeleton`). Save calls `useToast()` for its success/error feedback (Decision 10), so `Root` also requires a `ToastProvider` ancestor.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Behavior
|
|
57
|
+
|
|
58
|
+
- **Field registration, not enumeration**: a Control calls `registerField({ key, validate?, normalize? })` in an effect on mount and unregisters on cleanup. The Root validates, batches, and focuses only over the live registry — it never names a field itself (Decision 1).
|
|
59
|
+
- **Dirty is value comparison, not a touched flag**: `dirty(key) === draft[key] !== savedValues[key]`. Retyping a field back to its saved value makes Save a no-op for it, for free.
|
|
60
|
+
- **Absent and empty are different instructions**: a dirty field cleared to `''` erases upstream; a field the shopper never touched is omitted from the payload entirely.
|
|
61
|
+
- **Save order**: compute dirtiness first → nothing dirty raises the saved toast without dispatching → otherwise validate only the dirty registered fields' current draft values (Decision 9) → any error blocks the dispatch and focuses the first invalid field in rendered DOM order → otherwise normalize dirty values and dispatch one `$actions.identity.customerProfileUpdate` → a `userError` raises an error toast and keeps the edit state with the draft intact → success returns to the view state and raises a success toast (Decision 10). A field the shopper never touched is neither validated nor sent.
|
|
62
|
+
- **Edit always opens from the latest saved values**: pressing Edit re-seeds the draft from `savedValues` before switching modes, so a value the server normalized on a previous save (not this task's concern, but a sibling Control's) is what the shopper sees, not a stale local draft.
|
|
63
|
+
- **Background re-delivery**: when `$data` delivers new values while editing, a field re-seeds only if its draft still equals the previously saved value; a dirty field is never clobbered.
|
|
64
|
+
- **Self-gate**: renders nothing outside `state === 'loggedIn'`, even if the topology's `SessionGuard` is missing. Renders `MyAccountProfileSkeleton` while `$data` is unresolved and the shopper is logged in — never an empty summary.
|
|
65
|
+
- **PII**: no profile value appears in any log line. A write that throws (rather than resolving `userErrors`) logs only the error's name; the message shown to the shopper is the `saveFailedMessage` schema prop.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Props
|
|
70
|
+
|
|
71
|
+
**`MyAccountProfile.Root`** — `documentType` (string, e.g. `'CPF'`; the store-configured fallback used when the saved `documentType` is not a registered type), `savedMessage` (string, default `'Your profile has been saved.'`; the toast title on a successful Save), `saveFailedMessage` (string, default `'Unable to save your profile. Please try again.'`; the toast title on a failed Save). Declares `actions: { identity: ['customerProfileUpdate'] }`, `requiresContext: 'session'`, `providesContext: 'my-account-profile'`, `fetchStrategy: 'reactive'`.
|
|
72
|
+
|
|
73
|
+
**`.EditButton` / `.SaveButton` / `.CancelButton`** — `label` (sensible default) or composed `children`.
|
|
74
|
+
|
|
75
|
+
**`.Email` / `.Skeleton`** — no props beyond `className`.
|
|
76
|
+
|
|
77
|
+
**`.FirstNameControl` / `.LastNameControl`** — `label` (default `'First name'` / `'Last name'`), `requiredMessage` (shown when Save is blocked on an empty value).
|
|
78
|
+
|
|
79
|
+
**`.PhoneControl`** — `field` (`'homePhone' | 'mobilePhone'`, default `'mobilePhone'`; compose the Control twice to expose both numbers), `country` (`'US' | 'BR'`, default `'US'`), `label`, `invalidMessage`.
|
|
80
|
+
|
|
81
|
+
**`.TaxIdControl`** — `label` (default `'National tax ID'`), `invalidMessage`, `lockedReason` (the inline copy shown beside the disabled input when a CPF is already saved).
|
|
82
|
+
|
|
83
|
+
**`.BirthDateControl`** — `label` (default `'Birth date'`).
|
|
84
|
+
|
|
85
|
+
**`.GenderControl`** — `label` (default `'Gender'`), `placeholder`, `clearLabel` (default `'Clear'`; the button shown once a draft value is set, since the Select itself offers no way back to empty), `options` (`{ value, label }[]`, defaults to a sensible set: female / male / other / prefer not to say).
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Field Controls anatomy (SFS-3288/3289/3290)
|
|
90
|
+
|
|
91
|
+
Each Control is a thin binding over an existing presentational particle — it adds no validation or formatting logic of its own beyond wiring the particle into the Root's registration contract:
|
|
92
|
+
|
|
93
|
+
- **Registration**: in a mount effect, a Control calls `registerField({ key, validate?, normalize? })` and returns the unregister function from the effect. `firstName`/`lastName` register a validator that rejects an empty draft value; every other Control either registers a format-only validator (a present value must be well-formed; empty is always valid) or no validator at all.
|
|
94
|
+
- **Rendering**: `mode === 'view'` renders `savedValues[key]` as plain text (empty, never a placeholder, when the key was never saved); `mode === 'edit'` renders the bound particle over `draft[key]`, writing through `setField(key, value)`.
|
|
95
|
+
- **Normalization**: `PhoneControl` registers `normalize = (v) => v ? toE164(v, country) : v`; `BirthDateControl` registers the native `yyyy-mm-dd` ↔ stored-format identity map. Normalization runs once, on Save, only for a field the shopper actually changed.
|
|
96
|
+
- **The locked tax ID**: when a saved CPF exists, `TaxIdControl`'s edit branch renders a **disabled** input carrying the saved (masked) value, a `data-fs-my-account-profile-tax-id-readonly` hook, and `lockedReason` — never plain text, never omitted. It registers no validator in this branch and is never dirty. With no saved document, it renders an editable, optional `DocumentField` instead.
|
|
97
|
+
- **Self-gate**: `TaxIdControl` renders nothing when the Root's `activeDocumentType` is undefined or unsupported (V0 supports CPF only).
|
|
98
|
+
- **One phone Control, two bindings**: `PhoneControl`'s `field` prop selects which draft key an instance binds; composing it twice (once per `field` value) exposes both `homePhone` and `mobilePhone` as independent, independently-erasable fields.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Context contract (`useMyAccountProfile()`)
|
|
103
|
+
|
|
104
|
+
Every field Control consumes this hook — the seam SFS-3287 designs and SFS-3288/3289/3290 consumes as-is:
|
|
105
|
+
|
|
106
|
+
| Field | Shape | Notes |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `mode` | `'view' \| 'edit'` | A Control renders its own branch off this; there is no `show*` prop anywhere. |
|
|
109
|
+
| `savedValues` / `draft` | `Record<key, string>` | Every editable key is always a string; a `null` upstream value is `''`. |
|
|
110
|
+
| `fieldErrors` | `Partial<Record<key, string>>` | Set on a blocked Save; cleared on the field's own next change or on Cancel. |
|
|
111
|
+
| `email` | `string` | The read-only login identifier. |
|
|
112
|
+
| `activeDocumentType` | `string \| undefined` | The saved `documentType` when registered, else the Root's `documentType` prop. |
|
|
113
|
+
| `pending` | `boolean` | Set while the Save dispatch is in flight; parts self-gate and disable off it. Save success/failure feedback surfaces as a toast (Decision 10), not through context. |
|
|
114
|
+
| `registerField(registration)` | `() => unregister` | Call in an effect; the returned function unregisters on cleanup. |
|
|
115
|
+
| `setField(key, value)` | — | Writes one draft value; clears that field's error. |
|
|
116
|
+
| `edit()` / `cancel()` | — | Mode transitions; `cancel()` restores `draft` from `savedValues`. |
|
|
117
|
+
|
|
118
|
+
The `data-fs-my-account-profile-field="<key>"` attribute on each Control's own wrapper element is part of this contract — it is the seam `focusFirstInvalid` queries by rendered DOM order on a blocked Save.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Canonical schema example
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"toast-provider": { "type": "ToastProvider", "children": ["session"] },
|
|
127
|
+
"session": { "type": "SessionRoot", "children": ["profile-guard"] },
|
|
128
|
+
"profile-guard": {
|
|
129
|
+
"type": "SessionGuard",
|
|
130
|
+
"props": { "when": ["loggedIn"] },
|
|
131
|
+
"children": ["profile-root"]
|
|
132
|
+
},
|
|
133
|
+
"profile-root": {
|
|
134
|
+
"type": "MyAccountProfileRoot",
|
|
135
|
+
"props": { "documentType": "CPF" },
|
|
136
|
+
"children": [
|
|
137
|
+
"profile-email",
|
|
138
|
+
"profile-first-name",
|
|
139
|
+
"profile-last-name",
|
|
140
|
+
"profile-mobile-phone",
|
|
141
|
+
"profile-tax-id",
|
|
142
|
+
"profile-edit",
|
|
143
|
+
"profile-save",
|
|
144
|
+
"profile-cancel"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
"profile-email": { "type": "MyAccountProfileEmail", "children": [] },
|
|
148
|
+
"profile-edit": { "type": "MyAccountProfileEditButton", "children": [] },
|
|
149
|
+
"profile-save": { "type": "MyAccountProfileSaveButton", "children": [] },
|
|
150
|
+
"profile-cancel": { "type": "MyAccountProfileCancelButton", "children": [] },
|
|
151
|
+
"profile-first-name": { "type": "MyAccountProfileFirstNameControl", "children": [] },
|
|
152
|
+
"profile-last-name": { "type": "MyAccountProfileLastNameControl", "children": [] },
|
|
153
|
+
"profile-mobile-phone": {
|
|
154
|
+
"type": "MyAccountProfilePhoneControl",
|
|
155
|
+
"props": { "field": "mobilePhone", "country": "BR" },
|
|
156
|
+
"children": []
|
|
157
|
+
},
|
|
158
|
+
"profile-tax-id": { "type": "MyAccountProfileTaxIdControl", "children": [] }
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Add each field Control id to `profile-root`'s `children` array for the fields the store exposes. A sibling `SignInForm`, gated by a `SessionGuard` with `when: ["anonymous", "identified"]`, composes next to `profile-guard` for the signed-out audience.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Common mistakes
|
|
167
|
+
|
|
168
|
+
- **Rendering `MyAccountProfile.Root` outside `SessionRoot`.** `useSession()` throws; the page must host a `SessionRoot`.
|
|
169
|
+
- **Rendering `MyAccountProfile.Root` outside a `ToastProvider`.** Save calls `useToast()` (Decision 10); without a `ToastProvider` ancestor that call throws.
|
|
170
|
+
- **Expecting the email to be editable.** It is always read-only; there is no edit-state rendering for it anywhere in this compound.
|
|
171
|
+
- **Composing a field Control without an unmount cleanup on its `registerField` effect.** A Control that never unregisters leaks a stale validator into the registry after it stops rendering — every shipped Control returns the unregister function from its effect; keep that pattern in a custom Control.
|
|
172
|
+
- **Reading `draft` in the view state, or `savedValues` in the edit state.** Each Control must branch on `mode` itself (Decision 2); the Root does not hide this for you.
|
|
173
|
+
- **Rendering a frame part or field Control outside `MyAccountProfileRoot`.** Every part except `Skeleton` `requiresContext: 'my-account-profile'` and throws otherwise.
|
|
174
|
+
- **Giving a required Control's native input a `required` HTML attribute.** Requiredness is expressed only through the registered `validate` function (Decision 2 of SFS-3288/3289/3290); a native `required` attribute makes the browser block the form's `submit` event before the Root's own validation and focus logic ever runs.
|
|
175
|
+
- **Hiding the `TaxIdControl` in the edit state when a CPF is already saved.** It must render a disabled, locked input instead — a field disappearing between view and edit reads as a bug (product-confirmed, 2026-09-03).
|
|
176
|
+
- **Adding a `showPhone`/`visible` boolean prop to a Control.** Presence is composition; a boolean visibility prop contradicts compose-or-omit and is out of scope for every Control in this group.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Styling
|
|
181
|
+
|
|
182
|
+
| Component | What you style | Plausible classes |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| `MyAccountProfile.Root` | The `data-fs-my-account-profile` root `<form>`; carries `-mode`/`-pending` | `flex flex-col gap-4` |
|
|
185
|
+
| Every field Control's wrapper | `data-fs-my-account-profile-field="<key>"` — the Root's focus seam, on every Control | `flex flex-col gap-1` |
|
|
186
|
+
| `.Email` | `[data-fs-my-account-profile-email]` | `text-sm text-neutral-600` |
|
|
187
|
+
| Save feedback | Styled through the `[[toast]]` compound (`Toast.Root`/`Toast.Title`/`Toast.Description`), not through this Root's own `data-fs-*` hooks. | — |
|
|
188
|
+
| `.EditButton` / `.SaveButton` / `.CancelButton` | `[data-fs-my-account-profile-edit\|save\|cancel]` | `rounded-md bg-blue-600 px-4 py-2 font-semibold text-white` |
|
|
189
|
+
| `.Skeleton` | `[data-fs-my-account-profile-skeleton]`, lines via `[data-fs-my-account-profile-skeleton-line]` | `animate-pulse` |
|
|
190
|
+
| `.FirstNameControl` / `.LastNameControl` | `[data-fs-my-account-profile-first-name]` / `[data-fs-my-account-profile-last-name]` | `flex flex-col gap-1` |
|
|
191
|
+
| `.PhoneControl` | `[data-fs-my-account-profile-phone="<field>"]` | `flex flex-col gap-1` |
|
|
192
|
+
| `.TaxIdControl` | `[data-fs-my-account-profile-tax-id]`; the locked input also carries `[data-fs-my-account-profile-tax-id-readonly]` | `flex flex-col gap-1` / disabled-input treatment |
|
|
193
|
+
| `.BirthDateControl` | `[data-fs-my-account-profile-birth-date]` | `flex flex-col gap-1` |
|
|
194
|
+
| `.GenderControl` | `[data-fs-my-account-profile-gender]`; its clear button carries `[data-fs-my-account-profile-gender-clear]` | `flex flex-col gap-1` |
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Known gaps
|
|
199
|
+
|
|
200
|
+
- **The server's generic "invalid value" error carries no field key.** A blocked save whose failure came back from the schema, rather than from a registered `validate`, renders in the one error region with nothing to highlight and nowhere to move focus. Improving this needs an API-side change, tracked outside this task.
|
|
201
|
+
|
|
202
|
+
## Related
|
|
203
|
+
|
|
204
|
+
- [[session]] — provides the identity tri-state and the required `SessionRoot` ancestor.
|
|
205
|
+
- [[client-profile-form]] — the closest precedent for the draft-plus-dirty-plus-batched-save mechanics and the raw-fragment/hand-typed-`$data` pattern this Root follows (Decision 8).
|
|
206
|
+
- [[identity-edit]] — the closest precedent for the mode-lives-in-state, mode-is-which-component toggle model this Root follows (Decision 2); its context is scoped to checkout and is not reused here.
|
|
207
|
+
- [[sign-in-form]] — composed alongside this Root's `SessionGuard` sibling for the signed-out audience; not this task's file.
|
|
208
|
+
- [[toast]] — required `ToastProvider` ancestor; Save's success/error feedback surfaces through it (Decision 10, SFS-3366).
|
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
|