Skip to content

Commit

Permalink
feat: improved two step handling and form submit fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mszekiel committed Sep 26, 2024
1 parent e2496e6 commit d580317
Show file tree
Hide file tree
Showing 35 changed files with 185 additions and 140 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/elements-react/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"i18n-ally.localesPaths": ["src/locales", "src/util/i18n"]
}
4 changes: 2 additions & 2 deletions packages/elements-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
},
"devDependencies": {
"@hookform/devtools": "^4.3.1",
"@svgr/plugin-jsx": "^8.1.0",
"esbuild-plugin-svgr": "2.1.0",
"@svgr/plugin-svgo": "^8.1.0",
"esbuild-plugin-svgr": "^2.1.0",
"eslint-plugin-react": "7.35.0",
"postcss": "8.4.47",
"postcss-prefix-selector": "1.16.1",
Expand Down
87 changes: 45 additions & 42 deletions packages/elements-react/src/components/card/card-two-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
UiNodeGroupEnum,
UiNodeInputAttributes,
} from "@ory/client-fetch"
import { useState } from "react"
import { useEffect, useState } from "react"
import { OryCard, OryCardContent, OryCardFooter } from "."
import { useComponents, useNodeSorter, useOryFlow } from "../../context"
import { useNodesGroups } from "../../util/ui"
Expand All @@ -14,6 +14,12 @@ import { OryFormSocialButtonsForm } from "../form/social"
import { OryCardHeader } from "./header"
import { useFormContext } from "react-hook-form"

enum ProcessStep {
ProvideIdentifier,
ChooseAuthMethod,
ExecuteAuthMethod,
}

export function OryTwoStepCard() {
const {
flow: { ui },
Expand Down Expand Up @@ -60,10 +66,6 @@ export function OryTwoStepCard() {

const hasOIDC = Boolean(uniqueGroups.oidc?.length)

const handleOptionSelect = (group: UiNodeGroupEnum) => {
setSelectedGroup(group)
}

const zeroStepGroups = ui.nodes.filter(
(node) => node.group !== UiNodeGroupEnum.Oidc,
)
Expand All @@ -72,15 +74,6 @@ export function OryTwoStepCard() {
? (uniqueGroups[selectedGroup] ?? [])
: []

const nodeBackButton = ui.nodes.find(
(node) =>
("value" in node.attributes &&
node.attributes.value === "profile:back") ||
("name" in node.attributes &&
node.attributes.name === "identifier" &&
node.group === "identifier_first"),
)

const finalNodes = [
...(uniqueGroups?.identifier_first ?? []),
...(uniqueGroups?.default ?? []),
Expand All @@ -91,43 +84,41 @@ export function OryTwoStepCard() {
)
.concat(selectedNodes)

const step = selectedGroup ? 2 : isChoosingMethod ? 1 : 0
const step = selectedGroup
? ProcessStep.ExecuteAuthMethod
: isChoosingMethod
? ProcessStep.ChooseAuthMethod
: ProcessStep.ProvideIdentifier

return (
<OryCard>
<OryCardHeader />
<OryCardContent>
<OryCardValidationMessages />
{step === 0 && hasOIDC && <OryFormSocialButtonsForm />}
{step === ProcessStep.ProvideIdentifier && hasOIDC && (
<OryFormSocialButtonsForm />
)}
<OryForm>
<FormGroup>
{step === 0 &&
{step === ProcessStep.ProvideIdentifier &&
zeroStepGroups
.sort(sortNodes)
.map((node, k) => <Node node={node} key={k} />)}
{step === 1 && (
{step === ProcessStep.ChooseAuthMethod && (
<>
{nodeBackButton && <BackButton />}
<BackButton />
{options.map((option) => (
<Components.AuthMethodListItem
key={option}
group={option}
onClick={() => handleOptionSelect(option)}
onClick={() => setSelectedGroup(option)}
/>
))}
</>
)}
{step === 2 && (
{step === ProcessStep.ExecuteAuthMethod && (
<>
{nodeBackButton && (
<Components.CurrentIdentifierButton
node={nodeBackButton}
attributes={
nodeBackButton.attributes as UiNodeInputAttributes
}
onClick={() => setSelectedGroup(undefined)}
/>
)}
<BackButton onClick={() => setSelectedGroup(undefined)} />
{finalNodes.sort(sortNodes).map((node, k) => (
<Node node={node} key={k} />
))}
Expand All @@ -141,7 +132,11 @@ export function OryTwoStepCard() {
)
}

const BackButton = () => {
type BackButtonProps = {
onClick?: () => void
}

const BackButton = ({ onClick }: BackButtonProps) => {
const {
flow: { ui },
} = useOryFlow()
Expand All @@ -150,27 +145,35 @@ const BackButton = () => {

const nodeBackButton = ui.nodes.find(
(node) =>
("value" in node.attributes &&
node.attributes.value === "profile:back") ||
("name" in node.attributes &&
node.attributes.name === "identifier" &&
node.group === "identifier_first"),
// ("value" in node.attributes &&
// node.attributes.value === "profile:back") ||
"name" in node.attributes &&
node.attributes.name === "identifier" &&
node.group === "identifier_first",
)

useEffect(() => {
if (!nodeBackButton) return

setValue(
(nodeBackButton.attributes as UiNodeInputAttributes).name,
(nodeBackButton.attributes as UiNodeInputAttributes).value,
)
}, [nodeBackButton, setValue])

if (!nodeBackButton) {
return null
}

const handleClick = () => {
onClick?.()
}

return (
<Components.CurrentIdentifierButton
node={nodeBackButton}
attributes={nodeBackButton.attributes as UiNodeInputAttributes}
onClick={() => {
setValue(
(nodeBackButton.attributes as UiNodeInputAttributes).name,
(nodeBackButton.attributes as UiNodeInputAttributes).value,
)
}}
onClick={onClick ? handleClick : undefined}
/>
)
}
5 changes: 5 additions & 0 deletions packages/elements-react/src/components/form/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ export function OryForm({ children }: OryFormProps) {
if (submitData.method === "code" && data.code) {
submitData.resend = ""
}

console.log(submitData)
await onSubmitLogin(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
Expand All @@ -119,9 +121,11 @@ export function OryForm({ children }: OryFormProps) {
const submitData: UpdateRegistrationFlowBody = {
...(data as unknown as UpdateRegistrationFlowBody),
}

if (submitData.method === "code" && submitData.code) {
submitData.resend = ""
}

await onSubmitRegistration(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
Expand Down Expand Up @@ -152,6 +156,7 @@ export function OryForm({ children }: OryFormProps) {
break
}
case FlowType.Settings:
console.log(data)
await onSubmitSettings(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
Expand Down
9 changes: 1 addition & 8 deletions packages/elements-react/src/components/form/nodes/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { MouseEventHandler, ReactNode, useEffect, useRef } from "react"
export const NodeInput = ({
node,
attributes,
onClick,
}: NodeProps & {
attributes: UiNodeInputAttributes
onClick?: MouseEventHandler
Expand Down Expand Up @@ -40,13 +39,7 @@ export const NodeInput = ({
[],
)

const handleClick: MouseEventHandler = (e) => {
if (onClick) {
console.log("asd")
e.preventDefault()
onClick(e)
}

const handleClick: MouseEventHandler = () => {
if (onclickTrigger) {
triggerToWindowCall(onclickTrigger)
}
Expand Down
22 changes: 19 additions & 3 deletions packages/elements-react/src/components/form/social.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useComponents } from "../../context"
import { useOryFlow } from "../../context"
import { UiNode, UiNodeInputAttributes } from "@ory/client-fetch"
import { PropsWithChildren } from "react"
import { Node } from "./nodes/node"
import { OryForm } from "./form"
import { useFormContext } from "react-hook-form"

export type HeadlessSocialButtonsProps = PropsWithChildren<{
hideDivider?: boolean
Expand All @@ -16,6 +16,7 @@ export type HeadlessSocialButtonContainerProps = PropsWithChildren<{
export type HeadlessSocialButtonProps = PropsWithChildren<{
node: UiNode
attributes: UiNodeInputAttributes
onClick?: () => void
}>

export function OryFormSocialButtons({
Expand All @@ -25,11 +26,13 @@ export function OryFormSocialButtons({
const {
flow: { ui },
} = useOryFlow()
const { setValue } = useFormContext()

// Only get the oidc nodes.
const filteredNodes = ui.nodes.filter((node) => node.group === "oidc")

const { SocialButtonContainer, HorizontalDivider } = useComponents()
const { SocialButtonContainer, HorizontalDivider, SocialButton } =
useComponents()

if (filteredNodes.length === 0) {
return null
Expand All @@ -45,7 +48,20 @@ export function OryFormSocialButtons({
<SocialButtonContainer nodes={filteredNodes}>
{children ??
filteredNodes.map((node, k) => {
return <Node node={node} key={k} />
return (
<SocialButton
node={node}
key={k}
attributes={node.attributes as UiNodeInputAttributes}
onClick={() => {
setValue(
"provider",
(node.attributes as UiNodeInputAttributes).value,
)
setValue("method", "oidc")
}}
/>
)
})}
</SocialButtonContainer>
{!hideDivider && filteredNodes.length > 0 && otherNodes.length > 0 && (
Expand Down
7 changes: 6 additions & 1 deletion packages/elements-react/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

declare module "*.svg" {
const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>
import * as React from "react"

const ReactComponent: React.FunctionComponent<
React.ComponentProps<"svg"> & { size?: number }
>

export default ReactComponent
}
12 changes: 10 additions & 2 deletions packages/elements-react/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,13 @@
"verification.registration-button": "Sign up",
"verification.registration-label": "Don't have an account?",
"verification.title": "Verify your account",
"verification.back-button": "Back"
}
"verification.back-button": "Back",
"two-step.password.title": "Password",
"two-step.password.description": "Enter your password associated with your account",
"two-step.code.title": "Email code",
"two-step.code.description": "A verification code will be sent to your email",
"twos-step.webauthn.title": "Security Key",
"twos-step.webauthn.description": "Use your security key to authenticate",
"two-step.passkey.title": "Passkey (recommended)",
"two-step.passkey.description": "Use your device's for fingerprint or face recognition"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 0 additions & 3 deletions packages/elements-react/src/theme/default/code.svg

This file was deleted.

Loading

0 comments on commit d580317

Please sign in to comment.