Skip to content

Commit

Permalink
Merge branch 'master' into chore/fix-up-firebase-emulator
Browse files Browse the repository at this point in the history
  • Loading branch information
benfurber authored May 31, 2024
2 parents 941bb14 + 4123792 commit 508b591
Show file tree
Hide file tree
Showing 36 changed files with 265 additions and 165 deletions.
4 changes: 4 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ commands:
- checkout
- *restore_yarn_cache
- *install_packages
- *save_yarn_cache
inject_instance_configuration:
steps:
- run:
Expand Down Expand Up @@ -325,6 +326,9 @@ jobs:
- firebase_deploy:
# token: $FIREBASE_DEPLOY_TOKEN # This should be set as environment variable
alias: << parameters.DEPLOY_ALIAS >>
# run chromatic
- run:
command: npm run storybook:chromatic

# Run cypress e2e tests on chrome
test_e2e:
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Taken from https://www.chromatic.com/docs/github-actions/

name: "Chromatic"

on: push

jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install dependencies
run: yarn install

- name: Run Chromatic
uses: chromaui/action@latest
with:
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"test:madge": "npx madge --circular --extensions ts,tsx ./ --exclude src/stores",
"storybook": "yarn workspace oa-components start",
"storybook:build": "yarn build:themes && yarn workspace oa-components build:sb",
"storybook:chromatic": "yarn workspace oa-components chromatic",
"docs": "yarn workspace oa-docs start",
"install:clean": "yarn workspace oa-scripts install:clean",
"postinstall": "husky install",
Expand Down
4 changes: 3 additions & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
"lint": "eslint . --ext .js,.jsx,.ts,.tsx src --color",
"new-component": "ts-node scripts/newComponent.ts",
"test": "vitest",
"test-ci": "vitest --coverage --reporter=junit --outputFile.junit=./reports/output.xml"
"test-ci": "vitest --coverage --reporter=junit --outputFile.junit=./reports/output.xml",
"chromatic": "npx chromatic -b=build:sb"
},
"dependencies": {
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@faker-js/faker": "^7.6.0",
"@mui/base": "^5.0.0-beta.18",
"@react-icons/all-files": "^4.1.0",
"chromatic": "^11.4.0",
"date-fns": "^2.29.3",
"linkify-plugin-mention": "^4.0.2",
"linkify-react": "^4.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { vi } from 'vitest'
import { useState } from 'react'

import { createFakeComments } from '../utils'
import { ButtonShowReplies } from './ButtonShowReplies'

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

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

const mockSetIsShowReplies = vi.fn()

export const Default: StoryFn<typeof ButtonShowReplies> = () => {
const [isShowReplies, setIsShowReplies] = useState<boolean>(false)

const replies = createFakeComments(7)

return (
<ButtonShowReplies
creatorName="Jeff"
isShowReplies={false}
replies={replies}
setIsShowReplies={mockSetIsShowReplies}
isShowReplies={isShowReplies}
setIsShowReplies={() => setIsShowReplies(!isShowReplies)}
/>
)
}
Expand All @@ -33,7 +33,7 @@ export const RepliesShowing: StoryFn<typeof ButtonShowReplies> = () => {
creatorName="Annabeth"
isShowReplies={true}
replies={replies}
setIsShowReplies={mockSetIsShowReplies}
setIsShowReplies={() => null}
/>
)
}
Expand All @@ -46,7 +46,7 @@ export const OneReply: StoryFn<typeof ButtonShowReplies> = () => {
creatorName="Zelda"
isShowReplies={false}
replies={replies}
setIsShowReplies={mockSetIsShowReplies}
setIsShowReplies={() => null}
/>
)
}
Expand All @@ -57,7 +57,7 @@ export const NoReplies: StoryFn<typeof ButtonShowReplies> = () => {
creatorName="Link"
isShowReplies={false}
replies={[]}
setIsShowReplies={mockSetIsShowReplies}
setIsShowReplies={() => null}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { InternalLink } from '../InternalLink/InternalLink'
import { NotificationList } from './NotificationList'

import type { Meta, StoryFn } from '@storybook/react'
import type { UserNotificationList } from './NotificationList'
import type { UserNotificationItem } from '../NotificationItem/NotificationItem'

export default {
title: 'Components/NotificationList',
Expand Down Expand Up @@ -74,10 +74,14 @@ const notifications = [
</>
),
},
] as UserNotificationList
] as UserNotificationItem[]

export const Default: StoryFn<typeof NotificationList> = () => (
<NotificationList notifications={notifications} />
<NotificationList
notifications={notifications}
markAllNotified={() => {}}
markAllRead={() => {}}
/>
)

export const LongList: StoryFn<typeof NotificationList> = () => (
Expand All @@ -88,9 +92,15 @@ export const LongList: StoryFn<typeof NotificationList> = () => (
...notifications,
...notifications,
]}
markAllNotified={() => {}}
markAllRead={() => {}}
/>
)

export const Empty: StoryFn<typeof NotificationList> = () => (
<NotificationList notifications={[]} />
<NotificationList
notifications={[]}
markAllNotified={() => {}}
markAllRead={() => {}}
/>
)
22 changes: 10 additions & 12 deletions packages/components/src/NotificationList/NotificationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import { Box, Card, Text } from 'theme-ui'
import { Button } from '../Button/Button'
import { NotificationItem } from '../NotificationItem/NotificationItem'

import type { ThemeUIStyleObject } from 'theme-ui'
import type { UserNotificationItem } from '../NotificationItem/NotificationItem'

export type UserNotificationList = UserNotificationItem[]

export interface Props {
notifications: UserNotificationList
sx?: any
markAllRead?: () => void
markAllNotified?: () => void
notifications: UserNotificationItem[]
markAllRead: () => void
markAllNotified: () => void
sx?: ThemeUIStyleObject
}

const ModalItem = styled(Box)`
Expand All @@ -30,19 +29,18 @@ export const NotificationList = (props: Props) => {
const { notifications, markAllRead, markAllNotified } = props
const sx = props.sx || {}
useEffect(() => {
notifications.length && markAllNotified && markAllNotified()
}, [notifications, markAllNotified])
if (notifications.length) {
markAllNotified()
}
}, [])

return (
<Card sx={{ padding: 2, maxHeight: 310, overflowY: 'auto', ...sx }}>
{notifications.length ? (
<>
<ModalItem style={{ textAlign: 'center' }}>Notifications</ModalItem>
{notifications.map((notification, idx) => (
<NotificationItem
key={idx}
{...(notification as any)}
></NotificationItem>
<NotificationItem key={idx} {...(notification as any)} />
))}
<Button
style={{
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ export { UserEngagementWrapper } from './UserEngagementWrapper/UserEngagementWra
export { IconCountWithTooltip } from './IconCountWithTooltip/IconCountWithTooltip'
export { DonationRequest } from './DonationRequest/DonationRequest'
export { DonationRequestModal } from './DonationRequestModal/DonationRequestModal'
export { UserNotificationItem } from './NotificationItem/NotificationItem'
4 changes: 3 additions & 1 deletion packages/cypress/src/integration/howto/write.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { faker } from '@faker-js/faker'
import { DifficultyLevel } from 'oa-shared'
import { DifficultyLevel, IModerationStatus } from 'oa-shared'

import {
HOWTO_STEP_DESCRIPTION_MAX_LENGTH,
Expand Down Expand Up @@ -109,6 +109,7 @@ describe('[How To]', () => {
_deleted: false,
category: 'Moulds',
description: 'After creating, the how-to will be deleted',
moderation: IModerationStatus.AWAITING_MODERATION,
difficulty_level: DifficultyLevel.MEDIUM,
time: '1-2 weeks',
title: 'Create a how-to test',
Expand Down Expand Up @@ -361,6 +362,7 @@ describe('[How To]', () => {
_createdBy: 'howto_editor',
_deleted: false,
category: 'exhibition',
moderation: IModerationStatus.ACCEPTED,
description: 'After editing, all changes are reverted',
difficulty_level: DifficultyLevel.HARD,
files: [],
Expand Down
18 changes: 12 additions & 6 deletions packages/cypress/src/integration/questions/write.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const creatorEmail = 'howto_creator@test.com'
const creatorPassword = 'test1234'
import { MOCK_DATA } from '../../data'

const questions = Object.values(MOCK_DATA.questions)
const item = questions[0]

describe('[Question]', () => {
describe('[Create a question]', () => {
Expand All @@ -19,14 +21,18 @@ describe('[Question]', () => {
// https://github.com/ONEARMY/community-platform/pull/3461
// eslint-disable-next-line mocha/no-skipped-tests
it.skip('[By Authenticated]', () => {
cy.visit('/questions')
cy.login(creatorEmail, creatorPassword)
cy.signUpNewUser()

cy.step('Go to create page')
cy.get('[data-cy=loader]').should('not.exist')
cy.get('[data-cy=create]').click()
cy.visit('/questions/create')
cy.get('[data-cy=question-create-title]')

cy.step('Warn if title is identical to an existing one')
cy.get('[data-cy=field-title]').type(item.title).blur({ force: true })
cy.contains(
'Titles must be unique, please try being more specific',
).should('exist')

cy.step('Add title field')
cy.get('[data-cy=field-title]').type(initialTitle).blur({ force: true })

Expand Down
2 changes: 1 addition & 1 deletion packages/cypress/src/integration/research/read.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('[Research]', () => {
cy.visit(researchArticleUrl)
})

it('[Visible to everyone', () => {
it('[Visible to everyone]', () => {
const article = research[0]
cy.step('Delete button should not be visible')
cy.get('[data-cy="Research: delete button"]').should('not.exist')
Expand Down
3 changes: 3 additions & 0 deletions packages/cypress/src/support/CustomAssertations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const eqHowto = (chaiObj) => {
title,
tags,
previousSlugs,
moderation,
} = expected
expect(subject, 'Basic info').to.containSubset({
_createdBy,
Expand All @@ -59,6 +60,8 @@ const eqHowto = (chaiObj) => {
})
expect(subject.category.label, 'Category').to.eq(category)

expect(subject.moderation).to.equal(moderation)

// We want to validate that uploaded filename matches that originally specified
// by the user. The filename will include a timestamp to avoid collisions with
// existing files that have been uploaded.
Expand Down
3 changes: 3 additions & 0 deletions packages/cypress/src/support/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Cypress.on('uncaught:exception', (err) => {
if (err.message.includes('The query requires an index.')) {
return false
}
if (err.message.includes('No document to update')) {
return false
}
// we still want to ensure there are no other unexpected
// errors, so we let them fail the test
})
2 changes: 2 additions & 0 deletions src/common/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ export interface IErrorsListSet {
labels: ILabels
title?: string
}

export type MainFormAction = 'create' | 'edit'
2 changes: 1 addition & 1 deletion src/common/Tags/TagsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ export const TagList = ({ tags }: IProps) => {
.filter(Boolean)
.map((key) => allTagsByKey[key])

return <TagListUI tags={tagList} />
return !!tagList && <TagListUI tags={tagList} />
}
1 change: 1 addition & 0 deletions src/models/question.models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export namespace IQuestion {
ISharedFeatures

export interface FormInput extends IModerable {
_id: string
title: string
description: string
tags: ISelectedTags
Expand Down
8 changes: 5 additions & 3 deletions src/pages/Howto/Content/Common/Howto.form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ export const HowtoForm = observer((props: IProps) => {
return
}
setState((state) => ({ ...state, showSubmitModal: true }))
formValues.moderation = formValues.allowDraftSave
? IModerationStatus.DRAFT
: IModerationStatus.AWAITING_MODERATION
if (formValues.moderation !== IModerationStatus.ACCEPTED) {
formValues.moderation = formValues.allowDraftSave
? IModerationStatus.DRAFT
: IModerationStatus.AWAITING_MODERATION
}
logger.debug('submitting form', formValues)
await howtoStore.uploadHowTo(formValues)
form.reset(formValues)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import { Field } from 'react-final-form'
import { FieldInput } from 'oa-components'
import { useCommonStores } from 'src/common/hooks/useCommonStores'
import { FormFieldWrapper } from 'src/pages/Howto/Content/Common'
import {
QUESTION_MAX_TITLE_LENGTH,
QUESTION_MIN_TITLE_LENGTH,
} from 'src/pages/Question/constants'
import { fields } from 'src/pages/Question/labels'
import { composeValidators, minValue, required } from 'src/utils/validators'
import {
composeValidators,
minValue,
required,
validateTitle,
} from 'src/utils/validators'

import type { MainFormAction } from 'src/common/Form/types'

interface IProps {
parentType: MainFormAction
formValues: any
}

export const QuestionTitleField = (props: IProps) => {
const { parentType, formValues } = props
const { questionStore } = useCommonStores().stores

export const QuestionTitleField = () => {
const { placeholder, title } = fields.title
const name = 'title'
const questionId = (!!formValues && formValues._id) ?? formValues._id

return (
<FormFieldWrapper htmlFor={name} text={title} required>
Expand All @@ -21,6 +38,7 @@ export const QuestionTitleField = () => {
validate={composeValidators(
required,
minValue(QUESTION_MIN_TITLE_LENGTH),
validateTitle(parentType, questionId, questionStore),
)}
component={FieldInput}
placeholder={placeholder}
Expand Down
Loading

0 comments on commit 508b591

Please sign in to comment.