@akinon/pz-theme 2.0.83-rc.0 → 2.0.84-beta.0

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +39 -33
  2. package/package.json +4 -4
  3. package/src/__tests__/auth-flow-block.test.tsx +211 -0
  4. package/src/__tests__/auth-flow-bus.test.ts +290 -0
  5. package/src/__tests__/auth-form-error-block.test.tsx +326 -0
  6. package/src/__tests__/auth-form-validation.test.tsx +204 -0
  7. package/src/__tests__/auth-step-title.test.tsx +202 -0
  8. package/src/__tests__/detached-submit-state.test.tsx +216 -0
  9. package/src/__tests__/forgot-password-composed.test.tsx +313 -0
  10. package/src/__tests__/otp-form-composed.test.tsx +487 -0
  11. package/src/__tests__/password-reset-step.test.tsx +269 -0
  12. package/src/__tests__/register-form-composed.test.tsx +739 -0
  13. package/src/__tests__/registry-parity.test.ts +9 -0
  14. package/src/auth/auth-field-errors.tsx +151 -0
  15. package/src/auth/auth-flow-bus.ts +415 -0
  16. package/src/auth/form-error-context.tsx +55 -0
  17. package/src/auth/form-submit-bus.ts +93 -0
  18. package/src/auth/otp-form-context.tsx +80 -0
  19. package/src/blocks/__tests__/button-block.test.tsx +55 -0
  20. package/src/blocks/__tests__/link-block.test.tsx +48 -30
  21. package/src/blocks/__tests__/overlay-sweep.test.tsx +31 -8
  22. package/src/blocks/__tests__/product-gallery-block.test.tsx +12 -4
  23. package/src/blocks/__tests__/product-recommendations-block.test.tsx +9 -2
  24. package/src/blocks/__tests__/store-locator-block.test.tsx +35 -8
  25. package/src/blocks/__tests__/text-block.test.tsx +37 -3
  26. package/src/blocks/__tests__/variant-selector-block.test.tsx +16 -4
  27. package/src/blocks/auth-consent-block.tsx +214 -0
  28. package/src/blocks/auth-error-block.tsx +82 -0
  29. package/src/blocks/auth-field-block.tsx +270 -0
  30. package/src/blocks/auth-flow-block.tsx +251 -0
  31. package/src/blocks/auth-form-block.tsx +391 -47
  32. package/src/blocks/auth-step-block.tsx +81 -0
  33. package/src/blocks/auth-step-title-block.tsx +81 -0
  34. package/src/blocks/block-renderer-registry.tsx +18 -0
  35. package/src/blocks/button-block.tsx +91 -13
  36. package/src/blocks/forgot-password-block.tsx +318 -24
  37. package/src/blocks/link-block.tsx +22 -84
  38. package/src/blocks/otp-code-input-block.tsx +180 -0
  39. package/src/blocks/otp-form-block.tsx +1092 -0
  40. package/src/blocks/otp-phase-block.tsx +71 -0
  41. package/src/blocks/register-form-block.tsx +541 -74
  42. package/src/blocks/text-block.tsx +17 -40
  43. package/src/chrome/__tests__/theme-config-source.test.ts +4 -1
  44. package/src/hooks/__tests__/use-overlay.test.tsx +97 -5
  45. package/src/hooks/use-block-action.ts +87 -4
  46. package/src/hooks/use-overlay.ts +60 -9
  47. package/src/overlays/__tests__/modal-chrome-padding.test.tsx +119 -0
  48. package/src/overlays/__tests__/modal-registry.test.ts +6 -1
  49. package/src/overlays/__tests__/panel-stretch.test.tsx +60 -0
  50. package/src/overlays/__tests__/responsive-chrome.test.tsx +446 -0
  51. package/src/overlays/__tests__/responsive-presentation.test.tsx +192 -0
  52. package/src/overlays/__tests__/theme-modal-mount.test.tsx +12 -1
  53. package/src/overlays/modal-preview-host.tsx +39 -2
  54. package/src/overlays/modal-registry.ts +344 -4
  55. package/src/overlays/overlay-surface.tsx +249 -19
  56. package/src/overlays/overlay-tokens.ts +94 -0
  57. package/src/overlays/theme-modal-mount.tsx +34 -4
  58. package/src/theme-block.tsx +6 -0
  59. package/src/utils/__tests__/inner-root-styles.test.tsx +139 -0
  60. package/src/utils/__tests__/iterator-bindings.test.tsx +9 -3
  61. package/src/utils/index.ts +107 -6
  62. package/src/utils/iterator-utils.ts +39 -2
  63. package/src/utils/part-styles.ts +4 -0
@@ -0,0 +1,270 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { getResponsiveValue, partAttrs, resolveOption } from '../utils';
5
+ import { useAuthFieldError } from '../auth/auth-field-errors';
6
+ import { DesignerPlaceholder, readToggle } from './product-block-utils';
7
+ import { BlockRendererProps } from './block-renderer-registry';
8
+
9
+ /**
10
+ * One field of an auth form, as a block of its own.
11
+ *
12
+ * The monolithic auth form draws its fields in a fixed order the designer
13
+ * cannot touch — its layer-tree "elements" are style parts, not blocks. This
14
+ * block is the composable alternative: dropped INSIDE an Auth Form (or
15
+ * Register Form) it renders a native input whose `name` the form reads
16
+ * through FormData on submit, so the fields, the links between them and the
17
+ * CTA order are all ordinary block arrangement.
18
+ *
19
+ * Purely presentational on purpose: no state, no submit logic — the hosting
20
+ * form owns those. Standalone (outside any form) it still renders, but
21
+ * nothing collects its value, which the designer sees immediately.
22
+ *
23
+ * Properties: fieldType, label, placeholder, showLabel, required, and for
24
+ * a `gender` field the two option captions (femaleLabel, maleLabel).
25
+ * Parts: field, label, input, error.
26
+ */
27
+
28
+ type AuthFieldType =
29
+ | 'email'
30
+ | 'password'
31
+ | 'first_name'
32
+ | 'last_name'
33
+ | 'phone'
34
+ | 'gender'
35
+ | 'date_of_birth'
36
+ | 'custom';
37
+
38
+ const FIELD_TYPES: AuthFieldType[] = [
39
+ 'email',
40
+ 'password',
41
+ 'first_name',
42
+ 'last_name',
43
+ 'phone',
44
+ 'gender',
45
+ 'date_of_birth',
46
+ 'custom'
47
+ ];
48
+
49
+ /**
50
+ * What a `custom` field may be, so an author can add a key this list has never
51
+ * heard of — a national id, a company name, a brand's own attribute — without
52
+ * waiting for a release. Everything else about it is an ordinary auth field:
53
+ * the form submits it by name, validates it, and reports its message under it.
54
+ *
55
+ * A short list rather than free text: these are the input types whose built-in
56
+ * validity the form's own check already reads, and a typo'd `type` silently
57
+ * degrades to text.
58
+ */
59
+ const CUSTOM_INPUT_TYPES = [
60
+ 'text',
61
+ 'tel',
62
+ 'email',
63
+ 'number',
64
+ 'date',
65
+ 'password'
66
+ ] as const;
67
+
68
+ /**
69
+ * The two values commerce stores for `gender`, which is why they are not
70
+ * authorable: the brand's own profile page posts exactly these strings, so a
71
+ * composition that invented its own would write a value the account page could
72
+ * not read back. Their CAPTIONS are authorable, which is the part a brand
73
+ * actually wants to change (and to translate).
74
+ */
75
+ const GENDER_VALUES = ['female', 'male'] as const;
76
+
77
+ /** Input semantics per field: type, autocomplete, default caption. */
78
+ const FIELD_PRESETS: Record<
79
+ AuthFieldType,
80
+ { type: string; autoComplete: string; label: string }
81
+ > = {
82
+ email: { type: 'email', autoComplete: 'email', label: 'E-mail' },
83
+ password: {
84
+ type: 'password',
85
+ autoComplete: 'current-password',
86
+ label: 'Password'
87
+ },
88
+ first_name: {
89
+ type: 'text',
90
+ autoComplete: 'given-name',
91
+ label: 'First name'
92
+ },
93
+ last_name: { type: 'text', autoComplete: 'family-name', label: 'Last name' },
94
+ phone: { type: 'tel', autoComplete: 'tel', label: 'Phone' },
95
+ // A native date control rather than a masked text box: its value is already
96
+ // the `YYYY-MM-DD` commerce stores, and the browser shows the day/month/year
97
+ // order of the shopper's own locale — which is what a placeholder like
98
+ // "GG/AA/YYYY" was trying to say by hand.
99
+ date_of_birth: { type: 'date', autoComplete: 'bday', label: 'Birth date' },
100
+ // Rendered as a select rather than an input; the type is unused for it.
101
+ gender: { type: 'text', autoComplete: 'sex', label: 'Gender' },
102
+ // The author names it and picks its type; nothing here can guess either.
103
+ custom: { type: 'text', autoComplete: 'off', label: 'Field' }
104
+ };
105
+
106
+ const AuthFieldBlock = ({
107
+ block,
108
+ currentBreakpoint = 'desktop',
109
+ isDesigner = false
110
+ }: BlockRendererProps) => {
111
+ const prop = (key: string, fallback: string) =>
112
+ String(
113
+ getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
114
+ );
115
+
116
+ const fieldType = resolveOption<AuthFieldType>(
117
+ block.properties?.fieldType,
118
+ currentBreakpoint,
119
+ 'email',
120
+ FIELD_TYPES
121
+ );
122
+ const preset = FIELD_PRESETS[fieldType];
123
+
124
+ /**
125
+ * What the form will call this value.
126
+ *
127
+ * For every known field the type IS the name — `email` submits `email` — which
128
+ * is what lets a form validate and message by field without being told. A
129
+ * `custom` field has no such name until the author gives it one, and an input
130
+ * without a name submits nothing at all, so an unnamed one is refused below
131
+ * rather than drawn as a control that quietly collects into the void.
132
+ */
133
+ const customName = prop('name', '').trim();
134
+ const controlName = fieldType === 'custom' ? customName : fieldType;
135
+ const controlType =
136
+ fieldType === 'custom'
137
+ ? resolveOption<(typeof CUSTOM_INPUT_TYPES)[number]>(
138
+ block.properties?.inputType,
139
+ currentBreakpoint,
140
+ 'text',
141
+ CUSTOM_INPUT_TYPES as unknown as (typeof CUSTOM_INPUT_TYPES)[number][]
142
+ )
143
+ : preset.type;
144
+
145
+ const label = prop('label', preset.label);
146
+ const placeholder = prop('placeholder', '');
147
+ const showLabel = readToggle(
148
+ block.properties?.showLabel,
149
+ currentBreakpoint,
150
+ true
151
+ );
152
+ const required = readToggle(
153
+ block.properties?.required,
154
+ currentBreakpoint,
155
+ true
156
+ );
157
+ /**
158
+ * What the surrounding form found wrong about THIS field on its last submit.
159
+ * The form validates (it owns submit) and publishes the messages by control
160
+ * name; a field standing outside any form simply never has one.
161
+ */
162
+ const errorMessage = useAuthFieldError(controlName);
163
+
164
+ /** Authorable captions for a fixed pair of stored values. */
165
+ const femaleLabel = prop('femaleLabel', 'Female');
166
+ const maleLabel = prop('maleLabel', 'Male');
167
+
168
+ // One declaration for both controls: a select that does not look like the
169
+ // inputs beside it reads as a different form.
170
+ const controlStyle: React.CSSProperties = {
171
+ width: '100%',
172
+ border: `1px solid ${
173
+ errorMessage
174
+ ? 'var(--auth-field-border-invalid, #dc2626)'
175
+ : 'var(--auth-field-border, #d1d5db)'
176
+ }`,
177
+ background: 'transparent',
178
+ color: 'inherit',
179
+ padding: '10px 12px',
180
+ outline: 'none'
181
+ };
182
+
183
+ // A custom field with no name collects nothing, so a shopper must not be
184
+ // given it to fill in — but the author has to be told what it is waiting for,
185
+ // or dropping the block looks like nothing happened.
186
+ if (!controlName) {
187
+ if (!isDesigner) return null;
188
+
189
+ return (
190
+ <DesignerPlaceholder label="Custom field (give this field a Name)" />
191
+ );
192
+ }
193
+
194
+ return (
195
+ <label
196
+ data-auth-field={controlName}
197
+ // Fills its block wrapper; see the note in auth-step-block.tsx. In a row
198
+ // Group two fields still share the row — a flex item shrinks from 100%.
199
+ className="w-full"
200
+ {...partAttrs('field')}
201
+ style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}
202
+ >
203
+ {/* Hiding the caption must not cost the input its accessible name, so
204
+ the label text moves onto aria-label instead of disappearing. */}
205
+ {showLabel && <span {...partAttrs('label')}>{label}</span>}
206
+ {fieldType === 'gender' ? (
207
+ /**
208
+ * A select, because the answer is one of two stored values rather than
209
+ * free text — and it carries the SAME `data-part="input"` as every other
210
+ * field's control, so a brand's border, height and focus ring reach it
211
+ * without a second set of controls in the inspector.
212
+ *
213
+ * The empty first option is what makes "not answered" expressible: with
214
+ * `required` off a shopper may leave it, and with `required` on the form
215
+ * reports it like any other empty field.
216
+ */
217
+ <select
218
+ name={controlName}
219
+ autoComplete={preset.autoComplete}
220
+ defaultValue=""
221
+ aria-label={showLabel ? undefined : label}
222
+ aria-invalid={errorMessage ? true : undefined}
223
+ required={required}
224
+ {...partAttrs('input')}
225
+ style={controlStyle}
226
+ >
227
+ <option value="">{placeholder || label}</option>
228
+ {GENDER_VALUES.map((value) => (
229
+ <option key={value} value={value}>
230
+ {value === 'female' ? femaleLabel : maleLabel}
231
+ </option>
232
+ ))}
233
+ </select>
234
+ ) : (
235
+ <input
236
+ type={controlType}
237
+ name={controlName}
238
+ autoComplete={preset.autoComplete}
239
+ placeholder={placeholder || undefined}
240
+ aria-label={showLabel ? undefined : label}
241
+ aria-invalid={errorMessage ? true : undefined}
242
+ // `required` stays on the control even though the form runs the check
243
+ // itself: it IS the check. `noValidate` up on the form suppresses the
244
+ // browser's bubble, not its `validity` reporting.
245
+ required={required}
246
+ {...partAttrs('input')}
247
+ style={controlStyle}
248
+ />
249
+ )}
250
+ {/* Under the field and in the flow, so the layout makes room for it
251
+ instead of the message floating over the next control — which is the
252
+ half of the native bubble no brand ever wanted. */}
253
+ {errorMessage && (
254
+ <span
255
+ data-auth-field-error={controlName}
256
+ {...partAttrs('error')}
257
+ role="alert"
258
+ style={{
259
+ color: 'var(--auth-field-error-color, #dc2626)',
260
+ fontSize: '0.875em'
261
+ }}
262
+ >
263
+ {errorMessage}
264
+ </span>
265
+ )}
266
+ </label>
267
+ );
268
+ };
269
+
270
+ export default AuthFieldBlock;
@@ -0,0 +1,251 @@
1
+ 'use client';
2
+
3
+ import React, { useEffect } from 'react';
4
+ import { getResponsiveValue, partAttrs } from '../utils';
5
+ import {
6
+ ActiveAuthStepContext,
7
+ AuthFlowIdContext,
8
+ DEFAULT_AUTH_FLOW_ID,
9
+ goBackAuthStep,
10
+ registerAuthFlowMount,
11
+ useAuthFlowState
12
+ } from '../auth/auth-flow-bus';
13
+ import { Block } from '../theme-block';
14
+ import { renderComposedChildren } from './render-composed-children';
15
+ import { BlockRendererProps } from './block-renderer-registry';
16
+ import { DesignerPlaceholder, readToggle } from './product-block-utils';
17
+
18
+ /**
19
+ * A step-by-step auth journey assembled from `auth-step` children.
20
+ *
21
+ * The flow renders exactly one step at a time and any non-step children
22
+ * always (a shared heading above the journey, say). Which step is active
23
+ * lives on the auth-flow bus, so ANYTHING can drive it: a button inside a
24
+ * step (`actionType: 'auth-step'`, resolved to this flow through context), a
25
+ * header trigger in another placeholder (`actionTarget: 'auth:register'`), or
26
+ * the register form jumping to its verification step. The flow itself owns
27
+ * only the fallback (its authored initial step) and the optional back
28
+ * control.
29
+ *
30
+ * Nothing here is auth-specific beyond the name: the steps' contents are
31
+ * whatever blocks the author composed, which is what lets the same flow be a
32
+ * right-hand drawer on one brand and an inline page area on another.
33
+ *
34
+ * In the designer the bus is inert, so the canvas shows the step that owns
35
+ * the current selection — clicking a step (or anything inside one) in the
36
+ * layer tree brings that step up — then the author's `previewStep`, then the
37
+ * initial step.
38
+ *
39
+ * Properties: flowId, initialStep, showBack, backLabel, previewStep,
40
+ * startFresh.
41
+ * Parts: flow, back.
42
+ */
43
+
44
+ const containsBlockId = (
45
+ blocks: Block[] | undefined,
46
+ blockId: string
47
+ ): boolean =>
48
+ (blocks || []).some(
49
+ (candidate) =>
50
+ candidate.id === blockId || containsBlockId(candidate.blocks, blockId)
51
+ );
52
+
53
+ /** A step with no authored key still needs a stable one for matching. */
54
+ const stepTitleOf = (step: Block, currentBreakpoint: string): string =>
55
+ String(
56
+ getResponsiveValue(step.properties?.title, currentBreakpoint, '')
57
+ ).trim();
58
+
59
+ const stepKeyOf = (step: Block, currentBreakpoint: string): string =>
60
+ String(
61
+ getResponsiveValue(step.properties?.stepKey, currentBreakpoint, '')
62
+ ).trim() || step.id;
63
+
64
+ const AuthFlowBlock = ({
65
+ block,
66
+ placeholderId,
67
+ sectionId,
68
+ isDesigner = false,
69
+ selectedBlockId,
70
+ currentBreakpoint = 'desktop'
71
+ }: BlockRendererProps) => {
72
+ const prop = (key: string, fallback: string) =>
73
+ String(
74
+ getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
75
+ );
76
+
77
+ const flowId = prop('flowId', '').trim() || DEFAULT_AUTH_FLOW_ID;
78
+ const initialStep = prop('initialStep', '').trim();
79
+ const previewStep = prop('previewStep', '').trim();
80
+ const backLabel = prop('backLabel', 'Back');
81
+ const showBack = readToggle(
82
+ block.properties?.showBack,
83
+ currentBreakpoint,
84
+ false
85
+ );
86
+ // On: a flow that leaves the page (a drawer closing unmounts its content)
87
+ // forgets the abandoned journey and reopens on its initial step.
88
+ const startFresh = readToggle(
89
+ block.properties?.startFresh,
90
+ currentBreakpoint,
91
+ true
92
+ );
93
+
94
+ const flowState = useAuthFlowState(isDesigner ? '' : flowId);
95
+
96
+ useEffect(() => {
97
+ if (isDesigner) return undefined;
98
+
99
+ return registerAuthFlowMount(flowId, { resetOnLastUnmount: startFresh });
100
+ }, [isDesigner, flowId, startFresh]);
101
+
102
+ const childBlocks = (block.blocks || [])
103
+ .filter((childBlock) => (isDesigner ? true : !childBlock.hidden))
104
+ .sort((a, b) => (a.order || 0) - (b.order || 0));
105
+
106
+ const steps = childBlocks.filter(
107
+ (childBlock) => childBlock.type === 'auth-step'
108
+ );
109
+
110
+ const stepKeys = steps.map((step) => stepKeyOf(step, currentBreakpoint));
111
+
112
+ /**
113
+ * Which step to show. Every candidate is validated against the authored
114
+ * steps — a stale bus entry (a step the author renamed away) or a typo'd
115
+ * initial step must fall through, not blank the flow.
116
+ */
117
+ const resolveActiveKey = (): string => {
118
+ if (isDesigner && selectedBlockId) {
119
+ const selected = steps.find(
120
+ (step) =>
121
+ step.id === selectedBlockId ||
122
+ containsBlockId(step.blocks, selectedBlockId)
123
+ );
124
+ if (selected) return stepKeyOf(selected, currentBreakpoint);
125
+ }
126
+
127
+ const candidates = isDesigner
128
+ ? [previewStep, initialStep]
129
+ : [flowState?.active ?? '', initialStep];
130
+
131
+ for (const candidate of candidates) {
132
+ if (candidate && stepKeys.includes(candidate)) return candidate;
133
+ }
134
+
135
+ return stepKeys[0] ?? '';
136
+ };
137
+
138
+ const activeKey = resolveActiveKey();
139
+
140
+ if (steps.length === 0 && !isDesigner) return null;
141
+
142
+ /**
143
+ * The children, with the six editor callbacks each one needs to be movable,
144
+ * duplicable, hideable, deletable and renamable on the canvas.
145
+ *
146
+ * Through the package's own `renderComposedChildren` rather than a private
147
+ * copy of it. The copies each posted their messages to `'*'`, which
148
+ * `editor-bridge.ts` exists to prevent — it addresses the editor's real
149
+ * origin, so an editor command never reaches whatever other page happens to
150
+ * be embedding the storefront — and every copy was a place a new message
151
+ * field had to be remembered.
152
+ */
153
+ const renderChild = (childBlock: Block) =>
154
+ renderComposedChildren({
155
+ blocks: [childBlock],
156
+ placeholderId,
157
+ sectionId,
158
+ isDesigner,
159
+ selectedBlockId,
160
+ currentBreakpoint
161
+ })[0] ?? null;
162
+
163
+ // Shown on the storefront only once there is somewhere to go back TO; in the
164
+ // canvas it shows whenever enabled, so it can be styled without navigating.
165
+ const canGoBack = isDesigner
166
+ ? showBack
167
+ : showBack &&
168
+ Boolean(
169
+ flowState && (flowState.history.length > 0 || initialStep !== activeKey)
170
+ );
171
+
172
+ const backButton = canGoBack ? (
173
+ <button
174
+ type="button"
175
+ data-auth-flow-back=""
176
+ {...partAttrs('back')}
177
+ onClick={() => {
178
+ if (isDesigner) return;
179
+ goBackAuthStep(flowId);
180
+ }}
181
+ style={{
182
+ alignSelf: 'flex-start',
183
+ padding: 0,
184
+ margin: 0,
185
+ border: 'none',
186
+ background: 'transparent',
187
+ color: 'inherit',
188
+ font: 'inherit',
189
+ cursor: 'pointer',
190
+ textDecoration: 'underline'
191
+ }}
192
+ >
193
+ {backLabel}
194
+ </button>
195
+ ) : null;
196
+
197
+ /**
198
+ * What the shared title row renders. Derived here because this is where the
199
+ * active step is decided — a row that is a SIBLING of the steps could never
200
+ * work it out for itself.
201
+ */
202
+ const activeStepBlock = steps.find(
203
+ (step) => stepKeyOf(step, currentBreakpoint) === activeKey
204
+ );
205
+ const activeStep = {
206
+ key: activeKey,
207
+ title: activeStepBlock
208
+ ? stepTitleOf(activeStepBlock, currentBreakpoint)
209
+ : ''
210
+ };
211
+
212
+ return (
213
+ <AuthFlowIdContext.Provider value={flowId}>
214
+ <ActiveAuthStepContext.Provider value={activeStep}>
215
+ <div
216
+ data-auth-flow=""
217
+ // Fills its block wrapper; see the note in auth-step-block.tsx.
218
+ className="w-full"
219
+ data-auth-flow-id={flowId}
220
+ data-auth-flow-step={activeKey || undefined}
221
+ {...partAttrs('flow')}
222
+ style={{
223
+ display: 'flex',
224
+ flexDirection: 'column',
225
+ gap: 'var(--auth-flow-gap, 16px)'
226
+ }}
227
+ >
228
+ {backButton}
229
+
230
+ {childBlocks.length === 0 ? (
231
+ <DesignerPlaceholder label="Auth flow (add an Auth Step per screen)" />
232
+ ) : (
233
+ childBlocks.map((childBlock) => {
234
+ if (childBlock.type !== 'auth-step') {
235
+ return renderChild(childBlock);
236
+ }
237
+
238
+ if (stepKeyOf(childBlock, currentBreakpoint) !== activeKey) {
239
+ return null;
240
+ }
241
+
242
+ return renderChild(childBlock);
243
+ })
244
+ )}
245
+ </div>
246
+ </ActiveAuthStepContext.Provider>
247
+ </AuthFlowIdContext.Provider>
248
+ );
249
+ };
250
+
251
+ export default AuthFlowBlock;