diff --git a/CHANGELOG.md b/CHANGELOG.md index 9012f12fd..6cb981f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Hide minimum participation details on `ProposalVotingBreakdownToken` module component when minParticipation is set to zero. +- Correctly forward web3 params (e.g. `chainId`) to native `ProposalActions` components ## [1.0.41] - 2024-07-30 diff --git a/src/modules/components/proposal/proposalActions/proposalActions/proposalActions.tsx b/src/modules/components/proposal/proposalActions/proposalActions/proposalActions.tsx index 392d54562..a4b4d8998 100644 --- a/src/modules/components/proposal/proposalActions/proposalActions/proposalActions.tsx +++ b/src/modules/components/proposal/proposalActions/proposalActions/proposalActions.tsx @@ -58,7 +58,7 @@ export const ProposalActions: React.FC = (props) => { action={action} index={index} name={actionNames?.[action.type]} - customComponent={customActionComponents?.[action.type]} + CustomComponent={customActionComponents?.[action.type]} {...web3Props} /> ))} diff --git a/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.test.tsx b/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.test.tsx index a41addc44..a908b3e41 100644 --- a/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.test.tsx +++ b/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.test.tsx @@ -2,11 +2,25 @@ import { render, screen } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import { Accordion } from '../../../../../core'; import { modulesCopy } from '../../../../assets'; +import { + generateProposalActionChangeMembers, + generateProposalActionChangeSettings, + generateProposalActionTokenMint, + generateProposalActionUpdateMetadata, +} from '../actions/generators'; import { generateProposalAction } from '../actions/generators/proposalAction'; import { generateProposalActionWithdrawToken } from '../actions/generators/proposalActionWithdrawToken'; import type { IProposalAction } from '../proposalActionsTypes'; import { type IProposalActionsActionProps, ProposalActionsAction } from './proposalActionsAction'; +jest.mock('../actions', () => ({ + ProposalActionWithdrawToken: () =>
, + ProposalActionTokenMint: () =>
, + ProposalActionUpdateMetadata: () =>
, + ProposalActionChangeMembers: () =>
, + ProposalActionChangeSettings: () =>
, +})); + describe(' component', () => { const createTestComponent = (props?: Partial) => { const defaultProps: IProposalActionsActionProps = { @@ -29,16 +43,16 @@ describe(' component', () => { }); it('renders custom action component when provided', async () => { - const customComponent = (props: { action: IProposalAction }) => `Custom action for ${props.action.type}`; + const CustomComponent = (props: { action: IProposalAction }) => props.action.type; const action = generateProposalAction({ type: 'customType', inputData: { function: 'transfer', contract: 'DAI', parameters: [] }, }); - render(createTestComponent({ action, customComponent })); + render(createTestComponent({ action, CustomComponent })); await userEvent.click(screen.getByText('transfer')); - expect(screen.getByText(`Custom action for ${action.type}`)).toBeInTheDocument(); + expect(screen.getByText(action.type)).toBeInTheDocument(); }); it('renders action name when provided and contract is verified', () => { @@ -47,4 +61,16 @@ describe(' component', () => { render(createTestComponent({ name, action })); expect(screen.getByText(name)).toBeInTheDocument(); }); + + it.each([ + { action: generateProposalActionWithdrawToken(), testId: 'withdraw-token' }, + { action: generateProposalActionTokenMint(), testId: 'token-mint' }, + { action: generateProposalActionUpdateMetadata(), testId: 'update-metadata' }, + { action: generateProposalActionChangeMembers(), testId: 'change-members' }, + { action: generateProposalActionChangeSettings(), testId: 'change-settings' }, + ])('renders correct UI for $testId action', async ({ action, testId }) => { + render(createTestComponent({ action })); + await userEvent.click(screen.getByRole('button')); + expect(screen.getByTestId(testId)).toBeInTheDocument(); + }); }); diff --git a/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.tsx b/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.tsx index dead30b9d..f4120728e 100644 --- a/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.tsx +++ b/src/modules/components/proposal/proposalActions/proposalActionsAction/proposalActionsAction.tsx @@ -1,7 +1,15 @@ +import { useMemo } from 'react'; import { Accordion, Heading } from '../../../../../core'; import type { IWeb3ComponentProps } from '../../../../types'; import { useOdsModulesContext } from '../../../odsModulesProvider'; -import { ProposalActionsActionVerification } from '../proposalActionsActionVerfication/proposalActionsActionVerfication'; +import { + ProposalActionChangeMembers, + ProposalActionChangeSettings, + ProposalActionTokenMint, + ProposalActionUpdateMetadata, + ProposalActionWithdrawToken, +} from '../actions'; +import { ProposalActionsActionVerification } from '../proposalActionsActionVerfication'; import type { IProposalAction, ProposalActionComponent } from '../proposalActionsTypes'; import { proposalActionsUtils } from '../proposalActionsUtils'; @@ -21,33 +29,48 @@ export interface IProposalActionsActionProps extends IWeb3ComponentProps { /** * Custom component for the action */ - customComponent?: ProposalActionComponent; + CustomComponent?: ProposalActionComponent; } export const ProposalActionsAction: React.FC = (props) => { - const { action, index, name, customComponent, ...web3Props } = props; - - const ActionComponent = customComponent ?? proposalActionsUtils.getActionComponent(action); + const { action, index, name, CustomComponent, ...web3Props } = props; const { copy } = useOdsModulesContext(); + const ActionComponent = useMemo(() => { + if (CustomComponent) { + return ; + } + + if (proposalActionsUtils.isWithdrawTokenAction(action)) { + return ; + } else if (proposalActionsUtils.isTokenMintAction(action)) { + return ; + } else if (proposalActionsUtils.isUpdateMetadataAction(action)) { + return ; + } else if (proposalActionsUtils.isChangeMembersAction(action)) { + return ; + } else if (proposalActionsUtils.isChangeSettingsAction(action)) { + return ; + } + + return null; + }, [action, CustomComponent, web3Props]); + + const defaultTitle = name ?? action.inputData?.function; + const actionTitle = action.inputData == null ? copy.proposalActionsAction.notVerified : defaultTitle; + const isDisabled = action.inputData == null; return (
- - {action.inputData == null - ? copy.proposalActionsAction.notVerified - : (name ?? action.inputData.function)} - + {actionTitle}
- - {ActionComponent && } - + {ActionComponent}
); }; diff --git a/src/modules/components/proposal/proposalActions/proposalActionsUtils.test.tsx b/src/modules/components/proposal/proposalActions/proposalActionsUtils.test.tsx index 117fa8907..be9bf36c9 100644 --- a/src/modules/components/proposal/proposalActions/proposalActionsUtils.test.tsx +++ b/src/modules/components/proposal/proposalActions/proposalActionsUtils.test.tsx @@ -1,6 +1,4 @@ -import { render, screen } from '@testing-library/react'; import { - generateProposalAction, generateProposalActionChangeMembers, generateProposalActionChangeSettings, generateProposalActionTokenMint, @@ -10,75 +8,74 @@ import { import { ProposalActionType } from './proposalActionsTypes'; import { proposalActionsUtils } from './proposalActionsUtils'; -jest.mock('./actions', () => ({ - ProposalActionWithdrawToken: () =>
Mock ProposalActionWithdrawToken
, - ProposalActionUpdateMetadata: () =>
Mock ProposalActionUpdateMetaData
, - ProposalActionTokenMint: () =>
Mock ProposalActionTokenMint
, - ProposalActionChangeMembers: () =>
Mock ProposalActionChangeMembers
, - ProposalActionChangeSettings: () =>
Mock ProposalActionChangeSettings
, -})); - describe('ProposalActions utils', () => { - it('returns ProposalActionWithdrawToken component for withdrawToken action', () => { - const action = generateProposalActionWithdrawToken(); - - const Component = proposalActionsUtils.getActionComponent(action)!; - render(); - expect(screen.getByText('Mock ProposalActionWithdrawToken')).toBeInTheDocument(); + describe('isWithdrawTokenAction', () => { + it('returns true for withdraw action', () => { + const action = generateProposalActionWithdrawToken(); + expect(proposalActionsUtils.isWithdrawTokenAction(action)).toBeTruthy(); + }); + + it('returns false for other actions', () => { + const action = generateProposalActionUpdateMetadata(); + expect(proposalActionsUtils.isWithdrawTokenAction(action)).toBeFalsy(); + }); }); - it('returns ProposalActionUpdateMetadata component for updateMetadata action', () => { - const action = generateProposalActionUpdateMetadata(); - - const Component = proposalActionsUtils.getActionComponent(action)!; - render(); - expect(screen.getByText('Mock ProposalActionUpdateMetaData')).toBeInTheDocument(); + describe('isChangeMemberAction', () => { + it('returns true for change members actions', () => { + const addMembersAction = generateProposalActionChangeMembers({ type: ProposalActionType.ADD_MEMBERS }); + const removeMembersAction = generateProposalActionChangeMembers({ + type: ProposalActionType.REMOVE_MEMBERS, + }); + expect(proposalActionsUtils.isChangeMembersAction(addMembersAction)).toBeTruthy(); + expect(proposalActionsUtils.isChangeMembersAction(removeMembersAction)).toBeTruthy(); + }); + + it('returns false for other actions', () => { + const action = generateProposalActionWithdrawToken(); + expect(proposalActionsUtils.isChangeMembersAction(action)).toBeFalsy(); + }); }); - it('returns ProposalActionTokenMint component for tokenMint action', () => { - const action = generateProposalActionTokenMint(); + describe('isUpdateMetadataAction', () => { + it('returns true for update metadata action', () => { + const action = generateProposalActionUpdateMetadata(); + expect(proposalActionsUtils.isUpdateMetadataAction(action)).toBeTruthy(); + }); - const Component = proposalActionsUtils.getActionComponent(action)!; - render(); - expect(screen.getByText('Mock ProposalActionTokenMint')).toBeInTheDocument(); + it('returns false for other actions', () => { + const action = generateProposalActionChangeMembers(); + expect(proposalActionsUtils.isUpdateMetadataAction(action)).toBeFalsy(); + }); }); - it('returns ProposalActionChangeMembers component for addMember action', () => { - const action = generateProposalActionChangeMembers({ type: ProposalActionType.ADD_MEMBERS }); + describe('isTokenMintAction', () => { + it('returns true for token mint action', () => { + const action = generateProposalActionTokenMint(); + expect(proposalActionsUtils.isTokenMintAction(action)).toBeTruthy(); + }); - const Component = proposalActionsUtils.getActionComponent(action)!; - render(); - expect(screen.getByText('Mock ProposalActionChangeMembers')).toBeInTheDocument(); + it('returns false for other actions', () => { + const action = generateProposalActionUpdateMetadata(); + expect(proposalActionsUtils.isTokenMintAction(action)).toBeFalsy(); + }); }); - it('returns ProposalActionChangeMembers component for removeMember action', () => { - const action = generateProposalActionChangeMembers({ type: ProposalActionType.REMOVE_MEMBERS }); - - const Component = proposalActionsUtils.getActionComponent(action)!; - render(); - expect(screen.getByText('Mock ProposalActionChangeMembers')).toBeInTheDocument(); - }); - - it('returns ProposalActionChangeSettings component for Multisig action', () => { - const action = generateProposalActionChangeSettings({ type: ProposalActionType.CHANGE_SETTINGS_MULTISIG }); - - const Component = proposalActionsUtils.getActionComponent(action)!; - render(); - expect(screen.getByText('Mock ProposalActionChangeSettings')).toBeInTheDocument(); - }); - - it('returns ProposalActionChangeSettings component for TokenVote action', () => { - const action = generateProposalActionChangeSettings({ type: ProposalActionType.CHANGE_SETTINGS_TOKENVOTE }); - - const Component = proposalActionsUtils.getActionComponent(action)!; - render(); - expect(screen.getByText('Mock ProposalActionChangeSettings')).toBeInTheDocument(); - }); - - it('returns null for unknown action type', () => { - const action = generateProposalAction(); - - const Component = proposalActionsUtils.getActionComponent(action); - expect(Component).toBeNull(); + describe('isChangeSettingsAction', () => { + it('returns true for change settings actions', () => { + const changeMultisig = generateProposalActionChangeSettings({ + type: ProposalActionType.CHANGE_SETTINGS_MULTISIG, + }); + const changeToken = generateProposalActionChangeSettings({ + type: ProposalActionType.CHANGE_SETTINGS_TOKENVOTE, + }); + expect(proposalActionsUtils.isChangeSettingsAction(changeMultisig)).toBeTruthy(); + expect(proposalActionsUtils.isChangeSettingsAction(changeToken)).toBeTruthy(); + }); + + it('returns false for other actions', () => { + const action = generateProposalActionTokenMint(); + expect(proposalActionsUtils.isChangeSettingsAction(action)).toBeFalsy(); + }); }); }); diff --git a/src/modules/components/proposal/proposalActions/proposalActionsUtils.ts b/src/modules/components/proposal/proposalActions/proposalActionsUtils.ts index 6e32a0f2e..878652952 100644 --- a/src/modules/components/proposal/proposalActions/proposalActionsUtils.ts +++ b/src/modules/components/proposal/proposalActions/proposalActionsUtils.ts @@ -1,10 +1,3 @@ -import { - ProposalActionChangeMembers, - ProposalActionChangeSettings, - ProposalActionTokenMint, - ProposalActionUpdateMetadata, - ProposalActionWithdrawToken, -} from './actions'; import { type IProposalAction, type IProposalActionChangeMembers, @@ -16,22 +9,6 @@ import { } from './proposalActionsTypes'; class ProposalActionsUtils { - getActionComponent = (action: IProposalAction) => { - if (this.isWithdrawTokenAction(action)) { - return () => ProposalActionWithdrawToken({ action }); - } else if (this.isTokenMintAction(action)) { - return () => ProposalActionTokenMint({ action }); - } else if (this.isUpdateMetadataAction(action)) { - return () => ProposalActionUpdateMetadata({ action }); - } else if (this.isChangeMembersAction(action)) { - return () => ProposalActionChangeMembers({ action }); - } else if (this.isChangeSettingsAction(action)) { - return () => ProposalActionChangeSettings({ action }); - } - - return null; - }; - isWithdrawTokenAction = (action: Partial): action is IProposalActionWithdrawToken => { return action.type === ProposalActionType.WITHDRAW_TOKEN; };