From cbaf9a83eeb768f7885a370b9bfc89772c6b0e86 Mon Sep 17 00:00:00 2001 From: natalia Date: Wed, 5 Apr 2023 13:55:18 -0700 Subject: [PATCH 01/27] Authentication Styling --- documentation/schema/ERD.svg | 2 +- web/src/Routes.tsx | 14 +- .../ResetPassword/ResetPassword.test.tsx | 2 +- .../ResetPassword/ResetPassword.tsx | 88 +++++------ web/src/components/ZealLogo/ZealLogo.tsx | 10 +- .../components/ZealLogo/lumberstack-home.svg | 14 ++ .../ForgotPasswordPage/ForgotPasswordPage.tsx | 79 +++++----- web/src/pages/LoginPage/LoginPage.tsx | 148 +++++++++--------- web/src/pages/SignupPage/SignupPage.tsx | 125 +++++++-------- .../VerificationResetPage.tsx | 85 +++++----- 10 files changed, 280 insertions(+), 287 deletions(-) create mode 100644 web/src/components/ZealLogo/lumberstack-home.svg diff --git a/documentation/schema/ERD.svg b/documentation/schema/ERD.svg index 3636c93..0337516 100644 --- a/documentation/schema/ERD.svg +++ b/documentation/schema/ERD.svg @@ -1 +1 @@ -UserStringidPKStringemailStringnamenullableStringnicknamenullableStringpronounsnullableBooleanactiveBooleanadminStringhashedPasswordStringsaltStringresetTokennullableDateTimeresetTokenExpiresAtnullableDateTimeupdatedAtDateTimecreatedAtStringverifyTokennullableMembershipStringidPKStringuserIdStringteamIdTeamStringidPKStringnameBooleanactiveDateTimeupdatedAtDateTimecreatedAtMembershipRoleStringidPKStringmembershipIdStringroleIdRoleStringidPKStringnameDateTimecreatedAtDateTimeupdatedAtteamusermembershiprole \ No newline at end of file +UserStringidPKStringemailStringnamenullableStringnicknamenullableStringpronounsnullableBooleanactiveBooleanadminStringhashedPasswordStringsaltStringresetTokennullableDateTimeresetTokenExpiresAtnullableDateTimeupdatedAtDateTimecreatedAtStringverifyTokennullableMembershipStringidPKStringuserIdStringteamIdTeamStringidPKStringnameBooleanactiveDateTimeupdatedAtDateTimecreatedAtMembershipRoleStringidPKStringmembershipIdStringroleIdRoleStringidPKStringnameDateTimecreatedAtDateTimeupdatedAtteamusermembershiprole \ No newline at end of file diff --git a/web/src/Routes.tsx b/web/src/Routes.tsx index a9781cc..871a24c 100644 --- a/web/src/Routes.tsx +++ b/web/src/Routes.tsx @@ -12,14 +12,14 @@ import { ProfileLayout } from './layouts/ProfileLayout' const Routes = () => { return ( - - - - - - - + + + + + + + diff --git a/web/src/components/ResetPassword/ResetPassword.test.tsx b/web/src/components/ResetPassword/ResetPassword.test.tsx index 887f9cd..0e76fbd 100644 --- a/web/src/components/ResetPassword/ResetPassword.test.tsx +++ b/web/src/components/ResetPassword/ResetPassword.test.tsx @@ -43,7 +43,7 @@ describe('Reset Password Page', () => { .closest('input') await waitFor(() => userEvent.type(passwordInput, 'supersecret')) - const submitButton = screen.getByRole('button', { name: 'Submit' }) + const submitButton = screen.getByRole('button', { name: 'Reset Password' }) await waitFor(() => userEvent.click(submitButton)) expect(mockReset).toHaveBeenCalledWith({ resetToken: 'foo', diff --git a/web/src/components/ResetPassword/ResetPassword.tsx b/web/src/components/ResetPassword/ResetPassword.tsx index 5a77f05..2ab5d95 100644 --- a/web/src/components/ResetPassword/ResetPassword.tsx +++ b/web/src/components/ResetPassword/ResetPassword.tsx @@ -13,6 +13,8 @@ import { toast, Toaster } from '@redwoodjs/web/toast' import { useAuth } from 'src/auth' +import { ZealLogo } from '../ZealLogo' + const ResetPassword = ({ resetToken, title, message }) => { const { isAuthenticated, reauthenticate, validateResetToken, resetPassword } = useAuth() @@ -61,58 +63,50 @@ const ResetPassword = ({ resetToken, title, message }) => { return ( <> +
+
+ +
+

{title}

-
- -
-
-
-

{title}

-
- -
-
-
-
- - + + + - -
+ -
- - Submit - -
- -
-
+ + Reset Password + +
+
+
+
-
+
) } diff --git a/web/src/components/ZealLogo/ZealLogo.tsx b/web/src/components/ZealLogo/ZealLogo.tsx index a5019b8..3f5c06a 100644 --- a/web/src/components/ZealLogo/ZealLogo.tsx +++ b/web/src/components/ZealLogo/ZealLogo.tsx @@ -1,13 +1,7 @@ -import ZealSVG from './zeal-logo.svg' +import ZealSVG from './lumberstack-home.svg' const ZealLogo = () => { - return ( - - ) + return } export { ZealLogo } diff --git a/web/src/components/ZealLogo/lumberstack-home.svg b/web/src/components/ZealLogo/lumberstack-home.svg new file mode 100644 index 0000000..0eac8b9 --- /dev/null +++ b/web/src/components/ZealLogo/lumberstack-home.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx b/web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx index 41890a8..d19b64d 100644 --- a/web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx +++ b/web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx @@ -1,11 +1,12 @@ import { useEffect, useRef } from 'react' import { Form, Label, TextField, Submit, FieldError } from '@redwoodjs/forms' -import { navigate, routes } from '@redwoodjs/router' +import { Link, navigate, routes } from '@redwoodjs/router' import { MetaTags } from '@redwoodjs/web' import { toast, Toaster } from '@redwoodjs/web/toast' import { useAuth } from 'src/auth' +import { ZealLogo } from 'src/components/ZealLogo' const ForgotPasswordPage = () => { const { isAuthenticated, forgotPassword } = useAuth() @@ -40,50 +41,50 @@ const ForgotPasswordPage = () => { return ( <> +
+
+
+ +

+ Forgot Password? +

-
- -
-
-
-

- Forgot Password -

-
+
+ + -
-
- -
- - + - -
- -
- Submit -
- -
+ + Send me a Reset Email + + +
+ + Ready to Login! +
+
+
+
-
+
) } diff --git a/web/src/pages/LoginPage/LoginPage.tsx b/web/src/pages/LoginPage/LoginPage.tsx index 00b8d9b..2ac6472 100644 --- a/web/src/pages/LoginPage/LoginPage.tsx +++ b/web/src/pages/LoginPage/LoginPage.tsx @@ -14,6 +14,7 @@ import { MetaTags } from '@redwoodjs/web' import { toast, Toaster } from '@redwoodjs/web/toast' import { useAuth } from 'src/auth' +import { ZealLogo } from 'src/components/ZealLogo' const LoginPage = () => { const { isAuthenticated, logIn } = useAuth() @@ -47,85 +48,82 @@ const LoginPage = () => { return ( <> -
- -
-
-
-

Login

-
- -
-
-
- - - - - - - - -
- - Forgot Password? - -
- - - -
- Login -
- +
+
+ +
+

Login

+ +
+ + + + + +
+ + + Forgot Password? +
+ + + + + + + Login + + +
+ Don't have an account? + + Sign up! +
-
- Don't have an account?{' '} - - Sign up! - -
+
+ +
+
-
+ ) } diff --git a/web/src/pages/SignupPage/SignupPage.tsx b/web/src/pages/SignupPage/SignupPage.tsx index a98bd9f..976b66a 100644 --- a/web/src/pages/SignupPage/SignupPage.tsx +++ b/web/src/pages/SignupPage/SignupPage.tsx @@ -13,6 +13,7 @@ import { MetaTags } from '@redwoodjs/web' import { toast, Toaster } from '@redwoodjs/web/toast' import { useAuth } from 'src/auth' +import { ZealLogo } from 'src/components/ZealLogo' const SignupPage = () => { const { isAuthenticated, signUp } = useAuth() @@ -45,77 +46,71 @@ const SignupPage = () => { return ( <> +
+
+ +
+

Signup

-
- -
-
-
-

Signup

-
+
+ + + -
-
- - - - + + + - - - - -
- - Sign Up - -
- -
+ + Sign Up + + +
+ Already have an account? + + Log in! +
-
- Already have an account?{' '} - - Log in! - -
+
+
+
-
+
) } diff --git a/web/src/pages/VerificationResetPage/VerificationResetPage.tsx b/web/src/pages/VerificationResetPage/VerificationResetPage.tsx index dcc08f9..7320e63 100644 --- a/web/src/pages/VerificationResetPage/VerificationResetPage.tsx +++ b/web/src/pages/VerificationResetPage/VerificationResetPage.tsx @@ -5,6 +5,8 @@ import { navigate, routes } from '@redwoodjs/router' import { MetaTags, useMutation } from '@redwoodjs/web' import { toast, Toaster } from '@redwoodjs/web/toast' +import { ZealLogo } from 'src/components/ZealLogo' + const VERIFY_RESET_MUTATION = gql` mutation VerificationResetMutation($email: String!) { email: verifyReset(email: $email) @@ -37,55 +39,50 @@ const VerificationResetPage = ({ email }) => { <> -
- -
-
-
-

- Resend Verification Email -

-
- -
-
-
-
- - +
+
+ +
+

+ Resend Verification Email +

- -
+ +
+ + -
- - Send Email - -
- +
-
+ + + Send Email + +
+
+
+
- + ) } From 22654bc535a97e9eefb9ea81d6315fbdb9e59190 Mon Sep 17 00:00:00 2001 From: natalia Date: Wed, 5 Apr 2023 16:05:37 -0700 Subject: [PATCH 02/27] Layout and Admin Style Change --- web/src/components/Admin/Role/Roles/Roles.tsx | 17 +++-- .../Admin/Team/TeamsCell/TeamsCell.tsx | 3 + web/src/components/Admin/User/Users/Users.tsx | 72 ++++++++++--------- .../Admin/User/UsersCell/UsersCell.tsx | 10 +++ web/src/components/Footer/Footer.test.tsx | 18 ++++- web/src/components/Footer/Footer.tsx | 37 ++++++---- web/src/layouts/MainLayout/MainLayout.tsx | 2 +- 7 files changed, 101 insertions(+), 58 deletions(-) diff --git a/web/src/components/Admin/Role/Roles/Roles.tsx b/web/src/components/Admin/Role/Roles/Roles.tsx index 985f2b2..8469127 100644 --- a/web/src/components/Admin/Role/Roles/Roles.tsx +++ b/web/src/components/Admin/Role/Roles/Roles.tsx @@ -3,6 +3,9 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Admin/Role/RolesCell' +import { Archive } from 'src/components/Icon/Archive' +import { Eye } from 'src/components/Icon/Eye' +import { Pen } from 'src/components/Icon/Pen' const DELETE_ROLE_MUTATION = gql` mutation DeleteRoleMutation($id: String!) { @@ -41,38 +44,34 @@ const RolesList = ({ roles }) => { } return ( -
+
- - {roles.map((role) => { const membershipRoleCount = role.membershipRoles.length - return ( - diff --git a/web/src/components/Admin/Team/TeamsCell/TeamsCell.tsx b/web/src/components/Admin/Team/TeamsCell/TeamsCell.tsx index e2baa57..f07a15c 100644 --- a/web/src/components/Admin/Team/TeamsCell/TeamsCell.tsx +++ b/web/src/components/Admin/Team/TeamsCell/TeamsCell.tsx @@ -15,6 +15,9 @@ export const QUERY = gql` createdAt memberships { id + user { + name + } } } } diff --git a/web/src/components/Admin/User/Users/Users.tsx b/web/src/components/Admin/User/Users/Users.tsx index e844d0c..4daa89e 100644 --- a/web/src/components/Admin/User/Users/Users.tsx +++ b/web/src/components/Admin/User/Users/Users.tsx @@ -3,6 +3,11 @@ import { useMutation } from '@redwoodjs/web' import { toast } from '@redwoodjs/web/toast' import { QUERY } from 'src/components/Admin/User/UsersCell' +import { Avatar } from 'src/components/Avatar' +import { Archive } from 'src/components/Icon/Archive' +import { Check } from 'src/components/Icon/Check' +import { Eye } from 'src/components/Icon/Eye' +import { Pen } from 'src/components/Icon/Pen' const ARCHIVE_USER_MUTATION = gql` mutation ArchiveUserMutation($id: String!, $input: UpdateUserInput!) { @@ -21,20 +26,6 @@ const truncate = (text) => { return output } -const timeTag = (datetime) => { - return ( - datetime && ( - - ) - ) -} - -const checkboxInputTag = (label, checked) => { - return -} - const Users = ({ users }) => { const [archiveUser] = useMutation(ARCHIVE_USER_MUTATION, { onCompleted: () => { @@ -57,51 +48,62 @@ const Users = ({ users }) => { }) } } - return ( -
+
Id NameActions
{truncate(role.id)} {truncate(role.name)} -
- - - - - + + + + - - {users.map((user) => ( - + + + - - - - - - - + + + diff --git a/web/src/components/Admin/User/UsersCell/UsersCell.tsx b/web/src/components/Admin/User/UsersCell/UsersCell.tsx index 3d5aea0..3ed94ed 100644 --- a/web/src/components/Admin/User/UsersCell/UsersCell.tsx +++ b/web/src/components/Admin/User/UsersCell/UsersCell.tsx @@ -26,6 +26,16 @@ export const QUERY = gql` admin updatedAt createdAt + memberships { + team { + name + } + membershipRoles { + role { + name + } + } + } } } ` diff --git a/web/src/components/Footer/Footer.test.tsx b/web/src/components/Footer/Footer.test.tsx index 997bb71..bd40312 100644 --- a/web/src/components/Footer/Footer.test.tsx +++ b/web/src/components/Footer/Footer.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@redwoodjs/testing/web' +import { render, screen } from '@redwoodjs/testing/web' import { Footer } from './Footer' @@ -11,4 +11,20 @@ describe('Footer', () => { render(
) }).not.toThrow() }) + + it('shows copyright when unauthenticated', () => { + render(
) + const element = screen.getByTestId('copyright') + + expect(element).toBeInTheDocument() + }) + + it('shows social media icons when unauthenticated', () => { + render(
) + const element = screen.getByTestId('twitterIcon') + const element2 = screen.getByTestId('githubIcon') + + expect(element).toBeInTheDocument() + expect(element2).toBeInTheDocument() + }) }) diff --git a/web/src/components/Footer/Footer.tsx b/web/src/components/Footer/Footer.tsx index 0830d4d..bab388a 100644 --- a/web/src/components/Footer/Footer.tsx +++ b/web/src/components/Footer/Footer.tsx @@ -1,5 +1,8 @@ import { getYear } from 'date-fns' +import { Github } from '../Icon/Github' +import { Twitter } from '../Icon/Twitter' + const getCurrentYear = (): string => { const year = getYear(new Date(Date.now())).toString() return year @@ -7,21 +10,31 @@ const getCurrentYear = (): string => { const Footer = () => { return ( -
+
) diff --git a/web/src/layouts/MainLayout/MainLayout.tsx b/web/src/layouts/MainLayout/MainLayout.tsx index 5819d29..f10c390 100644 --- a/web/src/layouts/MainLayout/MainLayout.tsx +++ b/web/src/layouts/MainLayout/MainLayout.tsx @@ -5,7 +5,7 @@ import { Navigation } from 'src/components/Navigation/Navigation' const MainLayout = ({ children }) => { return ( -
+
{children} From ee98b9e9ac0b69a2a560c9a393d3edf820777875 Mon Sep 17 00:00:00 2001 From: natalia Date: Wed, 5 Apr 2023 16:19:13 -0700 Subject: [PATCH 03/27] Icon Additions, Layout Test Update --- web/src/components/Icon/Archive.tsx | 30 + web/src/components/Icon/Arrow.tsx | 30 + web/src/components/Icon/Check.tsx | 42 + web/src/components/Icon/Eye.tsx | 30 + web/src/components/Icon/Gear.tsx | 30 + web/src/components/Icon/Github.tsx | 42 + web/src/components/Icon/Icon.stories.tsx | 102 ++ web/src/components/Icon/Icon.test.tsx | 91 ++ web/src/components/Icon/Icon.tsx | 48 + web/src/components/Icon/Pen.tsx | 30 + web/src/components/Icon/Twitter.tsx | 42 + .../Icon/__snapshots__/Icon.test.tsx.snap | 1007 +++++++++++++++++ web/src/components/Icon/index.ts | 1 + .../Admin/RolesLayout/RolesLayout.test.tsx | 2 +- .../Admin/TeamsLayout/TeamsLayout.test.tsx | 2 +- .../Admin/UsersLayout/UsersLayout.test.tsx | 2 +- 16 files changed, 1528 insertions(+), 3 deletions(-) create mode 100644 web/src/components/Icon/Archive.tsx create mode 100644 web/src/components/Icon/Arrow.tsx create mode 100644 web/src/components/Icon/Check.tsx create mode 100644 web/src/components/Icon/Eye.tsx create mode 100644 web/src/components/Icon/Gear.tsx create mode 100644 web/src/components/Icon/Github.tsx create mode 100644 web/src/components/Icon/Icon.stories.tsx create mode 100644 web/src/components/Icon/Icon.test.tsx create mode 100644 web/src/components/Icon/Icon.tsx create mode 100644 web/src/components/Icon/Pen.tsx create mode 100644 web/src/components/Icon/Twitter.tsx create mode 100644 web/src/components/Icon/__snapshots__/Icon.test.tsx.snap create mode 100644 web/src/components/Icon/index.ts diff --git a/web/src/components/Icon/Archive.tsx b/web/src/components/Icon/Archive.tsx new file mode 100644 index 0000000..8d38892 --- /dev/null +++ b/web/src/components/Icon/Archive.tsx @@ -0,0 +1,30 @@ +interface IArchive { + className?: string + height?: string + width?: string +} + +const Archive = ({ + className = '', + height = '40', + width = '40', +}: IArchive): JSX.Element => { + return ( + + + + ) +} + +export { Archive } diff --git a/web/src/components/Icon/Arrow.tsx b/web/src/components/Icon/Arrow.tsx new file mode 100644 index 0000000..cc1977c --- /dev/null +++ b/web/src/components/Icon/Arrow.tsx @@ -0,0 +1,30 @@ +interface IArrow { + className?: string + height?: string + width?: string +} + +const Arrow = ({ + className = '', + height = '24', + width = '24', +}: IArrow): JSX.Element => { + return ( + + + + ) +} + +export { Arrow } diff --git a/web/src/components/Icon/Check.tsx b/web/src/components/Icon/Check.tsx new file mode 100644 index 0000000..936edfa --- /dev/null +++ b/web/src/components/Icon/Check.tsx @@ -0,0 +1,42 @@ +interface ICheck { + className?: string + height?: string + width?: string +} + +const Check = ({ + className = '', + height = '24', + width = '24', +}: ICheck): JSX.Element => { + return ( + + + + + + + + + + + ) +} + +export { Check } diff --git a/web/src/components/Icon/Eye.tsx b/web/src/components/Icon/Eye.tsx new file mode 100644 index 0000000..9168c4a --- /dev/null +++ b/web/src/components/Icon/Eye.tsx @@ -0,0 +1,30 @@ +interface IEye { + className?: string + height?: string + width?: string +} + +const Eye = ({ + className = '', + height = '44', + width = '44', +}: IEye): JSX.Element => { + return ( + + + + ) +} + +export { Eye } diff --git a/web/src/components/Icon/Gear.tsx b/web/src/components/Icon/Gear.tsx new file mode 100644 index 0000000..8b02b43 --- /dev/null +++ b/web/src/components/Icon/Gear.tsx @@ -0,0 +1,30 @@ +interface IGear { + className?: string + height?: string + width?: string +} + +const Gear = ({ + className = '', + height = '44', + width = '44', +}: IGear): JSX.Element => { + return ( + + + + ) +} + +export { Gear } diff --git a/web/src/components/Icon/Github.tsx b/web/src/components/Icon/Github.tsx new file mode 100644 index 0000000..bce342c --- /dev/null +++ b/web/src/components/Icon/Github.tsx @@ -0,0 +1,42 @@ +interface IGithub { + className?: string + height?: string + width?: string +} + +const Github = ({ + className = '', + height = '44', + width = '44', +}: IGithub): JSX.Element => { + return ( + + + + + + + + + + + ) +} + +export { Github } diff --git a/web/src/components/Icon/Icon.stories.tsx b/web/src/components/Icon/Icon.stories.tsx new file mode 100644 index 0000000..124248d --- /dev/null +++ b/web/src/components/Icon/Icon.stories.tsx @@ -0,0 +1,102 @@ +import { Icon, IIcon } from './Icon' + +const Template = (args: IIcon) => + +export const Archive = Template.bind({}) +Archive.args = { + name: 'Archive', +} + +export const Arrow = Template.bind({}) +Arrow.args = { + name: 'Arrow', +} + +export const Check = Template.bind({}) +Check.args = { + name: 'Check', +} + +export const Eye = Template.bind({}) +Eye.args = { + name: 'Eye', +} + +export const Gear = Template.bind({}) +Gear.args = { + name: 'Gear', +} + +export const Github = Template.bind({}) +Github.args = { + name: 'Github', +} + +export const Pen = Template.bind({}) +Pen.args = { + name: 'Pen', +} + +export const Twitter = Template.bind({}) +Twitter.args = { + name: 'Twitter', +} + +const iconOptions = ['unmute'] + +export default { + title: 'Components/Icon', + component: Icon, + argTypes: { + name: { + name: 'name', + description: 'name of the icon', + control: { + type: 'select', + options: iconOptions, + }, + table: { + type: { summary: 'string' }, + defaultValue: { summary: 'null' }, + }, + }, + className: { + name: 'className', + type: { name: 'string', required: true }, + defaultValue: '', + description: 'CSS class names', + control: { + type: 'text', + }, + table: { + type: { summary: 'string', defaultValue: { summary: '' } }, + }, + }, + height: { + name: 'height', + type: { name: 'number', required: false }, + description: 'avatar height in pixels', + defaultValue: 24, + control: { + type: 'number', + }, + table: { + type: { summary: 'number' }, + defaultValue: { summary: 24 }, + }, + }, + width: { + name: 'width', + type: { name: 'number', required: false }, + description: 'avatar width in pixels', + defaultValue: 24, + control: { + type: 'number', + }, + table: { + type: { summary: 'number' }, + defaultValue: { summary: 24 }, + }, + }, + }, +} diff --git a/web/src/components/Icon/Icon.test.tsx b/web/src/components/Icon/Icon.test.tsx new file mode 100644 index 0000000..4137d33 --- /dev/null +++ b/web/src/components/Icon/Icon.test.tsx @@ -0,0 +1,91 @@ +import { render } from '@redwoodjs/testing/web' + +import { Icon } from './Icon' + +describe('Icon', () => { + it('renders successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) + + // archive icon + it('renders the archive icon', () => { + const { getByTestId } = render() + expect(getByTestId('archiveIcon')).toBeInTheDocument() + }) + + it('matches the archive icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) + + // arrow icon + it('renders the arrow icon', () => { + const { getByTestId } = render() + expect(getByTestId('arrowIcon')).toBeInTheDocument() + }) + + it('matches the arrow icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) + + // check icon + it('renders the check icon', () => { + const { getByTestId } = render() + expect(getByTestId('checkIcon')).toBeInTheDocument() + }) + + it('matches the check icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) + + // eye icon + it('renders the eye icon', () => { + const { getByTestId } = render() + expect(getByTestId('eyeIcon')).toBeInTheDocument() + }) + + it('matches the eye icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) + + // gear icon + it('renders the gear icon', () => { + const { getByTestId } = render() + expect(getByTestId('gearIcon')).toBeInTheDocument() + }) + + it('matches the gear icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) + + // github icon + it('renders the github icon', () => { + const { getByTestId } = render() + expect(getByTestId('githubIcon')).toBeInTheDocument() + }) + + it('matches the github icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) + + // pen icon + it('renders the pen icon', () => { + const { getByTestId } = render() + expect(getByTestId('penIcon')).toBeInTheDocument() + }) + + it('matches the pen icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) + + // twitter icon + it('renders the twitter icon', () => { + const { getByTestId } = render() + expect(getByTestId('twitterIcon')).toBeInTheDocument() + }) + + it('matches the twitter icon snapshot', () => { + expect(render()).toMatchSnapshot() + }) +}) diff --git a/web/src/components/Icon/Icon.tsx b/web/src/components/Icon/Icon.tsx new file mode 100644 index 0000000..343c0e7 --- /dev/null +++ b/web/src/components/Icon/Icon.tsx @@ -0,0 +1,48 @@ +import { Archive } from './Archive' +import { Arrow } from './Arrow' +import { Check } from './Check' +import { Eye } from './Eye' +import { Gear } from './Gear' +import { Github } from './Github' +import { Pen } from './Pen' +import { Twitter } from './Twitter' + +export interface IIcon { + name: + | 'archive' + | 'arrow' + | 'check' + | 'eye' + | 'gear' + | 'github' + | 'pen' + | 'twitter' + className?: string + width?: string + height?: string +} + +const Icon = ({ name, className = '', width, height }: IIcon): JSX.Element => { + switch (name.toLowerCase()) { + case 'archive': + return + case 'arrow': + return + case 'check': + return + case 'eye': + return + case 'gear': + return + case 'github': + return + case 'pen': + return + case 'twitter': + return + default: + return
+ } +} + +export { Icon } diff --git a/web/src/components/Icon/Pen.tsx b/web/src/components/Icon/Pen.tsx new file mode 100644 index 0000000..c20d4d9 --- /dev/null +++ b/web/src/components/Icon/Pen.tsx @@ -0,0 +1,30 @@ +interface IPen { + className?: string + height?: string + width?: string +} + +const Pen = ({ + className = '', + height = '40', + width = '40', +}: IPen): JSX.Element => { + return ( + + + + ) +} + +export { Pen } diff --git a/web/src/components/Icon/Twitter.tsx b/web/src/components/Icon/Twitter.tsx new file mode 100644 index 0000000..5220409 --- /dev/null +++ b/web/src/components/Icon/Twitter.tsx @@ -0,0 +1,42 @@ +interface ITwitter { + className?: string + height?: string + width?: string +} + +const Twitter = ({ + className = '', + height = '44', + width = '44', +}: ITwitter): JSX.Element => { + return ( + + + + + + + + + + + ) +} + +export { Twitter } diff --git a/web/src/components/Icon/__snapshots__/Icon.test.tsx.snap b/web/src/components/Icon/__snapshots__/Icon.test.tsx.snap new file mode 100644 index 0000000..db53d0c --- /dev/null +++ b/web/src/components/Icon/__snapshots__/Icon.test.tsx.snap @@ -0,0 +1,1007 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Icon matches the archive icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the arrow icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the book icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the check icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + + + + + + + + +
+ , + "container":
+ + + + + + + + + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the eye icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the gear icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the github icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + + + + + + + + +
+ , + "container":
+ + + + + + + + + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the pen icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the question icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Icon matches the twitter icon snapshot 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + + + + + + + + +
+ , + "container":
+ + + + + + + + + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/web/src/components/Icon/index.ts b/web/src/components/Icon/index.ts new file mode 100644 index 0000000..d78603a --- /dev/null +++ b/web/src/components/Icon/index.ts @@ -0,0 +1 @@ +export { Icon } from './Icon' diff --git a/web/src/layouts/Admin/RolesLayout/RolesLayout.test.tsx b/web/src/layouts/Admin/RolesLayout/RolesLayout.test.tsx index a690c28..99df586 100644 --- a/web/src/layouts/Admin/RolesLayout/RolesLayout.test.tsx +++ b/web/src/layouts/Admin/RolesLayout/RolesLayout.test.tsx @@ -26,7 +26,7 @@ describe('RolesLayout', () => { it('Has link to add new role form', () => { renderComponent({ children }) - expect(screen.getByText('New Role')).toHaveAttribute( + expect(screen.getByText('Add Role')).toHaveAttribute( 'href', routes.adminNewRole() ) diff --git a/web/src/layouts/Admin/TeamsLayout/TeamsLayout.test.tsx b/web/src/layouts/Admin/TeamsLayout/TeamsLayout.test.tsx index 331869f..faaa5c2 100644 --- a/web/src/layouts/Admin/TeamsLayout/TeamsLayout.test.tsx +++ b/web/src/layouts/Admin/TeamsLayout/TeamsLayout.test.tsx @@ -26,7 +26,7 @@ describe('TeamsLayout', () => { it('Has link to add new team form', () => { renderComponent({ children }) - expect(screen.getByText('New Team')).toHaveAttribute( + expect(screen.getByText('Add Team')).toHaveAttribute( 'href', routes.adminNewTeam() ) diff --git a/web/src/layouts/Admin/UsersLayout/UsersLayout.test.tsx b/web/src/layouts/Admin/UsersLayout/UsersLayout.test.tsx index 7e36c11..c4fae57 100644 --- a/web/src/layouts/Admin/UsersLayout/UsersLayout.test.tsx +++ b/web/src/layouts/Admin/UsersLayout/UsersLayout.test.tsx @@ -26,7 +26,7 @@ describe('UsersLayout', () => { it('Has link to add new user form', () => { renderComponent({ children }) - expect(screen.getByText('New User')).toHaveAttribute( + expect(screen.getByText('Add User')).toHaveAttribute( 'href', routes.adminNewUser() ) From e0ea0e563b6ed8b878362337b8d4b7a57aba5a21 Mon Sep 17 00:00:00 2001 From: natalia Date: Fri, 7 Apr 2023 08:58:57 -0700 Subject: [PATCH 04/27] Tailwind Config change Admin Layouts style Avatar comp. add --- web/config/tailwind.config.js | 12 +++- web/src/components/Avatar/Avatar.mocks.ts | 5 ++ web/src/components/Avatar/Avatar.stories.tsx | 58 +++++++++++++++++++ web/src/components/Avatar/Avatar.test.tsx | 17 ++++++ web/src/components/Avatar/Avatar.tsx | 43 ++++++++++++++ web/src/components/Avatar/index.ts | 1 + web/src/index.css | 1 + .../layouts/Admin/RolesLayout/RolesLayout.tsx | 22 ++++--- .../layouts/Admin/TeamsLayout/TeamsLayout.tsx | 22 ++++--- .../layouts/Admin/UsersLayout/UsersLayout.tsx | 22 ++++--- 10 files changed, 178 insertions(+), 25 deletions(-) create mode 100644 web/src/components/Avatar/Avatar.mocks.ts create mode 100644 web/src/components/Avatar/Avatar.stories.tsx create mode 100644 web/src/components/Avatar/Avatar.test.tsx create mode 100644 web/src/components/Avatar/Avatar.tsx create mode 100644 web/src/components/Avatar/index.ts diff --git a/web/config/tailwind.config.js b/web/config/tailwind.config.js index baf368f..2952e36 100644 --- a/web/config/tailwind.config.js +++ b/web/config/tailwind.config.js @@ -2,7 +2,17 @@ module.exports = { content: ['src/**/*.{js,jsx,ts,tsx}'], theme: { - extend: {}, + extend: { + colors: { + rustyOrange: '#BC611E', + blackBean: '#371108', + grey: '#2E3747', + }, + fontFamily: { + sans: ['Jost', 'sans-serif'], + int: ['Inter', 'sans-serif'], + }, + }, }, plugins: [], } diff --git a/web/src/components/Avatar/Avatar.mocks.ts b/web/src/components/Avatar/Avatar.mocks.ts new file mode 100644 index 0000000..933944c --- /dev/null +++ b/web/src/components/Avatar/Avatar.mocks.ts @@ -0,0 +1,5 @@ +export const user = { + email: 'foobar@example.com', + name: 'Foo Bar', + nickname: 'Bar', +} diff --git a/web/src/components/Avatar/Avatar.stories.tsx b/web/src/components/Avatar/Avatar.stories.tsx new file mode 100644 index 0000000..362f7a0 --- /dev/null +++ b/web/src/components/Avatar/Avatar.stories.tsx @@ -0,0 +1,58 @@ +import { Avatar, IAvatar } from './Avatar' + +const Template = (args: IAvatar) => + +export const Primary = Template.bind({}) +Primary.args = { + user: { + email: 'foobar@example.com', + name: 'Foo Bar', + nickname: 'Bar', + }, +} + +export default { + title: 'Components/Avatar', + component: Avatar, + argTypes: { + className: { + name: 'className', + type: { name: 'string', required: false }, + description: 'CSS classes', + defaultValue: 'null', + control: { + type: 'text', + }, + table: { + type: { summary: 'string' }, + defaultValue: { summary: 'null' }, + }, + }, + height: { + name: 'height', + type: { name: 'number', required: false }, + description: 'avatar height in pixels', + defaultValue: 44, + control: { + type: 'number', + }, + table: { + type: { summary: 'number' }, + defaultValue: { summary: 44 }, + }, + }, + width: { + name: 'width', + type: { name: 'number', required: false }, + description: 'avatar width in pixels', + defaultValue: 44, + control: { + type: 'number', + }, + table: { + type: { summary: 'number' }, + defaultValue: { summary: 44 }, + }, + }, + }, +} diff --git a/web/src/components/Avatar/Avatar.test.tsx b/web/src/components/Avatar/Avatar.test.tsx new file mode 100644 index 0000000..167f478 --- /dev/null +++ b/web/src/components/Avatar/Avatar.test.tsx @@ -0,0 +1,17 @@ +import { render, screen } from '@redwoodjs/testing/web' + +import { Avatar } from './Avatar' +import { user } from './Avatar.mocks' + +describe('Avatar', () => { + it('renders successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) + + it('uses the correct alt text', () => { + render() + expect(screen.getByText(user.nickname)).toBeInTheDocument() + }) +}) diff --git a/web/src/components/Avatar/Avatar.tsx b/web/src/components/Avatar/Avatar.tsx new file mode 100644 index 0000000..1e877db --- /dev/null +++ b/web/src/components/Avatar/Avatar.tsx @@ -0,0 +1,43 @@ +export interface IAvatar { + className?: string + height?: number + user?: { + email?: string + name?: string + nickname?: string + } + width?: number +} + +const Avatar = ({ + className = '', + height = 44, + user, + width = 44, +}: IAvatar): JSX.Element => { + const displayName = (user) => user.nickname || user.name || user.email + return ( + <> +
+ + {displayName(user)} + + {displayName(user).charAt(0).toUpperCase()} + + +
+ + ) +} + +export { Avatar } diff --git a/web/src/components/Avatar/index.ts b/web/src/components/Avatar/index.ts new file mode 100644 index 0000000..2591a10 --- /dev/null +++ b/web/src/components/Avatar/index.ts @@ -0,0 +1 @@ +export { Avatar } from './Avatar' diff --git a/web/src/index.css b/web/src/index.css index 027a7cd..ef4c7a5 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -1,3 +1,4 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@500&family=Jost:wght@400;700&display=swap'); /** * START --- SETUP TAILWINDCSS EDIT * diff --git a/web/src/layouts/Admin/RolesLayout/RolesLayout.tsx b/web/src/layouts/Admin/RolesLayout/RolesLayout.tsx index 1d1459d..48ad42a 100644 --- a/web/src/layouts/Admin/RolesLayout/RolesLayout.tsx +++ b/web/src/layouts/Admin/RolesLayout/RolesLayout.tsx @@ -1,23 +1,29 @@ import { Link, routes } from '@redwoodjs/router' +import { Arrow } from 'src/components/Icon/Arrow' + type RoleLayoutProps = { children: React.ReactNode } const RolesLayout = ({ children }: RoleLayoutProps) => { return ( -
-
-

- +
+
+

+ Admin + + Roles

- -
+
New Role - +
+
+ Add Role +
+
-
{children}
+
{children}
) } diff --git a/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx b/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx index b451ab7..1d32d44 100644 --- a/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx +++ b/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx @@ -1,23 +1,29 @@ import { Link, routes } from '@redwoodjs/router' +import { Arrow } from 'src/components/Icon/Arrow' + type TeamLayoutProps = { children: React.ReactNode } const TeamsLayout = ({ children }: TeamLayoutProps) => { return ( -
-
-

- +
+
+

+ Admin + + Teams

- -
+
New Team - +
+
+ Add Team +
+
-
{children}
+
{children}
) } diff --git a/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx b/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx index 66eb5cb..115e616 100644 --- a/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx +++ b/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx @@ -1,23 +1,29 @@ import { Link, routes } from '@redwoodjs/router' +import { Arrow } from 'src/components/Icon/Arrow' + type UserLayoutProps = { children: React.ReactNode } const UsersLayout = ({ children }: UserLayoutProps) => { return ( -
-
-

- +
+
+

+ Admin + + Users

- -
+
New User - +
+
+ Add User +
+
-
{children}
+
{children}
) } From b986a5ca32f8cf98f3533e315dc61212b1104aa6 Mon Sep 17 00:00:00 2001 From: natalia Date: Fri, 7 Apr 2023 09:15:12 -0700 Subject: [PATCH 05/27] Authentication Pages style update --- web/src/components/ResetPassword/ResetPassword.tsx | 9 +++++---- web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx | 8 ++++---- web/src/pages/LoginPage/LoginPage.tsx | 6 +++--- web/src/pages/SignupPage/SignupPage.tsx | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/web/src/components/ResetPassword/ResetPassword.tsx b/web/src/components/ResetPassword/ResetPassword.tsx index 2ab5d95..394b01e 100644 --- a/web/src/components/ResetPassword/ResetPassword.tsx +++ b/web/src/components/ResetPassword/ResetPassword.tsx @@ -64,10 +64,12 @@ const ResetPassword = ({ resetToken, title, message }) => { <>
-
+
-

{title}

+

+ {title} +

IdEmail NameNicknamePronounsActiveTeamRoleEmailStatus AdminUpdated atCreated at  
{truncate(user.id)} + + {truncate(user.name)} + + {truncate( + user.memberships + ?.map((membership) => membership.team.name) + .join(', ') + )} + + {truncate( + user.memberships + ?.map((membership) => + membership.membershipRoles?.map((role) => role.role.name) + ) + .join(', ') + )} + {truncate(user.email)}{truncate(user.name)}{truncate(user.nickname)}{truncate(user.pronouns)}{checkboxInputTag('active', user.active)}{checkboxInputTag('admin', user.admin)}{timeTag(user.updatedAt)}{timeTag(user.createdAt)}{truncate(user.active) && 'Active'}{truncate(user.admin) && } -
-
diff --git a/web/src/layouts/Admin/AdminLayout/AdminLayout.tsx b/web/src/layouts/Admin/AdminLayout/AdminLayout.tsx index 137fc4b..62a3384 100644 --- a/web/src/layouts/Admin/AdminLayout/AdminLayout.tsx +++ b/web/src/layouts/Admin/AdminLayout/AdminLayout.tsx @@ -3,7 +3,7 @@ import { NavLink, routes } from '@redwoodjs/router' const AdminLayout = ({ children }) => { return (
-
+
{ return (
-

+

Admin diff --git a/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx b/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx index 0523f9d..161d5c0 100644 --- a/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx +++ b/web/src/layouts/Admin/TeamsLayout/TeamsLayout.tsx @@ -11,7 +11,7 @@ const TeamsLayout = ({ children }: TeamLayoutProps) => { return (
-

+

Admin diff --git a/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx b/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx index 02bc1c9..3948994 100644 --- a/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx +++ b/web/src/layouts/Admin/UsersLayout/UsersLayout.tsx @@ -11,7 +11,7 @@ const UsersLayout = ({ children }: UserLayoutProps) => { return (
-

+

Admin From 4682f4d1fe63ad906e960353059ea03a46edb642 Mon Sep 17 00:00:00 2001 From: natalia Date: Wed, 12 Apr 2023 16:02:40 -0700 Subject: [PATCH 16/27] Edit Profile Component style change Routes Update Teams user style change --- web/src/Routes.tsx | 9 +++------ web/src/components/Admin/Team/Teams/Teams.tsx | 19 +++++++++++------- .../Navigation/MobileMenu/MobileMenu.test.tsx | 4 ++-- .../Profile/EditEmail/EditEmail.tsx | 10 +++------- .../Profile/EditEmail/EditEmailForm.tsx | 13 +++++++----- .../Profile/EditPassword/EditPassword.tsx | 10 +++------- .../Profile/EditPassword/EditPasswordForm.tsx | 15 ++++++++------ .../Profile/EditProfile/EditProfile.tsx | 15 ++++++++------ .../EditProfileCell/EditProfileCell.tsx | 20 ++++++++----------- 9 files changed, 57 insertions(+), 58 deletions(-) diff --git a/web/src/Routes.tsx b/web/src/Routes.tsx index 871a24c..dc16047 100644 --- a/web/src/Routes.tsx +++ b/web/src/Routes.tsx @@ -7,7 +7,6 @@ import { RolesLayout } from './layouts/Admin/RolesLayout' import { TeamsLayout } from './layouts/Admin/TeamsLayout' import { UsersLayout } from './layouts/Admin/UsersLayout' import { MainLayout } from './layouts/MainLayout/MainLayout' -import { ProfileLayout } from './layouts/ProfileLayout' const Routes = () => { return ( @@ -26,11 +25,9 @@ const Routes = () => { - - - - - + + + diff --git a/web/src/components/Admin/Team/Teams/Teams.tsx b/web/src/components/Admin/Team/Teams/Teams.tsx index 4a353c9..e22c574 100644 --- a/web/src/components/Admin/Team/Teams/Teams.tsx +++ b/web/src/components/Admin/Team/Teams/Teams.tsx @@ -67,13 +67,18 @@ const TeamsList = ({ teams }) => { {truncate(team.name)} - {team.memberships?.map((membership) => { - return ( -
- -
- ) - })} +
+ {team.memberships?.map((membership) => { + return ( +
+ +
+ ) + })} +
{truncate(team.active) && 'Active'} diff --git a/web/src/components/Navigation/MobileMenu/MobileMenu.test.tsx b/web/src/components/Navigation/MobileMenu/MobileMenu.test.tsx index dd137c6..40a33c6 100644 --- a/web/src/components/Navigation/MobileMenu/MobileMenu.test.tsx +++ b/web/src/components/Navigation/MobileMenu/MobileMenu.test.tsx @@ -6,14 +6,14 @@ import { MobileMenu } from './MobileMenu' describe('Navigation', () => { const mock = jest.fn() - it('renders navigation component', () => { + it('renders mobile navigation component', () => { mockCurrentUser({ id: 'foobar', name: 'FooBar' }) render() expect(screen.getByTestId('mobileMenu')).toBeInTheDocument() }) - it('renders navigation component', () => { + it('renders mobile navigation component', () => { mockCurrentUser({ id: 'foobar', name: 'FooBar' }) render() const profile = screen.getByText('My Profile') diff --git a/web/src/components/Profile/EditEmail/EditEmail.tsx b/web/src/components/Profile/EditEmail/EditEmail.tsx index 27f246b..9b5b379 100644 --- a/web/src/components/Profile/EditEmail/EditEmail.tsx +++ b/web/src/components/Profile/EditEmail/EditEmail.tsx @@ -41,13 +41,9 @@ const EditEmail = ({ profile }) => { profile?.nickname || profile?.name || profile?.email } | Edit Email`} /> -
-
-

Edit Email

-
-
- -
+
+

Edit Email

+
) diff --git a/web/src/components/Profile/EditEmail/EditEmailForm.tsx b/web/src/components/Profile/EditEmail/EditEmailForm.tsx index f722291..964a61f 100644 --- a/web/src/components/Profile/EditEmail/EditEmailForm.tsx +++ b/web/src/components/Profile/EditEmail/EditEmailForm.tsx @@ -18,7 +18,7 @@ const EditEmailForm = ({ error, loading, onSubmit }) => { newEmailRef.current = formMethods.watch('newEmail', '') return ( -
+
{