Add collapsible rows to democracy (#8656)

* Add collapsible rows to democracy

* Adjust padding

* shortHash

* Fix test text

* Move bounty links

* Fix contacts tests
This commit is contained in:
Jaco 2022-12-30 10:38:16 +02:00 committed by GitHub
parent fd864a0368
commit f288a265a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 314 additions and 279 deletions

View File

@ -722,22 +722,19 @@ function Account ({ account: { address, meta }, className = '', delegation, filt
</tr>
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
<td />
<td className='top tagInfo'>
<td className='columar'>
<Columar isFull>
<Columar.Column>
<LinkExternal
data={address}
isMain
type='address'
withTitle
/>
</Columar.Column>
</Columar>
<Columar>
<Columar.Column>
<div
className='tags'
data-testid='tags'
>
<div data-testid='tags'>
<Tags
value={tags}
withTitle
@ -766,23 +763,6 @@ function Account ({ account: { address, meta }, className = '', delegation, filt
}
export default React.memo(styled(Account)`
&.isCollapsed {
visibility: collapse;
}
&.isExpanded {
visibility: visible;
}
.ui--Columar {
margin: 0.5rem 0 1.5rem 0;
}
.tags {
width: 100%;
min-height: 1.5rem;
}
.devBadge {
opacity: 0.65;
}

View File

@ -143,11 +143,11 @@ describe('Accounts page', () => {
await accountRows[0].assertShortAddress(aliceShortAddress);
});
it('when account is not tagged, account row details displays no tags info', async () => {
it('when account is not tagged, account row details displays none info', async () => {
accountsPage.renderDefaultAccounts(1);
const rows = await accountsPage.getAccountRows();
await rows[0].assertTags('no tags');
await rows[0].assertTags('none');
});
it('when account is tagged, account row details displays tags', async () => {

View File

@ -72,7 +72,7 @@ describe.skip('Sidebar', () => {
it('cannot be edited if edit button has not been pressed', async () => {
accountsPage.renderDefaultAccounts(1);
sideBar = await accountsPage.openSidebarForRow(0);
await sideBar.clickByText('no tags');
await sideBar.clickByText('none');
expect(sideBar.queryByRole('combobox')).toBeFalsy();
await expect(sideBar.typeAccountName(newName)).rejects.toThrowError(nameInputNotFoundError);
@ -95,7 +95,7 @@ describe.skip('Sidebar', () => {
);
sideBar = await accountsPage.openSidebarForRow(0);
await sideBar.assertTags('no tags');
await sideBar.assertTags('none');
sideBar.edit();
});
@ -104,7 +104,7 @@ describe.skip('Sidebar', () => {
await sideBar.selectTag(defaultTag);
sideBar.cancel();
await sideBar.assertTags('no tags');
await sideBar.assertTags('none');
await sideBar.assertAccountName(initialName);
});
@ -130,7 +130,7 @@ describe.skip('Sidebar', () => {
fireEvent.click(await screen.findByText('accounts'));
await sideBar.assertTags('no tags');
await sideBar.assertTags('none');
await sideBar.assertAccountName('ALICE');
expect(sideBar.queryByRole('button', { name: 'Cancel' })).toBeFalsy();

View File

@ -251,22 +251,19 @@ function Address ({ address, className = '', filter, isFavorite, toggleFavorite
</tr>
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
<td />
<td className='top'>
<td className='columar'>
<Columar isFull>
<Columar.Column>
<LinkExternal
data={address}
isMain
type='address'
withTitle
/>
</Columar.Column>
</Columar>
<Columar>
<Columar.Column>
<div
className='tags'
data-testid='tags'
>
<div data-testid='tags'>
<Tags
value={tags}
withTitle
@ -289,21 +286,5 @@ function Address ({ address, className = '', filter, isFavorite, toggleFavorite
);
}
export default React.memo(styled(Address)`
&.isCollapsed {
visibility: collapse;
}
&.isExpanded {
visibility: visible;
}
.tags {
width: 100%;
min-height: 1.5rem;
}
.ui--Columar {
margin: 0.5rem 0 1.5rem 0;
}
`);
// FIXME: This is weird, if we remove the styled wrapper we have test failures...
export default React.memo(styled(Address)``);

View File

@ -60,6 +60,7 @@ describe('Addresses page', () => {
describe('when some contacts exist', () => {
it('the contacts table contains some contact rows', async () => {
addressesPage.renderDefaultContacts(2);
const rows = await addressesPage.getAddressesRows();
expect(rows).toHaveLength(2);
@ -97,7 +98,7 @@ describe('Addresses page', () => {
addressesPage.renderDefaultContacts(1);
const rows = await addressesPage.getAddressesRows();
await rows[0].assertTags('no tags');
await rows[0].assertTags('none');
});
it('when a contact is tagged, the details row displays tags', async () => {

View File

@ -62,15 +62,15 @@ function Bounties ({ className }: Props): React.ReactElement {
export default React.memo(styled(Bounties)`
.bounties-table-wrapper table {
tr {
td,
&:not(.filter) th {
td, &:not(.filter) th {
&:last-child {
padding-right: 1.14rem;
}
}
}
}
.ui--IdentityIcon {
.ui--IdentityIcon {
margin-right: 0.42rem;
}

View File

@ -7,7 +7,7 @@ import type { BlockNumber, Bounty as BountyType, BountyIndex } from '@polkadot/t
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { AddressSmall, ExpandButton, LinkExternal } from '@polkadot/react-components';
import { AddressSmall, Columar, ExpandButton, LinkExternal } from '@polkadot/react-components';
import { useToggle } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
@ -118,10 +118,6 @@ function Bounty ({ bestNumber, bounty, className = '', description, index, propo
</td>
<td className='actions'>
<div>
<LinkExternal
data={index}
type='bounty'
/>
<BountyExtraActions
bestNumber={bestNumber}
description={description}
@ -137,33 +133,46 @@ function Bounty ({ bestNumber, bounty, className = '', description, index, propo
</td>
</tr>
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
<td colSpan={2}>
<div className='label-column-left'>
<div className='label'>{t('Proposer')}</div>
<AddressSmall value={proposer} />
</div>
</td>
<td colSpan={2}>
<div className='label-column-right'>
<div className='label'>{t('Bond')}</div>
<div className='inline-balance'><FormatBalance value={bond} /></div>
</div>
{curator && (
<div className='label-column-right'>
<div className='label'>{t("Curator's fee")}</div>
<div className='inline-balance'>{<FormatBalance value={fee} />}</div>
</div>
)}
<div className='label-column-right'>
{curator && !curatorDeposit.isZero() && (
<>
<div className='label'>{t("Curator's deposit")}</div>
<div className='inline-balance'>
<FormatBalance value={curatorDeposit} />
<td />
<td
className='columar'
colSpan={3}
>
<Columar>
<Columar.Column>
<LinkExternal
data={index}
type='bounty'
withTitle
/>
</Columar.Column>
<Columar.Column>
<div className='column'>
<h5>{t('Proposer')}</h5>
<AddressSmall value={proposer} />
</div>
<div className='column'>
<h5>{t('Bond')}</h5>
<div className='inline-balance'><FormatBalance value={bond} /></div>
</div>
{curator && (
<div className='column'>
<h5>{t("Curator's fee")}</h5>
<div className='inline-balance'>{<FormatBalance value={fee} />}</div>
</div>
</>
)}
</div>
)}
<div className='column'>
{curator && !curatorDeposit.isZero() && (
<>
<h5>{t("Curator's deposit")}</h5>
<div className='inline-balance'>
<FormatBalance value={curatorDeposit} />
</div>
</>
)}
</div>
</Columar.Column>
</Columar>
</td>
<td />
<td />
@ -210,31 +219,18 @@ export default React.memo(styled(Bounty)`
line-height: normal;
}
.label {
text-align: right;
padding: 0 1.7rem 0 0;
line-height: normal;
color: var(--color-label);
text-transform: lowercase;
}
.label-column-right, .label-column-left{
display: flex;
.column {
align-items: center;
display: flex;
padding: 0 0 0.5rem;
.label {
h5 {
text-align: right;
padding: 0 1.7rem 0 0;
width: 50%;
}
}
.label-column-right {
padding: 0 0 0.5rem;
}
.label-column-left {
padding: 0 0 0.5rem;
}
& .td-info-action-row {
padding-right: 0;
}

View File

@ -5,8 +5,8 @@ import type { DeriveProposalExternal } from '@polkadot/api-derive/types';
import React from 'react';
import { AddressMini, Button, LinkExternal } from '@polkadot/react-components';
import { useCollectiveMembers } from '@polkadot/react-hooks';
import { AddressMini, Button, Columar, ExpandButton, LinkExternal } from '@polkadot/react-components';
import { useCollectiveMembers, useToggle } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import Fasttrack from './Fasttrack';
@ -20,42 +20,59 @@ interface Props {
function External ({ className = '', value: { image, imageHash, threshold } }: Props): React.ReactElement<Props> | null {
const { isMember, members } = useCollectiveMembers('technicalCommittee');
const [isExpanded, toggleIsExpanded] = useToggle(false);
return (
<tr className={className}>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='address'>
{image && (
<AddressMini value={image.proposer} />
)}
</td>
<td className='number'>
{image && <FormatBalance value={image.balance} />}
</td>
<td className='button'>
<Button.Group>
{!image?.proposal && (
<PreImageButton imageHash={imageHash} />
)}
{threshold && isMember && (
<Fasttrack
imageHash={imageHash}
members={members}
threshold={threshold}
/>
)}
</Button.Group>
</td>
<td className='links media--1000'>
<LinkExternal
data={imageHash}
type='external'
<>
<tr className={`${className}${isExpanded ? ' noBorder' : ''}`}>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
</td>
</tr>
<td className='address'>
{image && (
<AddressMini value={image.proposer} />
)}
</td>
<td className='number'>
{image && <FormatBalance value={image.balance} />}
</td>
<td className='actions'>
<Button.Group>
{!image?.proposal && (
<PreImageButton imageHash={imageHash} />
)}
{threshold && isMember && (
<Fasttrack
imageHash={imageHash}
members={members}
threshold={threshold}
/>
)}
<ExpandButton
expanded={isExpanded}
onClick={toggleIsExpanded}
/>
</Button.Group>
</td>
</tr>
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
<td
className='columar'
colSpan={100}
>
<Columar isFull>
<Columar.Column>
<LinkExternal
data={imageHash}
type='external'
withTitle
/>
</Columar.Column>
</Columar>
</td>
</tr>
</>
);
}

View File

@ -24,8 +24,7 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
[t('external'), 'start'],
[t('proposer'), 'address'],
[t('locked')],
[],
[undefined, 'media--1000']
[]
]);
return (
@ -33,6 +32,7 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
className={className}
empty={external === null && t<string>('No external proposal')}
header={headerRef.current}
withCollapsibleRows
>
{external && <External value={external} />}
</Table>

View File

@ -5,7 +5,8 @@ import type { DeriveProposal } from '@polkadot/api-derive/types';
import React, { useCallback, useMemo } from 'react';
import { AddressMini, Button, ExpanderScroll, LinkExternal } from '@polkadot/react-components';
import { AddressMini, Button, Columar, ExpandButton, ExpanderScroll, LinkExternal } from '@polkadot/react-components';
import { useToggle } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
@ -21,6 +22,7 @@ interface Props {
function Proposal ({ className = '', value: { balance, image, imageHash, index, proposer, seconds } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isExpanded, toggleIsExpanded] = useToggle(false);
const seconding = useMemo(
() => seconds.filter((_address, index) => index !== 0),
@ -40,47 +42,64 @@ function Proposal ({ className = '', value: { balance, image, imageHash, index,
);
return (
<tr className={className}>
<td className='number'><h1>{formatNumber(index)}</h1></td>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='address'>
<AddressMini value={proposer} />
</td>
<td className='number together media--1200'>
<FormatBalance value={balance} />
</td>
<td className='expand'>
{seconding.length !== 0 && (
<ExpanderScroll
empty={seconding && t<string>('No endorsements')}
renderChildren={renderSeconds}
summary={t<string>('Endorsed ({{count}})', { replace: { count: seconding.length } })}
/>
)}
</td>
<td className='button'>
<Button.Group>
{!image?.proposal && (
<PreImageButton imageHash={imageHash} />
)}
<Seconding
deposit={balance}
depositors={seconds || []}
image={image}
proposalId={index}
/>
</Button.Group>
</td>
<td className='links media--1000'>
<LinkExternal
data={index}
type='proposal'
<>
<tr className={`${className}${isExpanded ? ' noBorder' : ''}`}>
<td className='number'><h1>{formatNumber(index)}</h1></td>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
</td>
</tr>
<td className='address'>
<AddressMini value={proposer} />
</td>
<td className='number together media--1200'>
<FormatBalance value={balance} />
</td>
<td className='expand'>
{seconding.length !== 0 && (
<ExpanderScroll
empty={seconding && t<string>('No endorsements')}
renderChildren={renderSeconds}
summary={t<string>('Endorsed ({{count}})', { replace: { count: seconding.length } })}
/>
)}
</td>
<td className='actions'>
<Button.Group>
{!image?.proposal && (
<PreImageButton imageHash={imageHash} />
)}
<Seconding
deposit={balance}
depositors={seconds || []}
image={image}
proposalId={index}
/>
<ExpandButton
expanded={isExpanded}
onClick={toggleIsExpanded}
/>
</Button.Group>
</td>
</tr>
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
<td />
<td
className='columar'
colSpan={100}
>
<Columar isFull>
<Columar.Column>
<LinkExternal
data={index}
type='proposal'
withTitle
/>
</Columar.Column>
</Columar>
</td>
</tr>
</>
);
}

View File

@ -6,6 +6,7 @@ import type { Hash, Proposal, ProposalIndex } from '@polkadot/types/interfaces';
import type { HexString } from '@polkadot/util/types';
import React from 'react';
import styled from 'styled-components';
import usePreimage from '@polkadot/app-preimages/usePreimage';
import { CallExpander } from '@polkadot/react-components';
@ -38,7 +39,7 @@ function ProposalCell ({ className = '', imageHash, proposal }: Props): React.Re
return (
<td className={`${className} all`}>
{t('preimage {{hash}}', { replace: { hash: `${textHash.slice(0, 8)}…${textHash.slice(-8)}` } })}
<div className='shortHash'>{textHash.slice(0, 8)}…{textHash.slice(-8)}</div>
</td>
);
}
@ -65,4 +66,12 @@ function ProposalCell ({ className = '', imageHash, proposal }: Props): React.Re
);
}
export default React.memo(ProposalCell);
export default React.memo(styled(ProposalCell)`
.shortHash {
font: var(--font-mono);
+ div {
margin-left: 0.5rem;
}
}
`);

View File

@ -24,8 +24,7 @@ function Proposals ({ className }: Props): React.ReactElement<Props> {
[t('proposals'), 'start', 2],
[t('proposer'), 'address'],
[t('locked'), 'media--1200'],
[undefined, undefined, 2],
[undefined, 'media--1000']
[undefined, undefined, 2]
]);
return (
@ -33,6 +32,7 @@ function Proposals ({ className }: Props): React.ReactElement<Props> {
className={className}
empty={proposals && t<string>('No active proposals')}
header={headerRef.current}
withCollapsibleRows
>
{proposals?.map((proposal): React.ReactNode => (
<ProposalDisplay

View File

@ -7,8 +7,8 @@ import type { Balance } from '@polkadot/types/interfaces';
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { Badge, Button, Icon, LinkExternal, Progress } from '@polkadot/react-components';
import { useAccounts, useApi, useBestNumber, useCall } from '@polkadot/react-hooks';
import { Badge, Button, Columar, ExpandButton, Icon, LinkExternal, Progress } from '@polkadot/react-components';
import { useAccounts, useApi, useBestNumber, useCall, useToggle } from '@polkadot/react-hooks';
import { BlockToTime } from '@polkadot/react-query';
import { BN, BN_ONE, formatNumber, isBoolean } from '@polkadot/util';
@ -44,6 +44,7 @@ function Referendum ({ className = '', value: { allAye, allNay, image, imageHash
const { api } = useApi();
const { allAccounts } = useAccounts();
const bestNumber = useBestNumber();
const [isExpanded, toggleIsExpanded] = useToggle(false);
const totalIssuance = useCall<Balance>(api.query.balances?.totalIssuance);
const { changeAye, changeNay } = useChangeCalc(status.threshold, votedAye, votedNay, votedTotal);
const threshold = useMemo(
@ -89,88 +90,105 @@ function Referendum ({ className = '', value: { allAye, allNay, image, imageHash
const remainBlock = status.end.sub(bestNumber).isub(BN_ONE);
return (
<tr className={className}>
<td className='number'><h1>{formatNumber(index)}</h1></td>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='number together media--1200'>
<BlockToTime value={remainBlock} />
{t<string>('{{blocks}} blocks', { replace: { blocks: formatNumber(remainBlock) } })}
</td>
<td className='number together media--1400'>
<BlockToTime value={enactBlock.sub(bestNumber)} />
#{formatNumber(enactBlock)}
</td>
<td className='number together media--1400'>
{percentages && (
<>
<div>{percentages.turnout}</div>
</>
)}
</td>
<td className='badge'>
{isBoolean(isPassing) && (
<Badge
color={isPassing ? 'green' : 'red'}
hover={
isPassing
? t<string>('{{threshold}}, passing', { replace: { threshold } })
: t<string>('{{threshold}}, not passing', { replace: { threshold } })
}
icon={isPassing ? 'check' : 'times'}
/>
)}
</td>
<td className='expand'>
<ReferendumVotes
change={changeAye}
count={voteCountAye}
isAye
isWinning={isPassing}
total={votedAye}
votes={allAye}
<>
<tr className={`${className}${isExpanded ? ' noBorder' : ''}`}>
<td className='number'><h1>{formatNumber(index)}</h1></td>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<ReferendumVotes
change={changeNay}
count={voteCountNay}
isAye={false}
isWinning={!isPassing}
total={votedNay}
votes={allNay}
/>
</td>
<td className='media--1000 middle chart'>
<Progress
total={totalCalculated}
value={votedAye}
/>
</td>
<td className='button'>
<Button.Group>
{!image?.proposal && (
<PreImageButton imageHash={imageHash} />
<td className='number together media--1200'>
<BlockToTime value={remainBlock} />
{t<string>('{{blocks}} blocks', { replace: { blocks: formatNumber(remainBlock) } })}
</td>
<td className='number together media--1400'>
<BlockToTime value={enactBlock.sub(bestNumber)} />
#{formatNumber(enactBlock)}
</td>
<td className='number together media--1400'>
{percentages && (
<>
<div>{percentages.turnout}</div>
</>
)}
<Voting
proposal={image?.proposal}
referendumId={index}
</td>
<td className='badge'>
{isBoolean(isPassing) && (
<Badge
color={isPassing ? 'green' : 'red'}
hover={
isPassing
? t<string>('{{threshold}}, passing', { replace: { threshold } })
: t<string>('{{threshold}}, not passing', { replace: { threshold } })
}
icon={isPassing ? 'check' : 'times'}
/>
)}
</td>
<td className='expand'>
<ReferendumVotes
change={changeAye}
count={voteCountAye}
isAye
isWinning={isPassing}
total={votedAye}
votes={allAye}
/>
</Button.Group>
</td>
<td className='badge'>
<Icon
color={hasVoted ? (hasVotedAye ? 'green' : 'red') : 'gray'}
icon='asterisk'
/>
</td>
<td className='links media--1000'>
<LinkExternal
data={index}
type='referendum'
/>
</td>
</tr>
<ReferendumVotes
change={changeNay}
count={voteCountNay}
isAye={false}
isWinning={!isPassing}
total={votedNay}
votes={allNay}
/>
</td>
<td className='media--1000 middle chart'>
<Progress
total={totalCalculated}
value={votedAye}
/>
</td>
<td className='badge'>
<Icon
color={hasVoted ? (hasVotedAye ? 'green' : 'red') : 'gray'}
icon='asterisk'
/>
</td>
<td className='actions'>
<Button.Group>
{!image?.proposal && (
<PreImageButton imageHash={imageHash} />
)}
<Voting
proposal={image?.proposal}
referendumId={index}
/>
<ExpandButton
expanded={isExpanded}
onClick={toggleIsExpanded}
/>
</Button.Group>
</td>
</tr>
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
<td />
<td
className='columar'
colSpan={100}
>
<Columar isFull>
<Columar.Column>
<LinkExternal
data={index}
type='referendum'
withTitle
/>
</Columar.Column>
</Columar>
</td>
</tr>
</>
);
}

View File

@ -26,8 +26,7 @@ function Referendums ({ className = '', referendums }: Props): React.ReactElemen
[undefined, 'badge'],
[t('votes'), 'expand'],
[t<string>('support'), 'media--1000'],
[undefined, undefined, 2],
[undefined, 'media--1000']
[undefined, undefined, 2]
]);
return (
@ -35,6 +34,7 @@ function Referendums ({ className = '', referendums }: Props): React.ReactElemen
className={className}
empty={referendums && t<string>('No active referendums')}
header={headerRef.current}
withCollapsibleRows
>
{referendums?.map((referendum): React.ReactNode => (
<Referendum

View File

@ -16,11 +16,11 @@ interface Props {
className?: string;
data: BN | number | string;
hash?: string;
isMain?: boolean;
isText?: boolean;
isSidebar?: boolean;
isSmall?: boolean;
type: LinkTypes;
withTitle?: boolean;
}
// function shortName (name: string): string {
@ -61,7 +61,7 @@ function genLinks (systemChain: string, { data, hash, isSidebar, isText, type }:
.filter((node): node is React.ReactNode => !!node);
}
function LinkExternal ({ className = '', data, hash, isMain, isSidebar, isSmall, isText, type }: Props): React.ReactElement<Props> | null {
function LinkExternal ({ className = '', data, hash, isSidebar, isSmall, isText, type, withTitle }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { systemChain } = useApi();
const links = useMemo(
@ -69,17 +69,22 @@ function LinkExternal ({ className = '', data, hash, isMain, isSidebar, isSmall,
[systemChain, data, hash, isSidebar, isText, type]
);
if (!links.length) {
if (!links.length && !withTitle) {
return null;
}
return (
<div className={`${className} ui--LinkExternal ${isText ? 'isText' : 'isLogo'}${isMain ? ' isMain' : ''}${isSmall ? ' isSmall' : ''}${isSidebar ? ' isSidebar' : ''}`}>
<div className={`${className} ui--LinkExternal ${isText ? 'isText' : 'isLogo'}${withTitle ? ' isMain' : ''}${isSmall ? ' isSmall' : ''}${isSidebar ? ' isSidebar' : ''}`}>
{(isText && !isSmall) && <div>{t<string>('View this externally')}</div>}
{isMain && (
{withTitle && (
<h5>{t('external links')}</h5>
)}
<div className='links'>{links.map((link, index) => <span key={index}>{link}</span>)}</div>
<div className='links'>
{links.length
? links.map((link, index) => <span key={index}>{link}</span>)
: <label>{t('none')}</label>
}
</div>
</div>
);
}

View File

@ -315,6 +315,15 @@ export default React.memo(styled(Table)`
vertical-align: top;
}
&.columar {
vertical-align: top;
.ui--Columar .ui--Column {
margin: 1rem 0 0.75rem 0;
padding: 0;
}
}
&.middle {
text-align: center;
}

View File

@ -33,7 +33,7 @@ function Tags ({ children, className = '', isEditable, isEditing, onChange, onSa
label={tag}
/>
))
: <label>{t<string>('no tags')}</label>,
: <label>{t<string>('none')}</label>,
[t, value]
);