@ory/elements-react 1.0.0-next.19 → 1.0.0-next.20

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 CHANGED
@@ -1,3 +1,19 @@
1
+ ## 1.0.0-next.20 (2025-01-16)
2
+
3
+ ### 🚀 Features
4
+
5
+ - handle state transition edge cases ([f2e4023](https://github.com/ory/elements/commit/f2e4023))
6
+ - user experience improvements and e2e test coverage ([f68744c](https://github.com/ory/elements/commit/f68744c))
7
+ - do not show two-step selector if only one method exists ([6453673](https://github.com/ory/elements/commit/6453673))
8
+
9
+ ### 🩹 Fixes
10
+
11
+ - better validation for code method ([b0d8e2c](https://github.com/ory/elements/commit/b0d8e2c))
12
+
13
+ ### ❤️ Thank You
14
+
15
+ - aeneasr @aeneasr
16
+
1
17
  ## 1.0.0-next.19 (2024-12-31)
2
18
 
3
19
  ### 🩹 Fixes
package/dist/index.js CHANGED
@@ -105,6 +105,101 @@ function getFinalNodes(uniqueGroups, selectedGroup) {
105
105
  (node) => "type" in node.attributes && node.attributes.type === "hidden"
106
106
  ).concat(selectedNodes);
107
107
  }
108
+ function triggerToWindowCall(trigger) {
109
+ if (!trigger) {
110
+ return;
111
+ }
112
+ const fn = triggerToFunction(trigger);
113
+ if (fn) {
114
+ fn();
115
+ return;
116
+ }
117
+ let i = 0;
118
+ const ms = 100;
119
+ const interval = setInterval(() => {
120
+ i++;
121
+ if (i > 100) {
122
+ clearInterval(interval);
123
+ throw new Error(
124
+ "Unable to load Ory's WebAuthn script. Is it being blocked or otherwise failing to load? If you are running an old version of Ory Elements, please upgrade. For more information, please check your browser's developer console."
125
+ );
126
+ }
127
+ const fn2 = triggerToFunction(trigger);
128
+ if (fn2) {
129
+ clearInterval(interval);
130
+ return fn2();
131
+ }
132
+ }, ms);
133
+ return;
134
+ }
135
+ function triggerToFunction(trigger) {
136
+ if (typeof window === "undefined") {
137
+ console.debug(
138
+ "The Ory SDK is missing a required function: window is undefined."
139
+ );
140
+ return void 0;
141
+ }
142
+ const typedWindow = window;
143
+ if (!(trigger in typedWindow) || !typedWindow[trigger]) {
144
+ console.debug(`The Ory SDK is missing a required function: ${trigger}.`);
145
+ return void 0;
146
+ }
147
+ const triggerFn = typedWindow[trigger];
148
+ if (typeof triggerFn !== "function") {
149
+ console.debug(
150
+ `The Ory SDK is missing a required function: ${trigger}. It is not a function.`
151
+ );
152
+ return void 0;
153
+ }
154
+ return triggerFn;
155
+ }
156
+ function nodesToAuthMethodGroups(nodes, excludeAuthMethods = [clientFetch.UiNodeGroupEnum.Oidc]) {
157
+ var _a;
158
+ const groups = {};
159
+ for (const node of nodes) {
160
+ if (node.type === "script") {
161
+ continue;
162
+ }
163
+ const groupNodes = (_a = groups[node.group]) != null ? _a : [];
164
+ groupNodes.push(node);
165
+ groups[node.group] = groupNodes;
166
+ }
167
+ return Object.values(clientFetch.UiNodeGroupEnum).filter((group) => {
168
+ var _a2;
169
+ return (_a2 = groups[group]) == null ? void 0 : _a2.length;
170
+ }).filter(
171
+ (group) => ![
172
+ clientFetch.UiNodeGroupEnum.Default,
173
+ clientFetch.UiNodeGroupEnum.IdentifierFirst,
174
+ clientFetch.UiNodeGroupEnum.Profile,
175
+ ...excludeAuthMethods
176
+ ].includes(group)
177
+ );
178
+ }
179
+ function useNodesGroups(nodes) {
180
+ const groupSorter = useGroupSorter();
181
+ const groups = react.useMemo(() => {
182
+ var _a;
183
+ const groups2 = {};
184
+ for (const node of nodes) {
185
+ if (node.type === "script") {
186
+ continue;
187
+ }
188
+ const groupNodes = (_a = groups2[node.group]) != null ? _a : [];
189
+ groupNodes.push(node);
190
+ groups2[node.group] = groupNodes;
191
+ }
192
+ return groups2;
193
+ }, [nodes]);
194
+ const entries = react.useMemo(
195
+ () => Object.entries(groups).sort(([a], [b]) => groupSorter(a, b)),
196
+ [groups, groupSorter]
197
+ );
198
+ return {
199
+ groups,
200
+ entries
201
+ };
202
+ }
108
203
 
109
204
  // src/context/form-state.ts
110
205
  function findMethodWithMessage(nodes) {
@@ -129,6 +224,10 @@ function parseStateFromFlow(flow) {
129
224
  } else if (flow.flow.active && !["default", "identifier_first", "oidc"].includes(flow.flow.active)) {
130
225
  return { current: "method_active", method: flow.flow.active };
131
226
  } else if (isChoosingMethod(flow.flow.ui.nodes)) {
227
+ const authMethods = nodesToAuthMethodGroups(flow.flow.ui.nodes);
228
+ if (authMethods.length === 1) {
229
+ return { current: "method_active", method: authMethods[0] };
230
+ }
132
231
  return { current: "select_method" };
133
232
  } else if ((_a = flow.flow.ui.messages) == null ? void 0 : _a.some((m) => m.id === 1010016)) {
134
233
  return { current: "select_method" };
@@ -2230,78 +2329,6 @@ function OryCardHeader() {
2230
2329
  const { Card } = useComponents();
2231
2330
  return /* @__PURE__ */ jsxRuntime.jsx(Card.Header, {});
2232
2331
  }
2233
- function triggerToWindowCall(trigger) {
2234
- if (!trigger) {
2235
- return;
2236
- }
2237
- const fn = triggerToFunction(trigger);
2238
- if (fn) {
2239
- fn();
2240
- return;
2241
- }
2242
- let i = 0;
2243
- const ms = 100;
2244
- const interval = setInterval(() => {
2245
- i++;
2246
- if (i > 100) {
2247
- clearInterval(interval);
2248
- throw new Error(
2249
- "Unable to load Ory's WebAuthn script. Is it being blocked or otherwise failing to load? If you are running an old version of Ory Elements, please upgrade. For more information, please check your browser's developer console."
2250
- );
2251
- }
2252
- const fn2 = triggerToFunction(trigger);
2253
- if (fn2) {
2254
- clearInterval(interval);
2255
- return fn2();
2256
- }
2257
- }, ms);
2258
- return;
2259
- }
2260
- function triggerToFunction(trigger) {
2261
- if (typeof window === "undefined") {
2262
- console.debug(
2263
- "The Ory SDK is missing a required function: window is undefined."
2264
- );
2265
- return void 0;
2266
- }
2267
- const typedWindow = window;
2268
- if (!(trigger in typedWindow) || !typedWindow[trigger]) {
2269
- console.debug(`The Ory SDK is missing a required function: ${trigger}.`);
2270
- return void 0;
2271
- }
2272
- const triggerFn = typedWindow[trigger];
2273
- if (typeof triggerFn !== "function") {
2274
- console.debug(
2275
- `The Ory SDK is missing a required function: ${trigger}. It is not a function.`
2276
- );
2277
- return void 0;
2278
- }
2279
- return triggerFn;
2280
- }
2281
- function useNodesGroups(nodes) {
2282
- const groupSorter = useGroupSorter();
2283
- const groups = react.useMemo(() => {
2284
- var _a;
2285
- const groups2 = {};
2286
- for (const node of nodes) {
2287
- if (node.type === "script") {
2288
- continue;
2289
- }
2290
- const groupNodes = (_a = groups2[node.group]) != null ? _a : [];
2291
- groupNodes.push(node);
2292
- groups2[node.group] = groupNodes;
2293
- }
2294
- return groups2;
2295
- }, [nodes]);
2296
- const entries = react.useMemo(
2297
- () => Object.entries(groups).sort(([a], [b]) => groupSorter(a, b)),
2298
- [groups, groupSorter]
2299
- );
2300
- return {
2301
- groups,
2302
- entries
2303
- };
2304
- }
2305
2332
  var NodeInput = ({
2306
2333
  node,
2307
2334
  attributes
@@ -2355,11 +2382,25 @@ var NodeInput = ({
2355
2382
  if (isResendNode || isScreenSelectionNode) {
2356
2383
  return null;
2357
2384
  }
2358
- return /* @__PURE__ */ jsxRuntime.jsx(Node2.Button, { attributes: attrs, node, onClick: handleClick });
2385
+ return /* @__PURE__ */ jsxRuntime.jsx(
2386
+ Node2.Label,
2387
+ {
2388
+ attributes: { ...attrs, label: void 0 },
2389
+ node: { ...node, meta: { ...node.meta, label: void 0 } },
2390
+ children: /* @__PURE__ */ jsxRuntime.jsx(Node2.Button, { attributes: attrs, node, onClick: handleClick })
2391
+ }
2392
+ );
2359
2393
  case clientFetch.UiNodeInputAttributesTypeEnum.DatetimeLocal:
2360
2394
  throw new Error("Not implemented");
2361
2395
  case clientFetch.UiNodeInputAttributesTypeEnum.Checkbox:
2362
- return /* @__PURE__ */ jsxRuntime.jsx(Node2.Checkbox, { attributes: attrs, node, onClick: handleClick });
2396
+ return /* @__PURE__ */ jsxRuntime.jsx(
2397
+ Node2.Label,
2398
+ {
2399
+ attributes: { ...attrs, label: void 0 },
2400
+ node: { ...node, meta: { ...node.meta, label: void 0 } },
2401
+ children: /* @__PURE__ */ jsxRuntime.jsx(Node2.Checkbox, { attributes: attrs, node, onClick: handleClick })
2402
+ }
2403
+ );
2363
2404
  case clientFetch.UiNodeInputAttributesTypeEnum.Hidden:
2364
2405
  return /* @__PURE__ */ jsxRuntime.jsx(Node2.Input, { attributes: attrs, node, onClick: handleClick });
2365
2406
  default:
@@ -2447,8 +2488,6 @@ function unrollTrait(input, output = {}) {
2447
2488
  });
2448
2489
  return output;
2449
2490
  }
2450
-
2451
- // src/components/form/form-resolver.ts
2452
2491
  function isCodeResendRequest(data) {
2453
2492
  var _a;
2454
2493
  return (_a = data.email) != null ? _a : data.resend;
@@ -2457,10 +2496,22 @@ function useOryFormResolver() {
2457
2496
  const flowContainer = useOryFlow();
2458
2497
  return (data) => {
2459
2498
  if (flowContainer.formState.current === "method_active") {
2460
- if (data.method === "code" && !data.code && !isCodeResendRequest(data)) {
2499
+ if (
2500
+ // When we submit a code
2501
+ data.method === "code" && // And the code is not present
2502
+ !data.code && // And the flow is not a code resend request
2503
+ !isCodeResendRequest(data) && // And the flow has a code input node
2504
+ flowContainer.flow.ui.nodes.find(({ attributes, group }) => {
2505
+ if (!clientFetch.isUiNodeInputAttributes(attributes)) {
2506
+ return false;
2507
+ }
2508
+ return group === "code" && attributes.name === "code" && attributes.type !== "hidden";
2509
+ })
2510
+ ) {
2461
2511
  return {
2462
2512
  values: data,
2463
2513
  errors: {
2514
+ // We know the code node exists, so we can safely hardcode the ID.
2464
2515
  code: {
2465
2516
  id: 4000002,
2466
2517
  context: {
@@ -2551,6 +2602,13 @@ function frontendClient(sdkUrl, opts = {}) {
2551
2602
  return new clientFetch.FrontendApi(config);
2552
2603
  }
2553
2604
 
2605
+ // src/util/internal.ts
2606
+ function replaceWindowFlowId(flow) {
2607
+ const url = new URL(window.location.href);
2608
+ url.searchParams.set("flow", flow);
2609
+ window.location.href = url.toString();
2610
+ }
2611
+
2554
2612
  // src/util/onSubmitLogin.ts
2555
2613
  async function onSubmitLogin({ config, flow }, {
2556
2614
  setFlowContainer,
@@ -2572,8 +2630,12 @@ async function onSubmitLogin({ config, flow }, {
2572
2630
  (_a2 = flow.return_to) != null ? _a2 : config.sdk.url + "/self-service/login/browser";
2573
2631
  }).catch(
2574
2632
  clientFetch.handleFlowError({
2575
- onRestartFlow: () => {
2576
- onRedirect(clientFetch.loginUrl(config), true);
2633
+ onRestartFlow: (useFlowId) => {
2634
+ if (useFlowId) {
2635
+ replaceWindowFlowId(useFlowId);
2636
+ } else {
2637
+ onRedirect(clientFetch.loginUrl(config), true);
2638
+ }
2577
2639
  },
2578
2640
  onValidationError: (body2) => {
2579
2641
  setFlowContainer({
@@ -2615,8 +2677,12 @@ async function onSubmitRecovery({ config, flow }, {
2615
2677
  });
2616
2678
  }).catch(
2617
2679
  clientFetch.handleFlowError({
2618
- onRestartFlow: () => {
2619
- onRedirect(clientFetch.recoveryUrl(config), true);
2680
+ onRestartFlow: (useFlowId) => {
2681
+ if (useFlowId) {
2682
+ replaceWindowFlowId(useFlowId);
2683
+ } else {
2684
+ onRedirect(clientFetch.recoveryUrl(config), true);
2685
+ }
2620
2686
  },
2621
2687
  onValidationError: (body2) => {
2622
2688
  if ("error" in body2) {
@@ -2673,8 +2739,12 @@ async function onSubmitRegistration({ config, flow }, {
2673
2739
  onRedirect(clientFetch.registrationUrl(config), true);
2674
2740
  }).catch(
2675
2741
  clientFetch.handleFlowError({
2676
- onRestartFlow: () => {
2677
- onRedirect(clientFetch.registrationUrl(config), true);
2742
+ onRestartFlow: (useFlowId) => {
2743
+ if (useFlowId) {
2744
+ replaceWindowFlowId(useFlowId);
2745
+ } else {
2746
+ onRedirect(clientFetch.registrationUrl(config), true);
2747
+ }
2678
2748
  },
2679
2749
  onValidationError: (body2) => {
2680
2750
  setFlowContainer({
@@ -2717,8 +2787,12 @@ async function onSubmitSettings({ config, flow }, {
2717
2787
  });
2718
2788
  }).catch(
2719
2789
  clientFetch.handleFlowError({
2720
- onRestartFlow: () => {
2721
- onRedirect(clientFetch.settingsUrl(config), true);
2790
+ onRestartFlow: (useFlowId) => {
2791
+ if (useFlowId) {
2792
+ replaceWindowFlowId(useFlowId);
2793
+ } else {
2794
+ onRedirect(clientFetch.settingsUrl(config), true);
2795
+ }
2722
2796
  },
2723
2797
  onValidationError: (body2) => {
2724
2798
  setFlowContainer({
@@ -2763,8 +2837,12 @@ async function onSubmitVerification({ config, flow }, {
2763
2837
  })
2764
2838
  ).catch(
2765
2839
  clientFetch.handleFlowError({
2766
- onRestartFlow: () => {
2767
- onRedirect(clientFetch.verificationUrl(config), true);
2840
+ onRestartFlow: (useFlowId) => {
2841
+ if (useFlowId) {
2842
+ replaceWindowFlowId(useFlowId);
2843
+ } else {
2844
+ onRedirect(clientFetch.verificationUrl(config), true);
2845
+ }
2768
2846
  },
2769
2847
  onValidationError: (body2) => {
2770
2848
  setFlowContainer({
@@ -3589,16 +3667,16 @@ var uiTextToFormattedMessage = ({ id, context = {}, text }, intl) => {
3589
3667
  new Date(value),
3590
3668
  /* @__PURE__ */ new Date()
3591
3669
  ),
3592
- [key + "_since_minutes"]: Math.abs(
3670
+ [key + "_since_minutes"]: Math.ceil(
3593
3671
  (value - (/* @__PURE__ */ new Date()).getTime() / 1e3) / 60
3594
- ).toFixed(2),
3672
+ ).toFixed(0),
3595
3673
  [key + "_until"]: intl.formatDateTimeRange(
3596
3674
  /* @__PURE__ */ new Date(),
3597
3675
  new Date(value)
3598
3676
  ),
3599
- [key + "_until_minutes"]: Math.abs(
3677
+ [key + "_until_minutes"]: Math.ceil(
3600
3678
  ((/* @__PURE__ */ new Date()).getTime() / 1e3 - value) / 60
3601
- ).toFixed(2)
3679
+ ).toFixed(0)
3602
3680
  };
3603
3681
  }
3604
3682
  }
@@ -3621,7 +3699,7 @@ var uiTextToFormattedMessage = ({ id, context = {}, text }, intl) => {
3621
3699
  // src/util/test-id.ts
3622
3700
  function messageTestId(message) {
3623
3701
  return {
3624
- "data-testid": `ory-message-${message.id}`
3702
+ "data-testid": `ory/ui/message/${message.id}`
3625
3703
  };
3626
3704
  }
3627
3705