Skip to content

Commit

Permalink
πŸ“– Add stories for the current and past proposals pages (#4465)
Browse files Browse the repository at this point in the history
* Enable linking stories with the Router

* Create current and past proposal stories

* Fix the `RecursivePartial` type

* Improve the type of mock resolvers

* Warn of missing chain data

* Generate current proposals

* Generate past proposals

* Reorganize the proposal stories in the navigation

* Simplify api mocks code
  • Loading branch information
thesan authored Jul 4, 2023
1 parent cbfaca1 commit 2f4d5ca
Show file tree
Hide file tree
Showing 25 changed files with 449 additions and 101 deletions.
22 changes: 2 additions & 20 deletions packages/ui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ import { Decorator } from '@storybook/react'
import React from 'react'
import { I18nextProvider } from 'react-i18next'
import { useForm, FormProvider } from 'react-hook-form'
import { MemoryRouter, Redirect, Route, Switch } from 'react-router'
import { createGlobalStyle } from 'styled-components'

import { GlobalModals } from '../src/app/GlobalModals'
import { NotFound } from '../src/app/pages/NotFound'
import { GlobalStyle } from '../src/app/providers/GlobalStyle'
import { OnBoardingProvider } from '../src/common/providers/onboarding/provider'
import { NotificationsHolder } from '../src/common/components/page/SideNotification'
import { TransactionStatus } from '../src/common/components/TransactionStatus/TransactionStatus'
import { Colors } from '../src/common/constants'
import { ModalContextProvider } from '../src/common/providers/modal/provider'
import { TransactionStatusProvider } from '../src/common/providers/transactionStatus/provider'
import { MockProvidersDecorator } from '../src/mocks/providers'
import { MockProvidersDecorator, MockRouterDecorator } from '../src/mocks/providers'
import { i18next } from '../src/services/i18n'

const stylesWrapperDecorator: Decorator = (Story) => (
Expand Down Expand Up @@ -49,22 +47,6 @@ const RHFDecorator: Decorator = (Story) => {
)
}

const RouterDecorator: Decorator = (Story, { parameters }) => {
const storyPath = `/story/${parameters.router?.href ?? ''}`
return (
<>
<div id="modal-container" />
<MemoryRouter initialEntries={[storyPath]}>
<Switch>
<Route component={Story} path={`/story/${parameters.router?.path ?? ''}`} />
{parameters.enable404 && <Route path="*" component={NotFound} />}
<Redirect from="*" to={storyPath} />
</Switch>
</MemoryRouter>
</>
)
}

const ModalDecorator: Decorator = (Story) => (
<TransactionStatusProvider>
<ModalContextProvider>
Expand All @@ -85,7 +67,7 @@ export const decorators = [
i18nextDecorator,
RHFDecorator,
MockProvidersDecorator,
RouterDecorator,
MockRouterDecorator,
]

export const parameters = {
Expand Down
110 changes: 110 additions & 0 deletions packages/ui/src/app/pages/Proposals/CurrentProposals.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { linkTo } from '@storybook/addon-links'
import { Meta, StoryContext, StoryObj } from '@storybook/react'
import { userEvent, within } from '@storybook/testing-library'
import { random } from 'faker'
import { FC } from 'react'

import { member } from '@/mocks/data/members'
import { generateProposals, MAX_ACTIVE_PROPOSAL, proposalsPagesChain } from '@/mocks/data/proposals'
import { getButtonByText } from '@/mocks/helpers'
import { MocksParameters } from '@/mocks/providers'
import { GetProposalVotesDocument, GetProposalsCountDocument, GetProposalsDocument } from '@/proposals/queries'

import { Proposals } from './Proposals'

import { randomMarkdown } from '@/../dev/query-node-mocks/generators/utils'

const PROPOSAL_DATA = {
title: random.words(4),
description: randomMarkdown(),
}

type Args = {
isCouncilMember: boolean
proposalCount: number
onVote: CallableFunction
}
type Story = StoryObj<FC<Args>>

export default {
title: 'Pages/Proposals/ProposalList/Current',
component: Proposals,

argTypes: {
proposalCount: { control: { type: 'range', max: MAX_ACTIVE_PROPOSAL } },
onVote: { action: 'Voted' },
},

args: {
isCouncilMember: false,
proposalCount: 15,
},

parameters: {
router: {
href: '/proposals/current',
actions: {
'/proposals/past': linkTo('Pages/Proposals/ProposalList/Past'),
},
},

mocks: ({ args }: StoryContext<Args>): MocksParameters => {
const alice = member('alice', { isCouncilMember: args.isCouncilMember })

return {
accounts: { active: { member: alice } },

chain: proposalsPagesChain(args.proposalCount, {
tx: {
proposalsEngine: {
vote: { event: 'Voted', onSend: args.onVote },
},
},
}),

queryNode: [
{
query: GetProposalsCountDocument,
data: { proposalsConnection: { totalCount: args.proposalCount } },
},

{
query: GetProposalsDocument,
resolver: ({ variables } = {}) => ({
loading: false,
data: {
proposals: generateProposals(
{
title: PROPOSAL_DATA.title,
description: PROPOSAL_DATA.description,
creator: alice,
statuses: ['ProposalStatusGracing', 'ProposalStatusDormant', 'ProposalStatusDeciding'],
limit: variables?.limit,
offset: variables?.offset,
},
args.proposalCount
),
},
}),
},

{
query: GetProposalVotesDocument,
data: {
proposalVotedEvents: [],
},
},
],
}
},
},
} satisfies Meta<Args>

export const Default: Story = {}

export const AddNewProposal: Story = {
play: async ({ canvasElement }) => {
const screen = within(canvasElement)
await userEvent.click(getButtonByText(screen, 'Add new proposal'))
},
}
91 changes: 91 additions & 0 deletions packages/ui/src/app/pages/Proposals/PastProposals.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { linkTo } from '@storybook/addon-links'
import { Meta, StoryContext, StoryObj } from '@storybook/react'
import { random } from 'faker'
import { FC } from 'react'

import { member } from '@/mocks/data/members'
import { generateProposals, proposalsPagesChain } from '@/mocks/data/proposals'
import { MocksParameters } from '@/mocks/providers'
import { GetProposalsCountDocument, GetProposalsDocument } from '@/proposals/queries'

import { PastProposals } from './PastProposals'

import { randomMarkdown } from '@/../dev/query-node-mocks/generators/utils'

const PROPOSAL_DATA = {
title: random.words(4),
description: randomMarkdown(),
}

const alice = member('alice')

type Args = {
proposalCount: number
}
type Story = StoryObj<FC<Args>>

export default {
title: 'Pages/Proposals/ProposalList/Past',
component: PastProposals,

argTypes: {
proposalCount: { control: { type: 'range', max: 30 } },
},

args: {
proposalCount: 15,
},

parameters: {
router: {
href: '/proposals/past',
actions: {
'/proposals/current': linkTo('Pages/Proposals/ProposalList/Current'),
},
},

mocks: ({ args }: StoryContext<Args>): MocksParameters => {
return {
chain: proposalsPagesChain(5),

queryNode: [
{
query: GetProposalsCountDocument,
data: { proposalsConnection: { totalCount: args.proposalCount } },
},

{
query: GetProposalsDocument,
resolver: ({ variables } = {}) => ({
loading: false,
data: {
proposals: generateProposals(
{
title: PROPOSAL_DATA.title,
description: PROPOSAL_DATA.description,
creator: alice,
statuses: [
'ProposalStatusCanceledByRuntime',
'ProposalStatusCancelled',
'ProposalStatusExecuted',
'ProposalStatusExecutionFailed',
'ProposalStatusExpired',
'ProposalStatusRejected',
'ProposalStatusSlashed',
'ProposalStatusVetoed',
],
limit: variables?.limit,
offset: variables?.offset,
},
args.proposalCount
),
},
}),
},
],
}
},
},
} satisfies Meta<Args>

export const Default: Story = {}
23 changes: 10 additions & 13 deletions packages/ui/src/app/pages/Proposals/ProposalPreview.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import { last } from 'lodash'
import { FC } from 'react'

import { ProposalVoteKind } from '@/common/api/queries'
import { RecursivePartial } from '@/common/types/helpers'
import { repeat } from '@/common/utils'
import { GetElectedCouncilDocument } from '@/council/queries'
import { member } from '@/mocks/data/members'
import {
ProposalStatus,
proposalDiscussionPosts,
proposalActiveStatus,
proposalDetailsMap,
generateProposal,
proposalTypes,
} from '@/mocks/data/proposals'
import { getButtonByText, getEditorByLabel, withinModal, isoDate, joy, Container } from '@/mocks/helpers'
import { ProposalDetailsType, proposalDetailsToConstantKey } from '@/mocks/helpers/proposalDetailsToConstantKey'
import { MocksParameters } from '@/mocks/providers'
import { GetProposalDocument, ProposalWithDetailsFieldsFragment } from '@/proposals/queries'
import { GetProposalDocument } from '@/proposals/queries'

import { ProposalPreview } from './ProposalPreview'

Expand Down Expand Up @@ -58,7 +58,7 @@ export default {
component: ProposalPreview,

argTypes: {
type: { control: { type: 'select' }, options: Object.keys(proposalDetailsMap) },
type: { control: { type: 'select' }, options: proposalTypes },
constitutionality: { control: { type: 'range', min: 1, max: 4 } },
vote1: { control: { type: 'inline-radio' }, options: voteArgs },
vote2: { control: { type: 'inline-radio' }, options: voteArgs },
Expand Down Expand Up @@ -148,10 +148,14 @@ export default {
{
query: GetProposalDocument,
data: {
proposal: {
proposal: generateProposal({
id: PROPOSAL_DATA.id,
title: PROPOSAL_DATA.title,
description: PROPOSAL_DATA.description,
status,
type: args.type,
creator: args.isProposer ? alice : bob,

discussionThread: {
posts: proposalDiscussionPosts,
mode: args.isDiscussionOpen
Expand All @@ -165,22 +169,15 @@ export default {
},
},

creator: args.isProposer ? alice : bob,
details: proposalDetailsMap[args.type],

createdInEvent: { inBlock: 123, createdAt: isoDate('2023/01/02') },
proposalStatusUpdates: updates.map((status: ProposalStatus) => ({
inBlock: 123,
createdAt: isoDate('2023/01/02'),
newStatus: { __typename: status },
})),
status: { __typename: status },
statusSetAtBlock: 123,
statusSetAtTime: isoDate('2023/01/12'),

councilApprovals: parameters.councilApprovals ?? constitutionality - 1,
votes,
} as RecursivePartial<ProposalWithDetailsFieldsFragment>,
}),
},
},

Expand Down
8 changes: 5 additions & 3 deletions packages/ui/src/common/types/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export type Awaited<T> = T extends PromiseLike<infer U> ? U : T
export type RecursivePartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[]
? RecursivePartial<U>[]
: T[P] extends object | undefined
? RecursivePartial<T[P]>
: T[P]
: T[P] extends infer U // This line distributes union types
? U extends object
? RecursivePartial<U>
: U
: never
}
Loading

2 comments on commit 2f4d5ca

@vercel
Copy link

@vercel vercel bot commented on 2f4d5ca Jul 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

pioneer-2-storybook – ./

pioneer-2-storybook.vercel.app
pioneer-2-storybook-git-dev-joystream.vercel.app
pioneer-2-storybook-joystream.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 2f4d5ca Jul 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

pioneer-2 – ./

pioneer-2-git-dev-joystream.vercel.app
pioneer-2-joystream.vercel.app
pioneer-2.vercel.app

Please sign in to comment.