feat(governance): add toggle for closed proposals list (#4190)
This commit is contained in:
parent
55143331f1
commit
ed2c82487d
@ -77,7 +77,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
|||||||
const proposalTitle = generateFreeFormProposalTitle();
|
const proposalTitle = generateFreeFormProposalTitle();
|
||||||
|
|
||||||
submitUniqueRawProposal({ proposalTitle: proposalTitle });
|
submitUniqueRawProposal({ proposalTitle: proposalTitle });
|
||||||
cy.get('[data-testid="set-proposals-filter-visible"]').click();
|
cy.get('[data-testid="proposal-filter-toggle"]').click();
|
||||||
cy.get('[data-testid="filter-input"]').type(proposerId);
|
cy.get('[data-testid="filter-input"]').type(proposerId);
|
||||||
// cy.get(`#${proposalId}`).should('contain', proposalId);
|
// cy.get(`#${proposalId}`).should('contain', proposalId);
|
||||||
cy.contains(proposalTitle).should('be.visible');
|
cy.contains(proposalTitle).should('be.visible');
|
||||||
|
@ -166,6 +166,7 @@ context(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
cy.get('[data-testid="closed-proposals-toggle-networkUpgrades"]').click();
|
||||||
cy.getByTestId('closed-proposals').within(() => {
|
cy.getByTestId('closed-proposals').within(() => {
|
||||||
cy.getByTestId('protocol-upgrade-proposals-list-item').should(
|
cy.getByTestId('protocol-upgrade-proposals-list-item').should(
|
||||||
'have.length',
|
'have.length',
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import { render, fireEvent } from '@testing-library/react';
|
||||||
|
import { CollapsibleToggle } from './collapsible-toggle';
|
||||||
|
|
||||||
|
describe('CollapsibleToggle', () => {
|
||||||
|
const testId = 'collapsible-toggle';
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const mockSetToggleState = jest.fn();
|
||||||
|
const { getByTestId, getByText } = render(
|
||||||
|
<CollapsibleToggle
|
||||||
|
toggleState={false}
|
||||||
|
setToggleState={mockSetToggleState}
|
||||||
|
dataTestId={testId}
|
||||||
|
>
|
||||||
|
<div>Test</div>
|
||||||
|
</CollapsibleToggle>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId(testId)).toBeInTheDocument();
|
||||||
|
expect(getByText('Test')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls setToggleState with the opposite of current toggleState when clicked', () => {
|
||||||
|
const mockSetToggleState = jest.fn();
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CollapsibleToggle
|
||||||
|
toggleState={false}
|
||||||
|
setToggleState={mockSetToggleState}
|
||||||
|
dataTestId={testId}
|
||||||
|
>
|
||||||
|
<div>Test</div>
|
||||||
|
</CollapsibleToggle>
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(getByTestId(testId));
|
||||||
|
|
||||||
|
expect(mockSetToggleState).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has the rotate-180 class if toggleState is true', () => {
|
||||||
|
const mockSetToggleState = jest.fn();
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CollapsibleToggle
|
||||||
|
toggleState={true}
|
||||||
|
setToggleState={mockSetToggleState}
|
||||||
|
dataTestId={testId}
|
||||||
|
>
|
||||||
|
<div>Test</div>
|
||||||
|
</CollapsibleToggle>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId('toggle-icon-wrapper')).toHaveClass('rotate-180');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not have the rotate-180 class if toggleState is false', () => {
|
||||||
|
const mockSetToggleState = jest.fn();
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CollapsibleToggle
|
||||||
|
toggleState={false}
|
||||||
|
setToggleState={mockSetToggleState}
|
||||||
|
dataTestId={testId}
|
||||||
|
>
|
||||||
|
<div>Test</div>
|
||||||
|
</CollapsibleToggle>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId('toggle-icon-wrapper')).not.toHaveClass('rotate-180');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,38 @@
|
|||||||
|
import classnames from 'classnames';
|
||||||
|
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import type { Dispatch, SetStateAction, ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface CollapsibleToggleProps {
|
||||||
|
toggleState: boolean;
|
||||||
|
setToggleState: Dispatch<SetStateAction<boolean>>;
|
||||||
|
children: ReactNode;
|
||||||
|
dataTestId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CollapsibleToggle = ({
|
||||||
|
toggleState,
|
||||||
|
setToggleState,
|
||||||
|
dataTestId,
|
||||||
|
children,
|
||||||
|
}: CollapsibleToggleProps) => {
|
||||||
|
const classes = classnames(
|
||||||
|
'mb-4 transition-transform ease-in-out duration-300',
|
||||||
|
{
|
||||||
|
'rotate-180': toggleState,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={() => setToggleState(!toggleState)}
|
||||||
|
data-testid={dataTestId}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{children}
|
||||||
|
<div className={classes} data-testid="toggle-icon-wrapper">
|
||||||
|
<Icon name="chevron-down" size={8} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './collapsible-toggle';
|
@ -833,5 +833,8 @@
|
|||||||
"multisigContractIncorrect": "is incorrectly configured. Validator and delegator rewards will be penalised until this is resolved.",
|
"multisigContractIncorrect": "is incorrectly configured. Validator and delegator rewards will be penalised until this is resolved.",
|
||||||
"learnMore": "Learn more",
|
"learnMore": "Learn more",
|
||||||
"AllValidators": "All validators",
|
"AllValidators": "All validators",
|
||||||
"AllProposals": "All proposals"
|
"AllProposals": "All proposals",
|
||||||
|
"RejectedProposals": "Rejected proposals",
|
||||||
|
"networkGovernance": "Network governance",
|
||||||
|
"networkUpgrades": "Network upgrades"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
export const collapsibleToggleStyles = (toggleState: boolean) =>
|
|
||||||
classnames('mb-4 transition-transform ease-in-out duration-300', {
|
|
||||||
'rotate-180': toggleState,
|
|
||||||
});
|
|
@ -1,9 +1,9 @@
|
|||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
|
|
||||||
export const ProposalDescription = ({
|
export const ProposalDescription = ({
|
||||||
description,
|
description,
|
||||||
@ -15,17 +15,13 @@ export const ProposalDescription = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-description">
|
<section data-testid="proposal-description">
|
||||||
<button
|
<CollapsibleToggle
|
||||||
onClick={() => setShowDescription(!showDescription)}
|
toggleState={showDescription}
|
||||||
data-testid="proposal-description-toggle"
|
setToggleState={setShowDescription}
|
||||||
|
dataTestId={'proposal-description-toggle'}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<SubHeading title={t('proposalDescription')} />
|
<SubHeading title={t('proposalDescription')} />
|
||||||
<div className={collapsibleToggleStyles(showDescription)}>
|
</CollapsibleToggle>
|
||||||
<Icon name="chevron-down" size={8} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showDescription && (
|
{showDescription && (
|
||||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
|
||||||
@ -16,17 +16,13 @@ export const ProposalJson = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-json">
|
<section data-testid="proposal-json">
|
||||||
<button
|
<CollapsibleToggle
|
||||||
onClick={() => setShowDetails(!showDetails)}
|
toggleState={showDetails}
|
||||||
data-testid="proposal-json-toggle"
|
setToggleState={setShowDetails}
|
||||||
|
dataTestId="proposal-json-toggle"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<SubHeading title={t('proposalJson')} />
|
<SubHeading title={t('proposalJson')} />
|
||||||
<div className={collapsibleToggleStyles(showDetails)}>
|
</CollapsibleToggle>
|
||||||
<Icon name="chevron-down" size={8} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showDetails && <SyntaxHighlighter data={proposal} />}
|
{showDetails && <SyntaxHighlighter data={proposal} />}
|
||||||
</section>
|
</section>
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
SyntaxHighlighter,
|
SyntaxHighlighter,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||||
import type { DataSourceDefinition } from '@vegaprotocol/types';
|
import type { DataSourceDefinition } from '@vegaprotocol/types';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
@ -77,17 +77,13 @@ export const ProposalMarketData = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative" data-testid="proposal-market-data">
|
<section className="relative" data-testid="proposal-market-data">
|
||||||
<button
|
<CollapsibleToggle
|
||||||
onClick={() => setShowDetails(!showDetails)}
|
toggleState={showDetails}
|
||||||
data-testid="proposal-market-data-toggle"
|
setToggleState={setShowDetails}
|
||||||
|
dataTestId="proposal-market-data-toggle"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<SubHeading title={t('marketSpecification')} />
|
<SubHeading title={t('marketSpecification')} />
|
||||||
<div className={collapsibleToggleStyles(showDetails)}>
|
</CollapsibleToggle>
|
||||||
<Icon name="chevron-down" size={8} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showDetails && (
|
{showDetails && (
|
||||||
<>
|
<>
|
||||||
|
@ -5,14 +5,13 @@ import {
|
|||||||
KeyValueTableRow,
|
KeyValueTableRow,
|
||||||
Thumbs,
|
Thumbs,
|
||||||
RoundedWrapper,
|
RoundedWrapper,
|
||||||
Icon,
|
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { formatNumber, formatNumberPercentage } from '@vegaprotocol/utils';
|
import { formatNumber, formatNumberPercentage } from '@vegaprotocol/utils';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { useVoteInformation } from '../../hooks';
|
import { useVoteInformation } from '../../hooks';
|
||||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||||
import { ProposalType } from '../proposal/proposal';
|
import { ProposalType } from '../proposal/proposal';
|
||||||
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
|
||||||
@ -59,17 +58,13 @@ export const ProposalVotesTable = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<CollapsibleToggle
|
||||||
onClick={() => setShowDetails(!showDetails)}
|
toggleState={showDetails}
|
||||||
data-testid="vote-breakdown-toggle"
|
setToggleState={setShowDetails}
|
||||||
|
dataTestId="vote-breakdown-toggle"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<SubHeading title={t('voteBreakdown')} />
|
<SubHeading title={t('voteBreakdown')} />
|
||||||
<div className={collapsibleToggleStyles(showDetails)}>
|
</CollapsibleToggle>
|
||||||
<Icon name="chevron-down" size={8} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showDetails && (
|
{showDetails && (
|
||||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||||
|
@ -3,6 +3,7 @@ import { render, screen } from '@testing-library/react';
|
|||||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||||
import { Proposal } from './proposal';
|
import { Proposal } from './proposal';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/network-parameters', () => ({
|
jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/network-parameters'),
|
...jest.requireActual('@vegaprotocol/network-parameters'),
|
||||||
@ -64,6 +65,17 @@ it('Renders with a link back to "all proposals"', async () => {
|
|||||||
expect(await screen.findByTestId('all-proposals-link')).toBeInTheDocument();
|
expect(await screen.findByTestId('all-proposals-link')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Renders a rejected proposals with a link back to "rejected proposals"', async () => {
|
||||||
|
const proposal = generateProposal({
|
||||||
|
state: ProposalState.STATE_REJECTED,
|
||||||
|
});
|
||||||
|
renderComponent(proposal);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByTestId('rejected-proposals-link')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('renders each section', async () => {
|
it('renders each section', async () => {
|
||||||
const proposal = generateProposal();
|
const proposal = generateProposal();
|
||||||
renderComponent(proposal);
|
renderComponent(proposal);
|
||||||
|
@ -17,6 +17,7 @@ import { ProposalMarketData } from '../proposal-market-data';
|
|||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||||
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export enum ProposalType {
|
export enum ProposalType {
|
||||||
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
||||||
@ -91,15 +92,23 @@ export const Proposal = ({
|
|||||||
return (
|
return (
|
||||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
<AsyncRenderer data={params} loading={loading} error={error}>
|
||||||
<section data-testid="proposal">
|
<section data-testid="proposal">
|
||||||
<div
|
<div className="flex items-center gap-1">
|
||||||
className="flex items-center gap-1"
|
|
||||||
data-testid="all-proposals-link"
|
|
||||||
>
|
|
||||||
<Icon name={'chevron-left'} />
|
<Icon name={'chevron-left'} />
|
||||||
|
|
||||||
|
{proposal.state === ProposalState.STATE_REJECTED ? (
|
||||||
|
<div data-testid="rejected-proposals-link">
|
||||||
|
<Link className="underline" to={Routes.PROPOSALS_REJECTED}>
|
||||||
|
{t('RejectedProposals')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div data-testid="all-proposals-link">
|
||||||
<Link className="underline" to={Routes.PROPOSALS}>
|
<Link className="underline" to={Routes.PROPOSALS}>
|
||||||
{t('AllProposals')}
|
{t('AllProposals')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<ProposalHeader proposal={proposal} isListItem={false} />
|
<ProposalHeader proposal={proposal} isListItem={false} />
|
||||||
|
|
||||||
<div id="details">
|
<div id="details">
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
import { render, fireEvent, screen } from '@testing-library/react';
|
||||||
|
import { ProposalsListFilter } from './proposals-list-filter';
|
||||||
|
|
||||||
|
describe('ProposalsListFilter', () => {
|
||||||
|
let setFilterString: jest.Mock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setFilterString = jest.fn();
|
||||||
|
render(
|
||||||
|
<ProposalsListFilter filterString="" setFilterString={setFilterString} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle the filter toggle click', () => {
|
||||||
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
|
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle input change', () => {
|
||||||
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
|
target: { value: 'test' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(setFilterString).toHaveBeenCalledWith('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 'clear filter' tests are handled in the proposals-list.spec.tsx file
|
||||||
|
// as it is responsible for the filter state
|
||||||
|
});
|
@ -1,13 +1,16 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ButtonLink, FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup, Icon, Input } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import type { Dispatch, SetStateAction } from 'react';
|
import type { Dispatch, SetStateAction } from 'react';
|
||||||
|
|
||||||
interface ProposalsListFilterProps {
|
interface ProposalsListFilterProps {
|
||||||
|
filterString: string;
|
||||||
setFilterString: Dispatch<SetStateAction<string>>;
|
setFilterString: Dispatch<SetStateAction<string>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProposalsListFilter = ({
|
export const ProposalsListFilter = ({
|
||||||
|
filterString,
|
||||||
setFilterString,
|
setFilterString,
|
||||||
}: ProposalsListFilterProps) => {
|
}: ProposalsListFilterProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -15,27 +18,39 @@ export const ProposalsListFilter = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="proposals-list-filter" className="mb-4">
|
<div data-testid="proposals-list-filter" className="mb-4">
|
||||||
{!filterVisible && (
|
<CollapsibleToggle
|
||||||
<ButtonLink
|
toggleState={filterVisible}
|
||||||
onClick={() => setFilterVisible(true)}
|
setToggleState={setFilterVisible}
|
||||||
data-testid="set-proposals-filter-visible"
|
dataTestId={'proposal-filter-toggle'}
|
||||||
>
|
>
|
||||||
{t('FilterProposals')}
|
<div className="text-xl mb-4">{t('FilterProposals')}</div>
|
||||||
</ButtonLink>
|
</CollapsibleToggle>
|
||||||
)}
|
|
||||||
{filterVisible && (
|
{filterVisible && (
|
||||||
<div data-testid="open-proposals-list-filter">
|
<div data-testid="proposals-list-filter-visible">
|
||||||
<p>{t('FilterProposalsDescription')}</p>
|
<p>{t('FilterProposalsDescription')}</p>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label="Filter text input"
|
label="Filter text input"
|
||||||
labelFor="filter-input"
|
labelFor="filter-input"
|
||||||
hideLabel={true}
|
hideLabel={true}
|
||||||
|
className="relative"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
|
value={filterString}
|
||||||
data-testid="filter-input"
|
data-testid="filter-input"
|
||||||
id="filter-input"
|
id="filter-input"
|
||||||
onChange={(e) => setFilterString(e.target.value)}
|
onChange={(e) => setFilterString(e.target.value)}
|
||||||
|
className="pr-8"
|
||||||
/>
|
/>
|
||||||
|
{filterString && filterString.length > 0 && (
|
||||||
|
<button
|
||||||
|
className="absolute top-2 right-2"
|
||||||
|
onClick={() => setFilterString('')}
|
||||||
|
data-testid="clear-filter"
|
||||||
|
>
|
||||||
|
<Icon name="cross" size={6} className="text-vega-light-200" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
import {
|
||||||
|
generateProposal,
|
||||||
|
generateProtocolUpgradeProposal,
|
||||||
|
} from '../../test-helpers/generate-proposals';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
@ -15,6 +18,7 @@ import {
|
|||||||
nextMonth,
|
nextMonth,
|
||||||
} from '../../test-helpers/mocks';
|
} from '../../test-helpers/mocks';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
|
|
||||||
const openProposalClosesNextMonth = generateProposal({
|
const openProposalClosesNextMonth = generateProposal({
|
||||||
id: 'proposal1',
|
id: 'proposal1',
|
||||||
@ -54,12 +58,22 @@ const failedProposalClosedLastMonth = generateProposal({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderComponent = (proposals: ProposalQuery['proposal'][]) => (
|
const closedProtocolUpgradeProposal = generateProtocolUpgradeProposal({
|
||||||
|
upgradeBlockHeight: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderComponent = (
|
||||||
|
proposals: ProposalQuery['proposal'][],
|
||||||
|
protocolUpgradeProposals?: ProtocolUpgradeProposalFieldsFragment[]
|
||||||
|
) => (
|
||||||
<Router>
|
<Router>
|
||||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposalsList proposals={proposals} protocolUpgradeProposals={[]} />
|
<ProposalsList
|
||||||
|
proposals={proposals}
|
||||||
|
protocolUpgradeProposals={protocolUpgradeProposals || []}
|
||||||
|
/>
|
||||||
</VegaWalletContext.Provider>
|
</VegaWalletContext.Provider>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
@ -143,17 +157,15 @@ describe('Proposals list', () => {
|
|||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
expect(
|
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||||
screen.getByTestId('open-proposals-list-filter')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Filters list by text - party id', () => {
|
it('Filters list by text - party id', () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'bvcx' },
|
target: { value: 'bvcx' },
|
||||||
});
|
});
|
||||||
@ -166,7 +178,7 @@ describe('Proposals list', () => {
|
|||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'proposal1' },
|
target: { value: 'proposal1' },
|
||||||
});
|
});
|
||||||
@ -179,7 +191,7 @@ describe('Proposals list', () => {
|
|||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'osal1' },
|
target: { value: 'osal1' },
|
||||||
});
|
});
|
||||||
@ -187,4 +199,92 @@ describe('Proposals list', () => {
|
|||||||
expect(container.querySelector('#proposal1')).toBeInTheDocument();
|
expect(container.querySelector('#proposal1')).toBeInTheDocument();
|
||||||
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
|
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('When filter is used, clear button is visible', () => {
|
||||||
|
render(
|
||||||
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
|
target: { value: 'test' },
|
||||||
|
});
|
||||||
|
expect(screen.getByTestId('clear-filter')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('When clear filter button is used, input is cleared', () => {
|
||||||
|
render(
|
||||||
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
|
target: { value: 'test' },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByTestId('clear-filter'));
|
||||||
|
expect((screen.getByTestId('filter-input') as HTMLInputElement).value).toBe(
|
||||||
|
''
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Displays a toggle for closed proposals if there are both closed governance proposals and closed upgrade proposals', () => {
|
||||||
|
render(
|
||||||
|
renderComponent(
|
||||||
|
[enactedProposalClosedLastWeek],
|
||||||
|
[closedProtocolUpgradeProposal]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('toggle-closed-proposals')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Does not display a toggle for closed proposals if there are only closed upgrade proposals', () => {
|
||||||
|
render(renderComponent([], [closedProtocolUpgradeProposal]));
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('toggle-closed-proposals')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Does not display a toggle for closed proposals if there are only closed governance proposals', () => {
|
||||||
|
render(renderComponent([enactedProposalClosedLastWeek]));
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('toggle-closed-proposals')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Does not display a toggle for closed proposals if the proposal filter is engaged', () => {
|
||||||
|
render(
|
||||||
|
renderComponent(
|
||||||
|
[enactedProposalClosedLastWeek],
|
||||||
|
[closedProtocolUpgradeProposal]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
|
target: { value: 'test' },
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('toggle-closed-proposals')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Displays closed governance proposals by default due to default for the toggle', () => {
|
||||||
|
render(
|
||||||
|
renderComponent(
|
||||||
|
[enactedProposalClosedLastWeek],
|
||||||
|
[closedProtocolUpgradeProposal]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('closed-governance-proposals')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Displays closed upgrade proposals when the toggle is clicked', () => {
|
||||||
|
render(
|
||||||
|
renderComponent(
|
||||||
|
[enactedProposalClosedLastWeek],
|
||||||
|
[closedProtocolUpgradeProposal]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByText('Network upgrades'));
|
||||||
|
expect(screen.getByTestId('closed-upgrade-proposals')).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,12 @@ import { ProposalsListItem } from '../proposals-list-item';
|
|||||||
import { ProtocolUpgradeProposalsListItem } from '../protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
import { ProtocolUpgradeProposalsListItem } from '../protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
import {
|
||||||
|
Button,
|
||||||
|
Toggle,
|
||||||
|
VegaIcon,
|
||||||
|
VegaIconNames,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
@ -54,6 +59,11 @@ export const orderByUpgradeBlockHeight = (
|
|||||||
['desc', 'desc']
|
['desc', 'desc']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
enum ClosedProposalsViewOptions {
|
||||||
|
NetworkGovernance = 'networkGovernance',
|
||||||
|
NetworkUpgrades = 'networkUpgrades',
|
||||||
|
}
|
||||||
|
|
||||||
export const ProposalsList = ({
|
export const ProposalsList = ({
|
||||||
proposals,
|
proposals,
|
||||||
protocolUpgradeProposals,
|
protocolUpgradeProposals,
|
||||||
@ -61,6 +71,10 @@ export const ProposalsList = ({
|
|||||||
}: ProposalsListProps) => {
|
}: ProposalsListProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [filterString, setFilterString] = useState('');
|
const [filterString, setFilterString] = useState('');
|
||||||
|
const [closedProposalsView, setClosedProposalsView] =
|
||||||
|
useState<ClosedProposalsViewOptions>(
|
||||||
|
ClosedProposalsViewOptions.NetworkGovernance
|
||||||
|
);
|
||||||
|
|
||||||
const sortedProposals: SortedProposalsProps = useMemo(() => {
|
const sortedProposals: SortedProposalsProps = useMemo(() => {
|
||||||
const initialSorting = proposals.reduce(
|
const initialSorting = proposals.reduce(
|
||||||
@ -109,7 +123,7 @@ export const ProposalsList = ({
|
|||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
open: orderByUpgradeBlockHeight(initialSorting.open),
|
open: orderByUpgradeBlockHeight(initialSorting.open),
|
||||||
closed: orderByUpgradeBlockHeight(initialSorting.closed).reverse(),
|
closed: orderByUpgradeBlockHeight(initialSorting.closed),
|
||||||
};
|
};
|
||||||
}, [protocolUpgradeProposals, lastBlockHeight]);
|
}, [protocolUpgradeProposals, lastBlockHeight]);
|
||||||
|
|
||||||
@ -127,6 +141,7 @@ export const ProposalsList = ({
|
|||||||
marginBottom={false}
|
marginBottom={false}
|
||||||
title={t('pageTitleProposals')}
|
title={t('pageTitleProposals')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{DocsLinks && (
|
||||||
<div className="xs:justify-self-end" data-testid="new-proposal-link">
|
<div className="xs:justify-self-end" data-testid="new-proposal-link">
|
||||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
|
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
|
||||||
@ -140,6 +155,7 @@ export const ProposalsList = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="mb-8">
|
<p className="mb-8">
|
||||||
{t(
|
{t(
|
||||||
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network. Network upgrades are proposed and approved by validators.`
|
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network. Network upgrades are proposed and approved by validators.`
|
||||||
@ -152,11 +168,26 @@ export const ProposalsList = ({
|
|||||||
{t(`Find out more about Vega governance`)}
|
{t(`Find out more about Vega governance`)}
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{proposals.length > 0 && (
|
{proposals.length > 0 && (
|
||||||
<ProposalsListFilter setFilterString={setFilterString} />
|
<ProposalsListFilter
|
||||||
|
filterString={filterString}
|
||||||
|
setFilterString={(value) => {
|
||||||
|
setFilterString(value);
|
||||||
|
if (value.length > 0) {
|
||||||
|
// If the filter is engaged, ensure the user is viewing governance proposals,
|
||||||
|
// as network upgrades do not have IDs to filter by and will be excluded.
|
||||||
|
setClosedProposalsView(
|
||||||
|
ClosedProposalsViewOptions.NetworkGovernance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
||||||
<SubHeading title={t('openProposals')} />
|
<SubHeading title={t('openProposals')} />
|
||||||
|
|
||||||
{sortedProposals.open.length > 0 ||
|
{sortedProposals.open.length > 0 ||
|
||||||
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
||||||
<ul data-testid="open-proposals">
|
<ul data-testid="open-proposals">
|
||||||
@ -166,6 +197,7 @@ export const ProposalsList = ({
|
|||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
||||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
||||||
))}
|
))}
|
||||||
@ -176,22 +208,81 @@ export const ProposalsList = ({
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
<section>
|
|
||||||
|
<section className="relative">
|
||||||
<SubHeading title={t('closedProposals')} />
|
<SubHeading title={t('closedProposals')} />
|
||||||
{sortedProposals.closed.length > 0 ||
|
{sortedProposals.closed.length > 0 ||
|
||||||
sortedProtocolUpgradeProposals.closed.length > 0 ? (
|
sortedProtocolUpgradeProposals.closed.length > 0 ? (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
// We need both the closed proposals and closed protocol upgrade
|
||||||
|
// proposals to be present for there to be a toggle. It also gets
|
||||||
|
// hidden if the user has filtered the list, as the upgrade proposals
|
||||||
|
// do not have the necessary fields for filtering.
|
||||||
|
sortedProposals.closed.length > 0 &&
|
||||||
|
sortedProtocolUpgradeProposals.closed.length > 0 &&
|
||||||
|
filterString.length < 1 && (
|
||||||
|
<div
|
||||||
|
className="grid w-full justify-end xl:-mt-12 pb-6"
|
||||||
|
data-testid="toggle-closed-proposals"
|
||||||
|
>
|
||||||
|
<div className="w-[440px]">
|
||||||
|
<Toggle
|
||||||
|
name="closed-proposals-toggle"
|
||||||
|
toggles={[
|
||||||
|
{
|
||||||
|
label: t(
|
||||||
|
ClosedProposalsViewOptions.NetworkGovernance
|
||||||
|
),
|
||||||
|
value: ClosedProposalsViewOptions.NetworkGovernance,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t(
|
||||||
|
ClosedProposalsViewOptions.NetworkUpgrades
|
||||||
|
),
|
||||||
|
value: ClosedProposalsViewOptions.NetworkUpgrades,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
checkedValue={closedProposalsView}
|
||||||
|
onChange={(e) =>
|
||||||
|
setClosedProposalsView(
|
||||||
|
e.target.value as ClosedProposalsViewOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
<ul data-testid="closed-proposals">
|
<ul data-testid="closed-proposals">
|
||||||
|
{closedProposalsView ===
|
||||||
|
ClosedProposalsViewOptions.NetworkUpgrades && (
|
||||||
|
<div data-testid="closed-upgrade-proposals">
|
||||||
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
|
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
|
||||||
<ProtocolUpgradeProposalsListItem
|
<ProtocolUpgradeProposalsListItem
|
||||||
key={proposal.upgradeBlockHeight}
|
key={proposal.upgradeBlockHeight}
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{sortedProposals.closed.filter(filterPredicate).map((proposal) => (
|
{closedProposalsView ===
|
||||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
ClosedProposalsViewOptions.NetworkGovernance && (
|
||||||
|
<div data-testid="closed-governance-proposals">
|
||||||
|
{sortedProposals.closed
|
||||||
|
.filter(filterPredicate)
|
||||||
|
.map((proposal) => (
|
||||||
|
<ProposalsListItem
|
||||||
|
key={proposal?.id}
|
||||||
|
proposal={proposal}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="mb-0" data-testid="no-closed-proposals">
|
<p className="mb-0" data-testid="no-closed-proposals">
|
||||||
{t('noClosedProposals')}
|
{t('noClosedProposals')}
|
||||||
|
@ -23,7 +23,10 @@ export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading title={t('pageTitleRejectedProposals')} />
|
<Heading title={t('pageTitleRejectedProposals')} />
|
||||||
<ProposalsListFilter setFilterString={setFilterString} />
|
<ProposalsListFilter
|
||||||
|
filterString={filterString}
|
||||||
|
setFilterString={setFilterString}
|
||||||
|
/>
|
||||||
<section>
|
<section>
|
||||||
{proposals.length > 0 ? (
|
{proposals.length > 0 ? (
|
||||||
<ul data-testid="rejected-proposals">
|
<ul data-testid="rejected-proposals">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
|
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import * as faker from 'faker';
|
import * as faker from 'faker';
|
||||||
import isArray from 'lodash/isArray';
|
import isArray from 'lodash/isArray';
|
||||||
@ -6,6 +7,40 @@ import mergeWith from 'lodash/mergeWith';
|
|||||||
|
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||||
|
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
|
|
||||||
|
export function generateProtocolUpgradeProposal(
|
||||||
|
override: PartialDeep<ProtocolUpgradeProposalFieldsFragment> = {}
|
||||||
|
): ProtocolUpgradeProposalFieldsFragment {
|
||||||
|
const defaultProposal: ProtocolUpgradeProposalFieldsFragment = {
|
||||||
|
__typename: 'ProtocolUpgradeProposal',
|
||||||
|
upgradeBlockHeight: '3917600',
|
||||||
|
vegaReleaseTag: 'v0.71.6',
|
||||||
|
approvers: [
|
||||||
|
'0ac70c4ccc7f961614fe49b93e639ddf916269b7dcf8391db264cefeadf5a6b7',
|
||||||
|
'63a1755006642bda9ab1bfa84660f944d30a113d1609590ca90c50b24aede472',
|
||||||
|
'68ed0770fc3e67b74d09c05443243d27e29a8513dc0e8628beb98338cd509159',
|
||||||
|
'a6e6f7daf8610f9242ab6ab46b394f6fb79cf9533d48051ca7a2f142b8b700a8',
|
||||||
|
'aad2be546ba83cbcab4c1d57ebe22b4a942f294f54333f1a7c2c9ef0e9fe19bb',
|
||||||
|
'acc55c7205cfcd5480e0235acab56a01487a39dc858a641fc04df6ba016870ee',
|
||||||
|
'b7e500deb24cc19bd6ebb2311997f0904ca0d9e51541249e9650ab41fd8ac376',
|
||||||
|
'cf295dff6d9506e8a905d168a44dfcff2f64bd0a6671783a469f8322959c62e2',
|
||||||
|
'f4686749895bf51c6df4092ef6be4279c384a3c380c24ea7a2fd20afc602a35d',
|
||||||
|
],
|
||||||
|
status:
|
||||||
|
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
return mergeWith<
|
||||||
|
ProtocolUpgradeProposalFieldsFragment,
|
||||||
|
PartialDeep<ProtocolUpgradeProposalFieldsFragment>
|
||||||
|
>(defaultProposal, override, (objValue, srcValue) => {
|
||||||
|
if (!isArray(objValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return srcValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function generateProposal(
|
export function generateProposal(
|
||||||
override: PartialDeep<ProposalQuery['proposal']> = {}
|
override: PartialDeep<ProposalQuery['proposal']> = {}
|
||||||
|
Loading…
Reference in New Issue
Block a user