Skip to content

Commit

Permalink
feat: add profile tags to app
Browse files Browse the repository at this point in the history
  • Loading branch information
benfurber committed Oct 23, 2024
1 parent 65856b3 commit bfb0808
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 146 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ProfileTagsList } from './ProfileTagsList'

import type { Meta, StoryFn } from '@storybook/react'

export default {
title: 'Components/ProfileTagsList',
component: ProfileTagsList,
} as Meta<typeof ProfileTagsList>

export const Default: StoryFn<typeof ProfileTagsList> = () => (
<ProfileTagsList
tagIds={{ uCzWZbz3aVKyx2keoqRi: true, J3LF7fMsDfniYT2ZX3rf: false }}
/>
)
19 changes: 19 additions & 0 deletions packages/components/src/ProfileTagsList/ProfileTagsList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import '@testing-library/jest-dom/vitest'

import { describe, expect, it } from 'vitest'

import { render } from '../test/utils'
import { Default } from './ProfileTagsList.stories'

import type { IProps } from './ProfileTagsList'

describe('ProfileTagsList', () => {
it('validates the component behaviour', () => {
const { getByText, queryByText } = render(
<Default {...(Default.args as IProps)} />,
)

expect(getByText('Electronics')).toBeInTheDocument()
expect(queryByText('Machining')).toBeNull()
})
})
32 changes: 32 additions & 0 deletions packages/components/src/ProfileTagsList/ProfileTagsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { profileTags } from 'oa-shared'
import { Flex } from 'theme-ui'

import { Category } from '../Category/Category'

import type { ISelectedTags } from 'oa-shared'

export interface IProps {
tagIds: ISelectedTags
}

export const ProfileTagsList = ({ tagIds }: IProps) => {
const selectedTagIds = Object.keys(tagIds).filter((id) => tagIds[id] === true)
const tags = selectedTagIds
.map((id) => profileTags.find(({ _id }) => id === _id))
.filter((tag) => tag !== undefined)

if (tags.length === 0) {
return null
}

return (
<Flex sx={{ gap: 2 }}>
{tags.map(
(tag, index) =>
tag?.label && (
<Category key={index} category={{ label: tag.label }} />
),
)}
</Flex>
)
}
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export { NotificationList } from './NotificationList/NotificationList'
export { OsmGeocoding } from './OsmGeocoding/OsmGeocoding'
export { PinProfile } from './PinProfile/PinProfile'
export { ProfileLink } from './ProfileLink/ProfileLink'
export { ProfileTagsList } from './ProfileTagsList/ProfileTagsList'
export { ResearchEditorOverview } from './ResearchEditorOverview/ResearchEditorOverview'
export { SearchField } from './SearchField/SearchField'
export { Select } from './Select/Select'
Expand Down
12 changes: 7 additions & 5 deletions packages/cypress/src/integration/settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ describe('[Settings]', () => {
const coverImage = 'profile-cover-2-edited'
const description = "We're mechanics and our jobs are making machines"
const displayName = 'machine_builder_pro'
const machineBuilderXp = ['electronics', 'welding']
const machineBuilderXp = ['Electronics', 'Welding']
const mapPinDescription = 'Informative workshop on machines every week'
const profileType = 'machine-builder'
const user = generateNewUserDetails()
Expand All @@ -272,17 +272,17 @@ describe('[Settings]', () => {
cy.get('[data-cy=save]').click()
cy.get('[data-cy=errors-container]').should('be.visible')

cy.step('Set profile tags')
cy.selectTag(machineBuilderXp[0], '[data-cy=tag-select]')
cy.selectTag(machineBuilderXp[1], '[data-cy=tag-select]')

cy.step('Populate profile')
cy.setSettingBasicUserInfo({
displayName,
description,
})
cy.setSettingImage(coverImage, 'coverImages-0')

cy.step('Choose Expertise')
cy.get(`[data-cy=${machineBuilderXp[0]}]`).click()
cy.get(`[data-cy=${machineBuilderXp[1]}]`).click()

cy.setSettingAddContactLink({
index: 0,
label: ExternalLinkLabel.BAZAR,
Expand All @@ -298,6 +298,8 @@ describe('[Settings]', () => {
cy.contains(user.username)
cy.contains(displayName)
cy.contains(description)
cy.contains(machineBuilderXp[0])
cy.contains(machineBuilderXp[1])
cy.get(`[data-cy=MemberBadge-${profileType}]`)
cy.get('[data-cy="active-image"]')
.should('have.attr', 'src')
Expand Down
13 changes: 8 additions & 5 deletions src/common/Tags/TagsSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface IProps extends Partial<FieldRenderProps<any, any>> {
onChange: (val: ISelectedTags) => void
styleVariant?: 'selector' | 'filter'
placeholder?: string
tagsSource?: ITag[]
}
interface IState {
selectedTags: string[]
Expand All @@ -22,6 +23,8 @@ interface IState {
const TagsSelect = (props: IProps) => {
const { tagsStore } = useCommonStores().stores
const { allTags } = tagsStore

const allTagsData = props.tagsSource ? props.tagsSource : allTags
const [state, setState] = useState<IState>({ selectedTags: [] })

// if we initialise with a value we want to update the state to reflect the selected tags
Expand All @@ -42,8 +45,8 @@ const TagsSelect = (props: IProps) => {

// as react-select can't keep track of which object key corresponds to the selected
// value include manual lookup so that value can also be passed from props
const _getSelected = (allTags: ITag[]) => {
return allTags?.filter((tag) => state.selectedTags.includes(tag._id))
const _getSelected = (allTagsData: ITag[]) => {
return allTagsData?.filter((tag) => state.selectedTags.includes(tag._id))
}

// whilst we deal with arrays of selected tag ids in the component we want to store as a json map
Expand All @@ -58,15 +61,15 @@ const TagsSelect = (props: IProps) => {
return (
<FieldContainer
// provide a data attribute that can be used to see if tags populated
data-cy={allTags?.length > 0 ? 'tag-select' : 'tag-select-empty'}
data-cy={allTagsData?.length > 0 ? 'tag-select' : 'tag-select-empty'}
>
<Select
variant={props.isForm ? 'form' : undefined}
options={allTags}
options={allTagsData}
placeholder={props.placeholder}
isClearable={true}
isMulti={true}
value={_getSelected(allTags)}
value={_getSelected(allTagsData)}
getOptionLabel={(tag: ITag) => tag.label}
getOptionValue={(tag: ITag) => tag._id}
onChange={(values) => onSelectedTagsChanged(values as ITag[])}
Expand Down
39 changes: 4 additions & 35 deletions src/pages/User/content/SpaceProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
ImageGallery,
MemberBadge,
ProfileTagsList,
Tab,
TabPanel,
Tabs,
Expand Down Expand Up @@ -39,12 +40,7 @@ import { heading } from '../impact/labels'
import UserContactAndLinks from './UserContactAndLinks'
import UserCreatedDocuments from './UserCreatedDocuments'

import type {
IMAchineBuilderXp,
IOpeningHours,
IUser,
PlasticTypeLabel,
} from 'oa-shared'
import type { IOpeningHours, IUser, PlasticTypeLabel } from 'oa-shared'
import type { UserCreatedDocs } from '../types'

interface IProps {
Expand Down Expand Up @@ -111,31 +107,6 @@ const renderOpeningHours = (openingHours: IOpeningHours[]) => (
</div>
)

const renderMachineBuilderXp = (machineBuilderXp: IMAchineBuilderXp[]) => (
<>
<h4>We offer the following services:</h4>
{machineBuilderXp.map((machineExperience, index) => {
return (
<Box
sx={{
backgroundColor: 'background',
borderColor: 'background',
borderRadius: '5px',
borderStyle: 'solid',
borderWidth: '1px',
display: 'inline-block',
marginRight: '10px',
padding: '10px',
}}
key={`machineXp-${index}`}
>
{machineExperience.label}
</Box>
)
})}
</>
)

const getCoverImages = (user: IUser) => {
if (user.coverImages && user.coverImages.length) {
return user.coverImages
Expand All @@ -152,6 +123,7 @@ export const SpaceProfile = ({ user, docs }: IProps) => {
links,
location,
profileType,
tags,
userName,
userImage,
} = user
Expand Down Expand Up @@ -275,6 +247,7 @@ export const SpaceProfile = ({ user, docs }: IProps) => {
width: ['100%', '100%', '80%'],
}}
>
{tags && <ProfileTagsList tagIds={tags} />}
{about && <Paragraph>{about}</Paragraph>}

{profileType === ProfileTypeList.COLLECTION_POINT &&
Expand All @@ -284,10 +257,6 @@ export const SpaceProfile = ({ user, docs }: IProps) => {
{profileType === ProfileTypeList.COLLECTION_POINT &&
user.openingHours &&
renderOpeningHours(user.openingHours)}

{profileType === ProfileTypeList.MACHINE_BUILDER &&
user.machineBuilderXp &&
renderMachineBuilderXp(user.machineBuilderXp)}
</Box>
<Box
sx={{
Expand Down
15 changes: 9 additions & 6 deletions src/pages/UserSettings/SettingsPageUserProfile.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ vi.mock('src/common/hooks/useCommonStores', () => ({
userStore: {
activeUser: mockUser,
},
tagsStore: {
allTags: [],
},
},
}),
}))
Expand All @@ -39,7 +42,7 @@ describe('UserSettings', () => {
expect(wrapper.queryByTestId('workspaceType')).toBeNull()
expect(wrapper.queryByTestId('CollectionSection')).toBeNull()
expect(wrapper.queryByTestId('CollectionSection')).toBeNull()
expect(wrapper.queryByTestId('ExpertiseSection')).toBeNull()
expect(wrapper.queryByTestId('ProfileTags')).toBeNull()
expect(wrapper.getAllByTestId('UserInfosSection')).toHaveLength(1)
expect(wrapper.queryByTestId('PublicContactSection')).toBeNull()
expect(wrapper.getAllByTestId('userImage')).toHaveLength(1)
Expand All @@ -58,7 +61,7 @@ describe('UserSettings', () => {
await waitFor(() => {
expect(wrapper.queryByTestId('workspaceType')).toBeNull()
expect(wrapper.getAllByTestId('CollectionSection')).toHaveLength(1)
expect(wrapper.queryByTestId('ExpertiseSection')).toBeNull()
expect(wrapper.queryByTestId('ProfileTags')).toBeNull()
expect(wrapper.getAllByTestId('UserInfosSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('PublicContactSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('userImage')).toHaveLength(1)
Expand All @@ -77,7 +80,7 @@ describe('UserSettings', () => {
await waitFor(() => {
expect(wrapper.queryByTestId('workspaceType')).toBeNull()
expect(wrapper.queryByTestId('CollectionSection')).toBeNull()
expect(wrapper.queryByTestId('ExpertiseSection')).toBeNull()
expect(wrapper.queryByTestId('ProfileTags')).toBeNull()
expect(wrapper.getAllByTestId('UserInfosSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('PublicContactSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('userImage')).toHaveLength(1)
Expand All @@ -96,7 +99,7 @@ describe('UserSettings', () => {
await waitFor(() => {
expect(wrapper.queryByTestId('workspaceType')).toBeNull()
expect(wrapper.queryByTestId('CollectionSection')).toBeNull()
expect(wrapper.getAllByTestId('ExpertiseSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('ProfileTags')).toHaveLength(1)
expect(wrapper.getAllByTestId('UserInfosSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('PublicContactSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('userImage')).toHaveLength(1)
Expand All @@ -115,7 +118,7 @@ describe('UserSettings', () => {
await waitFor(() => {
expect(wrapper.queryByTestId('workspaceType')).toBeNull()
expect(wrapper.queryByTestId('CollectionSection')).toBeNull()
expect(wrapper.queryByTestId('ExpertiseSection')).toBeNull()
expect(wrapper.queryByTestId('ProfileTags')).toBeNull()
expect(wrapper.getAllByTestId('UserInfosSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('PublicContactSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('userImage')).toHaveLength(1)
Expand All @@ -134,7 +137,7 @@ describe('UserSettings', () => {
await waitFor(() => {
expect(wrapper.getAllByTestId('workspaceType')).toHaveLength(1)
expect(wrapper.queryByTestId('CollectionSection')).toBeNull()
expect(wrapper.queryByTestId('ExpertiseSection')).toBeNull()
expect(wrapper.queryByTestId('ProfileTags')).toBeNull()
expect(wrapper.getAllByTestId('UserInfosSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('PublicContactSection')).toHaveLength(1)
expect(wrapper.getAllByTestId('userImage')).toHaveLength(1)
Expand Down
12 changes: 3 additions & 9 deletions src/pages/UserSettings/SettingsPageUserProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { Flex } from 'theme-ui'
import { v4 as uuid } from 'uuid'

import { CollectionSection } from './content/sections/Collection.section'
import { ExpertiseSection } from './content/sections/Expertise.section'
import { FocusSection } from './content/sections/Focus.section'
import { ProfileTags } from './content/sections/ProfileTags.section'
import { PublicContactSection } from './content/sections/PublicContact.section'
import { UserImagesSection } from './content/sections/UserImages.section'
import { UserInfosSection } from './content/sections/UserInfos.section'
Expand Down Expand Up @@ -106,11 +106,11 @@ export const SettingsPageUserProfile = () => {
openingHours: user.openingHours || [{}],
workspaceType: user.workspaceType || null,
collectedPlasticTypes: user.collectedPlasticTypes || null,
machineBuilderXp: user.machineBuilderXp || null,
isContactableByPublic:
user.isContactableByPublic || DEFAULT_PUBLIC_CONTACT_PREFERENCE,
userImage: user.userImage || null,
coverImages,
tags: user.tags || [],
}

const formId = 'userProfileForm'
Expand Down Expand Up @@ -167,13 +167,7 @@ export const SettingsPageUserProfile = () => {
)}

{values.profileType === ProfileTypeList.MACHINE_BUILDER && (
<ExpertiseSection
required={
values.machineBuilderXp
? values.machineBuilderXp.length === 0
: true
}
/>
<ProfileTags />
)}

<UserInfosSection formValues={values} />
Expand Down
Loading

0 comments on commit bfb0808

Please sign in to comment.