diff --git a/src/components/controls/User.stories.tsx b/src/components/controls/User.stories.tsx index 7e64d04..2bff14d 100644 --- a/src/components/controls/User.stories.tsx +++ b/src/components/controls/User.stories.tsx @@ -7,6 +7,13 @@ const meta: Meta = { title: "Components/Controls/User", component: User, tags: ["autodocs"], + parameters: { + docs: { + description: { + component: "A control to login/logout with, and to show user info.", + }, + }, + }, }; export default meta; @@ -14,14 +21,35 @@ type Story = StoryObj; export const LoggedOut: Story = { args: { user: null }, + parameters: { + docs: { + description: { + story: "Default display when not yet logged in.", + }, + }, + }, }; export const LoggedIn: Story = { args: { user: { name: "Name Surname", fedid: "FedID" }, onLogout: () => {} }, + parameters: { + docs: { + description: { + story: "Default display when logged in.", + }, + }, + }, }; -export const LoggedInNoName: Story = { - args: { user: { fedid: "FedID" }, onLogout: () => {} }, +export const LoggedInNoFedId: Story = { + args: { user: { name: "User's Name" }, onLogout: () => {} }, + parameters: { + docs: { + description: { + story: "Logged in, but no Fed ID.", + }, + }, + }, }; export const LoggedInLongName: Story = { @@ -29,6 +57,13 @@ export const LoggedInLongName: Story = { user: { name: "Jonathan Edwards Longname", fedid: "abc12345" }, onLogout: () => {}, }, + parameters: { + docs: { + description: { + story: "Logged in with a long name.", + }, + }, + }, }; export const LoggedInChangeColour: Story = { @@ -37,14 +72,28 @@ export const LoggedInChangeColour: Story = { user: { name: "Name Surname", fedid: "abc12345" }, onLogout: () => {}, }, + parameters: { + docs: { + description: { + story: "You can change the colour used to display it.", + }, + }, + }, }; export const LoggedInReplaceAvatar: Story = { args: { user: { name: "Name Surname", fedid: "abc12345" }, - avatar: JL, + avatar: SRU, onLogout: () => {}, }, + parameters: { + docs: { + description: { + story: "You can change the avatar image. Perhaps use a photo.", + }, + }, + }, }; export const AdditionalMenuItems: Story = { @@ -63,4 +112,44 @@ export const AdditionalMenuItems: Story = { ], onLogout: () => {}, }, + parameters: { + docs: { + description: { + story: "You can add additional menu items.", + }, + }, + }, +}; + +export const UsingAuth: Story = { + args: { + auth: { + authenticated: false, + initialised: false, + getProfileUrl: () => "", + getToken: () => "", + login() {}, + logout() {}, + _keycloak: null, + user: { + name: "User Name ", + givenName: "", + familyName: "", + fedId: "", + email: "", + }, + }, + }, + parameters: { + docs: { + description: { + story: + "If you are using SciReactUI's auth mechanism, you can simply pass the useAuth counterpart in." + + "

" + + "
const auth = useAuth();
" + + "
" + + "
<User auth={auth}/>
", + }, + }, + }, }; diff --git a/src/components/controls/User.test.tsx b/src/components/controls/User.test.tsx index 67cd46d..4188bec 100644 --- a/src/components/controls/User.test.tsx +++ b/src/components/controls/User.test.tsx @@ -1,16 +1,19 @@ import { fireEvent, screen } from "@testing-library/react"; import { Avatar, MenuItem } from "@mui/material"; -import { User } from "./User"; import { renderWithProviders } from "../../__test-utils__/helpers"; +import { Auth } from "../systems/auth"; + +import { User } from "./User"; describe("User", () => { it("should render", () => { renderWithProviders( - {}} onLogout={() => {}} user={null} />, + 0} onLogout={() => 0} user={null} />, ); - renderWithProviders( {}} user={null} />); - renderWithProviders( {}} user={null} />); + renderWithProviders( 0} user={null} />); + renderWithProviders( 0} user={null} />); renderWithProviders(); + renderWithProviders(); }); it("should display login button when not authenticated", () => { @@ -127,3 +130,69 @@ describe("User", () => { expect(screen.getByText("Logout")).toBeInTheDocument(); }); }); + +describe("User with Auth", () => { + const authDummy: Auth = { + authenticated: false, + initialised: false, + getProfileUrl: () => "", + getToken: () => "", + login() {}, + logout() {}, + _keycloak: null, + }; + const authDummyUser = { + name: "", + givenName: "", + familyName: "", + fedId: "", + email: "", + }; + + it("should render", () => { + renderWithProviders(); + }); + + it("should use auth name when passed in", () => { + const auth: Auth = { + ...authDummy, + user: { + ...authDummyUser, + name: "test name", + }, + }; + const { queryByText } = renderWithProviders(); + // @ts-expect-error It is not null, it will never be null. + expect(queryByText(auth.user.name)).toBeInTheDocument(); + }); + + it("should fire auth login callback when button is clicked", () => { + const loginCallback = vi.fn(); + const auth = { + ...authDummy, + login: loginCallback, + }; + const { getByText } = renderWithProviders(); + + const loginButton = getByText("Login"); + fireEvent.click(loginButton); + + expect(loginCallback).toHaveBeenCalledTimes(1); + }); + + it("should display additional menu item when auth", () => { + const auth: Auth = { + ...authDummy, + user: { + ...authDummyUser, + name: "test name", + }, + }; + const { getByRole } = renderWithProviders(); + + const userMenu = getByRole("button"); + fireEvent.click(userMenu); + + expect(screen.getByText("Profile")).toBeInTheDocument(); + }); +}); diff --git a/src/components/controls/User.tsx b/src/components/controls/User.tsx index 77cd4cd..75b27e4 100644 --- a/src/components/controls/User.tsx +++ b/src/components/controls/User.tsx @@ -10,23 +10,24 @@ import { Typography, useTheme, } from "@mui/material"; - import { ReactElement, ReactNode, useState } from "react"; - import { MdLogin } from "react-icons/md"; +import { Auth } from "../systems/auth"; + interface AuthState { - fedid: string; + fedid?: string; name?: string; } interface UserProps { - user: AuthState | null; + user?: AuthState | null; onLogin?: () => void; onLogout?: () => void; avatar?: ReactNode; colour?: string; menuItems?: ReactElement | ReactElement[]; + auth?: Auth; } const User = ({ @@ -36,6 +37,7 @@ const User = ({ avatar, colour, menuItems, + auth, }: UserProps) => { const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); @@ -48,15 +50,21 @@ const User = ({ }; const handleLogin = () => { + if (auth) auth.login(); if (onLogin) onLogin(); }; const handleLogout = () => { handleClose(); + if (auth) auth.logout(); if (onLogout) onLogout(); }; const theme = useTheme(); + if (!user && auth && auth.user) { + user = { name: auth.user.name }; + } + return ( <> @@ -117,7 +125,8 @@ const User = ({ - {(onLogout || menuItems) && ( + + {(onLogout || menuItems || auth) && ( {menuItems} + {auth && ( + + + Profile + + + )} Logout diff --git a/src/storybook/helpers/Auth.mdx b/src/storybook/helpers/Auth.mdx index 1302705..7870e11 100644 --- a/src/storybook/helpers/Auth.mdx +++ b/src/storybook/helpers/Auth.mdx @@ -52,13 +52,9 @@ import {useAuth} from "../../components/systems/auth"; return } + // Return page content for authenticated users return (<> - + I'm authenticating ) }