Skip to content

Commit

Permalink
feat: duplicate init split prebuilt UI (#665)
Browse files Browse the repository at this point in the history
* init web-js recipes, supertokens from auth-react

* move recipe output type to types.ts

* make combined recipes use web-js version

* get rid of recipeImplementation for single recipes

* add functionOverrides to thirdpartypasswordless recipe

* add functionOverrides to thirdpartyemailpassword recipe

* get rid of one the duplicate setLoginAttemptInfo calls

* fix tests

* update changelog

* fixes after review

* move initOutput type to root types

* fix tests

* review fixes

* review fixes

* add build files

* use normalisedConfig for webjs init in combined recipes

* remove commented code

* disable isIE check in order to avoid error

* test fixes caused by utils

* fix typo

* rename recipeImpl -> webJSRecipe for all recipes

* add comments

* utils-> normalization_function  fix

* type changes for recipe member

* type change, renamein

* move config normalization to init for session

* Update thirdpartypasswordless comments

Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* Update thirdpartyemailpassword/recipe comments

Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>

* review fixes

* reuse action type for RecipeInitResult

* move onHandelEvent call to combined recipe overrides

* fixes-session, overrides

* fix exampleTestHelper

* fix rollup watch mac issue

* initial splitting

* create class for each recipe to handle routes

* review fixes for /lib

* update examples

* Merge branch '0.31' into 'feat/splitting-prebuild-ui-components'

* fix: partially unit test

* fix: unit test case

* fix: broken components overriding after merge

* fix: respect rid when rendering UIs

* fix tests

* fix: add underscore prefix

* fix: emailverification prebuilt ui

* fix: review changes

* fix no router case

* fix: prebuiltui components types exoprt

* fix: emailverification exports

* fix: test apps

* fix: adjust routing component to handle duplicate renders

* fix: e2e tests

* update changelog

* fix: example apps

* fix examples & changelog

* fix: preBuiltUI -> prebuiltui

* fix filename casing

* fix: preBuiltUI -> prebuiltui

* fix filename casing

* expose signle routing functons

* rename: prebuiltui->ui

* update test apps

* fix: unit tests

* preBuiltUI -> prebuiltui

* update changelog

* update example apps

* getSuperTokensReactRouterDomRoutes casing fix

* getSuperTokensReactRouterDomRoutes casing fix

* review fixes

* review fixes

* review fixes

* fix: review comments(preBuiltUI interface consistency)

* fix example imports

* fix: providers form & example app

* chore: add note about web-js init into migration guides

* chore: update google-onetap example to use new interface

* chore: update version

---------

Co-authored-by: Alisher <alisher@supertokens.com>
Co-authored-by: Mihály Lengyel <mihaly@lengyel.tech>
Co-authored-by: Rishabh Poddar <rishabh.poddar@gmail.com>
  • Loading branch information
4 people committed May 2, 2023
1 parent e3ca010 commit 0abaff1
Show file tree
Hide file tree
Showing 277 changed files with 33,139 additions and 47,649 deletions.
123 changes: 122 additions & 1 deletion CHANGELOG.md
Expand Up @@ -5,10 +5,131 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [unreleased]
## [0.32.0] - 2023-05-02

### Changes

- Eliminated the need for duplicate `init` call for non-react applications that use pre-built UI. [See the issue](https://github.com/supertokens/supertokens-auth-react/issues/616)
- Split/separate pre-built UI components from its recipe to reduce bundle sizes for apps that do not use pre-built UI
- Added thirdparty login with popup window example

### Migration

#### Initializing web-js

This version also initializes the web-js SDK. If you previously did that manually, you can remove/replace it with the auth-react init call.
From this version forward, the recommended way to use this SDK, even with Angular and Vue, is to initialize auth-react in the root component. Importing `supertokens-auth-react` will not pull React into your main bundle; only importing the `prebuiltui` modules will.
For more details, check out our quick setup guides for angular and vue.

#### Adding the pre-built UI route for apps with react-router-dom

```tsx
import SuperTokens, { SuperTokensWrapper, getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react";
import Passwordless from "supertokens-auth-react/recipe/passwordless";
// .... other imports

SuperTokens.init({
appInfo: {
// appInfo
},
recipeList: [
Passwordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
Session.init(),
],
});

<SuperTokensWrapper>
<div className="App">
<Router>
<div className="fill">
<Routes>
{/* This shows the login UI on "/auth" route */}
{getSuperTokensRoutesForReactRouterDom(require("react-router-dom"))}
// ... other routes
</Routes>
</div>
<Footer />
</Router>
</div>
</SuperTokensWrapper>;
```

Should become

```tsx
import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react/ui";
import Passwordless from "supertokens-auth-react/recipe/passwordless";
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";
// .... other imports

SuperTokens.init({
appInfo: {
// appInfo
},
recipeList: [
Passwordless.init({
contactMethod: "EMAIL_OR_PHONE",
}),
Session.init(),
],
});

<SuperTokensWrapper>
<div className="App">
<Router>
<div className="fill">
<Routes>
{/* This shows the login UI on "/auth" route for Passwordless recipe */}
{getSuperTokensRoutesForReactRouterDom(require("react-router-dom"), [PasswordlessPreBuiltUI])}
// ... other routes
</Routes>
</div>
<Footer />
</Router>
</div>
</SuperTokensWrapper>;
```

#### Adding the pre-built UI route for apps without react-router-dom

```tsx
import React from "react";
import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";

class App extends React.Component {
render() {
if (SuperTokens.canHandleRoute()) {
// This renders the login UI on the /auth route
return SuperTokens.getRoutingComponent();
}

return <SuperTokensWrapper>{/*Your app*/}</SuperTokensWrapper>;
}
}
```

Should become

```tsx
import { SuperTokensWrapper } from "supertokens-auth-react";
import { canHandleRoute, getRoutingComponent } from "supertokens-auth-react/ui";
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";

class App extends React.Component {
render() {
if (canHandleRoute([PasswordlessPreBuiltUI])) {
// This renders the login UI on the /auth route
return getRoutingComponent([PasswordlessPreBuiltUI]);
}

return <SuperTokensWrapper>{/*Your app*/}</SuperTokensWrapper>;
}
}
```

## [0.31.5] - 2023-03-30

### Changes
Expand Down
36 changes: 32 additions & 4 deletions examples/for-tests-react-16/src/AppWithReactDomRouter.js
@@ -1,14 +1,42 @@
import React, { useState } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react";
import { SignInAndUp } from "supertokens-auth-react/recipe/emailpassword";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react/ui";
import { SignInAndUp } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import {
ResetPasswordUsingToken,
SignInAndUp as TPSignInAndUp,
ThirdPartySignInAndUpCallback,
} from "supertokens-auth-react/recipe/thirdpartyemailpassword";
} from "supertokens-auth-react/recipe/thirdpartyemailpassword/prebuiltui";
import { BaseComponent, Home, Contact, Dashboard, DashboardNoAuthRequired } from "./App";
import { ThirdPartyEmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartyemailpassword/prebuiltui";
import { ThirdPartyPasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartypasswordless/prebuiltui";
import { EmailVerificationPreBuiltUI } from "supertokens-auth-react/recipe/emailverification/prebuiltui";
import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";
import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui";

const authRecipe = window.localStorage.getItem("authRecipe") || "emailpassword";
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";

let recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
if (authRecipe === "thirdparty") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI];
} else if (authRecipe === "emailpassword") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
} else if (authRecipe === "both") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI, EmailPasswordPreBuiltUI];
} else if (authRecipe === "thirdpartyemailpassword") {
recipePreBuiltUIList = [ThirdPartyEmailPasswordPreBuiltUI];
} else if (authRecipe === "passwordless") {
recipePreBuiltUIList = [PasswordlessPreBuiltUI];
} else if (authRecipe === "thirdpartypasswordless") {
recipePreBuiltUIList = [ThirdPartyPasswordlessPreBuiltUI];
}

if (emailVerificationMode !== "OFF") {
recipePreBuiltUIList.push(EmailVerificationPreBuiltUI);
}

function AppWithReactDomRouter(props) {
/**
Expand All @@ -26,7 +54,7 @@ function AppWithReactDomRouter(props) {
<Router>
<BaseComponent>
<Routes caseSensitive>
{getSuperTokensRoutesForReactRouterDom(require("react-router-dom"))}
{getSuperTokensRoutesForReactRouterDom(require("react-router-dom", recipePreBuiltUIList))}
<Route path="/" element={<Home />} />
<Route
path="/CasE/Case-SensItive1-PAth"
Expand Down
33 changes: 30 additions & 3 deletions examples/for-tests-react-16/src/AppWithReactDomRouterV5.js
@@ -1,17 +1,44 @@
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-domv5";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react/ui";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import { SignInAndUp } from "supertokens-auth-react/recipe/emailpassword";
import { SignInAndUp } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { BaseComponent, Home, Contact, Dashboard, DashboardNoAuthRequired } from "./App";
import { ThirdPartyEmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartyemailpassword/prebuiltui";
import { ThirdPartyPasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartypasswordless/prebuiltui";
import { EmailVerificationPreBuiltUI } from "supertokens-auth-react/recipe/emailverification/prebuiltui";
import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";
import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui";

const authRecipe = window.localStorage.getItem("authRecipe") || "emailpassword";
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";

let recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
if (authRecipe === "thirdparty") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI];
} else if (authRecipe === "emailpassword") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
} else if (authRecipe === "both") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI, ThirdPartyPreBuiltUI];
} else if (authRecipe === "thirdpartyemailpassword") {
recipePreBuiltUIList = [ThirdPartyEmailPasswordPreBuiltUI];
} else if (authRecipe === "passwordless") {
recipePreBuiltUIList = [PasswordlessPreBuiltUI];
} else if (authRecipe === "thirdpartypasswordless") {
recipePreBuiltUIList = [ThirdPartyPasswordlessPreBuiltUI];
}
if (emailVerificationMode !== "OFF") {
recipePreBuiltUIList.push(EmailVerificationPreBuiltUI);
}

function AppWithReactDomRouter(props) {
return (
<div className="App">
<Router>
<BaseComponent>
<Switch>
{getSuperTokensRoutesForReactRouterDom(require("react-router-domv5"))}
{getSuperTokensRoutesForReactRouterDom(require("react-router-domv5", recipePreBuiltUIList))}
<Route exact path="/">
<Home />
</Route>
Expand Down
39 changes: 36 additions & 3 deletions examples/for-tests-react-16/src/AppWithoutRouter.js
@@ -1,6 +1,34 @@
import React, { useEffect, useState } from "react";
import { BaseComponent, Home } from "./App";
import { canHandleRoute, getRoutingComponent } from "supertokens-auth-react";
import { getRoutingComponent, canHandleRoute } from "supertokens-auth-react/ui";
import { ThirdPartyEmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartyemailpassword/prebuiltui";
import { ThirdPartyPasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartypasswordless/prebuiltui";
import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";
import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui";
import { EmailVerificationPreBuiltUI } from "supertokens-auth-react/recipe/emailverification/prebuiltui";

const authRecipe = window.localStorage.getItem("authRecipe") || "emailpassword";
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";

let recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
if (authRecipe === "thirdparty") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI];
} else if (authRecipe === "emailpassword") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
} else if (authRecipe === "both") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI, EmailPasswordPreBuiltUI];
} else if (authRecipe === "thirdpartyemailpassword") {
recipePreBuiltUIList = [ThirdPartyEmailPasswordPreBuiltUI];
} else if (authRecipe === "passwordless") {
recipePreBuiltUIList = [PasswordlessPreBuiltUI];
} else if (authRecipe === "thirdpartypasswordless") {
recipePreBuiltUIList = [ThirdPartyPasswordlessPreBuiltUI];
}

if (emailVerificationMode !== "OFF") {
recipePreBuiltUIList.push(EmailVerificationPreBuiltUI);
}

function AppWithoutRouter() {
return (
Expand All @@ -15,6 +43,7 @@ function AppWithoutRouter() {

function Routing() {
const [, setCurrentPath] = useState(window.location.pathname);
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";

useEffect(() => {
const onLocationChange = () => {
Expand All @@ -24,8 +53,12 @@ function Routing() {
window.addEventListener("popstate", onLocationChange);
}, [setCurrentPath]);

if (canHandleRoute()) {
return <BaseComponent>{getRoutingComponent()}</BaseComponent>;
if (emailVerificationMode !== "OFF" && EmailVerificationPreBuiltUI.canHandleRoute()) {
return EmailVerificationPreBuiltUI.getRoutingComponent();
}

if (canHandleRoute(recipePreBuiltUIList)) {
return <BaseComponent>{getRoutingComponent(recipePreBuiltUIList)}</BaseComponent>;
}

// Custom router...
Expand Down
37 changes: 32 additions & 5 deletions examples/for-tests/src/AppWithReactDomRouter.js
@@ -1,32 +1,59 @@
import React, { useState } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react";
import { SignInAndUp } from "supertokens-auth-react/recipe/emailpassword";
import { SignInAndUp } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import {
ResetPasswordUsingToken,
SignInAndUp as TPSignInAndUp,
ThirdPartySignInAndUpCallback,
} from "supertokens-auth-react/recipe/thirdpartyemailpassword";
} from "supertokens-auth-react/recipe/thirdpartyemailpassword/prebuiltui";
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react/ui";
import { ThirdPartyEmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartyemailpassword/prebuiltui";
import { ThirdPartyPasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartypasswordless/prebuiltui";
import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui";
import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui";
import { EmailVerificationPreBuiltUI } from "supertokens-auth-react/recipe/emailverification/prebuiltui";
import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui";
import { BaseComponent, Home, Contact, Dashboard, DashboardNoAuthRequired } from "./App";

function AppWithReactDomRouter(props) {
/**
* For user context tests we add this query param so the additional routes
* dont interfere with other tests
* don't interfere with other tests
*/
const urlParams = new URLSearchParams(window.location.search);
const isForUserContext = urlParams.get("forUserContext") === "true";
const [claimValidators, setClaimValidators] = useState(undefined);
window.setClaimValidators = setClaimValidators;
const keyWithClaimValidators =
claimValidators !== undefined ? claimValidators.map((a) => a.id).join("_") : undefined;
const authRecipe = window.localStorage.getItem("authRecipe") || "emailpassword";
const emailVerificationMode = window.localStorage.getItem("mode") || "OFF";

let recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
if (authRecipe === "thirdparty") {
recipePreBuiltUIList = [ThirdPartyPreBuiltUI];
} else if (authRecipe === "emailpassword") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI];
} else if (authRecipe === "both") {
recipePreBuiltUIList = [EmailPasswordPreBuiltUI, ThirdPartyPreBuiltUI];
} else if (authRecipe === "thirdpartyemailpassword") {
recipePreBuiltUIList = [ThirdPartyEmailPasswordPreBuiltUI];
} else if (authRecipe === "passwordless") {
recipePreBuiltUIList = [PasswordlessPreBuiltUI];
} else if (authRecipe === "thirdpartypasswordless") {
recipePreBuiltUIList = [ThirdPartyPasswordlessPreBuiltUI];
}
if (emailVerificationMode !== "OFF") {
recipePreBuiltUIList.push(EmailVerificationPreBuiltUI);
}

return (
<div className="App">
<Router>
<BaseComponent>
<Routes caseSensitive>
{getSuperTokensRoutesForReactRouterDom(require("react-router-dom"))}
{getSuperTokensRoutesForReactRouterDom(require("react-router-dom"), recipePreBuiltUIList)}
<Route path="/" element={<Home />} />
<Route
path="/CasE/Case-SensItive1-PAth"
Expand Down

0 comments on commit 0abaff1

Please sign in to comment.