diff --git a/package-lock.json b/package-lock.json index 4f352765ade257d3c9c0dd07343030c9c1841e2f..ea0f1b85a4ac4257a58efe70cf16183b3e9c4f09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ess-ics/ce-ui-common", - "version": "16.1.0", + "version": "17.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ess-ics/ce-ui-common", - "version": "16.1.0", + "version": "17.0.0", "dependencies": { "@fontsource/titillium-web": "^5.0.22", "@mui/x-data-grid-pro": "^6.5.0", diff --git a/package.json b/package.json index dd5f73607379ebeb89b88168436fd8a3dc81b292..bd2f36b5fb111b7f5dc18a71f01b5b64e60e9e31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ess-ics/ce-ui-common", - "version": "16.1.0", + "version": "17.0.0", "private": true, "type": "module", "main": "dist/index.js", diff --git a/src/components/auth/AccessControl.js b/src/components/auth/AccessControl.js deleted file mode 100644 index 612f6867a737e5ea21b3a4e0496363543164dd3b..0000000000000000000000000000000000000000 --- a/src/components/auth/AccessControl.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * AccessControl - * component checking user access & user role(s) - */ -import { useUserContext } from "../../contexts/User"; - -/** - * Checks if a given set of user roles permits an action. - * If allowedRoles is empty or undefined, then action is permitted. - * Otherwise, at least one of the user roles must match the allowed roles. - * - * @param {string[]} userRoles Roles of the user - * @param {string[]} allowedRoles Roles that allow access for this user - * @returns {boolean} true if action is permitted - */ -const checkPermissions = (userRoles, allowedRoles) => { - if (allowedRoles?.length === 0) { - return true; - } - - return userRoles?.some((role) => allowedRoles?.includes(role)); -}; - -/** - * Defines access for a specific user with a specific set of roles, for example to only - * permit an user with admin roles to perform admin actions on only their own resources. - * - * If allowedUsersWithRoles is empty or undefined, then action is forbidden. - * Otherwise, permit action if any definition in allowedUsersWithRoles meets both criteria: - * - username matches user - * - role matches at least one userRole - * - * @param {object} user User to be evaluated - * @param {string} user.loginName Login name of the user - * @param {string[]} userRoles Roles of the user - * @param {object[]} allowedUsersWithRoles Conditions the user will be evaluated against - * @param {string} allowedUsersWithRoles[].user Login name - * @param {string} allowedUsersWithRoles[].role Role name - * @returns {boolean} true if action is permitted - */ -const checkUser = (user, userRoles, allowedUsersWithRoles) => { - if (!allowedUsersWithRoles || allowedUsersWithRoles?.length === 0) { - return false; - } - - return allowedUsersWithRoles.find( - (userWithRole) => - userWithRole?.user === user?.loginName && - userRoles?.includes(userWithRole.role) - ); -}; - -/** - * Permits an action for an user if either is true: - * - allowedRoles is empty, or userRoles match at least one of those roles, OR - * - if any definition in allowedUsersWithRoles meets both criteria: - * - username matches user, AND - * - role matches at least one userRole - * - * @param {object} user User to be evaluated - * @param {string} user.loginName Login name of the user - * @param {string[]} userRoles Roles of the user - * @param {string[]} allowedRoles Roles that permit access to the user - * @param {object[]} allowedUsersWithRoles Conditions the user will be evaluated against - * @param {string} allowedUsersWithRoles[].user Login name - * @param {string} allowedUsersWithRoles[].role Role name - * @returns {boolean} true if action is permitted - */ -export const isPermitted = ({ - user, - userRoles, - allowedRoles, - allowedUsersWithRoles -}) => { - return ( - checkPermissions(userRoles, allowedRoles) || - checkUser(user, userRoles, allowedUsersWithRoles) - ); -}; - -export const useIsCurrentUserPermitted = ({ - allowedRoles, - allowedUsersWithRoles -}) => { - const { user, userRoles } = useUserContext(); - - return isPermitted({ user, userRoles, allowedRoles, allowedUsersWithRoles }); -}; - -export function AccessControl({ - allowedRoles, - allowedUsersWithRoles, - children, - renderNoAccess -}) { - const permitted = useIsCurrentUserPermitted({ - allowedRoles, - allowedUsersWithRoles - }); - if (permitted) { - return children; - } - - return renderNoAccess(); -} diff --git a/src/components/auth/tests/Login.cy.jsx b/src/components/auth/Login.cy.tsx similarity index 76% rename from src/components/auth/tests/Login.cy.jsx rename to src/components/auth/Login.cy.tsx index bd066065dfd0ae853fc3df638a3b9806af06ee5e..d142bd6350b69b7e67120dcc979f1488a065c386 100644 --- a/src/components/auth/tests/Login.cy.jsx +++ b/src/components/auth/Login.cy.tsx @@ -1,20 +1,24 @@ /** * Login component cypress testing */ -import { useState } from "react"; +import { useState, ReactNode } from "react"; import { Button, Box } from "@mui/material"; -import { LoginForm, LoginDialog } from "../Login"; +import { LoginForm, LoginDialog } from "./Login"; // Dummy login username & password for passing test const USERNAME = "username"; const PASSWORD = "password"; -function LoginFormTemplate({ dialog }) { +interface LoginFormTemplateProps { + dialog?: ReactNode | undefined; +} + +function LoginFormTemplate({ dialog }: LoginFormTemplateProps) { const [loginError, setLoginError] = useState(""); const [loginLoading, setLoginLoading] = useState(false); const [open, setOpen] = useState(false); - const login = (username, password) => { + const login = (username: string, password: string) => { setLoginLoading(true); if (username !== USERNAME || password !== PASSWORD) { setLoginError("Bad username/password"); @@ -62,15 +66,15 @@ describe("Login.spec.js", () => { it("Login should succeed", () => { cy.findByRole("textbox", { name: /username/i }).type("username"); cy.findByLabelText(/password/i).type("password"); - cy.getByData("submitButton").click(); - cy.getByData("progressbar").should("exist"); + cy.get('[data-test="submitButton"]').click(); + cy.get('[data-test="progressbar"]').should("exist"); }); it("Login should fail", () => { cy.findByRole("textbox", { name: /username/i }).type("usr"); cy.findByLabelText(/password/i).type("pwd"); - cy.getByData("submitButton").click(); - cy.getByData("alert").should("exist"); + cy.get('[data-test="submitButton"]').click(); + cy.get('[data-test="alert"]').should("exist"); }); }); @@ -80,11 +84,11 @@ describe("Login.spec.js", () => { }); it("Launch login dialog and login", () => { - cy.getByData("launchButton").click(); + cy.get('[data-test="launchButton"]').click(); cy.findByRole("textbox", { name: /username/i }).type("username"); cy.findByLabelText(/password/i).type("password"); cy.get("Button[type=submit]").click(); - cy.getByData("progressbar").should("exist"); + cy.get('[data-test="progressbar"]').should("exist"); }); }); }); diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx deleted file mode 100644 index 5dd5d273742f8637350d0e2876df6d6fe0f475d2..0000000000000000000000000000000000000000 --- a/src/components/auth/Login.jsx +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Login - * component for returning username & password - */ -import { useState } from "react"; -import { - TextField, - Button, - LinearProgress, - Alert, - Grid, - styled -} from "@mui/material"; -import { Dialog } from "../common/Dialog"; - -/** - * Login form - * @param {object} props - * @param {function} props.login function returning username & password - * @param {string} props.error containing error message - * @param {string} props.className containing css classname - */ -export const LoginForm = styled(({ login, error, loading, className }) => { - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - - return ( - <form className={`${className} CeuiLoginForm`}> - <Grid - container - spacing={1} - > - <Grid - item - xs={12} - md={12} - > - <TextField - label="Username" - variant="outlined" - onChange={(e) => { - setUsername(e.target.value); - }} - fullWidth - autoFocus - required - className="CeuiLoginForm-UserName" - /> - </Grid> - <Grid - item - xs={12} - md={12} - > - <TextField - label="Password" - variant="outlined" - type="password" - onChange={(e) => { - setPassword(e.target.value); - }} - fullWidth - required - className="CeuiLoginForm-Password" - /> - </Grid> - <Grid - item - xs={12} - md={12} - > - {error && ( - <Alert - data-test="alert" - severity="error" - className="CeuiLoginForm-Error" - > - {error} - </Alert> - )} - </Grid> - <Grid - item - xs={12} - md={12} - > - <Button - data-test="submitButton" - fullWidth - variant="contained" - color="primary" - onClick={(e) => { - e.preventDefault(); - login(username, password); - }} - type="submit" - className="CeuiLoginForm-LoginButton" - > - Login - </Button> - </Grid> - <Grid - item - xs={12} - md={12} - > - {loading && ( - <LinearProgress - data-test="progressbar" - className="CeuiLoginForm-LoadingIndicator" - /> - )} - </Grid> - </Grid> - </form> - ); -})({}); - -/** - * Dialog with login form - * @param {object} props - * @param {function} props.login function returning username & password - * @param {string} props.error containing error message - * @param {string} props.className containing css classname - * @param {boolean} props.open return true or false - * @param {function} props.handleClose callback fired when the component requests to be closed - */ -export const LoginDialog = styled( - ({ login, open, error, loading, handleClose, className }) => { - return ( - <Dialog - DialogProps={{ - fullWidth: true, - open: open - }} - onClose={handleClose} - className={`${className} CeuiLoginDialog`} - content={ - <LoginForm - login={login} - error={error} - loading={loading} - /> - } - /> - ); - } -)({}); diff --git a/src/components/auth/Login.tsx b/src/components/auth/Login.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2e49854f5c8eef482ffe8a23ddb0592a84e86f17 --- /dev/null +++ b/src/components/auth/Login.tsx @@ -0,0 +1,152 @@ +import { useState } from "react"; +import { + Dialog, + DialogContent, + TextField, + Button, + LinearProgress, + Alert, + Grid, + styled +} from "@mui/material"; + +interface LoginFormProps { + error?: string; + loading?: boolean; + className?: string; + login: (username: string, password: string) => void; +} +export const LoginForm = styled( + ({ error, loading, className, login }: LoginFormProps) => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + + return ( + <form className={`${className} CeuiLoginForm`}> + <Grid + container + spacing={1} + > + <Grid + item + xs={12} + md={12} + > + <TextField + label="Username" + variant="outlined" + onChange={(e) => { + setUsername(e.target.value); + }} + fullWidth + autoFocus + required + className="CeuiLoginForm-UserName" + /> + </Grid> + <Grid + item + xs={12} + md={12} + > + <TextField + label="Password" + variant="outlined" + type="password" + onChange={(e) => { + setPassword(e.target.value); + }} + fullWidth + required + className="CeuiLoginForm-Password" + /> + </Grid> + <Grid + item + xs={12} + md={12} + > + {error && ( + <Alert + data-test="alert" + severity="error" + className="CeuiLoginForm-Error" + > + {error} + </Alert> + )} + </Grid> + <Grid + item + xs={12} + md={12} + > + <Button + data-test="submitButton" + fullWidth + variant="contained" + color="primary" + onClick={(e) => { + e.preventDefault(); + login(username, password); + }} + type="submit" + className="CeuiLoginForm-LoginButton" + > + Login + </Button> + </Grid> + <Grid + item + xs={12} + md={12} + > + {loading && ( + <LinearProgress + data-test="progressbar" + className="CeuiLoginForm-LoadingIndicator" + /> + )} + </Grid> + </Grid> + </form> + ); + } +)({}); + +interface LoginDialogProps { + open: boolean; + error?: string; + className?: string; + loading?: boolean; + login: (username: string, password: string) => void; + handleClose: () => void; +} + +export const LoginDialog = styled( + ({ + open, + error, + className, + loading, + login, + handleClose + }: LoginDialogProps) => { + return ( + <Dialog + fullWidth + open={open} + onClose={handleClose} + className={`${className} CeuiLoginDialog`} + > + <DialogContent> + <LoginForm + login={login} + error={error} + loading={loading} + /> + </DialogContent> + </Dialog> + ); + } +)({}); diff --git a/src/components/auth/tests/AccessControl.cy.jsx b/src/components/auth/tests/AccessControl.cy.jsx deleted file mode 100644 index 882574454e0eccbb497f052ac2bdc9ceff6abf14..0000000000000000000000000000000000000000 --- a/src/components/auth/tests/AccessControl.cy.jsx +++ /dev/null @@ -1,100 +0,0 @@ -import { composeStories } from "@storybook/react"; -import * as stories from "../../../stories/auth/AccessControl.stories"; - -const { Default } = composeStories(stories); - -const testUser = { - loginName: "test user" -}; - -const mountTestComponent = ({ - testUser, - testRoles, - allowedRoles, - allowedUsersWithRoles -}) => { - cy.mount( - <Default - {...{ testUser, testRoles, allowedRoles, allowedUsersWithRoles }} - /> - ); -}; - -const isAllowed = () => { - cy.findByText("Result: Allowed!").should("exist"); -}; - -const isForbidden = () => { - cy.findByText("Result: Forbidden!").should("exist"); -}; - -describe("AccessControl.spec.js", () => { - context("Access Control", () => { - it("Does not allow anonymous users", () => { - mountTestComponent({ - testUser: undefined - }); - isForbidden(); - }); - it("Does not allow any users", () => { - mountTestComponent({ - testUser, - allowedRoles: null - }); - isForbidden(); - }); - - it("Allows authenticated user", () => { - mountTestComponent({ - testUser, - allowedRoles: [] - }); - isAllowed(); - }); - - it("Allows user matching available roles", () => { - mountTestComponent({ - testUser, - testRoles: ["banana"], - allowedRoles: ["apple", "banana"] - }); - isAllowed(); - }); - - it("Forbids user missing required roles", () => { - mountTestComponent({ - testUser, - testRoles: ["blueberry"], - allowedRoles: ["apple", "banana"] - }); - isForbidden(); - }); - - it("Forbids specific user without matching role", () => { - mountTestComponent({ - testUser, - allowedUsersWithRoles: [{ user: testUser.loginName }] - }); - isForbidden(); - }); - it("Allows specific user with role", () => { - mountTestComponent({ - testUser, - testRoles: ["lion"], - allowedUsersWithRoles: [ - { user: testUser.loginName, role: "parrot" }, - { user: testUser.loginName, role: "rhino" }, - { user: testUser.loginName, role: "lion" } - ] - }); - isAllowed(); - }); - it("Forbids user not matching username", () => { - mountTestComponent({ - testUser, - allowedUsersWithRoles: [{ user: "johnwick" }] - }); - isForbidden(); - }); - }); -}); diff --git a/src/components/common/Dialog/Dialog.tsx b/src/components/common/Dialog/Dialog.tsx index bacf66b53158db0e5ea7f040fb91a5c9d9c3fca0..e17135235fd54e14ff7379947cd26bfa77dd3d0b 100644 --- a/src/components/common/Dialog/Dialog.tsx +++ b/src/components/common/Dialog/Dialog.tsx @@ -67,14 +67,6 @@ const DialogTitle = ({ onClose, className, children }: DialogTitleProps) => ( * * NOTE: If you need to provide a confirmation (yes/no) experience, * the ConfirmationDialog offers these features already. - * - * @param {string|object} title - Title string or custom component - * @param {string|object} content - Content string or custom component - * @param {renderActionsCallback} renderActions - render function that receives the onClose callback and returns the dialog footer/actions - * @param {function} onClose callback fired when the component requests to be closed - * @param {object} DialogProps MUI Dialog component props; this might be how you change the - * width of the modal via fullWidth=true, maxWidth="md", for example. See MUI documentation. - * @param {string} className containing css classname */ export interface DialogProps { @@ -190,19 +182,6 @@ const ConfirmationDialogButtons = styled( * on your own. * * ConfirmationDialog is meant only for simple confirmation from the user! - * - * @param {function} onConfirm callback confirming whether confirm button was clicked - * @param {function} onClose callback confirming whether cancel button was clicked - * @param {string} confirmText text for confirm button - * @param {string} cancelText text for cancel button - * @param {object} ...props - Props passed to the underlying Dialog component (ours, not MUIs). - * @param {string|object} props.title - Title string or custom component - * @param {string|object} props.content - Content string or custom component - * @param {renderActionsCallback} props.renderActions - render function that receives the onClose callback and returns the dialog footer/actions - * @param {function} props.onClose callback fired when the component requests to be closed - * @param {object} props.DialogProps MUI Dialog component props; this might be how you change the - * width of the modal via fullWidth=true, maxWidth="md", for example. See MUI documentation. - * @param {string} props.className containing css classname */ export interface ConfirmationDialogProps extends DialogProps { diff --git a/src/components/common/Dialog/index.ts b/src/components/common/Dialog/index.ts index f6d6189fce7ffb8027aab49dcbd001a4a86752b7..296be34e900265e42422ee85c4f9951de082712b 100644 --- a/src/components/common/Dialog/index.ts +++ b/src/components/common/Dialog/index.ts @@ -1,17 +1,7 @@ import { Dialog, ConfirmationDialog, - ConfirmDangerActionDialog, - DialogProps, - ConfirmationDialogProps, - ConfirmDangerActionDialogProps + ConfirmDangerActionDialog } from "./Dialog"; -export { - Dialog, - ConfirmationDialog, - ConfirmDangerActionDialog, - type DialogProps, - type ConfirmationDialogProps, - type ConfirmDangerActionDialogProps -}; +export { Dialog, ConfirmationDialog, ConfirmDangerActionDialog }; diff --git a/src/contexts/AuthContext.ts b/src/contexts/AuthContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..37ede1f49420d6c728b2eac8037c7b9a1fbe451a --- /dev/null +++ b/src/contexts/AuthContext.ts @@ -0,0 +1,69 @@ +import { createContext, useContext } from "react"; + +interface User { + fullName?: string; + loginName?: string; + avatar?: string; + email?: string; + gitlabUserName?: string; +} + +export interface AuthContextProps { + user: User | undefined; + userRoles: string[] | undefined; + isAdmin: boolean | undefined; + isManager: boolean | undefined; + loginError: string | undefined; + loginLoading: boolean; + setUser: (user: User | undefined) => void; + setUserRoles: (roles: string[] | undefined) => void; + login: (userName: string, password: string) => void; + logout: () => void; + resetLoginError: () => void; +} + +const defaultValue: AuthContextProps = { + user: undefined, + userRoles: undefined, + isAdmin: false, + isManager: false, + loginError: undefined, + loginLoading: false, + setUser: () => {}, + setUserRoles: () => {}, + login: () => {}, + logout: () => {}, + resetLoginError: () => {} +}; + +export const AuthContext = createContext<AuthContextProps>(defaultValue); + +export const useAuthContext = () => { + const { + user, + userRoles, + isAdmin, + isManager, + loginError, + loginLoading, + setUser, + setUserRoles, + login, + logout, + resetLoginError + } = useContext(AuthContext); + + return { + user, + userRoles, + isAdmin, + isManager, + loginError, + loginLoading, + setUser, + setUserRoles, + login, + logout, + resetLoginError + }; +}; diff --git a/src/contexts/GlobalAppBarContext.ts b/src/contexts/GlobalAppBarContext.ts index a87150a1adcae044f73491b15a4c74a40b631399..0a631d04afa8006130f18b06c11f920d0419de86 100644 --- a/src/contexts/GlobalAppBarContext.ts +++ b/src/contexts/GlobalAppBarContext.ts @@ -1,6 +1,6 @@ import { createContext, useContext } from "react"; -interface GlobalAppContext { +export interface GlobalAppContext { title: string; setTitle: (title: string) => void; } diff --git a/src/contexts/User.js b/src/contexts/User.js deleted file mode 100644 index 4be55216cea20c742f5360d75a0229858117ea6e..0000000000000000000000000000000000000000 --- a/src/contexts/User.js +++ /dev/null @@ -1,13 +0,0 @@ -import { createContext, useContext } from "react"; - -export const userContext = createContext({ - user: null, - userRoles: [], - login: null, - logout: null -}); - -export const useUserContext = () => { - const { user, userRoles, login, logout } = useContext(userContext); - return { user, userRoles, login, logout }; -}; diff --git a/src/contexts/index.ts b/src/contexts/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..45be9ebe6884eff4d13ef24ede42fb8f0d2ff5f9 --- /dev/null +++ b/src/contexts/index.ts @@ -0,0 +1,12 @@ +import { AuthContext, useAuthContext } from "./AuthContext"; +import { + GlobalAppBarContext, + useGlobalAppBarContext +} from "./GlobalAppBarContext"; + +export { + AuthContext, + useAuthContext, + GlobalAppBarContext, + useGlobalAppBarContext +}; diff --git a/src/index.js b/src/index.js index 885030ecf8383e7193d865f7adc2d9df1a27dffb..683928af36cda9e27f32eb1453cb47a26e387dba 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,7 @@ export * from "./hooks/usePagination"; export * from "./hooks/useTypingTimer"; export * from "./hooks/useUniqueKeys"; -export * from "./contexts/User"; -export * from "./contexts/GlobalAppBarContext"; +export * from "./contexts"; export * from "./components/common/AlertBanner"; export * from "./components/common/ApiAlertError"; export * from "./components/common/AppLayout"; @@ -35,7 +34,6 @@ export * from "./components/navigation/ErrorBoundary"; export * from "./components/navigation/ServerErrorPage"; export * from "./components/navigation/Environment"; export * from "./components/auth/Login"; -export * from "./components/auth/AccessControl"; export * from "./components/common/Icons"; export * from "./providers"; export * from "./style/Theme"; diff --git a/src/stories/auth/AccessControl.stories.jsx b/src/stories/auth/AccessControl.stories.jsx deleted file mode 100644 index 55e75b31f06380aeedbbe5f7b502661faea30aa7..0000000000000000000000000000000000000000 --- a/src/stories/auth/AccessControl.stories.jsx +++ /dev/null @@ -1,103 +0,0 @@ -import { Box, Paper, Typography } from "@mui/material"; -import { useContext, useEffect, useState, useMemo } from "react"; -import CheckCircleIcon from "@mui/icons-material/CheckCircle"; -import CancelIcon from "@mui/icons-material/Cancel"; -import { AccessControl } from "../../components/auth/AccessControl"; -import { userContext } from "../../contexts/User"; - -export default { - title: "Auth/AccessControl" -}; - -const testAuthContext = userContext; - -const TestAuthContextProvider = ({ children }) => { - const [user, setUser] = useState(); - const [userRoles, setUserRoles] = useState([]); - - const value = useMemo( - () => ({ - user, - setUser, - userRoles, - setUserRoles - }), - [user, setUser, userRoles, setUserRoles] - ); - - return ( - <testAuthContext.Provider value={value}> - {children} - </testAuthContext.Provider> - ); -}; - -const Harness = ({ children }) => { - return <TestAuthContextProvider>{children}</TestAuthContextProvider>; -}; - -const TestComponent = ({ - testUser, - testRoles, - allowedRoles, - allowedUsersWithRoles -}) => { - const { user, userRoles, setUser, setUserRoles } = - useContext(testAuthContext); - - useEffect(() => { - setUser(testUser); - setUserRoles(testRoles); - }, [setUser, setUserRoles, testUser, testRoles]); - - return ( - <Paper> - <Typography>testUser: {user?.loginName}</Typography> - <Typography>testRoles: {userRoles?.join(", ")}</Typography> - <Typography>allowedRoles: {allowedRoles?.join(", ")}</Typography> - <Typography> - allowedUsersWithRoles:{" "} - {allowedUsersWithRoles - ?.map((it) => `{user: ${it?.user}, role: ${it?.role}}`) - .join(", ")} - </Typography> - <Box marginTop={2}> - <AccessControl - allowedRoles={allowedRoles} - allowedUsersWithRoles={allowedUsersWithRoles} - renderNoAccess={() => ( - <Typography - display="flex" - alignContent="center" - > - Result: Forbidden! <CancelIcon color="error" /> - </Typography> - )} - > - <Typography - display="flex" - alignContent="center" - > - Result: Allowed! <CheckCircleIcon color="success" /> - </Typography> - </AccessControl> - </Box> - </Paper> - ); -}; - -const Template = (args) => { - return ( - <Harness> - <TestComponent {...args} /> - </Harness> - ); -}; - -export const Default = (args) => <Template {...args} />; -Default.args = { - testUser: { loginName: "test user" }, - testRoles: ["admin", "user"], - allowedRoles: ["user"], - allowedUsersWithRoles: [{ user: "test user", role: "user" }] -}; diff --git a/src/stories/auth/Login.stories.jsx b/src/stories/auth/Login.stories.tsx similarity index 53% rename from src/stories/auth/Login.stories.jsx rename to src/stories/auth/Login.stories.tsx index 689d31cbb91ad5989e75fe343c656b8360191563..3a38c1509bc6efe7bdf9d4597812c75bfd4520bb 100644 --- a/src/stories/auth/Login.stories.jsx +++ b/src/stories/auth/Login.stories.tsx @@ -8,42 +8,36 @@ export default { const loginError = ""; -export const BasicLoginForm = () => { - const login = (username, password) => { - alert(`User log in: ${username}, ${password}`); - }; +const login = (username: string, password: string) => + alert(`User log in: ${username}, ${password}`); - return ( +export const BasicLoginForm = () => ( + <Grid + container + spacing={0} + alignItems="center" + justifyContent="center" + > <Grid - container - spacing={0} - alignItems="center" - justify="center" + item + xs={8} + md={5} + xl={3} > - <Grid - item - xs={8} - md={5} - xl={3} - > - <Card> - <CardContent> - <LoginForm - login={login} - error={loginError} - /> - </CardContent> - </Card> - </Grid> + <Card> + <CardContent> + <LoginForm + login={login} + error={loginError} + /> + </CardContent> + </Card> </Grid> - ); -}; + </Grid> +); export const BasicLoginDialog = () => { const [open, setOpen] = useState(false); - const login = (username, password) => { - alert(`User log in: ${username}, ${password}`); - }; const toggleOpen = () => setOpen(!open); return ( @@ -63,8 +57,6 @@ export const BasicLoginDialog = () => { open={open} error={loginError} handleClose={toggleOpen} - idUsername="dialog-username" - idPassword="dialog-password" /> </div> );