Skip to content

Commit 424c265

Browse files
authored
Add vote proposal card UI
Add vote proposal card UI
2 parents dfc724d + 68281e7 commit 424c265

File tree

16 files changed

+664
-8
lines changed

16 files changed

+664
-8
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"noop-ts": "^1.0.3",
5656
"react": "^16.9.6",
5757
"react-copy-to-clipboard": "^5.0.2",
58+
"react-countdown": "^2.3.2",
5859
"react-dom": "npm:@hot-loader/react-dom@^17.0.1",
5960
"react-i18next": "^11.16.5",
6061
"react-markdown": "^4.3.1",

src/components/v2/Icon/icons/check.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@ import * as React from 'react';
22
import { SVGProps } from 'react';
33

44
const SvgCheck = (props: SVGProps<SVGSVGElement>) => (
5-
<svg viewBox="0 0 65 64" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
5+
<svg
6+
viewBox="0 0 65 64"
7+
fill="none"
8+
xmlns="http://www.w3.org/2000/svg"
9+
strokeWidth={4}
10+
{...props}
11+
>
612
<circle cx={32.5} cy={32} r={24} fill="currentColor" />
713
<path
814
d="m41.5 26-12 12-6-6"
915
stroke="#fff"
10-
strokeWidth={4}
16+
strokeWidth="inherit"
1117
strokeLinecap="round"
1218
strokeLinejoin="round"
1319
/>

src/components/v2/Icon/icons/dots.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as React from 'react';
2+
import { SVGProps } from 'react';
3+
4+
const SvgDots = (props: SVGProps<SVGSVGElement>) => (
5+
<svg
6+
width="21"
7+
height="4"
8+
viewBox="0 0 21 4"
9+
fill="none"
10+
xmlns="http://www.w3.org/2000/svg"
11+
{...props}
12+
>
13+
<circle cx="10.5" cy="2" r="2" fill="currentColor" />
14+
<circle cx="18.5" cy="2" r="2" fill="currentColor" />
15+
<circle cx="2.5" cy="2" r="2" fill="currentColor" />
16+
</svg>
17+
);
18+
19+
export default SvgDots;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react';
2+
import { SVGProps } from 'react';
3+
4+
const SvgExclamation = (props: SVGProps<SVGSVGElement>) => (
5+
<svg viewBox="0 0 3 18" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
6+
<circle cx="1.5" cy="16.5" r="1.5" fill="currentColor" />
7+
<rect x="0.5" width="2" height="12" rx="1" fill="currentColor" />
8+
</svg>
9+
);
10+
11+
export default SvgExclamation;

src/components/v2/Icon/icons/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export { default as checkInline } from './checkInline';
3737
export { default as mark } from './mark';
3838
export { default as arrowShaft } from './arrowShaft';
3939
export { default as notice } from './notice';
40+
export { default as dots } from './dots';
41+
export { default as exclamation } from './exclamation';
4042

4143
// Coin icons
4244
export { default as aave } from './coins/aave';

src/components/v2/ProgressBar/index.stories.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ export const ValidProgressBar = () => (
1313
<ProgressBar value={50} step={5} mark={75} ariaLabel="Storybook slider" min={0} max={100} />
1414
);
1515

16+
export const ProgressBarWithCustomProgressColor = () => (
17+
<ProgressBar
18+
successColor="yellow"
19+
value={50}
20+
step={5}
21+
mark={75}
22+
ariaLabel="Storybook slider"
23+
min={0}
24+
max={100}
25+
/>
26+
);
27+
1628
export const ValidProgressBarWithTooltip = () => (
1729
<ProgressBar
1830
value={50}

src/components/v2/ProgressBar/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import MaterialSlider from '@mui/material/Slider';
44
import Box from '@mui/material/Box';
55
import { SliderTypeMap } from '@mui/material/Slider/Slider';
66

7+
import { PALETTE } from 'theme/MuiThemeProvider/muiTheme';
78
import { Tooltip, ITooltipProps } from '../Tooltip';
89
import { useStyles } from './styles';
910

@@ -19,6 +20,7 @@ export interface IProgressBarProps {
1920
markTooltip?: ITooltipProps['title'];
2021
className?: string;
2122
tooltipPlacement?: ITooltipProps['placement'];
23+
successColor?: string;
2224
}
2325

2426
export const ProgressBar = ({
@@ -33,13 +35,15 @@ export const ProgressBar = ({
3335
markTooltip,
3436
className,
3537
tooltipPlacement = 'top',
38+
successColor = PALETTE.interactive.success,
3639
}: IProgressBarProps) => {
3740
const safeValue = value < max ? value : max;
3841

3942
const marks = mark ? [{ value: mark }] : undefined;
4043
const styles = useStyles({
4144
over: mark ? safeValue > mark : false,
4245
secondaryOver: mark ? !!(secondaryValue && secondaryValue > mark) : false,
46+
successColor,
4347
});
4448

4549
const renderMark = (props?: NonNullable<SliderTypeMap['props']['componentsProps']>['mark']) => (

src/components/v2/ProgressBar/styles.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
import { css } from '@emotion/react';
22
import { useTheme } from '@mui/material';
33

4-
export const useStyles = ({ over, secondaryOver }: { over: boolean; secondaryOver: boolean }) => {
4+
export const useStyles = ({
5+
over,
6+
secondaryOver,
7+
successColor,
8+
}: {
9+
over: boolean;
10+
secondaryOver: boolean;
11+
successColor: string;
12+
}) => {
513
const theme = useTheme();
614
return {
715
slider: css`
816
display: block;
9-
color: ${over ? theme.palette.interactive.error50 : theme.palette.interactive.success};
17+
color: ${over ? theme.palette.interactive.error50 : successColor};
1018
background-color: ${theme.palette.background.default};
1119
height: ${theme.spacing(2)};
1220
padding: 0 !important;
1321
&.Mui-disabled {
14-
color: ${over ? theme.palette.interactive.error50 : theme.palette.interactive.success};
22+
color: ${over ? theme.palette.interactive.error50 : successColor};
1523
}
1624
.MuiSlider-track {
17-
background-color: ${over
18-
? theme.palette.interactive.error50
19-
: theme.palette.interactive.success};
25+
background-color: ${over ? theme.palette.interactive.error50 : successColor};
2026
height: ${theme.spacing(2)};
2127
border-radius: ${theme.spacing(1)};
2228
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/** @jsxImportSource @emotion/react */
2+
import React, { useMemo } from 'react';
3+
import { BigNumber } from 'bignumber.js';
4+
import Typography from '@mui/material/Typography';
5+
import { useTranslation } from 'translation';
6+
import { PALETTE } from 'theme/MuiThemeProvider/muiTheme';
7+
import { TokenId } from 'types';
8+
import { convertWeiToCoins } from 'utilities/common';
9+
import { ProgressBar } from '../../ProgressBar';
10+
import { useStyles } from '../styles';
11+
12+
interface IActiveVotingProgressProps {
13+
tokenId: TokenId;
14+
votedForWei?: BigNumber;
15+
votedAgainstWei?: BigNumber;
16+
abstainedWei?: BigNumber;
17+
votedTotalWei?: BigNumber;
18+
}
19+
20+
const getValueString = (tokenId: TokenId, valueWei?: BigNumber) => {
21+
// if !valueWei the progress row will not be rendered
22+
if (!valueWei) return undefined;
23+
return convertWeiToCoins({
24+
valueWei,
25+
tokenId,
26+
returnInReadableFormat: true,
27+
});
28+
};
29+
30+
const getValueNumber = (tokenId: TokenId, valueWei?: BigNumber) => {
31+
if (!valueWei) return 0;
32+
return +convertWeiToCoins({
33+
valueWei,
34+
tokenId,
35+
returnInReadableFormat: false,
36+
}).toFormat();
37+
};
38+
39+
export const ActiveVotingProgress: React.FC<IActiveVotingProgressProps> = ({
40+
tokenId,
41+
votedForWei,
42+
votedAgainstWei,
43+
abstainedWei,
44+
votedTotalWei,
45+
}) => {
46+
const styles = useStyles();
47+
const { t } = useTranslation();
48+
49+
const votedTotalCoins = getValueNumber(tokenId, votedTotalWei);
50+
51+
const defaultProgressbarProps = {
52+
step: 0.0001,
53+
min: 0,
54+
55+
// || 1 is used for rendering an empty progressbar for case when votedTotalCoins is 0
56+
max: votedTotalCoins || 1,
57+
};
58+
59+
const activeProposalVotingData = useMemo(
60+
() => [
61+
{
62+
id: 'for',
63+
label: t('voteProposalUi.statusCard.for'),
64+
value: getValueString(tokenId, votedForWei),
65+
progressBarProps: {
66+
ariaLabel: t('voteProposalUi.statusCard.ariaLabelFor'),
67+
value: getValueNumber(tokenId, votedForWei),
68+
},
69+
},
70+
{
71+
id: 'against',
72+
label: t('voteProposalUi.statusCard.against'),
73+
value: getValueString(tokenId, votedAgainstWei),
74+
progressBarProps: {
75+
successColor: PALETTE.interactive.error50,
76+
ariaLabel: t('voteProposalUi.statusCard.ariaLabelAgainst'),
77+
value: getValueNumber(tokenId, votedAgainstWei),
78+
},
79+
},
80+
{
81+
id: 'abstain',
82+
label: t('voteProposalUi.statusCard.abstain'),
83+
value: getValueString(tokenId, abstainedWei),
84+
progressBarProps: {
85+
successColor: PALETTE.text.secondary,
86+
ariaLabel: t('voteProposalUi.statusCard.ariaLabelAbstain'),
87+
value: getValueNumber(tokenId, abstainedWei),
88+
},
89+
},
90+
],
91+
[votedForWei, votedAgainstWei, abstainedWei],
92+
);
93+
94+
return (
95+
<div css={styles.votesWrapper}>
96+
{activeProposalVotingData.map(({ id, label, value, progressBarProps }) => {
97+
if (!value) {
98+
return null;
99+
}
100+
return (
101+
<React.Fragment key={id}>
102+
<div css={styles.voteRow}>
103+
<Typography variant="small2" color="textPrimary">
104+
{label}
105+
</Typography>
106+
107+
<Typography variant="small2" color="textPrimary">
108+
{value}
109+
</Typography>
110+
</div>
111+
<ProgressBar {...defaultProgressbarProps} {...progressBarProps} />
112+
</React.Fragment>
113+
);
114+
})}
115+
</div>
116+
);
117+
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React from 'react';
2+
import { BigNumber } from 'bignumber.js';
3+
import { withThemeProvider, withCenterStory } from 'stories/decorators';
4+
import { VoteProposalUi } from '.';
5+
6+
export default {
7+
title: 'Components/VoteProposalUi',
8+
decorators: [withThemeProvider, withCenterStory({ width: 750 })],
9+
parameters: {
10+
backgrounds: {
11+
default: 'Primary',
12+
},
13+
},
14+
};
15+
16+
export const Active = () => (
17+
<VoteProposalUi
18+
proposalNumber={58}
19+
proposalText="Buy back and burn and Tokenomic contribution finished soon"
20+
proposalStatus="active"
21+
votedForWei={new BigNumber('500000000000000000')}
22+
votedAgainstWei={new BigNumber('2000000000000000000')}
23+
abstainedWei={new BigNumber('0')}
24+
userVoteStatus="votedFor"
25+
cancelDate={new Date(Date.now() + 3650000)}
26+
tokenId="xvs"
27+
/>
28+
);
29+
export const Queued = () => (
30+
<VoteProposalUi
31+
proposalNumber={58}
32+
proposalText="Buy back and burn and Tokenomic contribution finished soon with very very very very very very very very very very very very very very very very long text example"
33+
proposalStatus="queued"
34+
cancelDate={new Date(Date.now() + 3650000)}
35+
/>
36+
);
37+
export const ReadyToExecute = () => (
38+
<VoteProposalUi
39+
proposalNumber={58}
40+
proposalText="Buy back and burn and Tokenomic contribution finished soon"
41+
proposalStatus="readyToExecute"
42+
cancelDate={new Date(Date.now() + 3650000)}
43+
/>
44+
);
45+
export const Executed = () => (
46+
<VoteProposalUi
47+
proposalNumber={58}
48+
proposalText="Buy back and burn and Tokenomic contribution finished soon"
49+
proposalStatus="executed"
50+
cancelDate={new Date(Date.now() + 3650000)}
51+
/>
52+
);
53+
export const Cancelled = () => (
54+
<VoteProposalUi
55+
proposalNumber={58}
56+
proposalText="Buy back and burn and Tokenomic contribution finished soon"
57+
proposalStatus="cancelled"
58+
cancelDate={new Date(Date.now())}
59+
/>
60+
);

0 commit comments

Comments
 (0)