Feat/707 link to market proposal (#1222)
* feat: link to market proposal (707) * feat: link to market proposal (707) * removed a line * removed label which made edd and barney sad
This commit is contained in:
parent
47554973c5
commit
e674efdb14
@ -9,6 +9,7 @@ import Routes from '../../../routes';
|
|||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Links } from '../../../../config';
|
import { Links } from '../../../../config';
|
||||||
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
interface ProposalsListProps {
|
interface ProposalsListProps {
|
||||||
proposals: Proposals_proposals[];
|
proposals: Proposals_proposals[];
|
||||||
@ -65,14 +66,9 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
|
|||||||
{t(
|
{t(
|
||||||
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network.`
|
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network.`
|
||||||
)}{' '}
|
)}{' '}
|
||||||
<a
|
<ExternalLink href={Links.GOVERNANCE_PAGE} className="text-white">
|
||||||
href={Links.GOVERNANCE_PAGE}
|
|
||||||
className="underline text-white"
|
|
||||||
target="_blank"
|
|
||||||
rel="nofollow noreferrer"
|
|
||||||
>
|
|
||||||
{t(`Find out more about Vega governance`)}
|
{t(`Find out more about Vega governance`)}
|
||||||
</a>
|
</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{proposals.length > 0 && (
|
{proposals.length > 0 && (
|
||||||
|
@ -20,6 +20,15 @@ export const generateMarketInfoQuery = (
|
|||||||
positionDecimalPlaces: 0,
|
positionDecimalPlaces: 0,
|
||||||
state: MarketState.STATE_ACTIVE,
|
state: MarketState.STATE_ACTIVE,
|
||||||
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||||
|
proposal: {
|
||||||
|
__typename: 'Proposal',
|
||||||
|
id: 'market-0',
|
||||||
|
rationale: {
|
||||||
|
__typename: 'ProposalRationale',
|
||||||
|
title: 'ETHBTC',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
accounts: [
|
accounts: [
|
||||||
{
|
{
|
||||||
type: AccountType.ACCOUNT_TYPE_INSURANCE,
|
type: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||||
|
@ -6,4 +6,5 @@ NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
|||||||
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
|
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
|
||||||
NX_USE_ENV_OVERRIDES=1
|
NX_USE_ENV_OVERRIDES=1
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||||
|
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789/api/v1
|
NX_VEGA_WALLET_URL=http://localhost:1789/api/v1
|
@ -6,3 +6,4 @@ NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
|
|||||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||||
|
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
@ -6,3 +6,4 @@ NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
|||||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://etherscan.io
|
NX_ETHERSCAN_URL=https://etherscan.io
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||||
|
NX_VEGA_TOKEN_URL=https://token.vega.xyz
|
@ -6,3 +6,4 @@ NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
|||||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||||
NX_VEGA_EXPLORER_URL=https://staging3.explorer.vega.xyz
|
NX_VEGA_EXPLORER_URL=https://staging3.explorer.vega.xyz
|
||||||
|
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
@ -5,3 +5,4 @@ NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
|||||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||||
|
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
@ -9,6 +9,34 @@ import { Interval, MarketState, AccountType, MarketTradingMode, AuctionTrigger }
|
|||||||
// GraphQL query operation: MarketInfoQuery
|
// GraphQL query operation: MarketInfoQuery
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
|
||||||
|
export interface MarketInfoQuery_market_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 MarketInfoQuery_market_proposal {
|
||||||
|
__typename: "Proposal";
|
||||||
|
/**
|
||||||
|
* Proposal ID that is filled by Vega once proposal reaches the network
|
||||||
|
*/
|
||||||
|
id: string | null;
|
||||||
|
/**
|
||||||
|
* Rationale behind the proposal
|
||||||
|
*/
|
||||||
|
rationale: MarketInfoQuery_market_proposal_rationale;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MarketInfoQuery_market_accounts_asset {
|
export interface MarketInfoQuery_market_accounts_asset {
|
||||||
__typename: "Asset";
|
__typename: "Asset";
|
||||||
/**
|
/**
|
||||||
@ -451,6 +479,10 @@ export interface MarketInfoQuery_market {
|
|||||||
* Current state of the market
|
* Current state of the market
|
||||||
*/
|
*/
|
||||||
state: MarketState;
|
state: MarketState;
|
||||||
|
/**
|
||||||
|
* The proposal that initiated this market
|
||||||
|
*/
|
||||||
|
proposal: MarketInfoQuery_market_proposal | null;
|
||||||
/**
|
/**
|
||||||
* Get account for a party or market
|
* Get account for a party or market
|
||||||
*/
|
*/
|
||||||
|
@ -8,6 +8,13 @@ export const MARKET_INFO_QUERY = gql`
|
|||||||
decimalPlaces
|
decimalPlaces
|
||||||
positionDecimalPlaces
|
positionDecimalPlaces
|
||||||
state
|
state
|
||||||
|
proposal {
|
||||||
|
id
|
||||||
|
rationale {
|
||||||
|
title
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
accounts {
|
accounts {
|
||||||
type
|
type
|
||||||
asset {
|
asset {
|
||||||
@ -16,13 +23,6 @@ export const MARKET_INFO_QUERY = gql`
|
|||||||
balance
|
balance
|
||||||
}
|
}
|
||||||
tradingMode
|
tradingMode
|
||||||
accounts {
|
|
||||||
type
|
|
||||||
asset {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
balance
|
|
||||||
}
|
|
||||||
fees {
|
fees {
|
||||||
factors {
|
factors {
|
||||||
makerFee
|
makerFee
|
||||||
@ -44,13 +44,6 @@ export const MARKET_INFO_QUERY = gql`
|
|||||||
short
|
short
|
||||||
long
|
long
|
||||||
}
|
}
|
||||||
accounts {
|
|
||||||
type
|
|
||||||
asset {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
balance
|
|
||||||
}
|
|
||||||
data {
|
data {
|
||||||
market {
|
market {
|
||||||
id
|
id
|
||||||
|
@ -26,6 +26,14 @@ import { useQuery } from '@apollo/client';
|
|||||||
import { totalFees } from '@vegaprotocol/market-list';
|
import { totalFees } from '@vegaprotocol/market-list';
|
||||||
import { AccountType, Interval } from '@vegaprotocol/types';
|
import { AccountType, Interval } from '@vegaprotocol/types';
|
||||||
import { MARKET_INFO_QUERY } from './info-market-query';
|
import { MARKET_INFO_QUERY } from './info-market-query';
|
||||||
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
import { generatePath } from 'react-router-dom';
|
||||||
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
|
const Links = {
|
||||||
|
PROPOSAL_PAGE: ':tokenUrl/governance/:proposalId',
|
||||||
|
};
|
||||||
|
|
||||||
export interface InfoProps {
|
export interface InfoProps {
|
||||||
market: MarketInfoQuery_market;
|
market: MarketInfoQuery_market;
|
||||||
@ -68,7 +76,8 @@ export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Info = ({ market }: InfoProps) => {
|
export const Info = ({ market }: InfoProps) => {
|
||||||
const headerClassName = 'uppercase text-lg mb-4';
|
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||||
|
const headerClassName = 'uppercase text-lg';
|
||||||
const dayVolume = calcCandleVolume(market);
|
const dayVolume = calcCandleVolume(market);
|
||||||
const marketDataPanels = [
|
const marketDataPanels = [
|
||||||
{
|
{
|
||||||
@ -272,16 +281,45 @@ export const Info = ({ market }: InfoProps) => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const marketGovPanels = [
|
||||||
|
{
|
||||||
|
title: t('Proposal'),
|
||||||
|
content: (
|
||||||
|
<p>
|
||||||
|
<ExternalLink
|
||||||
|
href={generatePath(Links.PROPOSAL_PAGE, {
|
||||||
|
tokenUrl: VEGA_TOKEN_URL,
|
||||||
|
proposalId: market.proposal?.id || '',
|
||||||
|
})}
|
||||||
|
title={
|
||||||
|
market.proposal?.rationale.title ||
|
||||||
|
market.proposal?.rationale.description ||
|
||||||
|
''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('View governance proposal')}
|
||||||
|
</ExternalLink>
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<p className={headerClassName}>{t('Market data')}</p>
|
<p className={headerClassName}>{t('Market data')}</p>
|
||||||
<Accordion panels={marketDataPanels} />
|
<Accordion panels={marketDataPanels} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="mb-4">
|
||||||
<p className={headerClassName}>{t('Market specification')}</p>
|
<p className={headerClassName}>{t('Market specification')}</p>
|
||||||
<Accordion panels={marketSpecPanels} />
|
<Accordion panels={marketSpecPanels} />
|
||||||
</div>
|
</div>
|
||||||
|
{VEGA_TOKEN_URL && market.proposal?.id && (
|
||||||
|
<div>
|
||||||
|
<p className={headerClassName}>{t('Market governance')}</p>
|
||||||
|
<Accordion panels={marketGovPanels} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -70,6 +70,8 @@ const getBundledEnvironmentValue = (key: EnvKey) => {
|
|||||||
return process.env['NX_VEGA_EXPLORER_URL'];
|
return process.env['NX_VEGA_EXPLORER_URL'];
|
||||||
case 'VEGA_WALLET_URL':
|
case 'VEGA_WALLET_URL':
|
||||||
return process.env['NX_VEGA_WALLET_URL'];
|
return process.env['NX_VEGA_WALLET_URL'];
|
||||||
|
case 'VEGA_TOKEN_URL':
|
||||||
|
return process.env['NX_VEGA_TOKEN_URL'];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ const schemaObject = {
|
|||||||
GITHUB_FEEDBACK_URL: z.optional(z.string()),
|
GITHUB_FEEDBACK_URL: z.optional(z.string()),
|
||||||
VEGA_ENV: z.nativeEnum(Networks),
|
VEGA_ENV: z.nativeEnum(Networks),
|
||||||
VEGA_EXPLORER_URL: z.optional(z.string()),
|
VEGA_EXPLORER_URL: z.optional(z.string()),
|
||||||
|
VEGA_TOKEN_URL: z.optional(z.string()),
|
||||||
VEGA_NETWORKS: z
|
VEGA_NETWORKS: z
|
||||||
.object(
|
.object(
|
||||||
Object.keys(Networks).reduce(
|
Object.keys(Networks).reduce(
|
||||||
|
@ -7,7 +7,7 @@ export type { IconName } from '@blueprintjs/icons';
|
|||||||
export interface IconProps {
|
export interface IconProps {
|
||||||
name: IconName;
|
name: IconName;
|
||||||
className?: string;
|
className?: string;
|
||||||
size?: 4 | 6 | 8 | 10 | 12 | 14 | 16;
|
size?: 2 | 3 | 4 | 6 | 8 | 10 | 12 | 14 | 16;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,6 +21,8 @@ export const Icon = ({ size = 4, name, className, ariaLabel }: IconProps) => {
|
|||||||
// Cant just concatenate as TW wont pick up that the class is being used
|
// Cant just concatenate as TW wont pick up that the class is being used
|
||||||
// so below syntax is required
|
// so below syntax is required
|
||||||
{
|
{
|
||||||
|
'w-2 h-2': size === 2,
|
||||||
|
'w-3 h-3': size === 3,
|
||||||
'w-4 h-4': size === 4,
|
'w-4 h-4': size === 4,
|
||||||
'w-6 h-6': size === 6,
|
'w-6 h-6': size === 6,
|
||||||
'w-8 h-8': size === 8,
|
'w-8 h-8': size === 8,
|
||||||
|
@ -1 +1 @@
|
|||||||
export { Link } from './link';
|
export * from './link';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { Link } from '.';
|
import { ExternalLink, Link } from '.';
|
||||||
|
|
||||||
it('renders a link with a text', () => {
|
describe('Link', () => {
|
||||||
|
it('renders a link with a text', () => {
|
||||||
render(
|
render(
|
||||||
<Link href="127.0.0.1" title="Link title">
|
<Link href="127.0.0.1" title="Link title">
|
||||||
Link text
|
Link text
|
||||||
@ -15,9 +16,9 @@ it('renders a link with a text', () => {
|
|||||||
expect(link).toHaveAttribute('title', 'Link title');
|
expect(link).toHaveAttribute('title', 'Link title');
|
||||||
expect(link).toHaveClass('cursor-pointer');
|
expect(link).toHaveClass('cursor-pointer');
|
||||||
expect(link).toHaveClass('underline');
|
expect(link).toHaveClass('underline');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a link with children elements', () => {
|
it('renders a link with children elements', () => {
|
||||||
render(
|
render(
|
||||||
<Link href="127.0.0.1" title="Link title">
|
<Link href="127.0.0.1" title="Link title">
|
||||||
<span>Link text</span>
|
<span>Link text</span>
|
||||||
@ -31,4 +32,31 @@ it('renders a link with children elements', () => {
|
|||||||
expect(link).toHaveAttribute('title', 'Link title');
|
expect(link).toHaveAttribute('title', 'Link title');
|
||||||
expect(link).toHaveClass('cursor-pointer');
|
expect(link).toHaveClass('cursor-pointer');
|
||||||
expect(link).not.toHaveClass('underline');
|
expect(link).not.toHaveClass('underline');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ExternalLink', () => {
|
||||||
|
it('should have an icon indicating that it is an external link', () => {
|
||||||
|
render(<ExternalLink href="https://vega.xyz/">Go to Vega</ExternalLink>);
|
||||||
|
const link = screen.getByTestId('external-link');
|
||||||
|
expect(link.children.length).toEqual(2);
|
||||||
|
expect(link.children[1].tagName.toUpperCase()).toEqual('SVG');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have an underlined text part', () => {
|
||||||
|
render(<ExternalLink href="https://vega.xyz/">Go to Vega</ExternalLink>);
|
||||||
|
const link = screen.getByTestId('external-link');
|
||||||
|
expect(link.children[0]).toHaveClass('underline');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not have an icon or underlined text if wrapping an element', () => {
|
||||||
|
render(
|
||||||
|
<ExternalLink href="https://vega.xyz/">
|
||||||
|
<div className="inner">inner element</div>
|
||||||
|
</ExternalLink>
|
||||||
|
);
|
||||||
|
const link = screen.getByTestId('external-link');
|
||||||
|
expect(link.children.length).toEqual(1);
|
||||||
|
expect(link.children[0]).toHaveClass('inner');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode, AnchorHTMLAttributes } from 'react';
|
import type { ReactNode, AnchorHTMLAttributes } from 'react';
|
||||||
|
import { Icon } from '../icon';
|
||||||
|
|
||||||
type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
|
type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
@ -27,5 +29,27 @@ export const Link = ({ className, children, ...props }: LinkProps) => {
|
|||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Link.displayName = 'Link';
|
Link.displayName = 'Link';
|
||||||
|
|
||||||
|
export const ExternalLink = ({ children, className, ...props }: LinkProps) => (
|
||||||
|
<Link
|
||||||
|
className={classNames(className, 'inline-flex items-baseline')}
|
||||||
|
{...props}
|
||||||
|
target="_blank"
|
||||||
|
data-testid="external-link"
|
||||||
|
>
|
||||||
|
{typeof children === 'string' ? (
|
||||||
|
<>
|
||||||
|
<span
|
||||||
|
className={classNames({ underline: typeof children === 'string' })}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
<Icon size={3} name={IconNames.SHARE} className="ml-1" />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
ExternalLink.displayName = 'ExternalLink';
|
||||||
|
Loading…
Reference in New Issue
Block a user