Compare commits

..
Author SHA1 Message Date
Madalina Raicu a2c3308695 feat: add status indicator 2022-09-06 20:13:09 +03:00
328 changed files with 3349 additions and 5793 deletions
@@ -78,7 +78,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
ref: develop
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
@@ -130,11 +130,9 @@ jobs:
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
working-directory: frontend-monorepo
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
@@ -62,7 +62,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
ref: develop
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
@@ -114,11 +114,9 @@ jobs:
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
working-directory: frontend-monorepo
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
+2 -2
View File
@@ -69,7 +69,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
ref: develop
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
@@ -130,7 +130,7 @@ jobs:
working-directory: frontend-monorepo
- name: Run Cypress tests
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
+4
View File
@@ -6,3 +6,7 @@ yarn commitlint --edit "${1}"
# Lint all staged files
yarn lint-staged
# Build & test affected apps
yarn nx affected:build --uncommitted
yarn nx affected:test --uncommitted
+1 -1
View File
@@ -72,7 +72,7 @@ Run `nx serve my-app` for a dev server. Navigate to the port specified in `app/<
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
```bash
export GRAPHQL_SCHEMA_PATH=https://api.n11.testnet.vega.xyz/graphql
export NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
yarn nx run types:generate
```
@@ -39,7 +39,7 @@ describe('market selector', () => {
connectVegaWallet();
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].tradableInstrument.instrument.name
markets[0].name
);
cy.getByTestId('arrow-button').click();
cy.getByTestId('market-pane').should('be.visible');
@@ -47,7 +47,7 @@ describe('market selector', () => {
.children()
.find('[role="button"]')
.first()
.should('contain.text', markets[0].tradableInstrument.instrument.name);
.should('contain.text', markets[0].name);
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
@@ -69,9 +69,7 @@ describe('market selector', () => {
cy.get('input[placeholder="Search"]').clear();
cy.get('input[placeholder="Search"]').type('aa');
const filtered = markets.filter(
(market) =>
market.state === 'STATE_ACTIVE' &&
market.tradableInstrument.instrument.name.match(/aa/i)
(market) => market.state === 'STATE_ACTIVE' && market.name.match(/aa/i)
);
cy.getByTestId('market-pane')
.children()
@@ -88,7 +86,7 @@ describe('market selector', () => {
);
cy.get('input[placeholder="Search"]').should(
'have.value',
filtered[filtered.length - 1].tradableInstrument.instrument.name
filtered[filtered.length - 1].name
);
}
});
@@ -131,7 +129,7 @@ describe('market selector', () => {
cy.getByTestId('dialog-close').click();
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].tradableInstrument.instrument.name
markets[0].name
);
}
});
@@ -170,50 +170,7 @@ describe('Market trade', () => {
.find('button')
.should('have.text', '2');
cy.get('button').contains('Max').click();
}
});
it('slippage value should be displayed', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('button').contains('Max').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '0.02%');
}
});
it('allow slippage value to be adjusted', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('button').contains('Max').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '0.02%');
cy.get('#step-2-panel').find('dl').eq(2).find('button').click();
cy.get('#input-order-slippage')
.focus()
.type('{backspace}{backspace}{backspace}1');
cy.getByTestId('slippage-dialog').find('button').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '1%');
cy.getByTestId('price-slippage-value').should('have.text', '0.02%');
}
});
@@ -238,9 +195,9 @@ describe('Market trade', () => {
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
cy.get('#step-2-panel')
.find('dt')
.eq(2)
.eq(3)
.should('have.text', 'Est. Position Size (tDAI)');
cy.get('#step-2-panel').find('dd').eq(2).should('have.text', '197.86012');
cy.get('#step-2-panel').find('dd').eq(3).should('have.text', '197.86012');
}
});
@@ -253,11 +210,11 @@ describe('Market trade', () => {
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dt')
.eq(3)
.eq(4)
.should('have.text', 'Est. Fees (tDAI)');
cy.get('#step-2-panel')
.find('dd')
.eq(3)
.eq(4)
.should('have.text', '3.00000 (3.03%)');
}
});
@@ -75,11 +75,11 @@ const protoCandles = [
export const protoMarket = {
id: 'first-btcusd-id',
name: 'AAVEDAI Monthly (30 Jun 2022)',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'AAVEDAI.MF21',
name: 'AAVEDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:2839D9B2329C9E70',
@@ -2,13 +2,13 @@ export const generateDealTicket = () => {
return {
market: {
id: 'first-btcusd-id',
name: 'AAVEDAI Monthly (30 Jun 2022)',
decimalPlaces: 5,
positionDecimalPlaces: 0,
state: 'STATE_ACTIVE',
tradingMode: 'Continuous',
tradableInstrument: {
instrument: {
name: 'AAVEDAI Monthly (30 Jun 2022)',
product: {
quoteName: 'DAI',
settlementAsset: {
@@ -6,11 +6,11 @@ export const generateMarketNames = () => {
{ ...protoMarket },
{
id: '1d7ddf67dac4924db03f5bf58571a7bcb1908d70c66580467717aabc5345b68a',
name: 'Apple Monthly (30 Jun 2022)',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:4899E01009F1A721',
@@ -32,11 +32,11 @@ export const generateMarketNames = () => {
},
{
id: '87ae87cd3244fc1fab4b0e2dad2437879864192bb969f3109b69293421644c8b',
name: 'Tesla Quarterly (30 Jun 2022)',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'TSLA.QM21',
name: 'Tesla Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:5A86B190C384997F',
@@ -58,11 +58,11 @@ export const generateMarketNames = () => {
},
{
id: '69205712a854f1bbfb69fa3d11b60e01a1e249bafb5ece88115e7451e8ef07b3',
name: 'BTCUSD Monthly (30 Jun 2022)',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -83,11 +83,11 @@ export const generateMarketNames = () => {
},
{
id: 'ethbtc-quaterly',
name: 'ETHBTC Quarterly (30 Jun 2022)',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:1F0BB6EB5703B099',
@@ -108,11 +108,11 @@ export const generateMarketNames = () => {
},
{
id: '3c62b2714c4332d1a689a5352bff090e6aabccfd6bd87ce018936b38372530c9',
name: 'UNIDAI Monthly (30 Jun 2022)',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:3C58ED2A4A6C5D7E',
@@ -6,11 +6,11 @@ export const generateSimpleMarkets = () => {
{ ...protoMarket },
{
id: '57fbaa322e97cfc8bb5f1de048c37e033c41b1ac1906d3aed9960912a067ef5a',
name: 'CELUSD (June 2022)',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'CELUSD',
name: 'CELUSD (June 2022)',
metadata: {
tags: [
'base:CEL',
@@ -35,11 +35,11 @@ export const generateSimpleMarkets = () => {
},
{
id: 'ccf2f04865e5951ac3405da6e16b7cbdb535a0ad32df4df2dbed4262cf473255',
name: 'XMRUSD market',
state: 'Suspended',
tradableInstrument: {
instrument: {
code: 'XMRUSD',
name: 'XMRUSD market',
metadata: {
tags: [
'base:monero',
@@ -64,11 +64,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '6030e5b4e0ca3297a26081e5af4d453f97f96baab2d74bf56f84efcffc4c382f',
name: 'UNIDAI Monthly (30 Jun 2022)',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:3C58ED2A4A6C5D7E',
@@ -167,11 +167,11 @@ export const generateSimpleMarkets = () => {
},
{
id: 'de74a5572045b300e8ec50d136896912ec76e7d7ec135bc305dfd4854d9363a4',
name: 'XMRUSD market',
state: 'Pending',
tradableInstrument: {
instrument: {
code: 'XMRUSD',
name: 'XMRUSD market',
metadata: {
tags: [
'base:monero',
@@ -269,11 +269,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '31ea96284611771e486c820acb26a325a99664f9854b5a7e7ad99023efa8f9e6',
name: 'ApeCoin (18 Jul 2022)',
state: 'Closed',
tradableInstrument: {
instrument: {
code: 'APE/USD',
name: 'ApeCoin (18 Jul 2022)',
metadata: {
tags: [
'quote:USD',
@@ -297,11 +297,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '34cff959cdc2ffdb0f167820d701fe8b51cc6b8588e650d93369aaa22d6f8b74',
name: 'BTCUSD Monthly (18 Jul 2022)',
state: 'Settled',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -327,11 +327,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '87b0bbb3c171baa5d97dfc3852332829c91e5c5dc9f7c8fb584c6d8ac75aaaf2',
name: 'Builders Club x ETHcc',
state: 'Settled',
tradableInstrument: {
instrument: {
code: 'LTCUSD',
name: 'Builders Club x ETHcc',
metadata: {
tags: [
'base:litecoin',
@@ -417,11 +417,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '45266143c6e9b58f4cff9b8906e971c531bb29ea7af01066973f9b77e8134823',
name: 'BTCUSD Monthly (18 Jul 2022)',
state: 'Pending',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -447,11 +447,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '65663ebdc96161162769c4d5c5508137416748178d7cb28e2cb0d07a151a2bc6',
name: 'ETHBTC Quarterly (30 Jun 2022)',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:1F0BB6EB5703B099',
@@ -550,11 +550,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '234afabd27e3bce1a879039c041f9f00f915d98459935ddafd0169d38dd13850',
name: 'Apple Monthly (30 Jun 2022)',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:4899E01009F1A721',
@@ -654,11 +654,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '9f2a3c1caa67bb0773ec18d908d32b55b129b9ec2d106a8e9a87f6aa5c0375a6',
name: 'Tesla Quarterly (30 Jun 2022)',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'TSLA.QM21',
name: 'Tesla Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:5A86B190C384997F',
@@ -758,11 +758,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '8a45ee934d3ddac4b036f9884df1064a5353c620a56f775ba36597d0edef9a7a',
name: 'Go big or Go home',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'LTCUSD',
name: 'Go big or Go home',
metadata: {
tags: [
'base:litecoin',
@@ -801,11 +801,11 @@ export const generateSimpleMarkets = () => {
},
{
id: 'aede7b9ac0c3b225004929c5455160a00f59864aad32ec366e8a2bff1b30fd0f',
name: 'BTCUSD Monthly (08 Jul 2022)',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (08 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -831,11 +831,11 @@ export const generateSimpleMarkets = () => {
},
{
id: '9398707e01daa1a1f1ca6ff87cf8d6c03fe7373ce31121ce81b99a129e6bda47',
name: 'BTCUSD Monthly (18 Jul 2022)',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -866,16 +866,11 @@ export const generateSimpleMarkets = () => {
export const generateLongListMarkets = (count: number) => {
const markets = [];
for (let i = 0; i < count; i++) {
const { id, name } = protoMarket;
markets.push({
...protoMarket,
id: protoMarket.id + i,
tradableInstrument: {
...protoMarket.tradableInstrument,
instrument: {
...protoMarket.tradableInstrument.instrument,
name: protoMarket.tradableInstrument.instrument.name + i,
},
},
id: id + i,
name: name + i,
});
}
return { markets };
-22
View File
@@ -1,29 +1,7 @@
import '@testing-library/jest-dom';
import { defaultFallbackInView } from 'react-intersection-observer';
import ResizeObserver from 'resize-observer-polyfill';
defaultFallbackInView(true);
global.ResizeObserver = ResizeObserver;
global.DOMRect = class DOMRect {
bottom = 0;
left = 0;
right = 0;
top = 0;
constructor(
public x = 0,
public y = 0,
public width = 0,
public height = 0
) {}
static fromRect(other?: DOMRectInit): DOMRect {
return new DOMRect(other?.x, other?.y, other?.width, other?.height);
}
toJSON() {
return JSON.stringify(this);
}
};
Object.defineProperty(window, 'matchMedia', {
writable: true,
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -4,7 +4,6 @@ import { t } from '@vegaprotocol/react-helpers';
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
import { IconNames } from '@blueprintjs/icons';
import * as constants from './constants';
import { TrafficLight } from '../traffic-light';
interface DealTicketEstimatesProps {
quoteName?: string;
@@ -14,7 +13,6 @@ interface DealTicketEstimatesProps {
fees?: string;
notionalSize?: string;
size?: string;
slippage?: string;
}
interface DataTitleProps {
@@ -22,7 +20,7 @@ interface DataTitleProps {
quoteName?: string;
}
export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
<dt>
{children}
{quoteName && <small> ({quoteName})</small>}
@@ -30,20 +28,14 @@ export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
);
interface ValueTooltipProps {
value?: string;
children?: ReactNode;
value: string;
description: string;
id?: string;
}
export const ValueTooltipRow = ({
value,
children,
description,
id,
}: ValueTooltipProps) => (
const ValueTooltipRow = ({ value, description, id }: ValueTooltipProps) => (
<dd className="flex gap-x-2 items-center">
{value || children}
{value}
<Tooltip align="center" description={description}>
<div className="cursor-help" id={id || ''} tabIndex={-1}>
<Icon
@@ -64,7 +56,6 @@ export const DealTicketEstimates = ({
fees,
notionalSize,
size,
slippage,
}: DealTicketEstimatesProps) => (
<dl className="text-black dark:text-white">
{size && (
@@ -102,7 +93,7 @@ export const DealTicketEstimates = ({
</div>
)}
{estMargin && (
<div className="flex justify-between mb-2">
<div className="flex justify-between mb-8">
<DataTitle quoteName={quoteName}>{t('Est. Margin')}</DataTitle>
<ValueTooltipRow
value={estMargin}
@@ -111,23 +102,17 @@ export const DealTicketEstimates = ({
</div>
)}
{estCloseOut && (
<div className="flex justify-between mb-2">
<DataTitle quoteName={quoteName}>{t('Est. Close out')}</DataTitle>
<div className="flex justify-between">
<dt>
<span>{t('Est. Close out')}</span>
&nbsp;
<small>({quoteName})</small>
</dt>
<ValueTooltipRow
value={estCloseOut}
description={constants.EST_CLOSEOUT_TOOLTIP_TEXT}
/>
</div>
)}
{slippage && (
<div className="flex justify-between mb-2">
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
<TrafficLight value={parseFloat(slippage)} q1={1} q2={5}>
{slippage}%
</TrafficLight>
</ValueTooltipRow>
</div>
)}
</dl>
);
@@ -1,149 +0,0 @@
import React, { useCallback, useState } from 'react';
import { BigNumber } from 'bignumber.js';
import { t } from '@vegaprotocol/react-helpers';
import {
SliderRoot,
SliderThumb,
SliderTrack,
SliderRange,
FormGroup,
} from '@vegaprotocol/ui-toolkit';
import { InputSetter } from '../input-setter';
interface DealTicketSizeInputProps {
step: number;
min: number;
max: number;
value: number;
onValueChange: (value: number) => void;
positionDecimalPlaces: number;
}
const getSizeLabel = (value: number): string => {
const MIN_LABEL = 'Min';
const MAX_LABEL = 'Max';
if (value === 0) {
return MIN_LABEL;
} else if (value === 100) {
return MAX_LABEL;
}
return `${value}%`;
};
export const DealTicketSizeInput = ({
value,
step,
min,
max,
onValueChange,
positionDecimalPlaces,
}: DealTicketSizeInputProps) => {
const sizeRatios = [0, 25, 50, 75, 100];
const [inputValue, setInputValue] = useState(value);
const onInputValueChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
let value = parseFloat(event.target.value);
const isLessThanMin = value < min;
const isMoreThanMax = value > max;
if (isLessThanMin) {
value = min;
} else if (isMoreThanMax) {
value = max;
}
if (value) {
onValueChange(value);
}
setInputValue(value);
},
[min, max, onValueChange, setInputValue]
);
const onButtonValueChange = (size: number) => {
const newVal = new BigNumber(size)
.decimalPlaces(positionDecimalPlaces)
.toNumber();
onValueChange(newVal);
setInputValue(newVal);
};
const onSliderValueChange = useCallback(
(value: number[]) => {
const val = value[0];
setInputValue(val);
onValueChange(val);
},
[onValueChange]
);
return (
<div>
<div className="flex justify-between text-black dark:text-white mb-2">
<span data-testid="min-label">{min}</span>
<span data-testid="max-label">{max}</span>
</div>
<SliderRoot
className="mb-2"
value={[value]}
onValueChange={onSliderValueChange}
step={step}
min={min}
max={max}
>
<SliderTrack className="bg-lightGrey dark:bg-offBlack">
<SliderRange className="!bg-black dark:!bg-white" />
</SliderTrack>
<SliderThumb />
</SliderRoot>
<div
data-testid="percentage-selector"
className="flex w-full justify-between text-black dark:text-white mb-6"
>
{sizeRatios.map((size, index) => {
const proportionalSize = size ? (size / 100) * max : min;
return (
<button
className="no-underline hover:underline text-blue"
onClick={() => onButtonValueChange(proportionalSize)}
type="button"
key={index}
>
{getSizeLabel(size)}
</button>
);
})}
</div>
<dl className="text-black dark:text-white">
<div className="flex items-center justify-between">
<dt>{t('Contracts')}</dt>
<dd className="flex justify-end w-full">
<FormGroup
hideLabel={true}
label="Enter Size"
labelFor="trade-size-input"
className="mb-1"
>
<InputSetter
id="input-order-size-market"
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={inputValue}
onChange={onInputValueChange}
>
{inputValue}
</InputSetter>
</FormGroup>
</dd>
</div>
</dl>
</div>
);
};
@@ -1,13 +1,27 @@
import React from 'react';
import React, { useCallback, useState } from 'react';
import classNames from 'classnames';
import { IconNames } from '@blueprintjs/icons';
import { t } from '@vegaprotocol/react-helpers';
import {
SliderRoot,
SliderThumb,
SliderTrack,
SliderRange,
FormGroup,
Icon,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
import { DealTicketEstimates } from './deal-ticket-estimates';
import { DealTicketSizeInput } from './deal-ticket-size-input';
import { InputSetter } from '../input-setter';
import * as constants from './constants';
interface DealTicketSizeProps {
step: number;
min: number;
max: number;
size: number;
onSizeChange: (value: number) => void;
value: number;
onValueChange: (value: number[]) => void;
name: string;
quoteName: string;
price: string;
@@ -16,33 +30,173 @@ interface DealTicketSizeProps {
fees: string;
positionDecimalPlaces: number;
notionalSize: string;
slippage: string | null;
}
const getSizeLabel = (value: number): string => {
const MIN_LABEL = 'Min';
const MAX_LABEL = 'Max';
if (value === 0) {
return MIN_LABEL;
} else if (value === 100) {
return MAX_LABEL;
}
return `${value}%`;
};
export const DealTicketSize = ({
value,
step,
min,
max,
price,
quoteName,
size,
onSizeChange,
onValueChange,
estCloseOut,
positionDecimalPlaces,
fees,
notionalSize,
slippage,
}: DealTicketSizeProps) => {
const sizeRatios = [0, 25, 50, 75, 100];
const [inputValue, setInputValue] = useState(value);
const onInputValueChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const value = parseFloat(event.target.value);
const isLessThanMin = value < min;
const isMoreThanMax = value > max;
if (value) {
if (isLessThanMin) {
onValueChange([min]);
} else if (isMoreThanMax) {
onValueChange([max]);
} else {
onValueChange([value]);
}
}
setInputValue(value);
},
[min, max, onValueChange, setInputValue]
);
const onButtonValueChange = useCallback(
(size: number) => {
const newVal = new BigNumber(size)
.decimalPlaces(positionDecimalPlaces)
.toNumber();
onValueChange([newVal]);
setInputValue(newVal);
},
[onValueChange, positionDecimalPlaces]
);
const onSliderValueChange = useCallback(
(value: number[]) => {
setInputValue(value[0]);
onValueChange(value);
},
[onValueChange]
);
return max === 0 ? (
<p>Not enough balance to trade</p>
) : (
<div>
<DealTicketSizeInput
<div className="flex justify-between text-black dark:text-white mb-2">
<span data-testid="min-label">{min}</span>
<span data-testid="max-label">{max}</span>
</div>
<SliderRoot
className="mb-2"
value={[value]}
onValueChange={onSliderValueChange}
step={step}
min={min}
max={max}
value={size}
onValueChange={onSizeChange}
positionDecimalPlaces={positionDecimalPlaces}
/>
>
<SliderTrack className="bg-lightGrey dark:bg-offBlack">
<SliderRange className="!bg-black dark:!bg-white" />
</SliderTrack>
<SliderThumb />
</SliderRoot>
<div
data-testid="percentage-selector"
className="flex w-full justify-between text-black dark:text-white mb-6"
>
{sizeRatios.map((size, index) => {
const proportionalSize = size ? (size / 100) * max : min;
return (
<button
className="no-underline hover:underline text-blue"
onClick={() => onButtonValueChange(proportionalSize)}
type="button"
key={index}
>
{getSizeLabel(size)}
</button>
);
})}
</div>
<dl className="text-black dark:text-white">
<div className="flex items-center justify-between mb-4">
<dt>{t('Contracts')}</dt>
<dd className="flex justify-end w-full">
<FormGroup
hideLabel={true}
label="Enter Size"
labelFor="trade-size-input"
>
<InputSetter
id="input-order-size-market"
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={inputValue}
onChange={onInputValueChange}
/>
</FormGroup>
</dd>
</div>
</dl>
{slippage && (
<dl className="text-black dark:text-white">
<div className="flex items-center justify-between mb-8">
<dt>{t('Est. Price Impact / Slippage')}</dt>
<dd
className="flex justify-end gap-x-5"
data-testid="price-slippage-value"
aria-label={t('Est. Price Impact / Slippage')}
>
<span
className={classNames({
'text-darkerGreen dark:text-lightGreen':
parseFloat(slippage) < 1,
'text-amber':
parseFloat(slippage) >= 1 && parseFloat(slippage) < 5,
'text-vega-red': parseFloat(slippage) >= 5,
})}
>
{slippage}%
</span>
<Tooltip align="center" description={constants.EST_SLIPPAGE}>
<div className="cursor-help" tabIndex={-1}>
<Icon
name={IconNames.ISSUE}
className="block rotate-180"
ariaLabel={constants.EST_SLIPPAGE}
/>
</div>
</Tooltip>
</dd>
</div>
</dl>
)}
<DealTicketEstimates
quoteName={quoteName}
fees={fees}
@@ -1,104 +0,0 @@
import React, { useCallback, useState } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import * as constants from './constants';
import { TrafficLight } from '../traffic-light';
import { Dialog, Icon, Intent, Tooltip } from '@vegaprotocol/ui-toolkit';
import { InputSetter } from '../../components/input-setter';
import { IconNames } from '@blueprintjs/icons';
import { DataTitle, ValueTooltipRow } from './deal-ticket-estimates';
interface DealTicketSlippageProps {
step?: number;
min?: number;
max?: number;
value: number;
onValueChange(value: number): void;
}
export const DealTicketSlippage = ({
value,
step = 0.01,
min = 0,
max = 50,
onValueChange,
}: DealTicketSlippageProps) => {
const [isDialogVisible, setIsDialogVisible] = useState(false);
const onChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
const numericValue = parseFloat(value);
onValueChange(numericValue);
},
[onValueChange]
);
const toggleDialog = useCallback(() => {
setIsDialogVisible(!isDialogVisible);
}, [isDialogVisible]);
const formLabel = (
<label className="flex items-center mb-1">
<span className="mr-1">{t('Adjust slippage tolerance')}</span>
<Tooltip align="center" description={constants.EST_SLIPPAGE}>
<div className="cursor-help" tabIndex={-1}>
<Icon
name={IconNames.ISSUE}
className="block rotate-180"
ariaLabel={constants.EST_SLIPPAGE}
/>
</div>
</Tooltip>
</label>
);
return (
<>
<Dialog
open={isDialogVisible}
onChange={toggleDialog}
intent={Intent.None}
title={t('Transaction Settings')}
>
<div data-testid="slippage-dialog">
{formLabel}
<InputSetter
id="input-order-slippage"
isInputVisible
hasError={!value}
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={value}
onChange={onChange}
>
{value}%
</InputSetter>
</div>
</Dialog>
<dl className="text-black dark:text-white">
<div className="flex justify-between mb-2">
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
<div className="flex">
<div className="mr-1">
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
<TrafficLight value={value} q1={1} q2={5}>
{value}%
</TrafficLight>
</ValueTooltipRow>
</div>
<button type="button" onClick={toggleDialog}>
<Icon
name={IconNames.COG}
className="block rotate-180"
ariaLabel={t('Override slippage value')}
/>
</button>
</div>
</div>
</dl>
</>
);
};
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm, Controller } from 'react-hook-form';
import { Stepper } from '../stepper';
@@ -10,7 +10,7 @@ import type { Order } from '@vegaprotocol/orders';
import { useVegaWallet, VegaTxStatus } from '@vegaprotocol/wallet';
import {
t,
addDecimalsFormatNumber,
addDecimal,
toDecimal,
removeDecimal,
} from '@vegaprotocol/react-helpers';
@@ -33,8 +33,6 @@ import useOrderCloseOut from '../../hooks/use-order-closeout';
import useOrderMargin from '../../hooks/use-order-margin';
import useMaximumPositionSize from '../../hooks/use-maximum-position-size';
import useCalculateSlippage from '../../hooks/use-calculate-slippage';
import { Side, OrderType } from '@vegaprotocol/types';
import { DealTicketSlippage } from './deal-ticket-slippage';
interface DealTicketMarketProps {
market: DealTicketQuery_market;
@@ -64,26 +62,28 @@ export const DealTicketSteps = ({
defaultValues: getDefaultOrder(market),
});
const emptyString = ' - ';
const [max, setMax] = useState<number | null>(null);
const step = toDecimal(market.positionDecimalPlaces);
const orderType = watch('type');
const orderTimeInForce = watch('timeInForce');
const orderSide = watch('side');
const orderSize = watch('size');
const order = watch();
const { message: invalidText, isDisabled } = useOrderValidation({
market,
orderType,
orderTimeInForce,
fieldErrors: errors,
});
const { submit, transaction, finalizedOrder, Dialog } = useOrderSubmit();
const estCloseOut = useOrderCloseOut({ order, market, partyData });
const { keypair } = useVegaWallet();
const estMargin = useOrderMargin({
order,
market,
partyId: keypair?.pub || '',
});
const value = new BigNumber(orderSize).toNumber();
const price =
market.depth.lastTrade &&
addDecimal(market.depth.lastTrade.price, market.decimalPlaces);
const emptyString = ' - ';
const [notionalSize, setNotionalSize] = useState<string | null>(null);
const [fees, setFees] = useState<string | null>(null);
const maxTrade = useMaximumPositionSize({
partyId: keypair?.pub || '',
@@ -94,48 +94,45 @@ export const DealTicketSteps = ({
price: market?.depth?.lastTrade?.price,
order,
});
const estCloseOut = useOrderCloseOut({ order, market, partyData });
const slippage = useCalculateSlippage({ marketId: market.id, order });
const [slippageValue, setSlippageValue] = useState(
slippage ? parseFloat(slippage) : 0
);
const transactionStatus =
transaction.status === VegaTxStatus.Requested ||
transaction.status === VegaTxStatus.Pending
? 'pending'
: 'default';
useEffect(() => {
setMax(
new BigNumber(maxTrade)
.decimalPlaces(market.positionDecimalPlaces)
.toNumber()
);
}, [maxTrade, market.positionDecimalPlaces]);
const { message: invalidText, isDisabled } = useOrderValidation({
market,
orderType,
orderTimeInForce,
fieldErrors: errors,
});
const { submit, transaction, finalizedOrder, Dialog } = useOrderSubmit();
const onSizeChange = (value: number[]) => {
const newVal = new BigNumber(value[0])
.decimalPlaces(market.positionDecimalPlaces)
.toString();
const isValid = validateSize(step)(newVal);
if (isValid !== 'step') {
setValue('size', newVal);
}
};
useEffect(() => {
setSlippageValue(slippage ? parseFloat(slippage) : 0);
}, [slippage]);
const price = useMemo(() => {
if (slippage && market?.depth?.lastTrade?.price) {
const isLong = order.side === Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
parseFloat(slippage) / 100
);
return new BigNumber(market?.depth?.lastTrade?.price)
.multipliedBy(multiplier)
if (market?.depth?.lastTrade?.price) {
const size = new BigNumber(market.depth.lastTrade.price)
.multipliedBy(value)
.toNumber();
setNotionalSize(addDecimal(size, market.decimalPlaces));
}
return null;
}, [market?.depth?.lastTrade?.price, order.side, slippage]);
}, [market, value]);
const formattedPrice =
price && addDecimalsFormatNumber(price, market.decimalPlaces);
const notionalSize = useMemo(() => {
if (price) {
const size = new BigNumber(price).multipliedBy(orderSize).toNumber();
return addDecimalsFormatNumber(size, market.decimalPlaces);
}
return null;
}, [market.decimalPlaces, orderSize, price]);
const fees = useMemo(() => {
useEffect(() => {
if (estMargin?.fees && notionalSize) {
const percentage = new BigNumber(estMargin?.fees)
.dividedBy(notionalSize)
@@ -143,66 +140,17 @@ export const DealTicketSteps = ({
.decimalPlaces(2)
.toString();
return `${estMargin.fees} (${percentage}%)`;
setFees(`${estMargin.fees} (${percentage}%)`);
}
}, [estMargin, notionalSize]);
return null;
}, [estMargin?.fees, notionalSize]);
const transactionStatus =
transaction.status === VegaTxStatus.Requested ||
transaction.status === VegaTxStatus.Pending
? 'pending'
: 'default';
const max = useMemo(() => {
return new BigNumber(maxTrade)
.decimalPlaces(market.positionDecimalPlaces)
.toNumber();
}, [market.positionDecimalPlaces, maxTrade]);
const onSizeChange = useCallback(
(value: number) => {
const newVal = new BigNumber(value)
.decimalPlaces(market.positionDecimalPlaces)
.toString();
const isValid = validateSize(step)(newVal);
if (isValid !== 'step') {
setValue('size', newVal);
}
},
[market.positionDecimalPlaces, setValue, step]
);
const onSlippageChange = useCallback(
(value: number) => {
if (market?.depth?.lastTrade?.price) {
if (value) {
const isLong = order.side === Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
value / 100
);
const bestAskPrice = new BigNumber(market?.depth?.lastTrade?.price)
.multipliedBy(multiplier)
.decimalPlaces(market.decimalPlaces)
.toString();
setValue('price', bestAskPrice);
if (orderType === OrderType.TYPE_MARKET) {
setValue('type', OrderType.TYPE_LIMIT);
}
} else {
setValue('type', OrderType.TYPE_MARKET);
setValue('price', market?.depth?.lastTrade?.price);
}
}
setSlippageValue(value);
},
[
market.decimalPlaces,
market?.depth?.lastTrade?.price,
order.side,
orderType,
setValue,
]
);
const onSubmit = useCallback(
const onSubmit = React.useCallback(
(order: Order) => {
if (transactionStatus !== 'pending') {
submit({
@@ -231,7 +179,7 @@ export const DealTicketSteps = ({
ItemRenderer={MarketNameRenderer}
/>
),
value: market.tradableInstrument.instrument.name,
value: market.name,
},
{
label: t('Select Direction'),
@@ -250,30 +198,25 @@ export const DealTicketSteps = ({
label: t('Choose Position Size'),
component:
max !== null ? (
<>
<DealTicketSize
step={step}
min={step}
max={max}
onSizeChange={onSizeChange}
size={new BigNumber(orderSize).toNumber()}
name="size"
price={formattedPrice || emptyString}
positionDecimalPlaces={market.positionDecimalPlaces}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
estCloseOut={estCloseOut}
fees={fees || emptyString}
estMargin={estMargin?.margin || emptyString}
/>
<DealTicketSlippage
value={slippageValue}
onValueChange={onSlippageChange}
/>
</>
<DealTicketSize
step={step}
min={step}
max={max}
onValueChange={onSizeChange}
value={new BigNumber(orderSize).toNumber()}
name="size"
price={price || emptyString}
positionDecimalPlaces={market.positionDecimalPlaces}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
estCloseOut={estCloseOut}
fees={fees || emptyString}
estMargin={estMargin?.margin || emptyString}
slippage={slippage}
/>
) : (
'loading...'
),
@@ -297,14 +240,13 @@ export const DealTicketSteps = ({
order={order}
estCloseOut={estCloseOut}
estMargin={estMargin?.margin || emptyString}
price={formattedPrice || emptyString}
price={price || emptyString}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
fees={fees || emptyString}
slippage={slippageValue}
/>
<Dialog
title={getOrderDialogTitle(finalizedOrder?.status)}
@@ -43,7 +43,6 @@ interface Props {
price: string;
fees: string;
notionalSize: string;
slippage: number;
}
export default ({
@@ -56,7 +55,6 @@ export default ({
fees,
price,
notionalSize,
slippage,
}: Props) => {
const { data: tagsData } = useQuery<MarketTags, MarketTagsVariables>(
MARKET_TAGS_QUERY,
@@ -107,7 +105,6 @@ export default ({
fees={fees}
estCloseOut={estCloseOut}
notionalSize={notionalSize}
slippage={slippage.toString()}
/>
<div className="mt-12 max-w-sm">
@@ -3,8 +3,6 @@
// @generated
// This file was automatically generated and should not be edited.
import { AssetStatus } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: Deposits
// ====================================================
@@ -41,10 +39,6 @@ export interface Deposits_assetsConnection_edges_node {
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
/**
* The status of the asset in the Vega network
*/
status: AssetStatus;
/**
* The origin source of the asset (e.g: an ERC20 asset)
*/
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,10 +1,11 @@
import { gql, useQuery } from '@apollo/client';
import { DepositManager } from '@vegaprotocol/deposits';
import { getEnabledAssets, t } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/react-helpers';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { Web3Container } from '@vegaprotocol/web3';
import { assetsConnectionToAssets } from '@vegaprotocol/react-helpers';
import type { Deposits } from './__generated__/Deposits';
const DEPOSITS_QUERY = gql`
@@ -16,7 +17,6 @@ const DEPOSITS_QUERY = gql`
name
symbol
decimals
status
source {
... on ERC20 {
contractAddress
@@ -40,7 +40,7 @@ export const DepositContainer = () => {
skip: !keypair?.pub,
});
const assets = getEnabledAssets(data);
const assets = assetsConnectionToAssets(data?.assetsConnection);
return (
<AsyncRenderer<Deposits> data={data} loading={loading} error={error}>
@@ -1,19 +1,20 @@
import React, { useCallback, useState } from 'react';
import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import { Input } from '@vegaprotocol/ui-toolkit';
import type { InputProps } from '@vegaprotocol/ui-toolkit';
interface InputSetterProps {
buttonLabel?: string;
value: string | number;
isInputVisible?: boolean;
children?: ReactNode;
onValueChange?: () => string;
}
export const InputSetter = ({
buttonLabel = t('set'),
value = '',
isInputVisible = false,
children,
onValueChange,
...props
}: InputSetterProps & InputProps) => {
const [isInputToggled, setIsInputToggled] = useState(isInputVisible);
@@ -34,7 +35,7 @@ export const InputSetter = ({
return isInputToggled ? (
<div className="flex items-center">
<Input {...props} onKeyDown={onInputEnter} />
<Input {...props} value={value} onKeyDown={onInputEnter} />
<button
type="button"
className="no-underline hover:underline text-blue ml-2"
@@ -46,10 +47,10 @@ export const InputSetter = ({
) : (
<button
type="button"
className="no-underline hover:underline text-blue py-1.5"
className="no-underline hover:underline text-blue"
onClick={toggleInput}
>
{children || props.value}
{value}
</button>
);
};
@@ -8,7 +8,7 @@ interface TabBarProps {
export const TabBar = ({ className }: TabBarProps) => (
<div role="group" aria-label="Tab Bar Navigation Menu" className={className}>
<div role="presentation" className="py-[42px]" />
<div className="md:hidden fixed bottom-0 left-0 right-0 bg-black py-4 border-t border-neutral-300 dark:border-neutral-600">
<div className="md:hidden fixed bottom-0 left-0 right-0 bg-black py-4 border-t border-neutral-300 dark:border-neutral-700">
<Nav tabs className="flex justify-evenly items-center" />
</div>
</div>
@@ -43,10 +43,6 @@ export interface SimpleMarkets_markets_tradableInstrument_instrument {
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
*/
code: string;
/**
* Full and fairly descriptive name for the instrument
*/
name: string;
/**
* Metadata for this instrument
*/
@@ -83,6 +79,10 @@ export interface SimpleMarkets_markets {
* Market ID
*/
id: string;
/**
* Market full name
*/
name: string;
/**
* Current state of the market
*/
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -14,11 +14,11 @@ export const MARKETS_QUERY = gql`
query SimpleMarkets($CandleSince: String!) {
markets {
id
name
state
tradableInstrument {
instrument {
code
name
metadata {
tags
}
@@ -6,19 +6,17 @@ import {
waitFor,
cleanup,
getAllByRole,
fireEvent,
} from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { BrowserRouter } from 'react-router-dom';
import { MarketState } from '@vegaprotocol/types';
import SimpleMarketList from './simple-market-list';
import { MARKETS_QUERY } from './data-provider';
import type {
SimpleMarkets_markets,
SimpleMarkets,
} from './__generated__/SimpleMarkets';
import type { SimpleMarketDataSub_marketData } from './__generated__/SimpleMarketDataSub';
import { MarketState } from '@vegaprotocol/types';
const mockedNavigate = jest.fn();
@@ -32,85 +30,85 @@ jest.mock('date-fns', () => ({
subDays: () => new Date('2022-06-02T11:11:21.721Z'),
}));
let updateMock: ({
delta,
}: {
delta: SimpleMarketDataSub_marketData;
}) => boolean;
let mockData = [
{
id: '1',
name: 'Market 1',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'tUSD',
},
},
metadata: {
tags: [],
},
},
},
},
{
id: '2',
name: 'Market 2',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'ETH',
},
},
metadata: {
tags: [],
},
},
},
},
] as unknown as SimpleMarkets_markets[];
const mockUseDataProvider = ({ update }: { update: () => boolean }) => {
updateMock = update;
return { data: mockData, loading: false, error: false };
};
jest.mock('@vegaprotocol/react-helpers', () => ({
...jest.requireActual('@vegaprotocol/react-helpers'),
useDataProvider: jest.fn((args) => mockUseDataProvider(args)),
}));
const mockIsTradable = jest.fn((_arg) => true);
jest.mock('../../constants', () => ({
...jest.requireActual('../../constants'),
IS_MARKET_TRADABLE: jest.fn((arg) => mockIsTradable(arg)),
}));
describe('SimpleMarketList', () => {
afterEach(() => {
jest.clearAllMocks();
cleanup();
});
const mocks: MockedResponse<SimpleMarkets> = {
request: {
query: MARKETS_QUERY,
variables: {
CandleSince: '2022-06-02T11:11:21.721Z',
it('should be properly renderer as empty', async () => {
const mocks: MockedResponse<SimpleMarkets> = {
request: {
query: MARKETS_QUERY,
variables: {
CandleSince: '2022-06-02T11:11:21.721Z',
},
},
},
result: {
data: { markets: mockData },
},
};
result: {
data: { markets: [] },
},
};
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(screen.getByText('No data to display')).toBeInTheDocument();
});
});
it('should be properly rendered with some data', async () => {
const data = [
{
id: '1',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'tUSD',
},
},
metadata: {
tags: [],
},
},
},
},
{
id: '2',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'ETH',
},
},
metadata: {
tags: [],
},
},
},
},
] as unknown as SimpleMarkets_markets[];
const mocks: MockedResponse<SimpleMarkets> = {
request: {
query: MARKETS_QUERY,
variables: {
CandleSince: '2022-06-02T11:11:21.721Z',
},
},
result: {
data: { markets: data },
},
};
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
@@ -130,92 +128,4 @@ describe('SimpleMarketList', () => {
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
});
});
it('update should return proper boolean value', async () => {
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
await expect(
updateMock({
delta: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '2',
state: MarketState.STATE_ACTIVE,
},
},
})
).toEqual(true);
await expect(
updateMock({
delta: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '2',
state: MarketState.STATE_SUSPENDED,
},
},
})
).toEqual(false);
});
it('click on row should be properly handled', async () => {
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
mockIsTradable.mockClear();
const container = document.querySelector('.ag-center-cols-container');
const firstRow = getAllByRole(container as HTMLDivElement, 'row')[0];
expect(firstRow).toHaveAttribute('row-id', '1');
fireEvent.click(firstRow);
await waitFor(() => {
expect(mockIsTradable).toHaveBeenCalledWith({
...mockData[0],
percentChange: '-',
});
expect(mockedNavigate).toHaveBeenCalledWith(`/trading/${mockData[0].id}`);
});
});
it('should be properly renderer as empty', async () => {
mockData = [];
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(screen.getByText('No data to display')).toBeInTheDocument();
});
});
});
@@ -1,36 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import type { MarketNames_markets } from '@vegaprotocol/deal-ticket';
import { MarketState } from '@vegaprotocol/types';
import MarketNameRenderer from './simple-market-renderer';
describe('SimpleMarketRenderer', () => {
const market = {
id: 'marketId',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
code: 'Market code',
name: 'Market Name',
product: {
quoteName: 'Quote name',
},
metadata: {
tags: null,
},
},
},
} as MarketNames_markets;
it('should properly render not mobile', () => {
render(<MarketNameRenderer market={market} isMobile={false} />);
expect(screen.getByText('Market Name')).toBeInTheDocument();
expect(screen.getByText('Quote name')).toBeInTheDocument();
});
it('should properly render mobile', () => {
render(<MarketNameRenderer market={market} isMobile={true} />);
expect(screen.getByText('Market code')).toBeInTheDocument();
expect(screen.getByText('Quote name')).toBeInTheDocument();
});
});
@@ -17,9 +17,7 @@ const MarketNameRenderer = ({ market, isMobile }: Props) => {
)}
/>
<div className="col-span-2 uppercase justify-start text-black dark:text-white text-ui-small md:text-market self-end">
{isMobile
? market.tradableInstrument.instrument.code
: market.tradableInstrument.instrument.name}{' '}
{isMobile ? market.tradableInstrument.instrument.code : market.name}{' '}
<SimpleMarketExpires
tags={market.tradableInstrument.instrument.metadata.tags}
/>
@@ -1,35 +1,11 @@
import React from 'react';
import {
useLocation,
useRoutes,
BrowserRouter,
useParams,
} from 'react-router-dom';
import {
render,
screen,
fireEvent,
waitFor,
act,
getAllByText,
} from '@testing-library/react';
import { useLocation, useRoutes, BrowserRouter } from 'react-router-dom';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { MarketState } from '@vegaprotocol/types';
import SimpleMarketToolbar from './simple-market-toolbar';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import { markets as filterData } from './mocks/market-filters.json';
const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => {
const actualRouter = jest.requireActual('react-router-dom');
return {
...actualRouter,
useNavigate: () => mockedNavigate,
useParams: jest.fn(() => actualRouter.useParams()),
};
});
describe('SimpleMarketToolbar', () => {
const WrappedCompForTest = () => {
const routes = useRoutes([
@@ -84,8 +60,9 @@ describe('SimpleMarketToolbar', () => {
</>
);
};
afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
it('should be properly rendered', async () => {
@@ -106,7 +83,7 @@ describe('SimpleMarketToolbar', () => {
expect(screen.getByTestId('market-assets-menu').children).toHaveLength(6);
});
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
expect(screen.getByRole('menu').children).toHaveLength(10);
});
@@ -127,7 +104,7 @@ describe('SimpleMarketToolbar', () => {
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
`/markets/${MarketState.STATE_ACTIVE}/Future`
'/markets/STATE_ACTIVE/Future'
);
});
@@ -138,91 +115,17 @@ describe('SimpleMarketToolbar', () => {
);
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
`/markets/${MarketState.STATE_ACTIVE}/Future/tEURO`
'/markets/STATE_ACTIVE/Future/tEURO'
);
});
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const pending = getAllByText(menu, 'Pending')[0];
await act(() => {
fireEvent.click(pending);
});
await waitFor(() => {
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_PENDING}/Future/tEURO`
fireEvent.click(screen.getByText('Pending'));
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Pending/Future/tEURO'
);
});
});
it('stateChange callback should work well', async () => {
(useParams as jest.Mock).mockImplementation(() => ({
asset: 'asset1',
product: 'product1',
state: 'state1',
}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const suspended = getAllByText(menu, 'Suspended')[0];
fireEvent.click(suspended);
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_SUSPENDED}/product1/asset1`
);
});
it('stateChange callback should call navigate with url with state only', async () => {
(useParams as jest.Mock).mockImplementation(() => ({}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const closed = getAllByText(menu, 'Closed')[0];
fireEvent.click(closed);
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_CLOSED}`
);
});
it('stateChange callback should call navigate with no asset', async () => {
(useParams as jest.Mock).mockImplementation(() => ({
asset: 'all',
state: 'state1',
}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const active = getAllByText(menu, 'Active')[0];
fireEvent.click(active);
expect(mockedNavigate).toHaveBeenCalledWith('/markets');
});
});
@@ -123,11 +123,10 @@ const SimpleMarketToolbar = ({ data }: Props) => {
</ul>
<div className="grid gap-4 pb-2 mt-2 md:mt-6 md:grid-cols-[min-content,min-content,1fr]">
<div className="pb-2">
<DropdownMenu open={isOpen} onOpenChange={(open) => setOpen(open)}>
<DropdownMenu onOpenChange={(open) => setOpen(open)}>
<DropdownMenuTrigger
className="mr-2 w-auto text-capMenu text-black dark:text-white"
data-testid="state-trigger"
onClick={() => setOpen(!isOpen)}
>
<div className="w-full justify-between uppercase inline-flex items-center justify-center box-border">
{STATES_FILTER.find(
@@ -88,7 +88,7 @@ export const Stepper = ({ steps }: StepperProps) => {
'md:mt-0 font-alpha uppercase text-black dark:text-white',
{
'mt-2 text-md md:text-2xl': isActive,
'mt-4 text-sm md:text-lg md:ml-2': !isActive,
'mt-4 text-sm md:text-lg md:ml-8': !isActive,
}
)}
>
@@ -1 +0,0 @@
export * from './traffic-light';
@@ -1,25 +0,0 @@
import React from 'react';
import type { ReactNode } from 'react';
import classNames from 'classnames';
interface TrafficLightProps {
value: number;
q1: number;
q2: number;
children: ReactNode;
}
export const TrafficLight = ({
value,
q1,
q2,
children,
}: TrafficLightProps) => {
const slippageClassName = classNames({
'text-darkerGreen dark:text-lightGreen': value < q1,
'text-amber': value >= q1 && value < q2,
'text-vega-red': value >= q2,
});
return <div className={slippageClassName}>{children || value}</div>;
};
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -22,7 +22,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
headerName: t('Markets'),
headerClass: 'uppercase',
minWidth: isMobile ? 160 : 350,
field: 'tradableInstrument.instrument.name',
field: 'name',
cellClass: 'overflow-visible',
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
<MarketNameRenderer market={data} isMobile={isMobile} />
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,5 +1,5 @@
import { gql, useQuery } from '@apollo/client';
import { getAssets, t } from '@vegaprotocol/react-helpers';
import { assetsConnectionToAssets, t } from '@vegaprotocol/react-helpers';
import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
@@ -39,7 +39,7 @@ export const ASSETS_QUERY = gql`
const Assets = () => {
const { data } = useQuery<AssetsQuery>(ASSETS_QUERY);
const assets = getAssets(data);
const assets = assetsConnectionToAssets(data?.assetsConnection);
return (
<section>
@@ -9,23 +9,7 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
// GraphQL query operation: ProposalsQuery
// ====================================================
export interface ProposalsQuery_proposalsConnection_edges_node_rationale {
__typename: "ProposalRationale";
/**
* Title to be used to give a short description of the proposal in lists.
* This is to be between 0 and 100 unicode characters.
* This is mandatory for all proposals.
*/
title: string;
/**
* Description to show a short title / something in case the link goes offline.
* This is to be between 0 and 20k unicode characters.
* This is mandatory for all proposals.
*/
description: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_party {
export interface ProposalsQuery_proposals_party {
__typename: "Party";
/**
* Party identifier
@@ -33,11 +17,11 @@ export interface ProposalsQuery_proposalsConnection_edges_node_party {
id: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateAsset {
export interface ProposalsQuery_proposals_terms_change_UpdateAsset {
__typename: "UpdateAsset" | "NewFreeform";
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket_instrument {
export interface ProposalsQuery_proposals_terms_change_NewMarket_instrument {
__typename: "InstrumentConfiguration";
/**
* Full and fairly descriptive name for the instrument
@@ -45,20 +29,20 @@ export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewM
name: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket {
export interface ProposalsQuery_proposals_terms_change_NewMarket {
__typename: "NewMarket";
/**
* New market instrument configuration
*/
instrument: ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket_instrument;
instrument: ProposalsQuery_proposals_terms_change_NewMarket_instrument;
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateMarket {
export interface ProposalsQuery_proposals_terms_change_UpdateMarket {
__typename: "UpdateMarket";
marketId: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset {
export interface ProposalsQuery_proposals_terms_change_NewAsset_source_BuiltinAsset {
__typename: "BuiltinAsset";
/**
* Maximum amount that can be requested by a party through the built-in asset faucet at a time
@@ -66,7 +50,7 @@ export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewA
maxFaucetAmountMint: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20 {
export interface ProposalsQuery_proposals_terms_change_NewAsset_source_ERC20 {
__typename: "ERC20";
/**
* The address of the ERC20 contract
@@ -74,9 +58,9 @@ export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewA
contractAddress: string;
}
export type ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source = ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset | ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20;
export type ProposalsQuery_proposals_terms_change_NewAsset_source = ProposalsQuery_proposals_terms_change_NewAsset_source_BuiltinAsset | ProposalsQuery_proposals_terms_change_NewAsset_source_ERC20;
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset {
export interface ProposalsQuery_proposals_terms_change_NewAsset {
__typename: "NewAsset";
/**
* The symbol of the asset (e.g: GBP)
@@ -85,10 +69,10 @@ export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewA
/**
* The source of the new asset
*/
source: ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source;
source: ProposalsQuery_proposals_terms_change_NewAsset_source;
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter {
export interface ProposalsQuery_proposals_terms_change_UpdateNetworkParameter_networkParameter {
__typename: "NetworkParameter";
/**
* The name of the network parameter
@@ -100,14 +84,14 @@ export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_Upda
value: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter {
export interface ProposalsQuery_proposals_terms_change_UpdateNetworkParameter {
__typename: "UpdateNetworkParameter";
networkParameter: ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter;
networkParameter: ProposalsQuery_proposals_terms_change_UpdateNetworkParameter_networkParameter;
}
export type ProposalsQuery_proposalsConnection_edges_node_terms_change = ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateAsset | ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket | ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateMarket | ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset | ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter;
export type ProposalsQuery_proposals_terms_change = ProposalsQuery_proposals_terms_change_UpdateAsset | ProposalsQuery_proposals_terms_change_NewMarket | ProposalsQuery_proposals_terms_change_UpdateMarket | ProposalsQuery_proposals_terms_change_NewAsset | ProposalsQuery_proposals_terms_change_UpdateNetworkParameter;
export interface ProposalsQuery_proposalsConnection_edges_node_terms {
export interface ProposalsQuery_proposals_terms {
__typename: "ProposalTerms";
/**
* RFC3339Nano time and date when voting closes for this proposal.
@@ -123,18 +107,18 @@ export interface ProposalsQuery_proposalsConnection_edges_node_terms {
/**
* Actual change being introduced by the proposal - action the proposal triggers if passed and enacted.
*/
change: ProposalsQuery_proposalsConnection_edges_node_terms_change;
change: ProposalsQuery_proposals_terms_change;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface ProposalsQuery_proposals_votes_yes_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
currentStakeAvailable: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party {
export interface ProposalsQuery_proposals_votes_yes_votes_party {
__typename: "Party";
/**
* Party identifier
@@ -143,10 +127,10 @@ export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_p
/**
* The staking information for this Party
*/
stakingSummary: ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary;
stake: ProposalsQuery_proposals_votes_yes_votes_party_stake;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes {
export interface ProposalsQuery_proposals_votes_yes_votes {
__typename: "Vote";
/**
* The vote value cast
@@ -155,14 +139,14 @@ export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes {
/**
* The party casting the vote
*/
party: ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party;
party: ProposalsQuery_proposals_votes_yes_votes_party;
/**
* RFC3339Nano time and date when the vote reached Vega network
*/
datetime: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes {
export interface ProposalsQuery_proposals_votes_yes {
__typename: "ProposalVoteSide";
/**
* Total number of governance tokens from the votes cast for this side
@@ -175,18 +159,18 @@ export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes {
/**
* All votes cast for this side
*/
votes: ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes[] | null;
votes: ProposalsQuery_proposals_votes_yes_votes[] | null;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface ProposalsQuery_proposals_votes_no_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
currentStakeAvailable: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party {
export interface ProposalsQuery_proposals_votes_no_votes_party {
__typename: "Party";
/**
* Party identifier
@@ -195,10 +179,10 @@ export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_pa
/**
* The staking information for this Party
*/
stakingSummary: ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary;
stake: ProposalsQuery_proposals_votes_no_votes_party_stake;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes {
export interface ProposalsQuery_proposals_votes_no_votes {
__typename: "Vote";
/**
* The vote value cast
@@ -207,14 +191,14 @@ export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes {
/**
* The party casting the vote
*/
party: ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party;
party: ProposalsQuery_proposals_votes_no_votes_party;
/**
* RFC3339Nano time and date when the vote reached Vega network
*/
datetime: string;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no {
export interface ProposalsQuery_proposals_votes_no {
__typename: "ProposalVoteSide";
/**
* Total number of governance tokens from the votes cast for this side
@@ -227,31 +211,27 @@ export interface ProposalsQuery_proposalsConnection_edges_node_votes_no {
/**
* All votes cast for this side
*/
votes: ProposalsQuery_proposalsConnection_edges_node_votes_no_votes[] | null;
votes: ProposalsQuery_proposals_votes_no_votes[] | null;
}
export interface ProposalsQuery_proposalsConnection_edges_node_votes {
export interface ProposalsQuery_proposals_votes {
__typename: "ProposalVotes";
/**
* Yes votes cast for this proposal
*/
yes: ProposalsQuery_proposalsConnection_edges_node_votes_yes;
yes: ProposalsQuery_proposals_votes_yes;
/**
* No votes cast for this proposal
*/
no: ProposalsQuery_proposalsConnection_edges_node_votes_no;
no: ProposalsQuery_proposals_votes_no;
}
export interface ProposalsQuery_proposalsConnection_edges_node {
export interface ProposalsQuery_proposals {
__typename: "Proposal";
/**
* Proposal ID that is filled by Vega once proposal reaches the network
*/
id: string | null;
/**
* Rationale behind the proposal
*/
rationale: ProposalsQuery_proposalsConnection_edges_node_rationale;
/**
* A UUID reference to aid tracking proposals on Vega
*/
@@ -271,36 +251,20 @@ export interface ProposalsQuery_proposalsConnection_edges_node {
/**
* Party that prepared the proposal
*/
party: ProposalsQuery_proposalsConnection_edges_node_party;
party: ProposalsQuery_proposals_party;
/**
* Terms of the proposal
*/
terms: ProposalsQuery_proposalsConnection_edges_node_terms;
terms: ProposalsQuery_proposals_terms;
/**
* Votes cast for this proposal
*/
votes: ProposalsQuery_proposalsConnection_edges_node_votes;
}
export interface ProposalsQuery_proposalsConnection_edges {
__typename: "ProposalEdge";
/**
* The proposal data
*/
node: ProposalsQuery_proposalsConnection_edges_node;
}
export interface ProposalsQuery_proposalsConnection {
__typename: "ProposalsConnection";
/**
* List of proposals available for the connection
*/
edges: (ProposalsQuery_proposalsConnection_edges | null)[] | null;
votes: ProposalsQuery_proposals_votes;
}
export interface ProposalsQuery {
/**
* All governance proposals in the Vega network
*/
proposalsConnection: ProposalsQuery_proposalsConnection;
proposals: ProposalsQuery_proposals[] | null;
}
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -4,90 +4,95 @@ import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { getProposals } from '@vegaprotocol/governance';
import type {
ProposalsQuery,
ProposalsQuery_proposalsConnection_edges_node,
ProposalsQuery_proposals_terms_change,
} from './__generated__/ProposalsQuery';
const PROPOSALS_QUERY = gql`
export function getProposalName(change: ProposalsQuery_proposals_terms_change) {
if (change.__typename === 'NewAsset') {
return t(`New asset: ${change.symbol}`);
} else if (change.__typename === 'NewMarket') {
return t(`New market: ${change.instrument.name}`);
} else if (change.__typename === 'UpdateMarket') {
return t(`Update market: ${change.marketId}`);
} else if (change.__typename === 'UpdateNetworkParameter') {
return t(`Update network: ${change.networkParameter.key}`);
}
return t('Unknown proposal');
}
const PROPOSAL_QUERY = gql`
query ProposalsQuery {
proposalsConnection {
edges {
node {
id
rationale {
title
description
proposals {
id
reference
state
datetime
rejectionReason
party {
id
}
terms {
closingDatetime
enactmentDatetime
change {
... on NewMarket {
instrument {
name
}
}
reference
state
datetime
rejectionReason
party {
id
... on UpdateMarket {
marketId
}
terms {
closingDatetime
enactmentDatetime
change {
... on NewMarket {
instrument {
name
}
... on NewAsset {
__typename
symbol
source {
... on BuiltinAsset {
maxFaucetAmountMint
}
... on UpdateMarket {
marketId
}
... on NewAsset {
__typename
symbol
source {
... on BuiltinAsset {
maxFaucetAmountMint
}
... on ERC20 {
contractAddress
}
}
}
... on UpdateNetworkParameter {
networkParameter {
key
value
}
... on ERC20 {
contractAddress
}
}
}
... on UpdateNetworkParameter {
networkParameter {
key
value
}
}
}
}
votes {
yes {
totalTokens
totalNumber
votes {
yes {
totalTokens
totalNumber
votes {
value
party {
id
stakingSummary {
currentStakeAvailable
}
}
datetime
value
party {
id
stake {
currentStakeAvailable
}
}
no {
totalTokens
totalNumber
votes {
value
party {
id
stakingSummary {
currentStakeAvailable
}
}
datetime
datetime
}
}
no {
totalTokens
totalNumber
votes {
value
party {
id
stake {
currentStakeAvailable
}
}
datetime
}
}
}
@@ -96,12 +101,9 @@ const PROPOSALS_QUERY = gql`
`;
const Governance = () => {
const { data } = useQuery<ProposalsQuery>(PROPOSALS_QUERY, {
const { data } = useQuery<ProposalsQuery>(PROPOSAL_QUERY, {
errorPolicy: 'ignore',
});
const proposals = getProposals(
data
) as ProposalsQuery_proposalsConnection_edges_node[];
if (!data) return null;
return (
@@ -109,11 +111,9 @@ const Governance = () => {
<RouteTitle data-testid="governance-header">
{t('Governance Proposals')}
</RouteTitle>
{proposals.map((p) => (
{data.proposals?.map((p) => (
<React.Fragment key={p.id}>
<SubHeading>
{p.rationale.title || p.rationale.description}
</SubHeading>
<SubHeading>{getProposalName(p.terms.change)}</SubHeading>
<SyntaxHighlighter data={p} />
</React.Fragment>
))}
@@ -465,6 +465,10 @@ export interface MarketsQuery_markets {
* Market ID
*/
id: string;
/**
* Market full name
*/
name: string;
/**
* Fees related data
*/
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -11,6 +11,7 @@ const MARKETS_QUERY = gql`
query MarketsQuery {
markets {
id
name
fees {
factors {
makerFee
@@ -153,9 +154,7 @@ const Markets = () => {
{data?.markets
? data.markets.map((m) => (
<React.Fragment key={m.id}>
<SubHeading data-testid="markets-header">
{m.tradableInstrument.instrument.name}
</SubHeading>
<SubHeading data-testid="markets-header">{m.name}</SubHeading>
<SyntaxHighlighter data={m} />
</React.Fragment>
))
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -89,7 +89,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1010.000000000000000001",
"total_removed": "668.4622323651",
"locked_amount": "79.60645389396243020007881827118214102",
"locked_amount": "98.32505010147130990009735153475393199",
"deposits": [
{
"amount": "1000",
@@ -1,6 +1,6 @@
{
"rationale": {
"title": "An example freeform proposal with unique number of: ",
"title": "An example freeform proposal",
"description": "I propose that everyone evaluate the following IPFS document and vote Yes if they agree. [bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si](https://dweb.link/ipfs/bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si)"
},
"terms": {
@@ -196,7 +196,11 @@ context('Governance flow - with eth and vega wallets connected', function () {
.then((proposalId) => {
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposalId);
cy.get(`#${proposalId}`).should('contain', proposalId);
cy.get(`#${proposalId}`).should(
'contain',
`Freeform proposal: ${proposalId}`,
txTimeout
);
});
});
@@ -226,7 +230,11 @@ context('Governance flow - with eth and vega wallets connected', function () {
let proposalId = proposal.response.body.data.busEvents[0].event.id;
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
cy.get(`#${proposalId}`).should('contain', proposalId);
cy.get(`#${proposalId}`).should(
'contain',
`Freeform proposal: ${proposalId}`,
txTimeout
);
});
});
@@ -256,7 +264,7 @@ context('Governance flow - with eth and vega wallets connected', function () {
.then((proposalId) => {
cy.get(openProposals).within(() => {
cy.get(`#${proposalId}`)
.should('contain', proposalId)
.should('contain', `Freeform proposal: ${proposalId}`, txTimeout)
.and('contain', 'Open')
.and('be.visible')
.within(() => {
@@ -1,6 +1,5 @@
const vegaWalletContainer = '[data-testid="vega-wallet"]';
const restConnectorForm = '[data-testid="rest-connector-form"]';
const vegaWalletNameElement = '[data-testid="wallet-name"]';
const vegaWalletName = Cypress.env('vegaWalletName');
const vegaWalletLocation = Cypress.env('vegaWalletLocation');
const vegaWalletPassphrase = Cypress.env('vegaWalletPassphrase');
@@ -15,11 +14,6 @@ Cypress.Commands.add('vega_wallet_import', () => {
cy.exec(
`vegawallet service run --network DV --automatic-consent --home ${vegaWalletLocation}`
);
cy.exec(`vegawallet version`)
.its('stdout')
.then((output) => {
cy.log(output);
});
});
Cypress.Commands.add('vega_wallet_connect', () => {
@@ -37,5 +31,5 @@ Cypress.Commands.add('vega_wallet_connect', () => {
cy.get('#passphrase').click().type(vegaWalletPassphrase);
cy.get('button').contains('Connect').click();
});
cy.get(vegaWalletNameElement).should('be.visible');
cy.contains(`${vegaWalletName} key`, { timeout: 20000 }).should('be.visible');
});
+1 -1
View File
@@ -1,5 +1,5 @@
# App configuration variables
NX_VEGA_ENV=CUSTOM
NX_VEGA_ENV=TESTNET
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -0,0 +1,27 @@
import { useQuery } from '@apollo/client';
import { gql } from '@apollo/client';
import type { NetworkParams } from './__generated__/NetworkParams';
export const NETWORK_PARAMS_QUERY = gql`
query NetworkParams {
networkParameters {
key
value
}
}
`;
export function useNetworkParam(params: string[]) {
const { data, loading, error } = useQuery<NetworkParams, never>(
NETWORK_PARAMS_QUERY
);
const foundParams = data?.networkParameters
?.filter((p) => params.includes(p.key))
.sort((a, b) => params.indexOf(a.key) - params.indexOf(b.key));
return {
data: foundParams ? foundParams.map((f) => f.value) : null,
loading,
error,
};
}
@@ -9,22 +9,6 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
// GraphQL fragment: ProposalFields
// ====================================================
export interface ProposalFields_rationale {
__typename: "ProposalRationale";
/**
* Title to be used to give a short description of the proposal in lists.
* This is to be between 0 and 100 unicode characters.
* This is mandatory for all proposals.
*/
title: string;
/**
* Description to show a short title / something in case the link goes offline.
* This is to be between 0 and 20k unicode characters.
* This is mandatory for all proposals.
*/
description: string;
}
export interface ProposalFields_party {
__typename: "Party";
/**
@@ -71,6 +55,14 @@ export interface ProposalFields_terms_change_NewMarket_instrument {
export interface ProposalFields_terms_change_NewMarket {
__typename: "NewMarket";
/**
* Decimal places used for the new market, sets the smallest price increment on the book
*/
decimalPlaces: number;
/**
* Metadata for this instrument, tags
*/
metadata: string[] | null;
/**
* New market instrument configuration
*/
@@ -154,8 +146,8 @@ export interface ProposalFields_terms {
change: ProposalFields_terms_change;
}
export interface ProposalFields_votes_yes_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface ProposalFields_votes_yes_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
@@ -171,7 +163,7 @@ export interface ProposalFields_votes_yes_votes_party {
/**
* The staking information for this Party
*/
stakingSummary: ProposalFields_votes_yes_votes_party_stakingSummary;
stake: ProposalFields_votes_yes_votes_party_stake;
}
export interface ProposalFields_votes_yes_votes {
@@ -206,8 +198,8 @@ export interface ProposalFields_votes_yes {
votes: ProposalFields_votes_yes_votes[] | null;
}
export interface ProposalFields_votes_no_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface ProposalFields_votes_no_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
@@ -223,7 +215,7 @@ export interface ProposalFields_votes_no_votes_party {
/**
* The staking information for this Party
*/
stakingSummary: ProposalFields_votes_no_votes_party_stakingSummary;
stake: ProposalFields_votes_no_votes_party_stake;
}
export interface ProposalFields_votes_no_votes {
@@ -276,10 +268,6 @@ export interface ProposalFields {
* Proposal ID that is filled by Vega once proposal reaches the network
*/
id: string | null;
/**
* Rationale behind the proposal
*/
rationale: ProposalFields_rationale;
/**
* A UUID reference to aid tracking proposals on Vega
*/
@@ -296,14 +284,14 @@ export interface ProposalFields {
* Reason for the proposal to be rejected by the core
*/
rejectionReason: ProposalRejectionReason | null;
/**
* Party that prepared the proposal
*/
party: ProposalFields_party;
/**
* Error details of the rejectionReason
*/
errorDetails: string | null;
/**
* Party that prepared the proposal
*/
party: ProposalFields_party;
/**
* Terms of the proposal
*/
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,10 +1,10 @@
import { ProposalState } from '@vegaprotocol/types';
import type { ProposalFields } from '../../__generated__/ProposalFields';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
export const CurrentProposalState = ({
proposal,
}: {
proposal: ProposalFields;
proposal: Proposals_proposals;
}) => {
const { state } = proposal;
let className = 'text-white';
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
import { ProposalState } from '@vegaprotocol/types';
import { useVoteInformation } from '../../hooks';
import type { ProposalFields } from '../../__generated__/ProposalFields';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
export const StatusPass = ({ children }: { children: React.ReactNode }) => (
<span className="text-vega-green">{children}</span>
@@ -17,7 +17,7 @@ export const StatusFail = ({ children }: { children: React.ReactNode }) => (
export const CurrentProposalStatus = ({
proposal,
}: {
proposal: ProposalFields;
proposal: Proposals_proposals;
}) => {
const { willPass, majorityMet, participationMet } = useVoteInformation({
proposal,
@@ -3,11 +3,11 @@ import { useTranslation } from 'react-i18next';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
import { CurrentProposalState } from '../current-proposal-state';
import type { ProposalFields } from '../../__generated__/ProposalFields';
interface ProposalChangeTableProps {
proposal: ProposalFields;
proposal: Proposals_proposals;
}
export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
@@ -1,9 +1,9 @@
import { render, screen } from '@testing-library/react';
import { generateProposal } from '../../test-helpers/generate-proposals';
import { ProposalHeader } from './proposal-header';
import type { Proposal_proposal } from '@vegaprotocol/governance';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
const renderComponent = (proposal: Proposal_proposal) => (
const renderComponent = (proposal: Proposals_proposals) => (
<ProposalHeader proposal={proposal} />
);
@@ -12,13 +12,10 @@ describe('Proposal header', () => {
render(
renderComponent(
generateProposal({
rationale: {
title: 'New some market',
description: 'A new some market',
},
terms: {
change: {
__typename: 'NewMarket',
decimalPlaces: 1,
instrument: {
__typename: 'InstrumentConfiguration',
name: 'Some market',
@@ -31,18 +28,16 @@ describe('Proposal header', () => {
},
},
},
metadata: [],
},
},
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
'New some market'
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'New market: Some market'
);
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
'A new some market'
);
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
'tGBP settled future.'
);
});
@@ -51,9 +46,6 @@ describe('Proposal header', () => {
render(
renderComponent(
generateProposal({
rationale: {
title: 'New market id',
},
terms: {
change: {
__typename: 'UpdateMarket',
@@ -63,13 +55,7 @@ describe('Proposal header', () => {
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
'New market id'
);
expect(
screen.queryByTestId('proposal-description')
).not.toBeInTheDocument();
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'Market change: MarketId'
);
});
@@ -78,10 +64,6 @@ describe('Proposal header', () => {
render(
renderComponent(
generateProposal({
rationale: {
title: 'New asset: Fake currency',
description: '',
},
terms: {
change: {
__typename: 'NewAsset',
@@ -96,10 +78,10 @@ describe('Proposal header', () => {
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'New asset: Fake currency'
);
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
'Symbol: FAKE. ERC20 0x0'
);
});
@@ -122,10 +104,10 @@ describe('Proposal header', () => {
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
'Unknown proposal'
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'New asset: Fake currency'
);
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
'Symbol: BIA. Max faucet amount mint: 300'
);
});
@@ -134,9 +116,6 @@ describe('Proposal header', () => {
render(
renderComponent(
generateProposal({
rationale: {
title: 'Network parameter',
},
terms: {
change: {
__typename: 'UpdateNetworkParameter',
@@ -150,22 +129,25 @@ describe('Proposal header', () => {
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'Network parameter'
);
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
'Network key to Network value'
);
});
it('Renders Freeform network - short rationale', () => {
// Skipped until proposals have rationale - https://github.com/vegaprotocol/frontend-monorepo/issues/824
// eslint-disable-next-line jest/no-disabled-tests
it.skip('Renders Freeform network - short rationale', () => {
render(
renderComponent(
generateProposal({
id: 'short',
rationale: {
title: '0x0',
},
// rationale: {
// hash: '0x0',
// description: 'freeform description',
// },
terms: {
change: {
__typename: 'NewFreeform',
@@ -174,23 +156,27 @@ describe('Proposal header', () => {
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
expect(
screen.queryByTestId('proposal-description')
).not.toBeInTheDocument();
expect(screen.getByTestId('proposal-details')).toHaveTextContent('short');
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'freeform description'
);
expect(screen.getByTestId('proposal-details-one')).toBeEmptyDOMElement();
expect(screen.getByTestId('proposal-details-two')).toHaveTextContent(
'short'
);
});
it('Renders Freeform proposal - long rationale (105 chars)', () => {
// Skipped until proposals have rationale
// eslint-disable-next-line jest/no-disabled-tests
it.skip('Renders Freeform proposal - long rationale (105 chars)', () => {
render(
renderComponent(
generateProposal({
id: 'long',
rationale: {
title: '0x0',
description:
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
},
// rationale: {
// hash: '0x0',
// description:
// 'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
// },
terms: {
change: {
__typename: 'NewFreeform',
@@ -201,24 +187,29 @@ describe('Proposal header', () => {
);
// For a rationale over 100 chars, we expect the header to be truncated at
// 100 chars with ellipsis and the details-one element to contain the rest.
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
'Class aptent taciti sociosqu ad litora torquent per conubia'
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean…'
);
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
'dolor'
);
expect(screen.getByTestId('proposal-details-two')).toHaveTextContent(
'long'
);
expect(screen.getByTestId('proposal-details')).toHaveTextContent('long');
});
it('Renders Freeform proposal - extra long rationale (165 chars)', () => {
// Skipped until proposals have rationale
// eslint-disable-next-line jest/no-disabled-tests
it.skip('Renders Freeform proposal - extra long rationale (165 chars)', () => {
render(
renderComponent(
generateProposal({
id: 'extraLong',
rationale: {
title:
'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra libero quis risus lobortis, sed ornare leo efficitur turpis duis.',
description:
'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra libero quis risus lobortis, sed ornare leo efficitur turpis duis.',
},
// rationale: {
// hash: '0x0',
// description:
// 'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra libero quis risus lobortis, sed ornare leo efficitur turpis duis.',
// },
terms: {
change: {
__typename: 'NewFreeform',
@@ -230,13 +221,13 @@ describe('Proposal header', () => {
// For a rationale over 160 chars, we expect the header to be truncated at 100
// chars with ellipsis and the details-one element to contain 60 chars and also
// be truncated with an ellipsis.
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra…'
);
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
'Aenean sem odio, eleifend non sodales vitae, porttitor eu e…'
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
'libero quis risus lobortis, sed ornare leo efficitur turpis…'
);
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
expect(screen.getByTestId('proposal-details-two')).toHaveTextContent(
'extraLong'
);
});
@@ -247,9 +238,6 @@ describe('Proposal header', () => {
renderComponent(
generateProposal({
id: 'freeform id',
rationale: {
title: 'freeform',
},
terms: {
change: {
__typename: 'NewFreeform',
@@ -258,13 +246,15 @@ describe('Proposal header', () => {
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent('freeform');
expect(
screen.queryByTestId('proposal-description')
).not.toBeInTheDocument();
expect(screen.queryByTestId('proposal-details')).toHaveTextContent(
'freeform id'
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'Freeform proposal: freeform id'
);
expect(
screen.queryByTestId('proposal-details-one')
).not.toBeInTheDocument();
expect(
screen.queryByTestId('proposal-details-two')
).not.toBeInTheDocument();
});
it("Renders unknown proposal if it's a different proposal type", () => {
@@ -280,7 +270,7 @@ describe('Proposal header', () => {
})
)
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
'Unknown proposal'
);
});
@@ -1,28 +1,24 @@
import { useTranslation } from 'react-i18next';
import { Lozenge } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react';
import { shorten } from '@vegaprotocol/react-helpers';
import type { ProposalFields } from '../../__generated__/ProposalFields';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
export const ProposalHeader = ({ proposal }: { proposal: ProposalFields }) => {
export const ProposalHeader = ({
proposal,
}: {
proposal: Proposals_proposals;
}) => {
const { t } = useTranslation();
const { change } = proposal.terms;
let details: ReactNode;
let title = proposal.rationale.title.trim();
let description = proposal.rationale.description.trim();
if (title.length === 0 && description.length > 0) {
title = description;
description = '';
}
const titleContent = shorten(title, 100);
const descriptionContent = shorten(description, 60);
let headerText: string;
let detailsOne: ReactNode;
let detailsTwo: ReactNode;
switch (change.__typename) {
case 'NewMarket': {
details = (
headerText = `${t('New market')}: ${change.instrument.name}`;
detailsOne = (
<>
{t('Code')}: {change.instrument.code}.{' '}
{change.instrument.futureProduct?.settlementAsset.symbol ? (
@@ -40,11 +36,12 @@ export const ProposalHeader = ({ proposal }: { proposal: ProposalFields }) => {
break;
}
case 'UpdateMarket': {
details = `${t('Market change')}: ${change.marketId}`;
headerText = `${t('Market change')}: ${change.marketId}`;
break;
}
case 'NewAsset': {
details = (
headerText = `${t('New asset')}: ${change.name}`;
detailsOne = (
<>
{t('Symbol')}: {change.symbol}.{' '}
<Lozenge>
@@ -60,7 +57,8 @@ export const ProposalHeader = ({ proposal }: { proposal: ProposalFields }) => {
}
case 'UpdateNetworkParameter': {
const parametersClasses = 'font-mono leading-none';
details = (
headerText = `${t('Network parameter')}`;
detailsOne = (
<>
<span className={`${parametersClasses} mr-2`}>
{change.networkParameter.key}
@@ -74,34 +72,44 @@ export const ProposalHeader = ({ proposal }: { proposal: ProposalFields }) => {
break;
}
case 'NewFreeform': {
details = `${proposal.id}`;
// When rationale exists (https://github.com/vegaprotocol/frontend-monorepo/issues/824):
// const description = proposal.rationale.description.trim();
// const headerMaxLength = 100;
// const descriptionOneMaxLength = 60;
// const headerOverflow = description.length > headerMaxLength;
// const descriptionOneOverflow =
// description.length > headerMaxLength + descriptionOneMaxLength;
//
// headerText = `${description.substring(0, headerMaxLength - 1).trim()}${
// headerOverflow ? '…' : ''
// }`;
// detailsOne = headerOverflow
// ? `${description
// .substring(
// headerMaxLength - 1,
// headerMaxLength + descriptionOneMaxLength - 1
// )
// .trim()}${descriptionOneOverflow ? '…' : ''}`
// : '';
// detailsTwo = `${proposal.id}`;
headerText = proposal.id
? `${t('Freeform proposal')}: ${proposal.id.trim()}`
: `${t('Unknown proposal')}`;
break;
}
default: {
headerText = `${t('Unknown proposal')}`;
}
}
return (
<div className="text-sm mb-2">
<header data-testid="proposal-title">
<h2
{...(title && title.length > titleContent.length && { title: title })}
className="text-lg mx-0 mt-0 mb-1 font-semibold"
>
{titleContent || t('Unknown proposal')}
</h2>
<header data-testid="proposal-header">
<h2 className="text-lg mx-0 mt-0 mb-1 font-semibold">{headerText}</h2>
</header>
{descriptionContent && (
<div
className="mb-4"
{...(description.length > descriptionContent.length && {
title: description,
})}
data-testid="proposal-description"
>
{descriptionContent}
</div>
)}
{details && <div data-testid="proposal-details">{details}</div>}
{detailsOne && <div data-testid="proposal-details-one">{detailsOne}</div>}
{detailsTwo && <div data-testid="proposal-details-two">{detailsTwo}</div>}
</div>
);
};
@@ -1,11 +1,11 @@
import { useTranslation } from 'react-i18next';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import type { ProposalFields_terms } from '../../__generated__/ProposalFields';
import type { Proposal_proposal_terms } from '../../proposal/__generated__/Proposal';
export const ProposalTermsJson = ({
terms,
}: {
terms: ProposalFields_terms;
terms: Proposal_proposal_terms;
}) => {
const { t } = useTranslation();
return (
@@ -6,11 +6,11 @@ import {
formatNumberPercentage,
} from '@vegaprotocol/react-helpers';
import { useVoteInformation } from '../../hooks';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import type { ProposalFields } from '../../__generated__/ProposalFields';
interface ProposalVotesTableProps {
proposal: ProposalFields;
proposal: Proposals_proposals;
}
export const ProposalVotesTable = ({ proposal }: ProposalVotesTableProps) => {
@@ -26,10 +26,10 @@ import {
lastWeek,
nextWeek,
} from '../../test-helpers/mocks';
import type { ProposalsConnection_proposalsConnection_edges_node as ProposalNode } from '@vegaprotocol/governance';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
const renderComponent = (
proposal: ProposalNode,
proposal: Proposals_proposals,
mock = networkParamsQueryMock
) => (
<Router>
@@ -173,8 +173,8 @@ describe('Proposals list item details', () => {
party: {
__typename: 'Party',
id: mockPubkey,
stakingSummary: {
__typename: 'StakingSummary',
stake: {
__typename: 'PartyStake',
currentStakeAvailable: '1000',
},
},
@@ -211,8 +211,8 @@ describe('Proposals list item details', () => {
party: {
__typename: 'Party',
id: mockPubkey,
stakingSummary: {
__typename: 'StakingSummary',
stake: {
__typename: 'PartyStake',
currentStakeAvailable: '1000',
},
},
@@ -10,12 +10,12 @@ import { format, formatDistanceToNowStrict } from 'date-fns';
import { useTranslation } from 'react-i18next';
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
import type { ReactNode } from 'react';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
import {
ProposalRejectionReasonMapping,
ProposalState,
} from '@vegaprotocol/types';
import Routes from '../../../routes';
import type { ProposalFields } from '../../__generated__/ProposalFields';
const MajorityNotReached = () => {
const { t } = useTranslation();
@@ -37,7 +37,7 @@ const ParticipationNotReached = () => {
export const ProposalsListItemDetails = ({
proposal,
}: {
proposal: ProposalFields;
proposal: Proposals_proposals;
}) => {
const { state } = proposal;
const { willPass, majorityMet, participationMet } = useVoteInformation({
@@ -1,9 +1,9 @@
import type { ProposalFields } from '../../__generated__/ProposalFields';
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
import { ProposalsListItemDetails } from './proposals-list-item-details';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
interface ProposalsListItemProps {
proposal: ProposalFields;
proposal: Proposals_proposals;
}
export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => {
@@ -14,7 +14,7 @@ import {
lastMonth,
nextMonth,
} from '../../test-helpers/mocks';
import type { ProposalsConnection_proposalsConnection_edges_node as ProposalNode } from '@vegaprotocol/governance';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
const openProposalClosesNextMonth = generateProposal({
id: 'proposal1',
@@ -58,7 +58,7 @@ const failedProposalClosedLastMonth = generateProposal({
},
});
const renderComponent = (proposals: ProposalNode[]) => (
const renderComponent = (proposals: Proposals_proposals[]) => (
<Router>
<MockedProvider mocks={[networkParamsQueryMock]}>
<AppStateProvider>
@@ -4,20 +4,20 @@ import { useTranslation } from 'react-i18next';
import { Heading } from '../../../../components/heading';
import { ProposalsListItem } from '../proposals-list-item';
import { ProposalsListFilter } from '../proposals-list-filter';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
import Routes from '../../../routes';
import { Button } from '@vegaprotocol/ui-toolkit';
import { Link } from 'react-router-dom';
import type { ProposalFields } from '../../__generated__/ProposalFields';
import { Links } from '../../../../config';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
interface ProposalsListProps {
proposals: ProposalFields[];
proposals: Proposals_proposals[];
}
interface SortedProposalsProps {
open: ProposalFields[];
closed: ProposalFields[];
open: Proposals_proposals[];
closed: Proposals_proposals[];
}
export const ProposalsList = ({ proposals }: ProposalsListProps) => {
@@ -39,7 +39,7 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
}
);
const filterPredicate = (p: ProposalFields) =>
const filterPredicate = (p: Proposals_proposals) =>
p.id?.includes(filterString) ||
p.party?.id?.toString().includes(filterString);
@@ -12,11 +12,11 @@ import {
nextWeek,
lastMonth,
} from '../../test-helpers/mocks';
import type { ProposalsConnection_proposalsConnection_edges_node as ProposalNode } from '@vegaprotocol/governance';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
const rejectedProposalClosesNextWeek = generateProposal({
id: 'rejected1',
state: ProposalState.STATE_OPEN,
state: ProposalState.Open,
party: {
id: 'bvcx',
},
@@ -28,14 +28,14 @@ const rejectedProposalClosesNextWeek = generateProposal({
const rejectedProposalClosedLastMonth = generateProposal({
id: 'rejected2',
state: ProposalState.STATE_REJECTED,
state: ProposalState.Rejected,
terms: {
closingDatetime: lastMonth.toString(),
enactmentDatetime: lastMonth.toString(),
},
});
const renderComponent = (proposals: ProposalNode[]) => (
const renderComponent = (proposals: Proposals_proposals[]) => (
<Router>
<MockedProvider mocks={[networkParamsQueryMock]}>
<AppStateProvider>
@@ -3,17 +3,17 @@ import { useTranslation } from 'react-i18next';
import { Heading } from '../../../../components/heading';
import { ProposalsListItem } from '../proposals-list-item';
import { ProposalsListFilter } from '../proposals-list-filter';
import type { Proposals_proposalsConnection_edges_node } from '../../proposals/__generated__/Proposals';
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
interface ProposalsListProps {
proposals: Proposals_proposalsConnection_edges_node[];
proposals: Proposals_proposals[];
}
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
const { t } = useTranslation();
const [filterString, setFilterString] = useState('');
const filterPredicate = (p: Proposals_proposalsConnection_edges_node) =>
const filterPredicate = (p: Proposals_proposals) =>
p.id?.includes(filterString) ||
p.party?.id?.toString().includes(filterString);
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -4,13 +4,13 @@ import { useTranslation } from 'react-i18next';
import { formatNumber } from '../../../../lib/format-number';
import { ConnectToVega } from '../../../staking/connect-to-vega';
import { useVoteInformation } from '../../hooks';
import type { Proposal_proposal } from '../../proposal/__generated__/Proposal';
import { CurrentProposalStatus } from '../current-proposal-status';
import { useUserVote } from './use-user-vote';
import { VoteButtonsContainer } from './vote-buttons';
import { VoteProgress } from './vote-progress';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { ProposalState } from '@vegaprotocol/types';
import type { Proposal_proposal } from '../../proposal/__generated__/Proposal';
interface VoteDetailsProps {
proposal: Proposal_proposal;
@@ -1,22 +1,22 @@
import { useNetworkParams } from '@vegaprotocol/react-helpers';
import React from 'react';
import { NetworkParams } from '../../../config';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import { useNetworkParam } from '../../../hooks/use-network-param';
import { BigNumber } from '../../../lib/bignumber';
import { addDecimal } from '../../../lib/decimals';
import type {
ProposalFields,
ProposalFields_votes_no_votes,
ProposalFields_votes_yes_votes,
} from '../__generated__/ProposalFields';
Proposal_proposal_votes_no_votes,
Proposal_proposal_votes_yes_votes,
} from '../proposal/__generated__/Proposal';
import type { Proposals_proposals } from '../proposals/__generated__/Proposals';
const useProposalNetworkParams = ({
proposal,
}: {
proposal: ProposalFields;
proposal: Proposals_proposals;
}) => {
const { data, loading } = useNetworkParams([
const { data, loading } = useNetworkParam([
NetworkParams.GOV_UPDATE_MARKET_REQUIRED_MAJORITY,
NetworkParams.GOV_UPDATE_MARKET_REQUIRED_PARTICIPATION,
NetworkParams.GOV_NEW_MARKET_REQUIRED_MAJORITY,
@@ -82,7 +82,7 @@ const useProposalNetworkParams = ({
export const useVoteInformation = ({
proposal,
}: {
proposal: ProposalFields;
proposal: Proposals_proposals;
}) => {
const {
appState: { totalSupply },
@@ -105,10 +105,10 @@ export const useVoteInformation = ({
return new BigNumber(0);
}
const totalNoVotes = proposal.votes.no.votes.reduce(
(prevValue: BigNumber, newValue: ProposalFields_votes_no_votes) => {
return new BigNumber(
newValue.party.stakingSummary.currentStakeAvailable
).plus(prevValue);
(prevValue: BigNumber, newValue: Proposal_proposal_votes_no_votes) => {
return new BigNumber(newValue.party.stake.currentStakeAvailable).plus(
prevValue
);
},
new BigNumber(0)
);
@@ -120,10 +120,10 @@ export const useVoteInformation = ({
return new BigNumber(0);
}
const totalYesVotes = proposal.votes.yes.votes.reduce(
(prevValue: BigNumber, newValue: ProposalFields_votes_yes_votes) => {
return new BigNumber(
newValue.party.stakingSummary.currentStakeAvailable
).plus(prevValue);
(prevValue: BigNumber, newValue: Proposal_proposal_votes_yes_votes) => {
return new BigNumber(newValue.party.stake.currentStakeAvailable).plus(
prevValue
);
},
new BigNumber(0)
);
@@ -1,25 +1,23 @@
import { gql } from '@apollo/client';
export const PROPOSAL_FRAGMENT = gql`
export const PROPOSALS_FRAGMENT = gql`
fragment ProposalFields on Proposal {
id
rationale {
title
description
}
reference
state
datetime
rejectionReason
errorDetails
party {
id
}
errorDetails
terms {
closingDatetime
enactmentDatetime
change {
... on NewMarket {
decimalPlaces
metadata
instrument {
name
code
@@ -39,9 +37,11 @@ export const PROPOSAL_FRAGMENT = gql`
symbol
source {
... on BuiltinAsset {
__typename
maxFaucetAmountMint
}
... on ERC20 {
__typename
contractAddress
}
}
@@ -62,7 +62,7 @@ export const PROPOSAL_FRAGMENT = gql`
value
party {
id
stakingSummary {
stake {
currentStakeAvailable
}
}
@@ -76,7 +76,7 @@ export const PROPOSAL_FRAGMENT = gql`
value
party {
id
stakingSummary {
stake {
currentStakeAvailable
}
}
@@ -9,22 +9,6 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
// GraphQL query operation: Proposal
// ====================================================
export interface Proposal_proposal_rationale {
__typename: "ProposalRationale";
/**
* Title to be used to give a short description of the proposal in lists.
* This is to be between 0 and 100 unicode characters.
* This is mandatory for all proposals.
*/
title: string;
/**
* Description to show a short title / something in case the link goes offline.
* This is to be between 0 and 20k unicode characters.
* This is mandatory for all proposals.
*/
description: string;
}
export interface Proposal_proposal_party {
__typename: "Party";
/**
@@ -71,6 +55,14 @@ export interface Proposal_proposal_terms_change_NewMarket_instrument {
export interface Proposal_proposal_terms_change_NewMarket {
__typename: "NewMarket";
/**
* Decimal places used for the new market, sets the smallest price increment on the book
*/
decimalPlaces: number;
/**
* Metadata for this instrument, tags
*/
metadata: string[] | null;
/**
* New market instrument configuration
*/
@@ -154,8 +146,8 @@ export interface Proposal_proposal_terms {
change: Proposal_proposal_terms_change;
}
export interface Proposal_proposal_votes_yes_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface Proposal_proposal_votes_yes_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
@@ -171,7 +163,7 @@ export interface Proposal_proposal_votes_yes_votes_party {
/**
* The staking information for this Party
*/
stakingSummary: Proposal_proposal_votes_yes_votes_party_stakingSummary;
stake: Proposal_proposal_votes_yes_votes_party_stake;
}
export interface Proposal_proposal_votes_yes_votes {
@@ -206,8 +198,8 @@ export interface Proposal_proposal_votes_yes {
votes: Proposal_proposal_votes_yes_votes[] | null;
}
export interface Proposal_proposal_votes_no_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface Proposal_proposal_votes_no_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
@@ -223,7 +215,7 @@ export interface Proposal_proposal_votes_no_votes_party {
/**
* The staking information for this Party
*/
stakingSummary: Proposal_proposal_votes_no_votes_party_stakingSummary;
stake: Proposal_proposal_votes_no_votes_party_stake;
}
export interface Proposal_proposal_votes_no_votes {
@@ -276,10 +268,6 @@ export interface Proposal_proposal {
* Proposal ID that is filled by Vega once proposal reaches the network
*/
id: string | null;
/**
* Rationale behind the proposal
*/
rationale: Proposal_proposal_rationale;
/**
* A UUID reference to aid tracking proposals on Vega
*/
@@ -296,14 +284,14 @@ export interface Proposal_proposal {
* Reason for the proposal to be rejected by the core
*/
rejectionReason: ProposalRejectionReason | null;
/**
* Party that prepared the proposal
*/
party: Proposal_proposal_party;
/**
* Error details of the rejectionReason
*/
errorDetails: string | null;
/**
* Party that prepared the proposal
*/
party: Proposal_proposal_party;
/**
* Terms of the proposal
*/
@@ -1,4 +1,5 @@
export {
ProposalContainer,
PROPOSAL_QUERY,
ProposalContainer as default,
} from './proposal-container';
@@ -4,14 +4,14 @@ import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { Proposal } from '../components/proposal';
import { PROPOSAL_FRAGMENT } from '../proposal-fragment';
import { PROPOSALS_FRAGMENT } from '../proposal-fragment';
import type {
Proposal as ProposalQueryResult,
ProposalVariables,
} from './__generated__/Proposal';
export const PROPOSAL_QUERY = gql`
${PROPOSAL_FRAGMENT}
${PROPOSALS_FRAGMENT}
query Proposal($proposalId: ID!) {
proposal(id: $proposalId) {
...ProposalFields
@@ -9,23 +9,7 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
// GraphQL query operation: Proposals
// ====================================================
export interface Proposals_proposalsConnection_edges_node_rationale {
__typename: "ProposalRationale";
/**
* Title to be used to give a short description of the proposal in lists.
* This is to be between 0 and 100 unicode characters.
* This is mandatory for all proposals.
*/
title: string;
/**
* Description to show a short title / something in case the link goes offline.
* This is to be between 0 and 20k unicode characters.
* This is mandatory for all proposals.
*/
description: string;
}
export interface Proposals_proposalsConnection_edges_node_party {
export interface Proposals_proposals_party {
__typename: "Party";
/**
* Party identifier
@@ -33,11 +17,11 @@ export interface Proposals_proposalsConnection_edges_node_party {
id: string;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateAsset {
export interface Proposals_proposals_terms_change_UpdateAsset {
__typename: "UpdateAsset" | "NewFreeform";
}
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct_settlementAsset {
export interface Proposals_proposals_terms_change_NewMarket_instrument_futureProduct_settlementAsset {
__typename: "Asset";
/**
* The symbol of the asset (e.g: GBP)
@@ -45,15 +29,15 @@ export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket
symbol: string;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct {
export interface Proposals_proposals_terms_change_NewMarket_instrument_futureProduct {
__typename: "FutureProduct";
/**
* Product asset ID
*/
settlementAsset: Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct_settlementAsset;
settlementAsset: Proposals_proposals_terms_change_NewMarket_instrument_futureProduct_settlementAsset;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument {
export interface Proposals_proposals_terms_change_NewMarket_instrument {
__typename: "InstrumentConfiguration";
/**
* Full and fairly descriptive name for the instrument
@@ -66,23 +50,31 @@ export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket
/**
* Future product specification
*/
futureProduct: Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct | null;
futureProduct: Proposals_proposals_terms_change_NewMarket_instrument_futureProduct | null;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket {
export interface Proposals_proposals_terms_change_NewMarket {
__typename: "NewMarket";
/**
* Decimal places used for the new market, sets the smallest price increment on the book
*/
decimalPlaces: number;
/**
* Metadata for this instrument, tags
*/
metadata: string[] | null;
/**
* New market instrument configuration
*/
instrument: Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument;
instrument: Proposals_proposals_terms_change_NewMarket_instrument;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateMarket {
export interface Proposals_proposals_terms_change_UpdateMarket {
__typename: "UpdateMarket";
marketId: string;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset {
export interface Proposals_proposals_terms_change_NewAsset_source_BuiltinAsset {
__typename: "BuiltinAsset";
/**
* Maximum amount that can be requested by a party through the built-in asset faucet at a time
@@ -90,7 +82,7 @@ export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset_
maxFaucetAmountMint: string;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20 {
export interface Proposals_proposals_terms_change_NewAsset_source_ERC20 {
__typename: "ERC20";
/**
* The address of the ERC20 contract
@@ -98,9 +90,9 @@ export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset_
contractAddress: string;
}
export type Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source = Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset | Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20;
export type Proposals_proposals_terms_change_NewAsset_source = Proposals_proposals_terms_change_NewAsset_source_BuiltinAsset | Proposals_proposals_terms_change_NewAsset_source_ERC20;
export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset {
export interface Proposals_proposals_terms_change_NewAsset {
__typename: "NewAsset";
/**
* The full name of the asset (e.g: Great British Pound)
@@ -113,10 +105,10 @@ export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset
/**
* The source of the new asset
*/
source: Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source;
source: Proposals_proposals_terms_change_NewAsset_source;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter {
export interface Proposals_proposals_terms_change_UpdateNetworkParameter_networkParameter {
__typename: "NetworkParameter";
/**
* The name of the network parameter
@@ -128,14 +120,14 @@ export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateNet
value: string;
}
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter {
export interface Proposals_proposals_terms_change_UpdateNetworkParameter {
__typename: "UpdateNetworkParameter";
networkParameter: Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter;
networkParameter: Proposals_proposals_terms_change_UpdateNetworkParameter_networkParameter;
}
export type Proposals_proposalsConnection_edges_node_terms_change = Proposals_proposalsConnection_edges_node_terms_change_UpdateAsset | Proposals_proposalsConnection_edges_node_terms_change_NewMarket | Proposals_proposalsConnection_edges_node_terms_change_UpdateMarket | Proposals_proposalsConnection_edges_node_terms_change_NewAsset | Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter;
export type Proposals_proposals_terms_change = Proposals_proposals_terms_change_UpdateAsset | Proposals_proposals_terms_change_NewMarket | Proposals_proposals_terms_change_UpdateMarket | Proposals_proposals_terms_change_NewAsset | Proposals_proposals_terms_change_UpdateNetworkParameter;
export interface Proposals_proposalsConnection_edges_node_terms {
export interface Proposals_proposals_terms {
__typename: "ProposalTerms";
/**
* RFC3339Nano time and date when voting closes for this proposal.
@@ -151,18 +143,18 @@ export interface Proposals_proposalsConnection_edges_node_terms {
/**
* Actual change being introduced by the proposal - action the proposal triggers if passed and enacted.
*/
change: Proposals_proposalsConnection_edges_node_terms_change;
change: Proposals_proposals_terms_change;
}
export interface Proposals_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface Proposals_proposals_votes_yes_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
currentStakeAvailable: string;
}
export interface Proposals_proposalsConnection_edges_node_votes_yes_votes_party {
export interface Proposals_proposals_votes_yes_votes_party {
__typename: "Party";
/**
* Party identifier
@@ -171,10 +163,10 @@ export interface Proposals_proposalsConnection_edges_node_votes_yes_votes_party
/**
* The staking information for this Party
*/
stakingSummary: Proposals_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary;
stake: Proposals_proposals_votes_yes_votes_party_stake;
}
export interface Proposals_proposalsConnection_edges_node_votes_yes_votes {
export interface Proposals_proposals_votes_yes_votes {
__typename: "Vote";
/**
* The vote value cast
@@ -183,14 +175,14 @@ export interface Proposals_proposalsConnection_edges_node_votes_yes_votes {
/**
* The party casting the vote
*/
party: Proposals_proposalsConnection_edges_node_votes_yes_votes_party;
party: Proposals_proposals_votes_yes_votes_party;
/**
* RFC3339Nano time and date when the vote reached Vega network
*/
datetime: string;
}
export interface Proposals_proposalsConnection_edges_node_votes_yes {
export interface Proposals_proposals_votes_yes {
__typename: "ProposalVoteSide";
/**
* Total number of governance tokens from the votes cast for this side
@@ -203,18 +195,18 @@ export interface Proposals_proposalsConnection_edges_node_votes_yes {
/**
* All votes cast for this side
*/
votes: Proposals_proposalsConnection_edges_node_votes_yes_votes[] | null;
votes: Proposals_proposals_votes_yes_votes[] | null;
}
export interface Proposals_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary {
__typename: "StakingSummary";
export interface Proposals_proposals_votes_no_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
currentStakeAvailable: string;
}
export interface Proposals_proposalsConnection_edges_node_votes_no_votes_party {
export interface Proposals_proposals_votes_no_votes_party {
__typename: "Party";
/**
* Party identifier
@@ -223,10 +215,10 @@ export interface Proposals_proposalsConnection_edges_node_votes_no_votes_party {
/**
* The staking information for this Party
*/
stakingSummary: Proposals_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary;
stake: Proposals_proposals_votes_no_votes_party_stake;
}
export interface Proposals_proposalsConnection_edges_node_votes_no_votes {
export interface Proposals_proposals_votes_no_votes {
__typename: "Vote";
/**
* The vote value cast
@@ -235,14 +227,14 @@ export interface Proposals_proposalsConnection_edges_node_votes_no_votes {
/**
* The party casting the vote
*/
party: Proposals_proposalsConnection_edges_node_votes_no_votes_party;
party: Proposals_proposals_votes_no_votes_party;
/**
* RFC3339Nano time and date when the vote reached Vega network
*/
datetime: string;
}
export interface Proposals_proposalsConnection_edges_node_votes_no {
export interface Proposals_proposals_votes_no {
__typename: "ProposalVoteSide";
/**
* Total number of governance tokens from the votes cast for this side
@@ -255,31 +247,27 @@ export interface Proposals_proposalsConnection_edges_node_votes_no {
/**
* All votes cast for this side
*/
votes: Proposals_proposalsConnection_edges_node_votes_no_votes[] | null;
votes: Proposals_proposals_votes_no_votes[] | null;
}
export interface Proposals_proposalsConnection_edges_node_votes {
export interface Proposals_proposals_votes {
__typename: "ProposalVotes";
/**
* Yes votes cast for this proposal
*/
yes: Proposals_proposalsConnection_edges_node_votes_yes;
yes: Proposals_proposals_votes_yes;
/**
* No votes cast for this proposal
*/
no: Proposals_proposalsConnection_edges_node_votes_no;
no: Proposals_proposals_votes_no;
}
export interface Proposals_proposalsConnection_edges_node {
export interface Proposals_proposals {
__typename: "Proposal";
/**
* Proposal ID that is filled by Vega once proposal reaches the network
*/
id: string | null;
/**
* Rationale behind the proposal
*/
rationale: Proposals_proposalsConnection_edges_node_rationale;
/**
* A UUID reference to aid tracking proposals on Vega
*/
@@ -296,43 +284,27 @@ export interface Proposals_proposalsConnection_edges_node {
* Reason for the proposal to be rejected by the core
*/
rejectionReason: ProposalRejectionReason | null;
/**
* Party that prepared the proposal
*/
party: Proposals_proposalsConnection_edges_node_party;
/**
* Error details of the rejectionReason
*/
errorDetails: string | null;
/**
* Party that prepared the proposal
*/
party: Proposals_proposals_party;
/**
* Terms of the proposal
*/
terms: Proposals_proposalsConnection_edges_node_terms;
terms: Proposals_proposals_terms;
/**
* Votes cast for this proposal
*/
votes: Proposals_proposalsConnection_edges_node_votes;
}
export interface Proposals_proposalsConnection_edges {
__typename: "ProposalEdge";
/**
* The proposal data
*/
node: Proposals_proposalsConnection_edges_node;
}
export interface Proposals_proposalsConnection {
__typename: "ProposalsConnection";
/**
* List of proposals available for the connection
*/
edges: (Proposals_proposalsConnection_edges | null)[] | null;
votes: Proposals_proposals_votes;
}
export interface Proposals {
/**
* All governance proposals in the Vega network
*/
proposalsConnection: Proposals_proposalsConnection;
proposals: Proposals_proposals[] | null;
}
@@ -1,5 +1,5 @@
export {
ProposalsContainer,
ProposalsContainer as default,
PROPOSALS_QUERY,
ProposalsContainer as default,
} from './proposals-container';
@@ -1,36 +1,56 @@
import { gql, useQuery } from '@apollo/client';
import { getNotRejectedProposals } from '@vegaprotocol/governance';
import { ProposalState } from '@vegaprotocol/types';
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import compact from 'lodash/compact';
import filter from 'lodash/filter';
import flow from 'lodash/flow';
import orderBy from 'lodash/orderBy';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SplashLoader } from '../../../components/splash-loader';
import { ProposalsList } from '../components/proposals-list';
import { PROPOSAL_FRAGMENT } from '../proposal-fragment';
import { PROPOSALS_FRAGMENT } from '../proposal-fragment';
import type { Proposals } from './__generated__/Proposals';
export const PROPOSALS_QUERY = gql`
${PROPOSAL_FRAGMENT}
${PROPOSALS_FRAGMENT}
query Proposals {
proposalsConnection {
edges {
node {
...ProposalFields
}
}
proposals {
...ProposalFields
}
}
`;
export const ProposalsContainer = () => {
const { t } = useTranslation();
const { data, loading, error } = useQuery<Proposals>(PROPOSALS_QUERY, {
const { data, loading, error } = useQuery<Proposals, never>(PROPOSALS_QUERY, {
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
errorPolicy: 'ignore', // this is to get around some backend issues and should be removed in future
});
const proposals = useMemo(() => getNotRejectedProposals(data), [data]);
const proposals = React.useMemo(() => {
if (!data?.proposals?.length) {
return [];
}
return flow([
compact,
(arr) =>
filter(arr, ({ state }) => state !== ProposalState.STATE_REJECTED),
(arr) =>
orderBy(
arr,
[
(p) => new Date(p.terms.enactmentDatetime).getTime(),
(p) => new Date(p.terms.closingDatetime).getTime(),
(p) => p.id,
],
['desc', 'desc', 'desc']
),
])(data.proposals);
}, [data]);
if (error) {
return (
@@ -1,19 +1,46 @@
import { useQuery } from '@apollo/client';
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import compact from 'lodash/compact';
import filter from 'lodash/filter';
import flow from 'lodash/flow';
import orderBy from 'lodash/orderBy';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { PROPOSALS_QUERY } from '../proposals';
import { SplashLoader } from '../../../components/splash-loader';
import { RejectedProposalsList } from '../components/proposals-list';
import { getRejectedProposals } from '@vegaprotocol/governance';
import { PROPOSALS_QUERY } from '../proposals';
import type { Proposals } from '../proposals/__generated__/Proposals';
import { ProposalState } from '@vegaprotocol/types';
export const RejectedProposalsContainer = () => {
const { t } = useTranslation();
const { data, loading, error } = useQuery<Proposals>(PROPOSALS_QUERY);
const { data, loading, error } = useQuery<Proposals, never>(PROPOSALS_QUERY, {
pollInterval: 5000,
errorPolicy: 'ignore', // this is to get around some backend issues and should be removed in future
});
const proposals = useMemo(() => getRejectedProposals(data), [data]);
const proposals = React.useMemo(() => {
if (!data?.proposals?.length) {
return [];
}
return flow([
compact,
(arr) =>
filter(arr, ({ state }) => state === ProposalState.STATE_REJECTED),
(arr) =>
orderBy(
arr,
[
(p) => new Date(p.terms.enactmentDatetime).getTime(),
(p) => new Date(p.terms.closingDatetime).getTime(),
(p) => p.id,
],
['desc', 'desc', 'desc']
),
])(data.proposals);
}, [data]);
if (error) {
return (
@@ -16,11 +16,6 @@ export function generateProposal(
const defaultProposal: ProposalFields = {
__typename: 'Proposal',
id: faker.datatype.uuid(),
rationale: {
__typename: 'ProposalRationale',
title: '',
description: '',
},
reference: 'ref' + faker.datatype.uuid(),
state: ProposalState.STATE_OPEN,
datetime: faker.date.past().toISOString(),
@@ -89,8 +84,8 @@ export const generateYesVotes = (
party: {
id: faker.datatype.uuid(),
__typename: 'Party',
stakingSummary: {
__typename: 'StakingSummary',
stake: {
__typename: 'PartyStake',
currentStakeAvailable: fixedTokenValue
? fixedTokenValue.toString()
: faker.datatype
@@ -124,8 +119,8 @@ export const generateNoVotes = (
party: {
id: faker.datatype.uuid(),
__typename: 'Party',
stakingSummary: {
__typename: 'StakingSummary',
stake: {
__typename: 'PartyStake',
currentStakeAvailable: fixedTokenValue
? fixedTokenValue.toString()
: faker.datatype
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
+3 -3
View File
@@ -1,7 +1,7 @@
import { useQuery } from '@apollo/client';
import { Button, Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { formatDistance } from 'date-fns';
// @ts-ignore No types available for duration-js
// @ts-ignore No types availabel for duration-js
import Duration from 'duration-js';
import gql from 'graphql-tag';
import React from 'react';
@@ -15,10 +15,10 @@ import {
AppStateActionType,
useAppState,
} from '../../../contexts/app-state/app-state-context';
import { useNetworkParam } from '../../../hooks/use-network-param';
import type { Rewards } from './__generated__/Rewards';
import { RewardInfo } from './reward-info';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useNetworkParams } from '@vegaprotocol/react-helpers';
export const REWARDS_QUERY = gql`
query Rewards($partyId: ID!) {
@@ -77,7 +77,7 @@ export const RewardsIndex = () => {
data: rewardAssetData,
loading: rewardAssetLoading,
error: rewardAssetError,
} = useNetworkParams([
} = useNetworkParam([
NetworkParams.REWARD_ASSET,
NetworkParams.REWARD_PAYOUT_DURATION,
]);
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -1,4 +1,4 @@
import { Schema as Types } from '@vegaprotocol/types';
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
@@ -7,6 +7,7 @@ import { useNavigate } from 'react-router-dom';
import { TokenInput } from '../../components/token-input';
import { NetworkParams } from '../../config';
import { useAppState } from '../../contexts/app-state/app-state-context';
import { useNetworkParam } from '../../hooks/use-network-param';
import { useSearchParams } from '../../hooks/use-search-params';
import { BigNumber } from '../../lib/bignumber';
import { addDecimal, removeDecimal } from '../../lib/decimals';
@@ -30,7 +31,6 @@ import type {
UndelegateSubmissionBody,
} from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useNetworkParam } from '@vegaprotocol/react-helpers';
export const PARTY_DELEGATIONS_QUERY = gql`
query PartyDelegations($partyId: ID!) {
@@ -103,9 +103,9 @@ export const StakingForm = ({
setAmount('');
}, [action, setAmount]);
const { data } = useNetworkParam(
NetworkParams.VALIDATOR_DELEGATION_MIN_AMOUNT
);
const { data } = useNetworkParam([
NetworkParams.VALIDATOR_DELEGATION_MIN_AMOUNT,
]);
const minTokensWithDecimals = React.useMemo(() => {
const minTokens = new BigNumber(data && data.length === 1 ? data[0] : '');
+7 -15
View File
@@ -3,28 +3,28 @@ import { generateDepositPage } from '../support/mocks/generate-deposit-page';
import { generateNetworkParameters } from '../support/mocks/generate-network-parameters';
const connectEthWalletBtn = 'connect-eth-wallet-btn';
const assetSelectField = 'select[name="asset"]';
const toAddressField = 'input[name="to"]';
const amountField = 'input[name="amount"]';
const formFieldError = 'input-error-text';
describe('deposit form validation', () => {
beforeEach(() => {
cy.mockWeb3Provider();
cy.mockGQL((req) => {
aliasQuery(req, 'NetworkParamsQuery', generateNetworkParameters());
aliasQuery(req, 'DepositPage', generateDepositPage());
aliasQuery(req, 'AssetsConnection', generateDepositPage());
});
cy.visit('/portfolio/deposit');
cy.wait('@AssetsConnection');
// Deposit page requires connection Ethereum wallet first
cy.getByTestId(connectEthWalletBtn).click();
cy.getByTestId('web3-connector-MetaMask').click();
cy.wait('@DepositPage');
});
it('handles empty fields', () => {
const assetSelectField = 'select[name="asset"]';
const toAddressField = 'input[name="to"]';
const amountField = 'input[name="amount"]';
const formFieldError = 'input-error-text';
// Submit form to trigger any empty validaion messages
cy.getByTestId('deposit-submit').click();
@@ -56,14 +56,6 @@ describe('deposit form validation', () => {
.should('have.text', 'Insufficient amount in Ethereum wallet');
});
it('unable to select assets not enabled', () => {
cy.getByTestId('deposit-submit').click();
// Assets not enabled in mocks
cy.get(assetSelectField + ' option:contains(Asset 2)').should('not.exist');
cy.get(assetSelectField + ' option:contains(Asset 3)').should('not.exist');
cy.get(assetSelectField + ' option:contains(Asset 4)').should('not.exist');
});
it('able to disconnect eth wallet', () => {
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');

Some files were not shown because too many files have changed in this diff Show More