From 73412e309905ff21683ae7be1438ade5abdbfd4c Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Thu, 16 Jan 2025 09:31:55 -0700 Subject: [PATCH 1/8] feat: add Accordion component --- .../src/Accordion/Accordion.stories.tsx | 14 +++++++ .../src/Accordion/Accordion.test.tsx | 17 ++++++++ .../components/src/Accordion/Accordion.tsx | 41 +++++++++++++++++++ packages/components/src/index.ts | 1 + 4 files changed, 73 insertions(+) create mode 100644 packages/components/src/Accordion/Accordion.stories.tsx create mode 100644 packages/components/src/Accordion/Accordion.test.tsx create mode 100644 packages/components/src/Accordion/Accordion.tsx diff --git a/packages/components/src/Accordion/Accordion.stories.tsx b/packages/components/src/Accordion/Accordion.stories.tsx new file mode 100644 index 0000000000..e008889174 --- /dev/null +++ b/packages/components/src/Accordion/Accordion.stories.tsx @@ -0,0 +1,14 @@ +import type { StoryFn, Meta } from '@storybook/react' +import { Accordion } from './Accordion' +import { Text } from 'theme-ui' + +export default { + title: 'Components/Accordion', + component: Accordion, +} as Meta + +export const Default: StoryFn = () => ( + + Default + +) diff --git a/packages/components/src/Accordion/Accordion.test.tsx b/packages/components/src/Accordion/Accordion.test.tsx new file mode 100644 index 0000000000..296c580e8b --- /dev/null +++ b/packages/components/src/Accordion/Accordion.test.tsx @@ -0,0 +1,17 @@ +import '@testing-library/jest-dom/vitest' + +import { describe, expect, it } from 'vitest' + +import { render } from '../test/utils' +import { Default } from './Accordion.stories' +import type { IProps } from './Accordion' + +describe('Accordion', () => { + it('validates the component behaviour', () => { + const { getByText } = render( + , + ) + + expect(getByText('Accordion')).toBeInTheDocument(); + }) +}) diff --git a/packages/components/src/Accordion/Accordion.tsx b/packages/components/src/Accordion/Accordion.tsx new file mode 100644 index 0000000000..546e7843aa --- /dev/null +++ b/packages/components/src/Accordion/Accordion.tsx @@ -0,0 +1,41 @@ +import { useState } from 'react' +import { Flex, Heading, Text, ThemeUIStyleObject } from 'theme-ui' +import { Icon } from '../Icon/Icon' + +export interface IProps { + children: React.ReactNode + sx?: ThemeUIStyleObject | undefined + title: string + subtitle?: string +} + +export const Accordion = (props: IProps) => { + const [isExpanded, setIsExpanded] = useState(false) + const { children, sx, title, subtitle } = props + + return ( + + setIsExpanded(!isExpanded)} + > + + {title} + + + + + {subtitle != undefined && ( + {subtitle} + )} + {isExpanded && children} + + ) +} diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 5f5672db07..12e3572105 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -76,3 +76,4 @@ export { VideoPlayer } from './VideoPlayer/VideoPlayer' export { CommentAvatar } from './CommentAvatar/CommentAvatar' export type { availableGlyphs } from './Icon/types' export type { ITab } from './SettingsFormWrapper/SettingsFormTab' +export { Accordion } from './Accordion/Accordion' From 6d7b3f90b0d2c25b32dc41a08dd97e6466a29a40 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Thu, 16 Jan 2025 09:32:53 -0700 Subject: [PATCH 2/8] feat: use Accordion component in ChangeEmail form --- .../content/sections/ChangeEmail.form.tsx | 40 ++++--------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx index 32f16c1d27..c58651de77 100644 --- a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx @@ -1,12 +1,12 @@ import { useEffect, useState } from 'react' import { Field, Form } from 'react-final-form' -import { Button, FieldInput, Icon } from 'oa-components' +import { Button, FieldInput, Accordion } from 'oa-components' import { PasswordField } from 'src/common/Form/PasswordField' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { FormFieldWrapper } from 'src/pages/Library/Content/Common' import { UserContactError } from 'src/pages/User/contact/UserContactError' -import { buttons, fields, headings } from 'src/pages/UserSettings/labels' -import { Flex, Heading, Text } from 'theme-ui' +import { buttons, fields } from 'src/pages/UserSettings/labels' +import { Flex } from 'theme-ui' import type { SubmitResults } from 'src/pages/User/contact/UserContactError' @@ -16,13 +16,11 @@ interface IFormValues { } export const ChangeEmailForm = () => { - const [isExpanded, setIsExpanded] = useState(false) const [submitResults, setSubmitResults] = useState(null) const [currentEmail, setCurrentEmail] = useState(null) const { userStore } = useCommonStores().stores const formId = 'changeEmail' - const glyph = isExpanded ? 'arrow-full-up' : 'arrow-full-down' useEffect(() => { getUserEmail() @@ -36,7 +34,6 @@ export const ChangeEmailForm = () => { type: 'success', message: `Email changed to ${newEmail}. You've been sent two emails now(!) One to your old email address to check this was you and the other to your new address to verify it.`, }) - setIsExpanded(false) getUserEmail() } catch (error) { setSubmitResults({ type: 'error', message: error.message }) @@ -54,8 +51,10 @@ export const ChangeEmailForm = () => { sx={{ flexDirection: 'column', gap: 2 }} > - - {isExpanded && currentEmail && ( +
{ data-cy="changeEmailForm" sx={{ flexDirection: 'column', gap: 2 }} > - - {headings.changeEmail} - - - - {fields.email.title}: {currentEmail} - - { ) }} /> - )} - - + ) } From df7ece4aa61bf94cfb15e8185af914299eed251d Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Thu, 16 Jan 2025 09:33:06 -0700 Subject: [PATCH 3/8] feat: use Accordion component in ChangePassword form --- .../content/sections/ChangePassword.form.tsx | 32 +++---------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/src/pages/UserSettings/content/sections/ChangePassword.form.tsx b/src/pages/UserSettings/content/sections/ChangePassword.form.tsx index 5bab867ecf..19b165ac07 100644 --- a/src/pages/UserSettings/content/sections/ChangePassword.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangePassword.form.tsx @@ -1,12 +1,12 @@ import { useState } from 'react' import { Form } from 'react-final-form' -import { Button, FieldInput, Icon } from 'oa-components' +import { Accordion, Button, FieldInput } from 'oa-components' import { PasswordField } from 'src/common/Form/PasswordField' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { FormFieldWrapper } from 'src/pages/Library/Content/Common' import { UserContactError } from 'src/pages/User/contact/UserContactError' -import { buttons, fields, headings } from 'src/pages/UserSettings/labels' -import { Flex, Heading, Text } from 'theme-ui' +import { buttons, fields } from 'src/pages/UserSettings/labels' +import { Flex } from 'theme-ui' import type { SubmitResults } from 'src/pages/User/contact/UserContactError' @@ -17,12 +17,10 @@ interface IFormValues { } export const ChangePasswordForm = () => { - const [isExpanded, setIsExpanded] = useState(false) const [submitResults, setSubmitResults] = useState(null) const { userStore } = useCommonStores().stores const formId = 'changePassword' - const glyph = isExpanded ? 'arrow-full-up' : 'arrow-full-down' const onSubmit = async (values: IFormValues) => { const { oldPassword, newPassword } = values @@ -33,7 +31,6 @@ export const ChangePasswordForm = () => { type: 'success', message: `Password changed.`, }) - setIsExpanded(false) } catch (error) { setSubmitResults({ type: 'error', message: error.message }) } @@ -46,7 +43,7 @@ export const ChangePasswordForm = () => { > - {isExpanded && ( + { data-cy="changePasswordForm" sx={{ flexDirection: 'column', gap: 1 }} > - - {headings.changePassword} - - { ) }} /> - )} - - + ) } From 4606c1be9a27add15fd3d64488777226bd460922 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Mon, 20 Jan 2025 14:35:09 -0700 Subject: [PATCH 4/8] fix: failing spec updated for accordion component --- packages/components/src/Accordion/Accordion.stories.tsx | 6 ++++-- packages/components/src/Accordion/Accordion.test.tsx | 7 +++---- packages/components/src/Accordion/Accordion.tsx | 9 ++++++--- packages/cypress/src/integration/SignUp.spec.ts | 1 + .../UserSettings/content/sections/ChangeEmail.form.tsx | 3 ++- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/components/src/Accordion/Accordion.stories.tsx b/packages/components/src/Accordion/Accordion.stories.tsx index e008889174..b0e3c44f12 100644 --- a/packages/components/src/Accordion/Accordion.stories.tsx +++ b/packages/components/src/Accordion/Accordion.stories.tsx @@ -1,7 +1,9 @@ -import type { StoryFn, Meta } from '@storybook/react' -import { Accordion } from './Accordion' import { Text } from 'theme-ui' +import { Accordion } from './Accordion' + +import type { Meta, StoryFn } from '@storybook/react' + export default { title: 'Components/Accordion', component: Accordion, diff --git a/packages/components/src/Accordion/Accordion.test.tsx b/packages/components/src/Accordion/Accordion.test.tsx index 296c580e8b..de5f5fb1c7 100644 --- a/packages/components/src/Accordion/Accordion.test.tsx +++ b/packages/components/src/Accordion/Accordion.test.tsx @@ -4,14 +4,13 @@ import { describe, expect, it } from 'vitest' import { render } from '../test/utils' import { Default } from './Accordion.stories' + import type { IProps } from './Accordion' describe('Accordion', () => { it('validates the component behaviour', () => { - const { getByText } = render( - , - ) + const { getByText } = render() - expect(getByText('Accordion')).toBeInTheDocument(); + expect(getByText('Accordion')).toBeInTheDocument() }) }) diff --git a/packages/components/src/Accordion/Accordion.tsx b/packages/components/src/Accordion/Accordion.tsx index 546e7843aa..7797c049ed 100644 --- a/packages/components/src/Accordion/Accordion.tsx +++ b/packages/components/src/Accordion/Accordion.tsx @@ -1,7 +1,10 @@ import { useState } from 'react' -import { Flex, Heading, Text, ThemeUIStyleObject } from 'theme-ui' +import { Flex, Heading, Text } from 'theme-ui' + import { Icon } from '../Icon/Icon' +import type { ThemeUIStyleObject } from 'theme-ui' + export interface IProps { children: React.ReactNode sx?: ThemeUIStyleObject | undefined @@ -15,8 +18,8 @@ export const Accordion = (props: IProps) => { return ( { cy.get('[data-cy="tab-Account"]').click() cy.step('Update Email') + cy.get('[data-cy=changeEmailAccordion]').click() cy.get('[data-cy="changeEmailButton"]').click() cy.get('[data-cy="changeEmailForm"]') .contains(`Current email address: ${email}`) diff --git a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx index c58651de77..c95332e05f 100644 --- a/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx +++ b/src/pages/UserSettings/content/sections/ChangeEmail.form.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' import { Field, Form } from 'react-final-form' -import { Button, FieldInput, Accordion } from 'oa-components' +import { Accordion, Button, FieldInput } from 'oa-components' import { PasswordField } from 'src/common/Form/PasswordField' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { FormFieldWrapper } from 'src/pages/Library/Content/Common' @@ -52,6 +52,7 @@ export const ChangeEmailForm = () => { > From e0a4d7727207d5cb83a0eba27d937a68f7187ae7 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Fri, 24 Jan 2025 12:40:55 -0700 Subject: [PATCH 5/8] fix: remove redundant data-cy attributes --- src/pages/User/content/UserProfile.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/pages/User/content/UserProfile.tsx b/src/pages/User/content/UserProfile.tsx index 1b625ebee1..3d8818ec15 100644 --- a/src/pages/User/content/UserProfile.tsx +++ b/src/pages/User/content/UserProfile.tsx @@ -63,17 +63,9 @@ export const UserProfile = observer( {() => ( <> {showMemberProfile ? ( - + ) : ( - + )} )} From c076628a184bfae33af1c4f9f3636ff6f34aef16 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Fri, 24 Jan 2025 12:43:25 -0700 Subject: [PATCH 6/8] feat: add wrapper Flex component to SpaceProfile --- src/pages/User/content/SpaceProfile.tsx | 338 ++++++++++++------------ 1 file changed, 171 insertions(+), 167 deletions(-) diff --git a/src/pages/User/content/SpaceProfile.tsx b/src/pages/User/content/SpaceProfile.tsx index 05c00c13ab..28732c16cc 100644 --- a/src/pages/User/content/SpaceProfile.tsx +++ b/src/pages/User/content/SpaceProfile.tsx @@ -82,135 +82,150 @@ export const SpaceProfile = ({ user, docs }: IProps) => { const defaultValue = useLocationHook?.hash?.slice(1) || 'profile' return ( - - - {coverImage.length ? ( - - ) : ( - - - No images available. - - - )} - - - - - - - - - - - - - + + + + {coverImage.length ? ( + + ) : ( + + + No images available. + + + )} + + + + + + + + + + + + + + - - - {userImage?.downloadUrl && ( - - )} - - - - {displayName} - + + {userImage?.downloadUrl && ( + + )} + + + + {displayName} + + - - + - - - Profile - {hasContributed && ( - - Contributions + + + Profile + {hasContributed && ( + + Contributions + + )} + {hasImpacted && isPreciousPlastic() && ( + + {heading} + + )} + + Contact - )} - {hasImpacted && isPreciousPlastic() && ( - - {heading} - - )} - - Contact - - - - - + + + - {tags && } - {about && {about}} - - - + {tags && } + {about && {about}} + + + + } + > { libraryCount={docs?.library.length || 0} usefulCount={user.totalUseful || 0} researchCount={docs?.research.length || 0} - totalViews={0} + totalViews={user.total_views || 0} /> - } - > - - - - - - - {hasContributed && ( - - + + + + - )} - {hasImpacted && isPreciousPlastic() && ( - - + {hasContributed && ( + + + + )} + {hasImpacted && isPreciousPlastic() && ( + + + + )} + + + }> + {() => ( + } + loggedOut={ + + } + /> + )} + + + - )} - - - }> - {() => ( - } - loggedOut={ - - } - /> - )} - - - - - - - - + + + + + ) } From a99790d8cc6bf6eaeb761c131ea459f9ef001dfd Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Fri, 24 Jan 2025 15:18:15 -0700 Subject: [PATCH 7/8] feat: remove MemberProfile component from UserProfile --- src/pages/User/content/UserProfile.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/User/content/UserProfile.tsx b/src/pages/User/content/UserProfile.tsx index 3d8818ec15..cd4068358b 100644 --- a/src/pages/User/content/UserProfile.tsx +++ b/src/pages/User/content/UserProfile.tsx @@ -7,7 +7,6 @@ import { ClientOnly } from 'remix-utils/client-only' import { useCommonStores } from 'src/common/hooks/useCommonStores' import { Flex } from 'theme-ui' -import { MemberProfile } from './MemberProfile' import { SpaceProfile } from './SpaceProfile' import type { IUserDB } from 'oa-shared' @@ -62,11 +61,11 @@ export const UserProfile = observer( }> {() => ( <> - {showMemberProfile ? ( - - ) : ( - - )} + )} From 9ae667c5480521c069bc9e99614e245e3ab30810 Mon Sep 17 00:00:00 2001 From: CrowsVeldt Date: Fri, 24 Jan 2025 15:19:20 -0700 Subject: [PATCH 8/8] feat: display member profiles with SpaceProfile component --- src/pages/User/content/SpaceProfile.tsx | 139 +++++++++++++++--------- 1 file changed, 90 insertions(+), 49 deletions(-) diff --git a/src/pages/User/content/SpaceProfile.tsx b/src/pages/User/content/SpaceProfile.tsx index 28732c16cc..c232bf655d 100644 --- a/src/pages/User/content/SpaceProfile.tsx +++ b/src/pages/User/content/SpaceProfile.tsx @@ -9,9 +9,10 @@ import { Username, UserStatistics, } from 'oa-components' -import { ExternalLinkLabel, UserRole } from 'oa-shared' +import { ExternalLinkLabel, ProfileTypeList, UserRole } from 'oa-shared' // eslint-disable-next-line import/no-unresolved import { ClientOnly } from 'remix-utils/client-only' +import DefaultMemberImage from 'src/assets/images/default_member.svg' import { AuthWrapper } from 'src/common/AuthWrapper' import { ProfileTags } from 'src/common/ProfileTags' import { UserAction } from 'src/common/UserAction' @@ -36,12 +37,13 @@ import { heading } from '../impact/labels' import UserContactAndLinks from './UserContactAndLinks' import UserCreatedDocuments from './UserCreatedDocuments' -import type { IUser } from 'oa-shared' +import type { IUser, ProfileTypeName } from 'oa-shared' import type { UserCreatedDocs } from '../types' interface IProps { user: IUser docs: UserCreatedDocs + type: ProfileTypeName } const getCoverImages = (user: IUser) => { @@ -52,7 +54,7 @@ const getCoverImages = (user: IUser) => { return [] } -export const SpaceProfile = ({ user, docs }: IProps) => { +export const SpaceProfile = ({ user, docs, type }: IProps) => { const { about, displayName, @@ -81,64 +83,90 @@ export const SpaceProfile = ({ user, docs }: IProps) => { const defaultValue = useLocationHook?.hash?.slice(1) || 'profile' + const profileImageSrc = userImage?.downloadUrl + ? cdnImageUrl(userImage.downloadUrl) + : DefaultMemberImage + return ( - - - - {coverImage.length ? ( - - ) : ( - - - No images available. - - - )} - + + {type === ProfileTypeList.MEMBER && ( + + )} + + {type === ProfileTypeList.SPACE && ( + + {coverImage.length ? ( + + ) : ( + + + No images available. + + + )} + + )} - - - - - - + {type === ProfileTypeList.SPACE && ( + + + + + + + + + + - - - - - + )} - {userImage?.downloadUrl && ( + {userImage?.downloadUrl && type === ProfileTypeList.SPACE && ( { }} /> )} + + {type === ProfileTypeList.MEMBER && ( + + )}