Browse Source

Democracy display updates (#1974)

* Democracy updates

* Democracy UI cleanups

* Cleanup labels

* Move seconding, again...
Jaco Greeff 5 years ago
parent
commit
ffbea9788c

+ 44 - 25
packages/app-democracy/src/Overview/Proposal.tsx

@@ -6,44 +6,63 @@ import { DeriveProposal } from '@polkadot/api-derive/types';
 import { I18nProps } from '@polkadot/react-components/types';
 
 import React from 'react';
-import { ActionItem, InputAddress, Static } from '@polkadot/react-components';
+import styled from 'styled-components';
+import { AddressMini, AddressSmall } from '@polkadot/react-components';
 import { FormatBalance } from '@polkadot/react-query';
+import { formatNumber } from '@polkadot/util';
 
 import translate from '../translate';
+import ProposalCell from './ProposalCell';
 import Seconding from './Seconding';
 
 interface Props extends I18nProps {
   value: DeriveProposal;
 }
 
-function Proposal ({ className, t, value: { balance, index, proposal, seconds } }: Props): React.ReactElement<Props> {
+function Proposal ({ className, t, value: { balance, index, proposal, proposer, seconds } }: Props): React.ReactElement<Props> {
   return (
-    <ActionItem
-      className={className}
-      idNumber={index}
-      proposal={proposal}
-    >
-      <Seconding
-        depositors={seconds || []}
-        proposalId={index}
-      />
-      {balance && seconds && (
-        <div>
-          {seconds.map((address, count): React.ReactNode => (
-            <InputAddress
-              isDisabled
-              label={count === 0 ? t('proposer') : t('depositor {{count}}', { replace: { count } })}
+    <tr className={className}>
+      <td className='number toppad'>{formatNumber(index)}</td>
+      <td className='top'>
+        <AddressSmall value={proposer} />
+      </td>
+      <td className='number together top'>
+        <FormatBalance label={<label>{t('locked')}</label>} value={balance} />
+      </td>
+      <ProposalCell className='top' proposal={proposal} />
+      <td className='top'>
+        {seconds
+          .filter((_address, index): boolean => index !== 0)
+          .map((address, count): React.ReactNode => (
+            <AddressMini
+              className='identityIcon'
               key={`${count}:${address}`}
-              defaultValue={address}
+              label={count ? undefined : t('seconds')}
+              value={address}
+              withBalance={false}
             />
           ))}
-          <Static label={t('balance')}>
-            <FormatBalance value={balance} />
-          </Static>
-        </div>
-      )}
-    </ActionItem>
+      </td>
+      <td className='together number top'>
+        <Seconding
+          depositors={seconds || []}
+          proposalId={index}
+        />
+      </td>
+    </tr>
   );
 }
 
-export default translate(Proposal);
+export default translate(
+  styled(Proposal)`
+    .identityIcon {
+      &:first-child {
+        padding-top: 0;
+      }
+
+      &:last-child {
+        margin-bottom: 4px;
+      }
+    }
+  `
+);

+ 39 - 0
packages/app-democracy/src/Overview/ProposalCell.tsx

@@ -0,0 +1,39 @@
+// Copyright 2017-2019 @polkadot/app-democracy authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { Proposal } from '@polkadot/types/interfaces';
+import { I18nProps } from '@polkadot/react-components/types';
+
+import React from 'react';
+import { registry } from '@polkadot/react-api';
+import { Call } from '@polkadot/react-components';
+
+import translate from '../translate';
+
+interface Props extends I18nProps {
+  proposal: Proposal;
+}
+
+function ProposalCell ({ className, proposal, t }: Props): React.ReactElement<Props> {
+  const { meta, method, section } = registry.findMetaCall(proposal.callIndex);
+
+  return (
+    <td className={`${className} all`}>
+      <div>{section}.{method}</div>
+      <details>
+        <summary>{
+          meta && meta.documentation
+            ? meta.documentation.join(' ')
+            : t('Details')
+        }</summary>
+        <Call
+          value={proposal}
+          withHash
+        />
+      </details>
+    </td>
+  );
+}
+
+export default translate(ProposalCell);

+ 15 - 9
packages/app-democracy/src/Overview/Proposals.tsx

@@ -6,6 +6,7 @@ import { DeriveProposal } from '@polkadot/api-derive/types';
 import { I18nProps as Props } from '@polkadot/react-components/types';
 
 import React from 'react';
+import { Table } from '@polkadot/react-components';
 import { useApi, trackStream } from '@polkadot/react-hooks';
 
 import ProposalDisplay from './Proposal';
@@ -18,15 +19,20 @@ function Proposals ({ className, t }: Props): React.ReactElement<Props> {
   return (
     <div className={`proposalSection ${className}`}>
       <h1>{t('proposals')}</h1>
-      {
-        proposals?.length
-          ? proposals.map((proposal): React.ReactNode => (
-            <ProposalDisplay
-              key={proposal.index.toString()}
-              value={proposal}
-            />
-          ))
-          : t('No active proposals')
+      {proposals?.length
+        ? (
+          <Table>
+            <Table.Body>
+              {proposals.map((proposal): React.ReactNode => (
+                <ProposalDisplay
+                  key={proposal.index.toString()}
+                  value={proposal}
+                />
+              ))}
+            </Table.Body>
+          </Table>
+        )
+        : t('No active proposals')
       }
     </div>
   );

+ 28 - 64
packages/app-democracy/src/Overview/Referendum.tsx

@@ -9,15 +9,13 @@ import { I18nProps } from '@polkadot/react-components/types';
 import BN from 'bn.js';
 import React, { useEffect, useState } from 'react';
 import styled from 'styled-components';
-import { ActionItem, Chart, Static, Voting } from '@polkadot/react-components';
-import { formatBalance, formatNumber } from '@polkadot/util';
-import VoteThreshold from '@polkadot/react-params/Param/VoteThreshold';
+import { formatNumber } from '@polkadot/util';
 import { withCalls, withMulti } from '@polkadot/react-api';
+import { FormatBalance } from '@polkadot/react-query';
 
 import translate from '../translate';
-
-const COLORS_AYE = ['#64bebe', '#5badad'];
-const COLORS_NAY = ['#d75ea1', '#e189ba'];
+import ProposalCell from './ProposalCell';
+import Voting from './Voting';
 
 interface Props extends I18nProps {
   idNumber: BN;
@@ -37,7 +35,7 @@ interface State {
 }
 
 function Referendum ({ chain_bestNumber, className, democracy_enactmentPeriod, democracy_referendumVotesFor, t, value }: Props): React.ReactElement<Props> | null {
-  const [{ voteCount, voteCountAye, voteCountNay, votedAye, votedNay, votedTotal }, setState] = useState<State>({
+  const [{ voteCountAye, voteCountNay, votedAye, votedNay }, setState] = useState<State>({
     voteCount: 0,
     voteCountAye: 0,
     voteCountNay: 0,
@@ -85,63 +83,29 @@ function Referendum ({ chain_bestNumber, className, democracy_enactmentPeriod, d
   const enactBlock = (democracy_enactmentPeriod || new BN(0)).add(value.info.end);
 
   return (
-    <ActionItem
-      className={className}
-      idNumber={value.index}
-      proposal={value.proposal}
-      accessory={
-        <Voting
-          idNumber={value.index}
-          proposal={value.proposal}
-        />
-      }
-    >
-      <div>
-        <Static label={t('ending at')}>
-          {t('block #{{blockNumber}}, {{remaining}} blocks remaining', {
-            replace: {
-              blockNumber: formatNumber(value.info.end),
-              remaining: formatNumber(value.info.end.sub(chain_bestNumber).subn(1))
-            }
-          })}
-        </Static>
-        <Static label={t('activate at (if passed)')}>
-          {t('block #{{blockNumber}}', {
-            replace: {
-              blockNumber: formatNumber(enactBlock)
-            }
-          })}
-        </Static>
-        <VoteThreshold
-          isDisabled
-          defaultValue={{ isValid: true, value: value.info.threshold }}
-          label={t('vote threshold')}
-          name='voteThreshold'
-          type={{
-            info: 0,
-            type: 'VoteThreshold'
-          }}
-        />
-        {voteCount !== 0 && votedTotal.gtn(0) && (
-          <div className='democracy--Referendum-results chart'>
-            <Chart.HorizBar
-              values={[
-                {
-                  colors: COLORS_AYE,
-                  label: `Aye, ${formatBalance(votedAye, { forceUnit: '-' })} (${formatNumber(voteCountAye)})`,
-                  value: votedAye.muln(10000).div(votedTotal).toNumber() / 100
-                },
-                {
-                  colors: COLORS_NAY,
-                  label: `Nay, ${formatBalance(votedNay, { forceUnit: '-' })} (${formatNumber(voteCountNay)})`,
-                  value: votedNay.muln(10000).div(votedTotal).toNumber() / 100
-                }
-              ]}
-            />
-          </div>
-        )}
-      </div>
-    </ActionItem>
+    <tr className={className}>
+      <td className='number toppad'>{formatNumber(value.index)}</td>
+      <ProposalCell className='top' proposal={value.proposal} />
+      <td className='number together top'>
+        <label>{t('remaining')}</label>
+        {formatNumber(value.info.end.sub(chain_bestNumber).subn(1))} blocks
+      </td>
+      <td className='number together top'>
+        <label>{t('activate at')}</label>
+        {formatNumber(enactBlock)}
+      </td>
+      <td className='number together top'>
+        <label>{t('Aye ({{count}})', { replace: { count: formatNumber(voteCountAye) } })}</label>
+        <FormatBalance value={votedAye} />
+      </td>
+      <td className='number together top'>
+        <label>{t('Nay ({{count}})', { replace: { count: formatNumber(voteCountNay) } })}</label>
+        <FormatBalance value={votedNay} />
+      </td>
+      <td className='number together top'>
+        <Voting referendumId={value.index} />
+      </td>
+    </tr>
   );
 }
 

+ 16 - 10
packages/app-democracy/src/Overview/Referendums.tsx

@@ -6,6 +6,7 @@ import { DerivedReferendum } from '@polkadot/api-derive/types';
 import { I18nProps as Props } from '@polkadot/react-components/types';
 
 import React from 'react';
+import { Table } from '@polkadot/react-components';
 import { useApi, trackStream } from '@polkadot/react-hooks';
 
 import Referendum from './Referendum';
@@ -18,16 +19,21 @@ function Referendums ({ className, t }: Props): React.ReactElement<Props> {
   return (
     <div className={`proposalSection ${className}`}>
       <h1>{t('referenda')}</h1>
-      {
-        referendums?.length
-          ? referendums.map((referendum): React.ReactNode => (
-            <Referendum
-              idNumber={referendum.index}
-              key={referendum.index.toString()}
-              value={referendum}
-            />
-          ))
-          : t('No active referendums')
+      {referendums?.length
+        ? (
+          <Table>
+            <Table.Body>
+              {referendums.map((referendum): React.ReactNode => (
+                <Referendum
+                  idNumber={referendum.index}
+                  key={referendum.index.toString()}
+                  value={referendum}
+                />
+              ))}
+            </Table.Body>
+          </Table>
+        )
+        : t('No active referendums')
       }
     </div>
   );

+ 7 - 9
packages/app-democracy/src/Overview/Seconding.tsx

@@ -62,7 +62,7 @@ function Seconding ({ depositors, proposalId, t }: Props): React.ReactElement<Pr
                 isPrimary
                 label={t('Second')}
                 icon='sign-in'
-                onClick={_toggleSeconding}
+                onStart={_toggleSeconding}
                 params={[proposalId]}
                 tx='democracy.second'
               />
@@ -70,14 +70,12 @@ function Seconding ({ depositors, proposalId, t }: Props): React.ReactElement<Pr
           </Modal.Actions>
         </Modal>
       )}
-      <Button.Group>
-        <Button
-          isPrimary
-          label={t('Second proposal')}
-          icon='toggle off'
-          onClick={_toggleSeconding}
-        />
-      </Button.Group>
+      <Button
+        isPrimary
+        label={t('Second')}
+        icon='toggle off'
+        onClick={_toggleSeconding}
+      />
     </>
   );
 }

+ 3 - 3
packages/app-staking/src/Actions/Account/index.tsx

@@ -573,7 +573,7 @@ export default withMulti(
       }
 
       .staking--label {
-        margin: 0 1.75rem -0.75rem 0;
+        margin: 0 0 -0.75rem 1.75rem;
       }
     }
 
@@ -602,10 +602,10 @@ export default withMulti(
     }
 
     .staking--Account-Nominee {
-      text-align: right;
+      text-align: left;
 
       .staking--label {
-        margin: 0 2.25rem -.75rem 0;
+        margin: 0 0 -.75rem 2.25rem;
       }
     }
 

+ 1 - 2
packages/app-staking/src/Overview/Address.tsx

@@ -10,12 +10,11 @@ import { ValidatorFilter } from '../types';
 import BN from 'bn.js';
 import React, { useEffect, useState } from 'react';
 import styled from 'styled-components';
-import { AddressMini, Badge, Icon } from '@polkadot/react-components';
+import { AddressMini, AddressSmall, Badge, Icon } from '@polkadot/react-components';
 import { trackStream, useApi } from '@polkadot/react-hooks';
 import { FormatBalance } from '@polkadot/react-query';
 import { formatNumber } from '@polkadot/util';
 
-import AddressSmall from '../AddressSmall';
 import translate from '../translate';
 
 interface Props extends I18nProps {

+ 12 - 29
packages/app-staking/src/Overview/CurrentList.tsx

@@ -8,10 +8,10 @@ import { AccountId, EraPoints, Points } from '@polkadot/types/interfaces';
 import { ValidatorFilter } from '../types';
 
 import React, { useEffect, useState } from 'react';
+import { Dropdown, FilterOverlay, Table } from '@polkadot/react-components';
 import { useApi, useFavorites } from '@polkadot/react-hooks';
 import keyring from '@polkadot/ui-keyring';
 
-import Table from '../Table';
 import { STORE_FAVS_BASE } from '../constants';
 import translate from '../translate';
 import Address from './Address';
@@ -60,7 +60,7 @@ function accountsToString (accounts: AccountId[]): string[] {
 function CurrentList ({ authorsMap, hasQueries, isIntentions, lastAuthors, next, recentlyOnline, stakingOverview, t }: Props): React.ReactElement<Props> {
   const { isSubstrateV2 } = useApi();
   const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
-  const [filter] = useState<ValidatorFilter>('all');
+  const [filter, setFilter] = useState<ValidatorFilter>('all');
   const [myAccounts] = useState(keyring.getAccounts().map(({ address }): string => address));
   const [{ elected, validators, waiting }, setFiltered] = useState<{ elected: AccountExtend[]; validators: AccountExtend[]; waiting: AccountExtend[] }>({ elected: [], validators: [], waiting: [] });
 
@@ -102,26 +102,9 @@ function CurrentList ({ authorsMap, hasQueries, isIntentions, lastAuthors, next,
       />
     ));
 
-  const _renderTable = (className: string, rows: React.ReactNode): React.ReactNode =>
-    <Table className={className}>
-      {/* <Table.Head>
-        <th>&nbsp;</th>
-        <th>&nbsp;</th>
-        <th>&nbsp;</th>
-        <th className='number'>{t('own stake')}</th>
-        <th className='number'>{t('other stake')}</th>
-        <th className='number'>{t('nominators')}</th>
-        <th className='number'>{t('commission')}</th>
-        <th className='number'>{t('points')}</th>
-        <th className='number'>{t('last #')}</th>
-        <th>&nbsp;</th>
-      </Table.Head> */}
-      <Table.Body>{rows}</Table.Body>
-    </Table>;
-
   return (
     <div>
-      {/* <FilterOverlay>
+      <FilterOverlay>
         <Dropdown
           onChange={setFilter}
           options={[
@@ -136,18 +119,18 @@ function CurrentList ({ authorsMap, hasQueries, isIntentions, lastAuthors, next,
           value={filter}
           withLabel={false}
         />
-      </FilterOverlay> */}
-      {_renderTable(isIntentions ? 'staking--hidden' : '', (
-        <>
+      </FilterOverlay>
+      <Table className={isIntentions ? 'staking--hidden' : ''}>
+        <Table.Body>
           {_renderRows(validators, t('validators'), true)}
-        </>
-      ))}
-      {_renderTable(isIntentions ? '' : 'staking--hidden', (
-        <>
+        </Table.Body>
+      </Table>
+      <Table className={isIntentions ? '' : 'staking--hidden'}>
+        <Table.Body>
           {_renderRows(elected, t('intention'), false)}
           {_renderRows(waiting, t('intention'), false)}
-        </>
-      ))}
+        </Table.Body>
+      </Table>
     </div>
   );
 }

+ 1 - 2
packages/app-staking/src/Targets/Validator.tsx

@@ -6,11 +6,10 @@ import { I18nProps } from '@polkadot/react-components/types';
 import { ValidatorInfo } from './types';
 
 import React from 'react';
-import { Icon } from '@polkadot/react-components';
+import { AddressSmall, Icon } from '@polkadot/react-components';
 import { FormatBalance } from '@polkadot/react-query';
 import { formatNumber } from '@polkadot/util';
 
-import AddressSmall from '../AddressSmall';
 import translate from '../translate';
 
 interface Props extends I18nProps {

+ 1 - 24
packages/app-staking/src/Targets/index.tsx

@@ -11,10 +11,9 @@ import { ValidatorInfo } from './types';
 import BN from 'bn.js';
 import React, { useEffect, useState } from 'react';
 import styled from 'styled-components';
-import { InputBalance } from '@polkadot/react-components';
+import { InputBalance, Table } from '@polkadot/react-components';
 import { useAccounts, useApi, useFavorites, trackStream } from '@polkadot/react-hooks';
 
-import Table from '../Table';
 import { STORE_FAVS_BASE } from '../constants';
 import translate from '../translate';
 import Summary from './Summary';
@@ -174,28 +173,6 @@ function Targets ({ className, sessionRewards, t }: Props): React.ReactElement<P
               value={amount}
             />
             <Table>
-              {/* <Table.Head>
-                <th>&nbsp;</th>
-                <th className='number'>
-                  <Icon
-                    name='info circle'
-                    data-tip
-                    data-for='ranking-trigger'
-                  />
-                  <Tooltip
-                    text={t('Ranking is done of the estimated best return, taking the commission and total bonded amount into account. It does not incorporate validator liveliness according to length of operation nor number of blocks produced.')}
-                    trigger='ranking-trigger'
-                  />
-                </th>
-                <th className='number'>&nbsp;</th>
-                <th className='number'>{t('commission')}</th>
-                <th className='number'>{t('nominators')}</th>
-                <th className='number'>{t('total stake')}</th>
-                <th className='number'>{t('own stake')}</th>
-                <th className='number'>{t('other stake')}</th>
-                <th className='number'>{t('payout (est.)')}</th>
-                <th>&nbsp;</th>
-              </Table.Head> */}
               <Table.Body>
                 {validators.map((info): React.ReactNode =>
                   <Validator

+ 26 - 18
packages/react-components/src/AddressMini.tsx

@@ -24,6 +24,7 @@ interface Props extends BareProps {
   iconInfo?: React.ReactNode;
   isPadded?: boolean;
   isShort?: boolean;
+  label?: React.ReactNode;
   type?: KeyringItemType;
   value?: AccountId | AccountIndex | Address | string;
   withAddress?: boolean;
@@ -33,7 +34,7 @@ interface Props extends BareProps {
   withName?: boolean;
 }
 
-function AddressMini ({ balance, bonded, children, className, iconInfo, isPadded = true, style, value, withAddress = true, withBalance = false, withBonded = false, withLockedVote = false, withName = true }: Props): React.ReactElement<Props> | null {
+function AddressMini ({ balance, bonded, children, className, iconInfo, isPadded = true, label, style, value, withAddress = true, withBalance = false, withBonded = false, withLockedVote = false, withName = true }: Props): React.ReactElement<Props> | null {
   if (!value) {
     return null;
   }
@@ -43,17 +44,9 @@ function AddressMini ({ balance, bonded, children, className, iconInfo, isPadded
       className={classes('ui--AddressMini', isPadded ? 'padded' : '', className)}
       style={style}
     >
-      <div className='ui--AddressMini-info'>
-        {withAddress && (
-          <div className='ui--AddressMini-address'>
-            {withName
-              ? <AccountName params={value} />
-              : toShortAddress(value)
-            }
-          </div>
-        )}
-        {children}
-      </div>
+      {label && (
+        <label className='ui--AddressMini-label'>{label}</label>
+      )}
       <div className='ui--AddressMini-icon'>
         <IdentityIcon
           size={24}
@@ -65,6 +58,17 @@ function AddressMini ({ balance, bonded, children, className, iconInfo, isPadded
           </div>
         )}
       </div>
+      <div className='ui--AddressMini-info'>
+        {withAddress && (
+          <div className='ui--AddressMini-address'>
+            {withName
+              ? <AccountName params={value} />
+              : toShortAddress(value)
+            }
+          </div>
+        )}
+        {children}
+      </div>
       <div className='ui--AddressMini-balances'>
         {withBalance && (
           <BalanceDisplay
@@ -90,11 +94,12 @@ function AddressMini ({ balance, bonded, children, className, iconInfo, isPadded
 export default styled(AddressMini)`
   display: inline-block;
   padding: 0 0.25rem 0 1rem;
+  text-align: left;
   white-space: nowrap;
 
   &.padded {
     display: inline-block;
-    padding: 0.25rem 0 0 1rem;
+    padding: 0.25rem 1rem 0 0;
   }
 
   &.summary {
@@ -105,12 +110,15 @@ export default styled(AddressMini)`
   .ui--AddressMini-address {
     font-family: monospace;
     max-width: 9rem;
-    min-width: 9em;
     overflow: hidden;
-    text-align: right;
+    text-align: left;
     text-overflow: ellipsis;
   }
 
+  .ui--AddressMini-label {
+    margin: 0 0 -0.5rem 2.25rem;
+  }
+
   .ui--AddressMini-balances {
     display: grid;
 
@@ -118,14 +126,14 @@ export default styled(AddressMini)`
     .ui--Bonded,
     .ui--LockedVote {
       font-size: 0.75rem;
-      margin-right: 2.25rem;
+      margin-left: 2.25rem;
       margin-top: -0.5rem;
-      text-align: right;
+      text-align: left;
     }
   }
 
   .ui--AddressMini-icon {
-    margin: 0 0 0 0.5rem;
+    margin: 0 0.5rem 0 0;
 
     .ui--AddressMini-icon-info {
       position: absolute;

+ 3 - 2
packages/app-staking/src/AddressSmall.tsx → packages/react-components/src/AddressSmall.tsx

@@ -1,4 +1,4 @@
-// Copyright 2017-2019 @polkadot/app-staking authors & contributors
+// Copyright 2017-2019 @polkadot/react-components authors & contributors
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
@@ -6,9 +6,10 @@ import { Address, AccountId } from '@polkadot/types/interfaces';
 
 import React from 'react';
 import styled from 'styled-components';
-import { IdentityIcon } from '@polkadot/react-components';
 import { AccountIndex, AccountName } from '@polkadot/react-query';
 
+import IdentityIcon from './IdentityIcon';
+
 interface Props {
   className?: string;
   defaultName?: string;

+ 7 - 7
packages/react-components/src/Chart/HorizBar.tsx

@@ -19,6 +19,7 @@ interface Value {
 interface Props extends BareProps {
   aspectRatio?: number;
   max?: number;
+  showLabels?: boolean;
   values: Value[];
   withColors?: boolean;
 }
@@ -41,7 +42,7 @@ interface Config {
 const alphaColor = (hexColor: string): string =>
   ChartJs.helpers.color(hexColor).alpha(0.65).rgbString();
 
-function calculateOptions (aspectRatio: number, values: Value[], jsonValues: string, max: number): State {
+function calculateOptions (aspectRatio: number, values: Value[], jsonValues: string, max: number, showLabels: boolean): State {
   const chartData = values.reduce((data, { colors: [normalColor = '#00f', hoverColor], label, value }): Config => {
     const dataset = data.datasets[0];
 
@@ -71,10 +72,9 @@ function calculateOptions (aspectRatio: number, values: Value[], jsonValues: str
       },
       scales: {
         xAxes: [{
-          ticks: {
-            beginAtZero: true,
-            max
-          }
+          ticks: showLabels
+            ? { beginAtZero: true, max }
+            : { display: false }
         }]
       }
     },
@@ -82,14 +82,14 @@ function calculateOptions (aspectRatio: number, values: Value[], jsonValues: str
   };
 }
 
-export default function ChartHorizBar ({ aspectRatio = 8, className, max = 100, style, values }: Props): React.ReactElement<Props> | null {
+export default function ChartHorizBar ({ aspectRatio = 8, className, max = 100, showLabels = false, style, values }: Props): React.ReactElement<Props> | null {
   const [{ chartData, chartOptions, jsonValues }, setState] = useState<State>({});
 
   useEffect((): void => {
     const newJsonValues = JSON.stringify(values);
 
     if (newJsonValues !== jsonValues) {
-      setState(calculateOptions(aspectRatio, values, newJsonValues, max));
+      setState(calculateOptions(aspectRatio, values, newJsonValues, max, showLabels));
     }
   }, [values]);
 

+ 16 - 1
packages/app-staking/src/Table.tsx → packages/react-components/src/Table.tsx

@@ -1,4 +1,4 @@
-// Copyright 2017-2019 @polkadot/app-staking authors & contributors
+// Copyright 2017-2019 @polkadot/react-components authors & contributors
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
@@ -63,6 +63,8 @@ export default styled(Table)`
     width: 100%;
 
     tr {
+      width: 100%;
+
       &.isHighlight {
         td {
           background: #ffffed;
@@ -86,6 +88,10 @@ export default styled(Table)`
       td, th {
         text-align: left;
 
+        &.all {
+          width: 100%;
+        }
+
         &.number {
           text-align: right;
         }
@@ -97,6 +103,15 @@ export default styled(Table)`
         &.toggle {
           cursor: pointer;
         }
+
+        &.top {
+          vertical-align: top;
+        }
+
+        &.toppad {
+          padding-top: 1.25rem;
+          vertical-align: top;
+        }
       }
 
       td {

+ 2 - 0
packages/react-components/src/index.tsx

@@ -7,6 +7,7 @@ export { default as AddressCard } from './AddressCard';
 export { default as AddressInfo } from './AddressInfo';
 export { default as AddressMini } from './AddressMini';
 export { default as AddressRow } from './AddressRow';
+export { default as AddressSmall } from './AddressSmall';
 export { default as Available } from './Available';
 export { default as Badge } from './Badge';
 export { default as Balance } from './Balance';
@@ -71,6 +72,7 @@ export { default as Row } from './Row';
 export { default as Static } from './Static';
 export { default as Status, StatusContext } from './Status';
 export { default as SummaryBox } from './SummaryBox';
+export { default as Table } from './Table';
 export { default as Tabs } from './Tabs';
 export { default as Toggle } from './Toggle';
 export { default as Tooltip } from './Tooltip';