feat: implement multi-colored dot icon

This commit is contained in:
jamaljsr 2020-04-25 23:06:02 -04:00
parent 011620b064
commit 05c769c25c
7 changed files with 119 additions and 23 deletions

View file

@ -0,0 +1,16 @@
import React from 'react';
import StatusDot from 'components/common/StatusDot';
export default {
title: 'Components/Status Dot',
component: StatusDot,
parameters: { centered: true },
};
export const Success = () => <StatusDot status="success" />;
export const Warn = () => <StatusDot status="warn" />;
export const Error = () => <StatusDot status="error" />;
export const Idle = () => <StatusDot status="idle" />;

View file

@ -4,18 +4,7 @@ import { renderWithProviders } from 'util/tests';
import ChannelRow from 'components/loop/ChannelRow';
describe('ChannelRow component', () => {
const channel: Channel = {
active: true,
capacity: 15000000,
chanId: '150633093070848',
localBalance: 9990950,
remoteBalance: 5000000,
remotePubkey: '02ac59099da6d4bd818e6a81098f5d54580b7c3aa8255c707fa0f95ca89b02cb8c',
uptime: 97,
localPercent: 67,
balancePercent: 67,
balanceLevel: BalanceLevel.warn,
};
let channel: Channel;
const render = () => {
const result = renderWithProviders(<ChannelRow channel={channel} />);
@ -24,6 +13,21 @@ describe('ChannelRow component', () => {
};
};
beforeEach(() => {
channel = {
active: true,
capacity: 15000000,
chanId: '150633093070848',
localBalance: 9990950,
remoteBalance: 5000000,
remotePubkey: '02ac59099da6d4bd818e6a81098f5d54580b7c3aa8255c707fa0f95ca89b02cb8c',
uptime: 97,
localPercent: 67,
balancePercent: 67,
balanceLevel: BalanceLevel.warn,
};
});
it('should display the remote balance', () => {
const { getByText } = render();
expect(getByText(channel.remoteBalance.toLocaleString())).toBeInTheDocument();
@ -48,4 +52,22 @@ describe('ChannelRow component', () => {
const { getByText } = render();
expect(getByText(channel.capacity.toLocaleString())).toBeInTheDocument();
});
it('should display correct dot icon for an inactive channel', () => {
channel.active = false;
const { getByText, getByLabelText } = render();
expect(getByText('dot.svg')).toBeInTheDocument();
expect(getByLabelText('idle')).toBeInTheDocument();
});
it.each<[BalanceLevel, string]>([
[BalanceLevel.good, 'success'],
[BalanceLevel.warn, 'warn'],
[BalanceLevel.bad, 'error'],
])('should display correct dot icon for a "%s" balance', (level, label) => {
channel.balanceLevel = level;
const { getByText, getByLabelText } = render();
expect(getByText('dot.svg')).toBeInTheDocument();
expect(getByLabelText(label)).toBeInTheDocument();
});
});

View file

@ -67,7 +67,7 @@ class SwapAction {
case SwapState.FAILED:
return 'Failed';
case SwapState.INVOICE_SETTLED:
return 'Invoice Settles';
return 'Invoice Settled';
}
return 'Unknown';

View file

@ -0,0 +1,31 @@
import React from 'react';
import { styled } from 'components/theme';
import { Dot } from './icons';
const Styled = {
DotIcon: styled(Dot)`
&.success {
color: ${props => props.theme.colors.green};
}
&.warn {
color: ${props => props.theme.colors.orange};
}
&.error {
color: ${props => props.theme.colors.pink};
}
&.idle {
color: ${props => props.theme.colors.gray};
}
`,
};
interface Props {
status: 'success' | 'warn' | 'error' | 'idle';
}
const StatusDot: React.FC<Props> = ({ status }) => {
const { DotIcon } = Styled;
return <DotIcon className={status} aria-label={status} />;
};
export default StatusDot;

View file

@ -1,7 +1,7 @@
import React, { CSSProperties } from 'react';
import { Channel } from 'types/state';
import { BalanceLevel, Channel } from 'types/state';
import { Column, Row } from 'components/common/grid';
import { Dot } from 'components/common/icons';
import StatusDot from 'components/common/StatusDot';
import { Title } from 'components/common/text';
import { styled } from 'components/theme';
import ChannelBalance from './ChannelBalance';
@ -36,11 +36,6 @@ const Styled = {
`,
};
interface Props {
channel: Channel;
style?: CSSProperties;
}
export const ChannelRowHeader: React.FC = () => (
<Row>
<Column right>
@ -62,13 +57,31 @@ export const ChannelRowHeader: React.FC = () => (
</Row>
);
const ChannelDot: React.FC<{ channel: Channel }> = ({ channel }) => {
if (!channel.active) return <StatusDot status="idle" />;
switch (channel.balanceLevel) {
case BalanceLevel.good:
return <StatusDot status="success" />;
case BalanceLevel.warn:
return <StatusDot status="warn" />;
case BalanceLevel.bad:
return <StatusDot status="error" />;
}
};
interface Props {
channel: Channel;
style?: CSSProperties;
}
const ChannelRow: React.FC<Props> = ({ channel, style }) => {
const { Row, Column, StatusIcon, Balance } = Styled;
return (
<Row style={style}>
<Column right>
<StatusIcon>
<Dot />
<ChannelDot channel={channel} />
</StatusIcon>
{channel.remoteBalance.toLocaleString()}
</Column>

View file

@ -1,6 +1,7 @@
import React from 'react';
import { Swap } from 'types/state';
import { Column, Row } from 'components/common/grid';
import StatusDot from 'components/common/StatusDot';
import { SmallText } from 'components/common/text';
import { styled } from 'components/theme';
@ -10,9 +11,21 @@ const Styled = {
`,
SmallText: styled(SmallText)`
line-height: 1;
margin-left: 10px;
`,
};
const SwapDot: React.FC<{ swap: Swap }> = ({ swap }) => {
switch (swap.status) {
case 'Success':
return <StatusDot status="success" />;
case 'Failed':
return <StatusDot status="error" />;
default:
return <StatusDot status="warn" />;
}
};
interface Props {
swaps: Swap[];
}
@ -26,6 +39,7 @@ const LoopHistory: React.FC<Props> = ({ swaps }) => {
{recentSwaps.map(swap => (
<Row key={swap.id}>
<Column cols={6}>
<SwapDot swap={swap} />
<SmallText>{swap.createdOn.toLocaleDateString()}</SmallText>
</Column>
<RightColumn cols={6}>

View file

@ -114,8 +114,8 @@ export const loopListSwaps: LOOP.ListSwapsResponse.AsObject = {
amt: 500000 + i * 5000,
id: `f4eb118383c2b09d8c7289ce21c25900cfb4545d46c47ed23a31ad2aa57ce83${i}`,
idBytes: '9OsRg4PCsJ2MconOIcJZAM+0VF1GxH7SOjGtKqV86DU=',
type: (i % 3) as any,
state: i as any,
type: (i % 3) as LOOP.SwapStatus.AsObject['type'],
state: (i % 7) as LOOP.SwapStatus.AsObject['state'],
initiationTime: 1586390353623905000 + i * 100000000000000,
lastUpdateTime: 1586398369729857000,
htlcAddress: 'bcrt1qzu4077erkr78k52yuf2rwkk6ayr6m3wtazdfz2qqmd7taa5vvy9s5d75gd',