diff --git a/packages/elements-react/src/components/form/form-helpers.ts b/packages/elements-react/src/components/form/form-helpers.ts
index dda41c66..c6b78d6b 100644
--- a/packages/elements-react/src/components/form/form-helpers.ts
+++ b/packages/elements-react/src/components/form/form-helpers.ts
@@ -10,14 +10,19 @@ export function computeDefaultValues(nodes: UiNode[]): FormValues {
if (isUiNodeInputAttributes(attrs)) {
// Skip the "method" field and "submit" button
- if (attrs.name === "method" || attrs.type === "submit") return acc
+ if (
+ attrs.name === "method" ||
+ attrs.type === "submit" ||
+ typeof attrs.value === "undefined"
+ )
+ return acc
// Unroll nested traits or assign default values
const unrolled = unrollTrait({
name: attrs.name,
- value: attrs.value ?? "",
+ value: attrs.value,
})
- Object.assign(acc, unrolled ?? { [attrs.name]: attrs.value ?? "" })
+ Object.assign(acc, unrolled ?? { [attrs.name]: attrs.value })
}
return acc
diff --git a/packages/elements-react/src/components/form/nodes/input.tsx b/packages/elements-react/src/components/form/nodes/input.tsx
index e8b19e49..e4681a59 100644
--- a/packages/elements-react/src/components/form/nodes/input.tsx
+++ b/packages/elements-react/src/components/form/nodes/input.tsx
@@ -64,8 +64,8 @@ export const NodeInput = ({
const isPinCodeInput =
(attrs.name === "code" && node.group === "code") ||
(attrs.name === "totp_code" && node.group === "totp")
- const isResend = node.meta.label?.id === 1070008
- const isScreenSelection =
+ const isResendNode = node.meta.label?.id === 1070008
+ const isScreenSelectionNode =
"name" in node.attributes && node.attributes.name === "screen"
switch (attributes.type) {
@@ -74,7 +74,7 @@ export const NodeInput = ({
if (isSocial) {
return
}
- if (isResend || isScreenSelection) {
+ if (isResendNode || isScreenSelectionNode) {
return null
}
diff --git a/packages/elements-react/src/context/form-state.test.ts b/packages/elements-react/src/context/form-state.test.ts
index 52a78d06..d029fad0 100644
--- a/packages/elements-react/src/context/form-state.test.ts
+++ b/packages/elements-react/src/context/form-state.test.ts
@@ -208,15 +208,14 @@ test('should fallback to "impossible_unknown" for unknown recovery state', () =>
flow: { id: "unknown", active: null, ui: { nodes: [] } },
} as unknown as OryFlowContainer
- act(() => {
- dispatch({
- type: "action_flow_update",
- flow: mockFlow,
- })
- })
-
- const [state] = result.current
- expect(state).toEqual({ current: "impossible_unknown" })
+ expect(() =>
+ act(() => {
+ dispatch({
+ type: "action_flow_update",
+ flow: mockFlow,
+ })
+ }),
+ ).toThrow("Unknown form state")
})
test('should fallback to "impossible_unknown" for unrecognized flow', () => {
@@ -228,13 +227,12 @@ test('should fallback to "impossible_unknown" for unrecognized flow', () => {
flow: { id: "unknown", active: null, ui: { nodes: [] } },
} as unknown as OryFlowContainer
- act(() => {
- dispatch({
- type: "action_flow_update",
- flow: mockFlow,
- })
- })
-
- const [state] = result.current
- expect(state).toEqual({ current: "impossible_unknown" })
+ expect(() =>
+ act(() => {
+ dispatch({
+ type: "action_flow_update",
+ flow: mockFlow,
+ })
+ }),
+ ).toThrow("Unknown form state")
})
diff --git a/packages/elements-react/src/context/form-state.ts b/packages/elements-react/src/context/form-state.ts
index 16329ff8..44257277 100644
--- a/packages/elements-react/src/context/form-state.ts
+++ b/packages/elements-react/src/context/form-state.ts
@@ -11,7 +11,7 @@ export type FormState =
| { current: "select_method" }
| { current: "method_active"; method: UiNodeGroupEnum }
| { current: "success_screen" }
- | { current: "impossible_unknown" }
+ | { current: "settings" }
export type FormStateAction =
| {
@@ -48,11 +48,13 @@ function parseStateFromFlow(flow: OryFlowContainer): FormState {
return { current: "method_active", method: flow.flow.active }
}
break
+ case FlowType.Settings:
+ return { current: "settings" }
}
console.warn(
`[Ory/Elements React] Encountered an unknown form state on ${flow.flowType} flow with ID ${flow.flow.id}`,
)
- return { current: "impossible_unknown" }
+ throw new Error("Unknown form state")
}
export function formStateReducer(
diff --git a/packages/elements-react/src/locales/de.json b/packages/elements-react/src/locales/de.json
index d32c98b1..29e9874b 100644
--- a/packages/elements-react/src/locales/de.json
+++ b/packages/elements-react/src/locales/de.json
@@ -240,5 +240,5 @@
"settings.title-totp": "Verwalten Sie die 2FA TOTP Authenticator-App",
"settings.title-webauthn": "Hardware-Token verwalten",
"settings.webauthn.info": "Hardware-Tokens werden für die Zweitfaktor-Authentifizierung oder als Erstfaktor-Authentifizierung mit Passkeys verwendet",
- "card.footer.select-another-method": "Eine andere Methode versuchen"
+ "card.footer.select-another-method": "Eine andere Methode verwenden"
}
diff --git a/packages/elements-react/src/theme/default/components/card/current-identifier-button.tsx b/packages/elements-react/src/theme/default/components/card/current-identifier-button.tsx
index 7982cfbe..ea600bb5 100644
--- a/packages/elements-react/src/theme/default/components/card/current-identifier-button.tsx
+++ b/packages/elements-react/src/theme/default/components/card/current-identifier-button.tsx
@@ -4,6 +4,7 @@
import { FlowType, UiNode } from "@ory/client-fetch"
import { useOryFlow } from "@ory/elements-react"
import IconArrowLeft from "../../assets/icons/arrow-left.svg"
+import { omit } from "../../utils/attributes"
export function DefaultCurrentIdentifierButton() {
const {
@@ -26,17 +27,21 @@ export function DefaultCurrentIdentifierButton() {
return null
}
const initFlowUrl = `${config.sdk.url}/self-service/${flowType}/browser`
+ const attributes = omit(nodeBackButton.attributes, [
+ "autocomplete",
+ "node_type",
+ ])
return (
-
+
{nodeBackButton?.attributes.value}
diff --git a/packages/elements-react/src/theme/default/components/form/input.tsx b/packages/elements-react/src/theme/default/components/form/input.tsx
index 5204670a..9d6aeb45 100644
--- a/packages/elements-react/src/theme/default/components/form/input.tsx
+++ b/packages/elements-react/src/theme/default/components/form/input.tsx
@@ -18,7 +18,14 @@ export const DefaultInput = ({
}: OryNodeInputProps) => {
const label = getNodeLabel(node)
const { register } = useFormContext()
- const { value, autocomplete, name, maxlength, ...rest } = attributes
+ const {
+ value,
+ autocomplete,
+ name,
+ maxlength,
+ node_type: _,
+ ...rest
+ } = attributes
const intl = useIntl()
const { flowType } = useOryFlow()
diff --git a/packages/elements-react/src/theme/default/components/form/label.tsx b/packages/elements-react/src/theme/default/components/form/label.tsx
index cfcd94bd..2c4a873e 100644
--- a/packages/elements-react/src/theme/default/components/form/label.tsx
+++ b/packages/elements-react/src/theme/default/components/form/label.tsx
@@ -23,7 +23,7 @@ export function DefaultLabel({
const isPassword = attributes.type === "password"
- const hasResend = flow.ui.nodes.some(
+ const hasResendNode = flow.ui.nodes.some(
(n) =>
"name" in n.attributes &&
n.attributes.name === "email" &&
@@ -31,7 +31,7 @@ export function DefaultLabel({
)
return (
-
+
{label && (
)}
- {hasResend && (
+ {hasResendNode && (
))}
-
+
)
}
diff --git a/packages/elements-react/src/theme/default/utils/attributes.ts b/packages/elements-react/src/theme/default/utils/attributes.ts
new file mode 100644
index 00000000..b514f457
--- /dev/null
+++ b/packages/elements-react/src/theme/default/utils/attributes.ts
@@ -0,0 +1,10 @@
+export function omit(
+ obj: OBJ,
+ keys: (keyof OBJ)[],
+): Omit {
+ const ret = { ...obj }
+ for (const key of keys) {
+ delete ret[key]
+ }
+ return ret
+}
diff --git a/packages/elements-react/src/util/ui/__test__/ui.spec.ts b/packages/elements-react/src/util/ui/__test__/ui.spec.ts
index 49bf215c..cbd67db7 100644
--- a/packages/elements-react/src/util/ui/__test__/ui.spec.ts
+++ b/packages/elements-react/src/util/ui/__test__/ui.spec.ts
@@ -16,7 +16,7 @@ describe("utils/ui", () => {
expect(result.current.groups.oidc).toHaveLength(2)
expect(result.current.groups.default).toHaveLength(2)
- expect(result.current.groups.webauthn).toHaveLength(2)
+ expect(result.current.groups.webauthn).toHaveLength(1)
expect(result.current.groups.passkey).toHaveLength(3)
expect(result.current.groups.password).toHaveLength(2)
expect(result.current.groups.code).toHaveLength(1)
diff --git a/packages/elements-react/src/util/ui/index.ts b/packages/elements-react/src/util/ui/index.ts
index 6e106e84..a60b8b71 100644
--- a/packages/elements-react/src/util/ui/index.ts
+++ b/packages/elements-react/src/util/ui/index.ts
@@ -108,7 +108,8 @@ export function useNodesGroups(nodes: UiNode[]) {
for (const node of nodes) {
if (node.type === "script") {
- // WebAuthn scripts are part of the nodes, for passkey flows
+ // We always render all scripts, because the scripts for passkeys are part of the webauthn group,
+ // which leads to this hook returning a webauthn group on passkey flows (which it should not - webauthn is the "legacy" passkey implementation).
continue
}
const groupNodes = groups[node.group] ?? []