Browse Source

Functional components (app-*) (#1559)

* Functional components (app-*)

* refactor

* refactor

* refactor

* refactor

* refactor

* Revert "refactor"

This reverts commit 300a12ed7de5b1000f32f5269dff67f5ee1c1515.

* onRemove
kwingram25 5 years ago
parent
commit
2752bedc6e
49 changed files with 900 additions and 1089 deletions
  1. 9 13
      packages/app-123code/src/Summary.tsx
  2. 1 1
      packages/app-accounts/src/Account.tsx
  3. 32 36
      packages/app-accounts/src/Banner.tsx
  4. 1 1
      packages/app-accounts/src/Vanity/Match.tsx
  5. 1 1
      packages/app-accounts/src/modals/Transfer.tsx
  6. 1 1
      packages/app-contracts/src/Codes/Code.tsx
  7. 1 1
      packages/app-contracts/src/Codes/ValidateCode.tsx
  8. 1 1
      packages/app-contracts/src/Codes/index.tsx
  9. 6 11
      packages/app-contracts/src/Contracts/Contract.tsx
  10. 1 1
      packages/app-contracts/src/Contracts/ValidateAddr.tsx
  11. 27 59
      packages/app-contracts/src/RemoveABI.tsx
  12. 17 27
      packages/app-council/src/Motions/index.tsx
  13. 13 12
      packages/app-council/src/Overview/Candidate.tsx
  14. 8 12
      packages/app-council/src/Overview/Member.tsx
  15. 28 33
      packages/app-council/src/Overview/Members.tsx
  16. 24 29
      packages/app-council/src/Overview/Summary.tsx
  17. 12 16
      packages/app-council/src/Overview/index.tsx
  18. 14 18
      packages/app-dashboard/src/Entry.tsx
  19. 45 49
      packages/app-democracy/src/Overview/Proposal.tsx
  20. 17 33
      packages/app-democracy/src/Overview/Proposals.tsx
  21. 20 28
      packages/app-democracy/src/Overview/Referendums.tsx
  22. 33 35
      packages/app-democracy/src/Overview/Summary.tsx
  23. 10 16
      packages/app-democracy/src/Overview/index.tsx
  24. 13 17
      packages/app-explorer/src/BestHash.tsx
  25. 57 59
      packages/app-explorer/src/BlockHeader/index.tsx
  26. 15 13
      packages/app-explorer/src/BlockHeaders.tsx
  27. 22 26
      packages/app-explorer/src/BlockInfo/ByHash.tsx
  28. 7 11
      packages/app-explorer/src/BlockInfo/ByNumber.tsx
  29. 16 20
      packages/app-explorer/src/BlockInfo/Events.tsx
  30. 52 47
      packages/app-explorer/src/Events.tsx
  31. 15 19
      packages/app-explorer/src/Main.tsx
  32. 44 50
      packages/app-explorer/src/NodeInfo/Peers.tsx
  33. 33 37
      packages/app-explorer/src/Summary.tsx
  34. 49 55
      packages/app-explorer/src/SummarySession.tsx
  35. 10 14
      packages/app-extrinsics/src/Balance.tsx
  36. 16 20
      packages/app-extrinsics/src/index.tsx
  37. 12 16
      packages/app-js/src/Output.tsx
  38. 7 11
      packages/app-js/src/index.tsx
  39. 24 28
      packages/app-parachains/src/Overview/Parachain.tsx
  40. 15 19
      packages/app-parachains/src/Overview/Parachains.tsx
  41. 14 18
      packages/app-parachains/src/Overview/Summary.tsx
  42. 32 34
      packages/app-staking/src/Overview/Summary.tsx
  43. 39 40
      packages/app-staking/src/Overview/index.tsx
  44. 15 19
      packages/app-storage/src/Queries.tsx
  45. 27 29
      packages/app-toolbox/src/Rpc/Results.tsx
  46. 11 16
      packages/app-toolbox/src/Rpc/index.tsx
  47. 6 8
      packages/app-transfer/src/index.tsx
  48. 1 1
      packages/app-treasury/src/Overview/Proposals.tsx
  49. 26 28
      packages/app-treasury/src/Overview/Summary.tsx

+ 9 - 13
packages/app-123code/src/Summary.tsx

@@ -11,19 +11,15 @@ interface Props extends BareProps {
   children: React.ReactNode;
 }
 
-class Summary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, style } = this.props;
-
-    return (
-      <div
-        className={className}
-        style={style}
-      >
-        {children}
-      </div>
-    );
-  }
+function Summary ({ children, className, style }: Props): React.ReactElement<Props> {
+  return (
+    <div
+      className={className}
+      style={style}
+    >
+      {children}
+    </div>
+  );
 }
 
 export default styled(Summary)`

+ 1 - 1
packages/app-accounts/src/Account.tsx

@@ -27,7 +27,7 @@ interface State {
   isTransferOpen: boolean;
 }
 
-class Account extends React.PureComponent<Props> {
+class Account extends React.PureComponent<Props, State> {
   public state: State;
 
   public constructor (props: Props) {

+ 32 - 36
packages/app-accounts/src/Banner.tsx

@@ -49,45 +49,41 @@ const browserInfo = detect();
 const browserName: Browser | null = (browserInfo && (browserInfo.name as Browser)) || null;
 const isSupported = browserName && Object.keys(available).includes(browserName);
 
-class Banner extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, t } = this.props;
-
-    if (isWeb3Injected || !isSupported || !browserName) {
-      return null;
-    }
+function Banner ({ className, t }: Props): React.ReactElement<Props> | null {
+  if (isWeb3Injected || !isSupported || !browserName) {
+    return null;
+  }
 
-    return (
-      <div className={className}>
-        <div className='box'>
-          <div className='info'>
-            <p>{t('It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -', {
-              replace: {
-                yourBrowser: stringUpperFirst(browserName)
-              }
-            })}</p>
-            <ul>{available[browserName].map(({ desc, name, link }): React.ReactNode => (
-              <li key={name}>
-                <a
-                  href={link}
-                  rel='noopener noreferrer'
-                  target='_blank'
-                >
-                  {name}
-                </a> ({desc})
-              </li>
-            ))
-            }</ul>
-            <p>{t('Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.')}&nbsp;<a
-              href='https://github.com/polkadot-js/extension'
-              rel='noopener noreferrer'
-              target='_blank'
-            >{t('Learn more...')}</a></p>
-          </div>
+  return (
+    <div className={className}>
+      <div className='box'>
+        <div className='info'>
+          <p>{t('It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -', {
+            replace: {
+              yourBrowser: stringUpperFirst(browserName)
+            }
+          })}</p>
+          <ul>{available[browserName].map(({ desc, name, link }): React.ReactNode => (
+            <li key={name}>
+              <a
+                href={link}
+                rel='noopener noreferrer'
+                target='_blank'
+              >
+                {name}
+              </a> ({desc})
+            </li>
+          ))
+          }</ul>
+          <p>{t('Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.')}&nbsp;<a
+            href='https://github.com/polkadot-js/extension'
+            rel='noopener noreferrer'
+            target='_blank'
+          >{t('Learn more...')}</a></p>
         </div>
       </div>
-    );
-  }
+    </div>
+  );
 }
 
 export default translate(styled(Banner)`

+ 1 - 1
packages/app-accounts/src/Vanity/Match.tsx

@@ -84,7 +84,7 @@ class Match extends React.PureComponent<Props, State> {
   }
 }
 
-export default styled(Match as React.ComponentClass<Props>)`
+export default styled(Match as React.ComponentClass<Props, State>)`
   text-align: center;
 
   &:hover {

+ 1 - 1
packages/app-accounts/src/modals/Transfer.tsx

@@ -40,7 +40,7 @@ interface State {
 
 const ZERO = new BN(0);
 
-class Transfer extends React.PureComponent<Props> {
+class Transfer extends React.PureComponent<Props, State> {
   public state: State;
 
   public constructor (props: Props) {

+ 1 - 1
packages/app-contracts/src/Codes/Code.tsx

@@ -35,7 +35,7 @@ const CodeCard = styled(Card)`
   }
 `;
 
-class Contract extends React.PureComponent<Props> {
+class Contract extends React.PureComponent<Props, State> {
   public state: State = {
     isForgetOpen: false,
     isRemoveABIOpen: false

+ 1 - 1
packages/app-contracts/src/Codes/ValidateCode.tsx

@@ -27,7 +27,7 @@ interface State {
   isValid: boolean;
 }
 
-class ValidateCode extends React.PureComponent<Props> {
+class ValidateCode extends React.PureComponent<Props, State> {
   public state: State = {
     isStored: false,
     isValidHex: false,

+ 1 - 1
packages/app-contracts/src/Codes/index.tsx

@@ -22,7 +22,7 @@ interface State {
   isUploadOpen: boolean;
 }
 
-class Codes extends React.PureComponent<Props> {
+class Codes extends React.PureComponent<Props, State> {
   public state: State = {
     isAddOpen: false,
     isUploadOpen: false

+ 6 - 11
packages/app-contracts/src/Contracts/Contract.tsx

@@ -27,17 +27,12 @@ interface State {
   isPasswordOpen: boolean;
 }
 
-class Contract extends React.PureComponent<Props> {
-  public state: State;
-
-  public constructor (props: Props) {
-    super(props);
-    this.state = {
-      isBackupOpen: false,
-      isForgetOpen: false,
-      isPasswordOpen: false
-    };
-  }
+class Contract extends React.PureComponent<Props, State> {
+  public state: State = {
+    isBackupOpen: false,
+    isForgetOpen: false,
+    isPasswordOpen: false
+  };
 
   public render (): React.ReactNode {
     const { address, onCall } = this.props;

+ 1 - 1
packages/app-contracts/src/Contracts/ValidateAddr.tsx

@@ -27,7 +27,7 @@ interface State {
   isValid: boolean;
 }
 
-class ValidateAddr extends React.PureComponent<Props> {
+class ValidateAddr extends React.PureComponent<Props, State> {
   public state: State = {
     isStored: false,
     isValidAddr: false,

+ 27 - 59
packages/app-contracts/src/RemoveABI.tsx

@@ -16,42 +16,30 @@ interface Props extends I18nProps {
   onRemove: () => void;
 }
 
-class RemoveABI extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { onClose, t } = this.props;
-    return (
-      <Modal
-        className='app--accounts-Modal'
-        dimmer='inverted'
-        onClose={onClose}
-        open
-      >
-        <Modal.Header>
-          {t('Confirm ABI removal')}
-        </Modal.Header>
-        <Modal.Content>
-          {this.renderContent()}
-        </Modal.Content>
-        {this.renderButtons()}
-      </Modal>
-    );
-  }
-
-  private content = (): React.ReactNode => {
-    const { t } = this.props;
-
-    return (
-      <>
-        <p>{t('You are about to remove this code\'s ABI. Once completed, should you need to access it again, you will have to manually re-upload it.')}</p>
-        <p>{t('This operaion does not impact the associated on-chain code or any of its contracts.')}</p>
-      </>
-    );
-  }
-
-  private renderButtons (): React.ReactNode {
-    const { onClose, t } = this.props;
-
-    return (
+function RemoveABI ({ code, onClose, onRemove, t }: Props): React.ReactElement<Props> {
+  const _onRemove = (): void => {
+    onClose && onClose();
+    onRemove();
+  };
+  return (
+    <Modal
+      className='app--accounts-Modal'
+      dimmer='inverted'
+      onClose={onClose}
+      open
+    >
+      <Modal.Header>
+        {t('Confirm ABI removal')}
+      </Modal.Header>
+      <Modal.Content>
+        <CodeRow
+          code={code}
+          isInline
+        >
+          <p>{t('You are about to remove this code\'s ABI. Once completed, should you need to access it again, you will have to manually re-upload it.')}</p>
+          <p>{t('This operaion does not impact the associated on-chain code or any of its contracts.')}</p>
+        </CodeRow>
+      </Modal.Content>
       <Modal.Actions>
         <Button.Group>
           <Button
@@ -62,33 +50,13 @@ class RemoveABI extends React.PureComponent<Props> {
           <Button.Or />
           <Button
             isPrimary
-            onClick={this.onRemove}
+            onClick={_onRemove}
             label={t('Remove')}
           />
         </Button.Group>
       </Modal.Actions>
-    );
-  }
-
-  private renderContent (): React.ReactNode {
-    const { code } = this.props;
-
-    return (
-      <CodeRow
-        code={code}
-        isInline
-      >
-        {this.content()}
-      </CodeRow>
-    );
-  }
-
-  private onRemove = (): void => {
-    const { onClose, onRemove } = this.props;
-
-    onClose && onClose();
-    onRemove();
-  }
+    </Modal>
+  );
 }
 
 export default translate(RemoveABI);

+ 17 - 27
packages/app-council/src/Motions/index.tsx

@@ -18,33 +18,23 @@ interface Props extends I18nProps {
   council_proposals?: Hash[];
 }
 
-class Proposals extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { t } = this.props;
-
-    return (
-      <CardGrid
-        emptyText={t('No council motions')}
-        headerText={t('Motions')}
-        buttons={
-          <Propose />
-        }
-      >
-        {this.renderProposals()}
-      </CardGrid>
-    );
-  }
-
-  private renderProposals (): React.ReactNode {
-    const { council_proposals = [] } = this.props;
-
-    return council_proposals.map((hash: Hash): React.ReactNode => (
-      <Motion
-        hash={hash.toHex()}
-        key={hash.toHex()}
-      />
-    ));
-  }
+function Proposals ({ council_proposals = [], t }: Props): React.ReactElement<Props> {
+  return (
+    <CardGrid
+      emptyText={t('No council motions')}
+      headerText={t('Motions')}
+      buttons={
+        <Propose />
+      }
+    >
+      {council_proposals.map((hash: Hash): React.ReactNode => (
+        <Motion
+          hash={hash.toHex()}
+          key={hash.toHex()}
+        />
+      ))}
+    </CardGrid>
+  );
 }
 
 export default translate(

+ 13 - 12
packages/app-council/src/Overview/Candidate.tsx

@@ -2,24 +2,25 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
+import { I18nProps } from '@polkadot/react-components/types';
 import { AccountId } from '@polkadot/types/interfaces';
 
 import React from 'react';
 import { AddressCard } from '@polkadot/react-components';
 
-interface Props {
+import translate from '../translate';
+
+interface Props extends I18nProps {
   address: AccountId;
 }
 
-export default class Candidate extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { address } = this.props;
-
-    return (
-      <AddressCard
-        defaultName='candidate'
-        value={address}
-      />
-    );
-  }
+function Candidate ({ address, t }: Props): React.ReactElement<Props> {
+  return (
+    <AddressCard
+      defaultName={t('candidate')}
+      value={address}
+    />
+  );
 }
+
+export default translate(Candidate);

+ 8 - 12
packages/app-council/src/Overview/Member.tsx

@@ -16,18 +16,14 @@ interface Props extends I18nProps {
   block: BlockNumber;
 }
 
-class Member extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { address, block, t } = this.props;
-
-    return (
-      <AddressCard
-        buttons={<div><label>{t('active until')}</label>#{formatNumber(block)}</div>}
-        defaultName='council member'
-        value={address}
-      />
-    );
-  }
+function Member ({ address, block, t }: Props): React.ReactElement<Props> {
+  return (
+    <AddressCard
+      buttons={<div><label>{t('active until')}</label>#{formatNumber(block)}</div>}
+      defaultName={t('council member')}
+      value={address}
+    />
+  );
 }
 
 export default translate(Member);

+ 28 - 33
packages/app-council/src/Overview/Members.tsx

@@ -15,39 +15,34 @@ import Member from './Member';
 
 interface Props extends I18nProps, ComponentProps {}
 
-class Members extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { electionsInfo, t } = this.props;
-    const { members, candidates } = electionsInfo;
-
-    return (
-      <Columar>
-        <Column
-          emptyText={t('No members found')}
-          headerText={t('members')}
-        >
-          {Object.entries(members).map(([address, block]): React.ReactNode => (
-            <Member
-              address={address}
-              block={block}
-              key={address}
-            />
-          ))}
-        </Column>
-        <Column
-          emptyText={t('No members found')}
-          headerText={t('candidates')}
-        >
-          {candidates.map((address): React.ReactNode => (
-            <Candidate
-              address={address}
-              key={address.toString()}
-            />
-          ))}
-        </Column>
-      </Columar>
-    );
-  }
+function Members ({ electionsInfo: { candidates, members }, t }: Props): React.ReactElement<Props> {
+  return (
+    <Columar>
+      <Column
+        emptyText={t('No members found')}
+        headerText={t('members')}
+      >
+        {Object.entries(members).map(([address, block]): React.ReactNode => (
+          <Member
+            address={address}
+            block={block}
+            key={address}
+          />
+        ))}
+      </Column>
+      <Column
+        emptyText={t('No members found')}
+        headerText={t('candidates')}
+      >
+        {candidates.map((address): React.ReactNode => (
+          <Candidate
+            address={address}
+            key={address.toString()}
+          />
+        ))}
+      </Column>
+    </Columar>
+  );
 }
 
 export default translate(Members);

+ 24 - 29
packages/app-council/src/Overview/Summary.tsx

@@ -14,35 +14,30 @@ import translate from '../translate';
 
 interface Props extends I18nProps, ComponentProps {}
 
-class Summary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { electionsInfo, t } = this.props;
-    const { members, candidateCount, desiredSeats, termDuration, voteCount } = electionsInfo;
-
-    return (
-      <SummaryBox>
-        <section>
-          <CardSummary label={t('seats')}>
-            {formatNumber(Object.keys(members).length)}/{formatNumber(desiredSeats)}
-          </CardSummary>
-          <CardSummary label={t('candidates')}>
-            {formatNumber(candidateCount)}
-          </CardSummary>
-        </section>
-        <section>
-          <CardSummary label={t('voting round')}>
-            #{formatNumber(voteCount)}
-          </CardSummary>
-        </section>
-
-        <section>
-          <CardSummary label={t('term duration')}>
-            {formatNumber(termDuration)}
-          </CardSummary>
-        </section>
-      </SummaryBox>
-    );
-  }
+function Summary ({ electionsInfo: { members, candidateCount, desiredSeats, termDuration, voteCount }, t }: Props): React.ReactElement<Props> {
+  return (
+    <SummaryBox>
+      <section>
+        <CardSummary label={t('seats')}>
+          {formatNumber(Object.keys(members).length)}/{formatNumber(desiredSeats)}
+        </CardSummary>
+        <CardSummary label={t('candidates')}>
+          {formatNumber(candidateCount)}
+        </CardSummary>
+      </section>
+      <section>
+        <CardSummary label={t('voting round')}>
+          #{formatNumber(voteCount)}
+        </CardSummary>
+      </section>
+
+      <section>
+        <CardSummary label={t('term duration')}>
+          {formatNumber(termDuration)}
+        </CardSummary>
+      </section>
+    </SummaryBox>
+  );
 }
 
 export default translate(Summary);

+ 12 - 16
packages/app-council/src/Overview/index.tsx

@@ -27,22 +27,18 @@ const NULL_INFO: DerivedElectionsInfo = {
   voterCount: new BN(0) as SetIndex
 };
 
-class Overview extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { electionsInfo = NULL_INFO } = this.props;
-
-    return (
-      <>
-        <Summary electionsInfo={electionsInfo} />
-        <Button.Group>
-          <SubmitCandidacy electionsInfo={electionsInfo} />
-          <Button.Or />
-          <Vote electionsInfo={electionsInfo} />
-        </Button.Group>
-        <Members electionsInfo={electionsInfo} />
-      </>
-    );
-  }
+function Overview ({ electionsInfo = NULL_INFO }: Props): React.ReactElement<Props> {
+  return (
+    <>
+      <Summary electionsInfo={electionsInfo} />
+      <Button.Group>
+        <SubmitCandidacy electionsInfo={electionsInfo} />
+        <Button.Or />
+        <Vote electionsInfo={electionsInfo} />
+      </Button.Group>
+      <Members electionsInfo={electionsInfo} />
+    </>
+  );
 }
 
 export default withCalls<Props>(

+ 14 - 18
packages/app-dashboard/src/Entry.tsx

@@ -16,24 +16,20 @@ interface Props extends I18nProps {
   route: Route;
 }
 
-class Entry extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, route: { i18n, icon, name }, t } = this.props;
-
-    return (
-      <div className={className}>
-        <Link to={`/${name}`}>
-          <Icon
-            name={icon}
-            size='massive'
-          />
-          <div className='name'>
-            {t(`entry.${name}`, i18n)}
-          </div>
-        </Link>
-      </div>
-    );
-  }
+function Entry ({ className, route: { i18n, icon, name }, t }: Props): React.ReactElement<Props> {
+  return (
+    <div className={className}>
+      <Link to={`/${name}`}>
+        <Icon
+          name={icon}
+          size='massive'
+        />
+        <div className='name'>
+          {t(`entry.${name}`, i18n)}
+        </div>
+      </Link>
+    </div>
+  );
 }
 
 export default translate(styled(Entry)`

+ 45 - 49
packages/app-democracy/src/Overview/Proposal.tsx

@@ -3,7 +3,7 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { AccountId, Balance, Proposal } from '@polkadot/types/interfaces';
+import { AccountId, Balance, Proposal as ProposalType } from '@polkadot/types/interfaces';
 import { I18nProps } from '@polkadot/react-components/types';
 
 import BN from 'bn.js';
@@ -19,64 +19,60 @@ import Seconding from './Seconding';
 interface Props extends I18nProps {
   democracy_depositOf?: [Balance, Vec<AccountId>] | null;
   idNumber: BN;
-  value: Proposal;
+  value: ProposalType;
 }
 
-class ProposalDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, democracy_depositOf, idNumber, value } = this.props;
-    const depositors = democracy_depositOf
-      ? democracy_depositOf[1]
-      : [];
-
-    return (
-      <ActionItem
-        className={className}
-        idNumber={idNumber}
-        proposal={value}
-        accessory={
-          <Seconding
-            depositors={depositors}
-            proposalId={idNumber}
-          />
-        }
-      >
-        {this.renderInfo()}
-      </ActionItem>
-    );
+function renderProposal ({ democracy_depositOf, t }: Props): React.ReactNode {
+  if (!democracy_depositOf) {
+    return null;
   }
 
-  private renderInfo (): React.ReactNode {
-    const { democracy_depositOf, t } = this.props;
+  const [balance, addresses] = democracy_depositOf;
 
-    if (!democracy_depositOf) {
-      return null;
-    }
+  return (
+    <div>
+      <Labelled label={t('depositors')}>
+        {addresses.map((address, index): React.ReactNode => (
+          <InputAddress
+            isDisabled
+            key={`${index}:${address}`}
+            value={address}
+            withLabel={false}
+          />
+        ))}
+      </Labelled>
+      <Static label={t('balance')}>
+        {formatBalance(balance)}
+      </Static>
+    </div>
+  );
+}
 
-    const [balance, addresses] = democracy_depositOf;
+function Proposal (props: Props): React.ReactElement<Props> {
+  const { className, democracy_depositOf, idNumber, value } = props;
+  const depositors = democracy_depositOf
+    ? democracy_depositOf[1]
+    : [];
 
-    return (
-      <div>
-        <Labelled label={t('depositors')}>
-          {addresses.map((address, index): React.ReactNode => (
-            <InputAddress
-              isDisabled
-              key={`${index}:${address}`}
-              value={address}
-              withLabel={false}
-            />
-          ))}
-        </Labelled>
-        <Static label={t('balance')}>
-          {formatBalance(balance)}
-        </Static>
-      </div>
-    );
-  }
+  return (
+    <ActionItem
+      className={className}
+      idNumber={idNumber}
+      proposal={value}
+      accessory={
+        <Seconding
+          depositors={depositors}
+          proposalId={idNumber}
+        />
+      }
+    >
+      {renderProposal(props)}
+    </ActionItem>
+  );
 }
 
 export default withMulti(
-  ProposalDisplay,
+  Proposal,
   translate,
   withCalls<Props>(
     ['query.democracy.depositOf', {

+ 17 - 33
packages/app-democracy/src/Overview/Proposals.tsx

@@ -18,39 +18,23 @@ interface Props extends I18nProps {
   democracy_publicProps?: [BN, Proposal][];
 }
 
-interface State {
-  isProposeOpen: boolean;
-}
-
-class Proposals extends React.PureComponent<Props> {
-  public state: State = {
-    isProposeOpen: false
-  };
-
-  public render (): React.ReactNode {
-    const { t } = this.props;
-
-    return (
-      <Column
-        emptyText={t('No available proposals')}
-        headerText={t('proposals')}
-      >
-        {this.renderProposals()}
-      </Column>
-    );
-  }
-
-  private renderProposals (): React.ReactNode {
-    const { democracy_publicProps = [] } = this.props;
-
-    return democracy_publicProps.map(([idNumber, proposal]): React.ReactNode => (
-      <ProposalDisplay
-        idNumber={idNumber}
-        key={idNumber.toString()}
-        value={proposal}
-      />
-    ));
-  }
+function Proposals ({ democracy_publicProps = [], t }: Props): React.ReactElement<Props> {
+  return (
+    <Column
+      emptyText={t('No available proposals')}
+      headerText={t('proposals')}
+    >
+      {
+        democracy_publicProps.map(([idNumber, proposal]): React.ReactNode => (
+          <ProposalDisplay
+            idNumber={idNumber}
+            key={idNumber.toString()}
+            value={proposal}
+          />
+        ))
+      }
+    </Column>
+  );
 }
 
 export default withMulti(

+ 20 - 28
packages/app-democracy/src/Overview/Referendums.tsx

@@ -18,34 +18,26 @@ interface Props extends I18nProps {
   democracy_referendums?: Option<ReferendumInfoExtended>[];
 }
 
-class Referendums extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { t } = this.props;
-
-    return (
-      <Column
-        emptyText={t('No available referendums')}
-        headerText={t('referendum')}
-      >
-        {this.renderReferendums()}
-      </Column>
-    );
-  }
-
-  private renderReferendums (): React.ReactNode {
-    const { democracy_referendums = [] } = this.props;
-    const referendums = democracy_referendums
-      .filter((opt): boolean => opt.isSome)
-      .map((opt): ReferendumInfoExtended => opt.unwrap());
-
-    return referendums.map((referendum): React.ReactNode => (
-      <Referendum
-        idNumber={referendum.index}
-        key={referendum.index.toString()}
-        value={referendum}
-      />
-    ));
-  }
+function Referendums ({ democracy_referendums = [], t }: Props): React.ReactElement<Props> {
+  return (
+    <Column
+      emptyText={t('No available referendums')}
+      headerText={t('referendum')}
+    >
+      {
+        democracy_referendums
+          .filter((opt): boolean => opt.isSome)
+          .map((opt): ReferendumInfoExtended => opt.unwrap())
+          .map((referendum): React.ReactNode => (
+            <Referendum
+              idNumber={referendum.index}
+              key={referendum.index.toString()}
+              value={referendum}
+            />
+          ))
+      }
+    </Column>
+  );
 }
 
 export default translate(

+ 33 - 35
packages/app-democracy/src/Overview/Summary.tsx

@@ -21,42 +21,40 @@ interface Props extends I18nProps {
   democracy_referendumCount?: BN;
 }
 
-class Summary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const {
-      chain_bestNumber = new BN(0),
-      democracy_launchPeriod = new BN(1),
-      democracy_nextTally = new BN(0),
-      democracy_publicPropCount,
-      democracy_referendumCount = new BN(0),
-      t
-    } = this.props;
+function Summary (props: Props): React.ReactElement<Props> {
+  const {
+    chain_bestNumber = new BN(0),
+    democracy_launchPeriod = new BN(1),
+    democracy_nextTally = new BN(0),
+    democracy_publicPropCount,
+    democracy_referendumCount = new BN(0),
+    t
+  } = props;
 
-    return (
-      <SummaryBox>
-        <section>
-          <CardSummary label={t('proposals')}>
-            {formatNumber(democracy_publicPropCount)}
-          </CardSummary>
-          <CardSummary label={t('referenda')}>
-            {formatNumber(democracy_referendumCount)}
-          </CardSummary>
-          <CardSummary label={t('active')}>
-            {formatNumber(democracy_referendumCount.sub(democracy_nextTally))}
-          </CardSummary>
-        </section>
-        <section className='ui--media-medium'>
-          <CardSummary
-            label={t('launch period')}
-            progress={{
-              value: chain_bestNumber.mod(democracy_launchPeriod).addn(1),
-              total: democracy_launchPeriod || new BN(1)
-            }}
-          />
-        </section>
-      </SummaryBox>
-    );
-  }
+  return (
+    <SummaryBox>
+      <section>
+        <CardSummary label={t('proposals')}>
+          {formatNumber(democracy_publicPropCount)}
+        </CardSummary>
+        <CardSummary label={t('referenda')}>
+          {formatNumber(democracy_referendumCount)}
+        </CardSummary>
+        <CardSummary label={t('active')}>
+          {formatNumber(democracy_referendumCount.sub(democracy_nextTally))}
+        </CardSummary>
+      </section>
+      <section className='ui--media-medium'>
+        <CardSummary
+          label={t('launch period')}
+          progress={{
+            value: chain_bestNumber.mod(democracy_launchPeriod).addn(1),
+            total: democracy_launchPeriod || new BN(1)
+          }}
+        />
+      </section>
+    </SummaryBox>
+  );
 }
 
 export default translate(

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

@@ -2,8 +2,6 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { AppProps, BareProps, I18nProps } from '@polkadot/react-components/types';
-
 import React from 'react';
 import { Columar } from '@polkadot/react-components';
 
@@ -11,18 +9,14 @@ import Proposals from './Proposals';
 import Referendums from './Referendums';
 import Summary from './Summary';
 
-type Props = AppProps & BareProps & I18nProps;
-
-export default class Overview extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    return (
-      <>
-        <Summary />
-        <Columar>
-          <Referendums />
-          <Proposals />
-        </Columar>
-      </>
-    );
-  }
+export default function Overview (): React.ReactElement {
+  return (
+    <>
+      <Summary />
+      <Columar>
+        <Referendums />
+        <Proposals />
+      </Columar>
+    </>
+  );
 }

+ 13 - 17
packages/app-explorer/src/BestHash.tsx

@@ -14,23 +14,19 @@ interface Props extends BareProps, CallProps {
   chain_subscribeNewHeads?: Header;
 }
 
-class BestHash extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, label = '', style, chain_subscribeNewHeads } = this.props;
-
-    return (
-      <div
-        className={className}
-        style={style}
-      >
-        {label}{
-          chain_subscribeNewHeads
-            ? chain_subscribeNewHeads.hash.toHex()
-            : undefined
-        }
-      </div>
-    );
-  }
+function BestHash ({ className, label = '', style, chain_subscribeNewHeads }: Props): React.ReactElement<Props> {
+  return (
+    <div
+      className={className}
+      style={style}
+    >
+      {label}{
+        chain_subscribeNewHeads
+          ? chain_subscribeNewHeads.hash.toHex()
+          : undefined
+      }
+    </div>
+  );
 }
 
 export default withCalls<Props>('rpc.chain.subscribeNewHeads')(BestHash);

+ 57 - 59
packages/app-explorer/src/BlockHeader/index.tsx

@@ -19,70 +19,68 @@ interface Props extends BareProps {
   withLink?: boolean;
 }
 
-export default class BlockHeader extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { isSummary, value, withExplorer, withLink } = this.props;
+const renderDetails = ({ number: blockNumber, extrinsicsRoot, parentHash, stateRoot }: HeaderExtended): React.ReactNode => {
+  const parentHex = parentHash.toHex();
 
-    if (!value) {
-      return null;
-    }
-
-    const hashHex = value.hash.toHex();
-    const textNumber = formatNumber(value.number);
+  return (
+    <div className='contains'>
+      <div className='info'>
+        <label>parentHash</label>
+        <span className='hash'>{
+          blockNumber.unwrap().gtn(1)
+            ? <Link to={`/explorer/query/${parentHex}`}>{parentHex}</Link>
+            : parentHex
+        }</span>
+      </div>
+      <div className='info'>
+        <label>extrinsicsRoot</label>
+        <span className='hash'>{extrinsicsRoot.toHex()}</span>
+      </div>
+      <div className='info'>
+        <label>stateRoot</label>
+        <span className='hash'>{stateRoot.toHex()}</span>
+      </div>
+    </div>
+  );
+};
 
-    return (
-      <article className='explorer--BlockHeader'>
-        <div className='header-outer'>
-          <div className='header'>
-            <div className='number'>{
-              withLink
-                ? <Link to={`/explorer/query/${hashHex}`}>{textNumber}</Link>
-                : textNumber
-            }&nbsp;</div>
-            <div className='hash'>{hashHex}</div>
-            <div className='author ui--media-small'>{
-              value.author
-                ? <AddressMini value={value.author} />
-                : undefined
-            }</div>
-          </div>
-        </div>
-        {
-          isSummary
-            ? undefined
-            : this.renderDetails(value)
-        }
-        {
-          withExplorer
-            ? <LinkPolkascan data={hashHex} type='block' />
-            : undefined
-        }
-      </article>
-    );
+function BlockHeader ({ isSummary, value, withExplorer, withLink }: Props): React.ReactElement<Props> | null {
+  if (!value) {
+    return null;
   }
 
-  private renderDetails ({ number: blockNumber, extrinsicsRoot, parentHash, stateRoot }: HeaderExtended): React.ReactNode {
-    const parentHex = parentHash.toHex();
+  const hashHex = value.hash.toHex();
+  const textNumber = formatNumber(value.number);
 
-    return (
-      <div className='contains'>
-        <div className='info'>
-          <label>parentHash</label>
-          <span className='hash'>{
-            blockNumber.unwrap().gtn(1)
-              ? <Link to={`/explorer/query/${parentHex}`}>{parentHex}</Link>
-              : parentHex
-          }</span>
-        </div>
-        <div className='info'>
-          <label>extrinsicsRoot</label>
-          <span className='hash'>{extrinsicsRoot.toHex()}</span>
-        </div>
-        <div className='info'>
-          <label>stateRoot</label>
-          <span className='hash'>{stateRoot.toHex()}</span>
+  return (
+    <article className='explorer--BlockHeader'>
+      <div className='header-outer'>
+        <div className='header'>
+          <div className='number'>{
+            withLink
+              ? <Link to={`/explorer/query/${hashHex}`}>{textNumber}</Link>
+              : textNumber
+          }&nbsp;</div>
+          <div className='hash'>{hashHex}</div>
+          <div className='author ui--media-small'>{
+            value.author
+              ? <AddressMini value={value.author} />
+              : undefined
+          }</div>
         </div>
       </div>
-    );
-  }
+      {
+        isSummary
+          ? undefined
+          : renderDetails(value)
+      }
+      {
+        withExplorer
+          ? <LinkPolkascan data={hashHex} type='block' />
+          : undefined
+      }
+    </article>
+  );
 }
+
+export default BlockHeader;

+ 15 - 13
packages/app-explorer/src/BlockHeaders.tsx

@@ -39,19 +39,21 @@ interface Props extends CallProps {
   headers?: HeaderExtended[];
 }
 
-class BlockHeaders extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { headers = [] } = this.props;
-
-    return headers.map((header, index): React.ReactNode => (
-      <BlockHeader
-        isSummary={!!index}
-        key={header.number.toString()}
-        value={header}
-        withLink={!header.number.isEmpty}
-      />
-    ));
-  }
+function BlockHeaders ({ headers = [] }: Props): React.ReactElement<Props> {
+  return (
+    <>
+      {
+        headers.map((header, index): React.ReactNode => (
+          <BlockHeader
+            isSummary={!!index}
+            key={header.number.toString()}
+            value={header}
+            withLink={!header.number.isEmpty}
+          />
+        ))
+      }
+    </>
+  );
 }
 
 export default withCalls<Props>(['derive.chain.subscribeNewHeads', {

+ 22 - 26
packages/app-explorer/src/BlockInfo/ByHash.tsx

@@ -25,33 +25,29 @@ type Props = ApiProps & I18nProps & {
   value: string;
 };
 
-class BlockByHash extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { system_events, chain_getBlock, chain_getHeader } = this.props;
-
-    if (!chain_getBlock || chain_getBlock.isEmpty || !chain_getHeader || chain_getHeader.isEmpty) {
-      return null;
-    }
-
-    return (
-      <>
-        <header>
-          <BlockHeader
-            value={chain_getHeader}
-            withExplorer
-          />
-        </header>
-        <Columar>
-          <Extrinsics
-            blockNumber={chain_getHeader.number.unwrap()}
-            value={chain_getBlock.block.extrinsics}
-          />
-          <Events value={system_events} />
-          <Logs value={chain_getHeader.digest.logs} />
-        </Columar>
-      </>
-    );
+function BlockByHash ({ system_events, chain_getBlock, chain_getHeader }: Props): React.ReactElement<Props> | null {
+  if (!chain_getBlock || chain_getBlock.isEmpty || !chain_getHeader || chain_getHeader.isEmpty) {
+    return null;
   }
+
+  return (
+    <>
+      <header>
+        <BlockHeader
+          value={chain_getHeader}
+          withExplorer
+        />
+      </header>
+      <Columar>
+        <Extrinsics
+          blockNumber={chain_getHeader.number.unwrap()}
+          value={chain_getBlock.block.extrinsics}
+        />
+        <Events value={system_events} />
+        <Logs value={chain_getHeader.digest.logs} />
+      </Columar>
+    </>
+  );
 }
 
 export default translate(

+ 7 - 11
packages/app-explorer/src/BlockInfo/ByNumber.tsx

@@ -16,18 +16,14 @@ interface Props extends ApiProps {
   value: string;
 }
 
-class BlockByNumber extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { chain_getBlockHash } = this.props;
-
-    if (!chain_getBlockHash) {
-      return null;
-    }
-
-    return (
-      <BlockByHash value={chain_getBlockHash.toHex()} />
-    );
+function BlockByNumber ({ chain_getBlockHash }: Props): React.ReactElement<Props> | null {
+  if (!chain_getBlockHash) {
+    return null;
   }
+
+  return (
+    <BlockByHash value={chain_getBlockHash.toHex()} />
+  );
 }
 
 export default withCalls<Props>(

+ 16 - 20
packages/app-explorer/src/BlockInfo/Events.tsx

@@ -15,27 +15,23 @@ interface Props extends I18nProps {
   value?: EventRecord[];
 }
 
-class Events extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { t, value } = this.props;
-
-    if (!value || !value.length) {
-      return null;
-    }
-
-    const events = value.map((record, index): { key: string; record: EventRecord } => ({
-      key: `${index}`, record
-    }));
-
-    return (
-      <Column headerText={t('events')}>
-        <EventsDisplay
-          eventClassName='explorer--BlockByHash-block'
-          events={events}
-        />
-      </Column>
-    );
+function Events ({ value, t }: Props): React.ReactElement<Props> | null {
+  if (!value || !value.length) {
+    return null;
   }
+
+  return (
+    <Column headerText={t('events')}>
+      <EventsDisplay
+        eventClassName='explorer--BlockByHash-block'
+        events={
+          value.map((record, index): { key: string; record: EventRecord } => ({
+            key: `${index}`, record
+          }))
+        }
+      />
+    </Column>
+  );
 }
 
 export default translate(Events);

+ 52 - 47
packages/app-explorer/src/Events.tsx

@@ -18,57 +18,62 @@ interface Props extends I18nProps {
   withoutIndex?: boolean;
 }
 
-class Events extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { emptyLabel, events, t } = this.props;
-
-    if (!events || events.length === 0) {
-      return <article>{emptyLabel || t('no events available')}</article>;
-    }
-
-    return events.map(this.renderEvent);
-  }
-
-  private renderEvent = ({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => {
-    const { eventClassName, withoutIndex } = this.props;
-    const extIndex = !withoutIndex && phase.isApplyExtrinsic
-      ? phase.asApplyExtrinsic
-      : -1;
-
-    if (!event.method || !event.section) {
-      return null;
-    }
-
+function Events ({ emptyLabel, eventClassName, events, withoutIndex, t }: Props): React.ReactElement<Props> {
+  if (!events || events.length === 0) {
     return (
-      <article
-        className={`explorer--Container ${eventClassName}`}
-        key={key}
-      >
-        <div className='header'>
-          <h3>
-            {event.section}.{event.method}&nbsp;{
-              extIndex !== -1
-                ? `(#${formatNumber(extIndex)})`
-                : ''
-            }
-          </h3>
-        </div>
-        <details>
-          <summary>
-            {
-              event.meta && event.meta.documentation
-                ? event.meta.documentation.join(' ')
-                : 'Details'
-            }
-          </summary>
-          <EventDisplay
-            className='details'
-            value={event}
-          />
-        </details>
+      <article>
+        {emptyLabel || t('no events available')}
       </article>
     );
   }
+
+  return (
+    <>
+      {
+        events.map(
+          ({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => {
+            const extIndex = !withoutIndex && phase.isApplyExtrinsic
+              ? phase.asApplyExtrinsic
+              : -1;
+
+            if (!event.method || !event.section) {
+              return null;
+            }
+
+            return (
+              <article
+                className={`explorer--Container ${eventClassName}`}
+                key={key}
+              >
+                <div className='header'>
+                  <h3>
+                    {event.section}.{event.method}&nbsp;{
+                      extIndex !== -1
+                        ? `(#${formatNumber(extIndex)})`
+                        : ''
+                    }
+                  </h3>
+                </div>
+                <details>
+                  <summary>
+                    {
+                      event.meta && event.meta.documentation
+                        ? event.meta.documentation.join(' ')
+                        : 'Details'
+                    }
+                  </summary>
+                  <EventDisplay
+                    className='details'
+                    value={event}
+                  />
+                </details>
+              </article>
+            );
+          }
+        )
+      }
+    </>
+  );
 }
 
 export default translate(Events);

+ 15 - 19
packages/app-explorer/src/Main.tsx

@@ -18,25 +18,21 @@ interface Props extends I18nProps {
   events: KeyedEvent[];
 }
 
-class Main extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { events, t } = this.props;
-
-    return (
-      <>
-        <Query />
-        <Summary />
-        <Columar>
-          <Column headerText={t('recent blocks')}>
-            <BlockHeaders />
-          </Column>
-          <Column headerText={t('recent events')}>
-            <Events events={events} />
-          </Column>
-        </Columar>
-      </>
-    );
-  }
+function Main ({ events, t }: Props): React.ReactElement<Props> {
+  return (
+    <>
+      <Query />
+      <Summary />
+      <Columar>
+        <Column headerText={t('recent blocks')}>
+          <BlockHeaders />
+        </Column>
+        <Column headerText={t('recent events')}>
+          <Events events={events} />
+        </Column>
+      </Columar>
+    </>
+  );
 }
 
 export default translate(Main);

+ 44 - 50
packages/app-explorer/src/NodeInfo/Peers.tsx

@@ -14,63 +14,57 @@ interface Props extends I18nProps {
   peers?: PeerInfo[] | null;
 }
 
-class Peers extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { t } = this.props;
+const renderPeer = (peer: PeerInfo): React.ReactNode => {
+  const peerId = peer.peerId.toString();
 
-    return (
-      <section className='status--Peers'>
-        <h1>{t('connected peers')}</h1>
-        {this.renderPeers()}
-      </section>
-    );
-  }
-
-  private renderPeers (): React.ReactNode {
-    const { peers, t } = this.props;
-
-    if (!peers || !peers.length) {
-      return (
-        <div className='ui disabled'>
-          {t('no peers connected')}
-        </div>
-      );
-    }
+  return (
+    <tr key={peerId}>
+      <td className='roles'>{peer.roles.toString().toLowerCase()}</td>
+      <td className='peerid ui--media-medium'>{peerId}</td>
+      <td className='number'>{formatNumber(peer.bestNumber)}</td>
+      <td className='hash'>{peer.bestHash.toHex()}</td>
+    </tr>
+  );
+};
 
+const renderPeers = ({ peers, t }: Props): React.ReactNode => {
+  if (!peers || !peers.length) {
     return (
-      <article>
-        <table>
-          <thead>
-            <tr>
-              <th className='roles'>{t('role')}</th>
-              <th className='peerid ui--media-medium'>{t('peer id')}</th>
-              <th className='number'>{t('best #')}</th>
-              <th className='hash'>{t('best hash')}</th>
-            </tr>
-          </thead>
-          <tbody>
-            {peers
-              .sort((a, b): number => b.bestNumber.cmp(a.bestNumber))
-              .map(this.renderPeer)
-            }
-          </tbody>
-        </table>
-      </article>
+      <div className='ui disabled'>
+        {t('no peers connected')}
+      </div>
     );
   }
 
-  private renderPeer = (peer: PeerInfo): React.ReactNode => {
-    const peerId = peer.peerId.toString();
+  return (
+    <article>
+      <table>
+        <thead>
+          <tr>
+            <th className='roles'>{t('role')}</th>
+            <th className='peerid ui--media-medium'>{t('peer id')}</th>
+            <th className='number'>{t('best #')}</th>
+            <th className='hash'>{t('best hash')}</th>
+          </tr>
+        </thead>
+        <tbody>
+          {peers
+            .sort((a, b): number => b.bestNumber.cmp(a.bestNumber))
+            .map(renderPeer)
+          }
+        </tbody>
+      </table>
+    </article>
+  );
+};
 
-    return (
-      <tr key={peerId}>
-        <td className='roles'>{peer.roles.toString().toLowerCase()}</td>
-        <td className='peerid ui--media-medium'>{peerId}</td>
-        <td className='number'>{formatNumber(peer.bestNumber)}</td>
-        <td className='hash'>{peer.bestHash.toHex()}</td>
-      </tr>
-    );
-  }
+function Peers (props: Props): React.ReactElement<Props> {
+  return (
+    <section className='status--Peers'>
+      <h1>{props.t('connected peers')}</h1>
+      {renderPeers(props)}
+    </section>
+  );
 }
 
 export default translate(Peers);

+ 33 - 37
packages/app-explorer/src/Summary.tsx

@@ -13,43 +13,39 @@ import translate from './translate';
 
 type Props = I18nProps;
 
-class Summary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { t } = this.props;
-
-    return (
-      <SummaryBox>
-        <section>
-          <CardSummary label={t('last block')}>
-            <TimeNow />
-          </CardSummary>
-          <CardSummary
-            className='ui--media-small'
-            label={t('target')}
-          >
-            <TimePeriod />
-          </CardSummary>
-          <CardSummary
-            className='ui--media-small'
-            label={t('total issuance')}
-          >
-            <TotalIssuance />
-          </CardSummary>
-        </section>
-        <section className='ui--media-large'>
-          <SummarySession />
-        </section>
-        <section>
-          <CardSummary label={t('finalized')}>
-            <BestFinalized />
-          </CardSummary>
-          <CardSummary label={t('best')}>
-            <BestNumber />
-          </CardSummary>
-        </section>
-      </SummaryBox>
-    );
-  }
+function Summary ({ t }: Props): React.ReactElement<Props> {
+  return (
+    <SummaryBox>
+      <section>
+        <CardSummary label={t('last block')}>
+          <TimeNow />
+        </CardSummary>
+        <CardSummary
+          className='ui--media-small'
+          label={t('target')}
+        >
+          <TimePeriod />
+        </CardSummary>
+        <CardSummary
+          className='ui--media-small'
+          label={t('total issuance')}
+        >
+          <TotalIssuance />
+        </CardSummary>
+      </section>
+      <section className='ui--media-large'>
+        <SummarySession />
+      </section>
+      <section>
+        <CardSummary label={t('finalized')}>
+          <BestFinalized />
+        </CardSummary>
+        <CardSummary label={t('best')}>
+          <BestNumber />
+        </CardSummary>
+      </section>
+    </SummaryBox>
+  );
 }
 
 export default translate(Summary);

+ 49 - 55
packages/app-explorer/src/SummarySession.tsx

@@ -19,69 +19,63 @@ interface Props extends I18nProps {
   withSession?: boolean;
 }
 
-class SummarySession extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    return (
-      <>
-        {this.renderSession()}
-        {this.renderEra()}
-      </>
-    );
+function renderSession ({ session_info, t, withSession = true }: Props): React.ReactNode {
+  if (!withSession || !session_info) {
+    return null;
   }
 
-  private renderEra (): React.ReactNode {
-    const { session_info, t, withEra = true } = this.props;
-
-    if (!withEra || !session_info) {
-      return null;
-    }
+  const label = session_info.isEpoch
+    ? t('epoch')
+    : t('session');
 
-    const label = t('era');
+  return session_info.sessionLength.gtn(0)
+    ? (
+      <CardSummary
+        label={label}
+        progress={{
+          total: session_info.sessionLength,
+          value: session_info.sessionProgress
+        }}
+      />
+    )
+    : (
+      <CardSummary label={label}>
+        {formatNumber(session_info.currentIndex)}
+      </CardSummary>
+    );
+}
 
-    return session_info.sessionLength.gtn(0)
-      ? (
-        <CardSummary
-          label={label}
-          progress={{
-            total: session_info && session_info.eraLength,
-            value: session_info && session_info.eraProgress
-          }}
-        />
-      )
-      : (
-        <CardSummary label={label}>
-          {formatNumber(session_info.currentEra)}
-        </CardSummary>
-      );
+function renderEra ({ session_info, t, withEra = true }: Props): React.ReactNode {
+  if (!withEra || !session_info) {
+    return null;
   }
 
-  private renderSession (): React.ReactNode {
-    const { session_info, t, withSession = true } = this.props;
+  const label = t('era');
 
-    if (!withSession || !session_info) {
-      return null;
-    }
-
-    const label = session_info.isEpoch
-      ? t('epoch')
-      : t('session');
+  return session_info.sessionLength.gtn(0)
+    ? (
+      <CardSummary
+        label={label}
+        progress={{
+          total: session_info && session_info.eraLength,
+          value: session_info && session_info.eraProgress
+        }}
+      />
+    )
+    : (
+      <CardSummary label={label}>
+        {formatNumber(session_info.currentEra)}
+      </CardSummary>
+    );
+}
 
-    return session_info.sessionLength.gtn(0)
-      ? (
-        <CardSummary
-          label={label}
-          progress={{
-            total: session_info.sessionLength,
-            value: session_info.sessionProgress
-          }}
-        />
-      )
-      : (
-        <CardSummary label={label}>
-          {formatNumber(session_info.currentIndex)}
-        </CardSummary>
-      );
-  }
+function SummarySession (props: Props): React.ReactElement<Props> {
+  return (
+    <>
+      {renderSession(props)}
+      {renderEra(props)}
+    </>
+  );
 }
 
 export default translate(

+ 10 - 14
packages/app-extrinsics/src/Balance.tsx

@@ -15,20 +15,16 @@ interface Props extends BareProps, CallProps {
   label?: React.ReactNode;
 }
 
-class BalanceDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, label, style, balances_all } = this.props;
-
-    return (
-      <InputBalance
-        className={className}
-        isDisabled
-        label={label}
-        style={style}
-        defaultValue={balances_all && balances_all.freeBalance}
-      />
-    );
-  }
+function BalanceDisplay ({ className, label, style, balances_all }: Props): React.ReactElement<Props> {
+  return (
+    <InputBalance
+      className={className}
+      isDisabled
+      label={label}
+      style={style}
+      defaultValue={balances_all && balances_all.freeBalance}
+    />
+  );
 }
 
 export default withMulti(

+ 16 - 20
packages/app-extrinsics/src/index.tsx

@@ -12,26 +12,22 @@ import translate from './translate';
 
 type Props = AppProps & I18nProps;
 
-class ExtrinsicsApp extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { basePath, t } = this.props;
-
-    return (
-      <main className='extrinsics--App'>
-        <header>
-          <Tabs
-            basePath={basePath}
-            items={[{
-              isRoot: true,
-              name: 'create',
-              text: t('Extrinsic submission')
-            }]}
-          />
-        </header>
-        <Selection />
-      </main>
-    );
-  }
+function ExtrinsicsApp ({ basePath, t }: Props): React.ReactElement<Props> {
+  return (
+    <main className='extrinsics--App'>
+      <header>
+        <Tabs
+          basePath={basePath}
+          items={[{
+            isRoot: true,
+            name: 'create',
+            text: t('Extrinsic submission')
+          }]}
+        />
+      </header>
+      <Selection />
+    </main>
+  );
 }
 
 export { ExtrinsicsApp };

+ 12 - 16
packages/app-js/src/Output.tsx

@@ -35,21 +35,17 @@ const renderEntry = ({ args, type }: Log, index: number): React.ReactNode => (
   </div>
 );
 
-export default class Output extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, logs } = this.props;
-
-    return (
-      <article className='container js--Output'>
-        <div className='logs-wrapper'>
-          <div className='logs-container'>
-            <pre className='logs-content'>
-              {logs.map(renderEntry)}
-            </pre>
-          </div>
+export default function Output ({ children, logs }: Props): React.ReactElement<Props> {
+  return (
+    <article className='container js--Output'>
+      <div className='logs-wrapper'>
+        <div className='logs-container'>
+          <pre className='logs-content'>
+            {logs.map(renderEntry)}
+          </pre>
         </div>
-        {children}
-      </article>
-    );
-  }
+      </div>
+      {children}
+    </article>
+  );
 }

+ 7 - 11
packages/app-js/src/index.tsx

@@ -12,15 +12,11 @@ import Playground from './Playground';
 
 type Props = AppProps & BareProps;
 
-export default class AppJs extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { basePath } = this.props;
-
-    return (
-      <Switch>
-        <Route path={`${basePath}/share/:base64`} component={Playground} />
-        <Route component={Playground} />
-      </Switch>
-    );
-  }
+export default function AppJs ({ basePath }: Props): React.ReactElement<Props> {
+  return (
+    <Switch>
+      <Route path={`${basePath}/share/:base64`} component={Playground} />
+      <Route component={Playground} />
+    </Switch>
+  );
 }

+ 24 - 28
packages/app-parachains/src/Overview/Parachain.tsx

@@ -23,36 +23,32 @@ interface Props extends I18nProps {
   parachains_relayDispatchQueueSize?: [BN, BN];
 }
 
-class Parachain extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, paraId, parachains_heads, parachains_relayDispatchQueueSize, t } = this.props;
-
-    return (
-      <Card className={className}>
-        <div className='ui--Row'>
-          <div className='ui--Row-base'>
-            <div className='ui--Row-details parachains--Item-header'>
-              <h3>#{formatNumber(paraId)}</h3>
-            </div>
+function Parachain ({ className, paraId, parachains_heads, parachains_relayDispatchQueueSize, t }: Props): React.ReactElement<Props> {
+  return (
+    <Card className={className}>
+      <div className='ui--Row'>
+        <div className='ui--Row-base'>
+          <div className='ui--Row-details parachains--Item-header'>
+            <h3>#{formatNumber(paraId)}</h3>
           </div>
-          <Static
-            help={t('the last heads of this parachain')}
-            label={t('heads')}
-            value={parachains_heads || t('<unknown>')}
-          />
-          <Static
-            help={t('the relay dispatch queue size')}
-            label={t('relay queue')}
-            value={
-              parachains_relayDispatchQueueSize
-                ? formatNumber(parachains_relayDispatchQueueSize[0])
-                : '-'
-            }
-          />
         </div>
-      </Card>
-    );
-  }
+        <Static
+          help={t('the last heads of this parachain')}
+          label={t('heads')}
+          value={parachains_heads || t('<unknown>')}
+        />
+        <Static
+          help={t('the relay dispatch queue size')}
+          label={t('relay queue')}
+          value={
+            parachains_relayDispatchQueueSize
+              ? formatNumber(parachains_relayDispatchQueueSize[0])
+              : '-'
+          }
+        />
+      </div>
+    </Card>
+  );
 }
 
 export default withMulti(

+ 15 - 19
packages/app-parachains/src/Overview/Parachains.tsx

@@ -17,25 +17,21 @@ interface Props extends I18nProps {
   parachains_parachains?: BN[];
 }
 
-class Parachains extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { parachains_parachains = [], t } = this.props;
-
-    return (
-      <Column
-        emptyText={t('no deployed parachains')}
-        headerText={t('parachains')}
-      >
-        {
-          parachains_parachains.length
-            ? parachains_parachains.map((paraId): React.ReactNode =>
-              <Parachain key={paraId.toString()} paraId={paraId} />
-            )
-            : null
-        }
-      </Column>
-    );
-  }
+function Parachains ({ parachains_parachains = [], t }: Props): React.ReactElement<Props> {
+  return (
+    <Column
+      emptyText={t('no deployed parachains')}
+      headerText={t('parachains')}
+    >
+      {
+        parachains_parachains.length
+          ? parachains_parachains.map((paraId): React.ReactNode =>
+            <Parachain key={paraId.toString()} paraId={paraId} />
+          )
+          : null
+      }
+    </Column>
+  );
 }
 
 export default translate(

+ 14 - 18
packages/app-parachains/src/Overview/Summary.tsx

@@ -18,27 +18,23 @@ interface Props extends I18nProps {
   parachains_parachains?: BN[];
 }
 
-class Summary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { parachains_nextFreeId, parachains_parachains = [], t } = this.props;
-
-    return (
-      <SummaryBox>
+function Summary ({ parachains_nextFreeId, parachains_parachains = [], t }: Props): React.ReactElement<Props> {
+  return (
+    <SummaryBox>
+      <section>
+        <CardSummary label={t('parachains')}>
+          {formatNumber(parachains_parachains.length)}
+        </CardSummary>
+      </section>
+      {parachains_nextFreeId && (
         <section>
-          <CardSummary label={t('parachains')}>
-            {formatNumber(parachains_parachains.length)}
+          <CardSummary label={t('next id')}>
+            {formatNumber(parachains_nextFreeId)}
           </CardSummary>
         </section>
-        {parachains_nextFreeId && (
-          <section>
-            <CardSummary label={t('next id')}>
-              {formatNumber(parachains_nextFreeId)}
-            </CardSummary>
-          </section>
-        )}
-      </SummaryBox>
-    );
-  }
+      )}
+    </SummaryBox>
+  );
 }
 
 export default translate(

+ 32 - 34
packages/app-staking/src/Overview/Summary.tsx

@@ -24,41 +24,39 @@ interface Props extends I18nProps {
   next: string[];
 }
 
-class Summary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, currentValidatorsControllersV1OrStashesV2, lastAuthor, lastBlock, next, style, staking_validatorCount, t } = this.props;
+function Summary (props: Props): React.ReactElement<Props> {
+  const { className, currentValidatorsControllersV1OrStashesV2, lastAuthor, lastBlock, next, style, staking_validatorCount, t } = props;
 
-    return (
-      <SummaryBox
-        className={className}
-        style={style}
-      >
-        <section>
-          <CardSummary label={t('validators')}>
-            {currentValidatorsControllersV1OrStashesV2.length}/{staking_validatorCount ? staking_validatorCount.toString() : '-'}
-          </CardSummary>
-          <CardSummary label={t('waiting')}>
-            {next.length}
-          </CardSummary>
-        </section>
-        <section>
-          <CardSummary label={t('last block')}>
-            {lastAuthor && (
-              <IdentityIcon
-                className='validator--Account-block-icon'
-                size={24}
-                value={lastAuthor}
-              />
-            )}
-            {lastBlock}
-          </CardSummary>
-        </section>
-        <section>
-          <SummarySession />
-        </section>
-      </SummaryBox>
-    );
-  }
+  return (
+    <SummaryBox
+      className={className}
+      style={style}
+    >
+      <section>
+        <CardSummary label={t('validators')}>
+          {currentValidatorsControllersV1OrStashesV2.length}/{staking_validatorCount ? staking_validatorCount.toString() : '-'}
+        </CardSummary>
+        <CardSummary label={t('waiting')}>
+          {next.length}
+        </CardSummary>
+      </section>
+      <section>
+        <CardSummary label={t('last block')}>
+          {lastAuthor && (
+            <IdentityIcon
+              className='validator--Account-block-icon'
+              size={24}
+              value={lastAuthor}
+            />
+          )}
+          {lastBlock}
+        </CardSummary>
+      </section>
+      <section>
+        <SummarySession />
+      </section>
+    </SummaryBox>
+  );
 }
 
 export default withMulti(

+ 39 - 40
packages/app-staking/src/Overview/index.tsx

@@ -20,50 +20,49 @@ interface Props extends ApiProps, BareProps, ComponentProps {
   chain_subscribeNewHeads?: HeaderExtended;
 }
 
-class Overview extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { chain_subscribeNewHeads, allControllers, allStashes, currentValidatorsControllersV1OrStashesV2, isSubstrateV2, recentlyOnline } = this.props;
-    let nextSorted: string[];
+// TODO: Switch to useState
+function Overview (props: Props): React.ReactElement<Props> {
+  const { chain_subscribeNewHeads, allControllers, allStashes, currentValidatorsControllersV1OrStashesV2, isSubstrateV2, recentlyOnline } = props;
+  let nextSorted: string[];
 
-    if (isSubstrateV2) {
-      // this is a V2 node currentValidatorsControllersV1OrStashesV2 is a list of stashes
-      nextSorted = allStashes.filter((address): boolean =>
-        !currentValidatorsControllersV1OrStashesV2.includes(address)
-      );
-    } else {
-      // this is a V1 node currentValidatorsControllersV1OrStashesV2 is a list of controllers
-      nextSorted = allControllers.filter((address): boolean =>
-        !currentValidatorsControllersV1OrStashesV2.includes(address)
-      );
-    }
-
-    let lastBlock = '—';
-    let lastAuthor: string | undefined;
+  if (isSubstrateV2) {
+    // this is a V2 node currentValidatorsControllersV1OrStashesV2 is a list of stashes
+    nextSorted = allStashes.filter((address): boolean =>
+      !currentValidatorsControllersV1OrStashesV2.includes(address)
+    );
+  } else {
+    // this is a V1 node currentValidatorsControllersV1OrStashesV2 is a list of controllers
+    nextSorted = allControllers.filter((address): boolean =>
+      !currentValidatorsControllersV1OrStashesV2.includes(address)
+    );
+  }
 
-    if (chain_subscribeNewHeads) {
-      lastBlock = formatNumber(chain_subscribeNewHeads.number);
-      lastAuthor = (chain_subscribeNewHeads.author || '').toString();
-    }
+  let lastBlock = '—';
+  let lastAuthor: string | undefined;
 
-    return (
-      <div className='staking--Overview'>
-        <Summary
-          allControllers={allControllers}
-          currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
-          lastBlock={lastBlock}
-          lastAuthor={lastAuthor}
-          next={nextSorted}
-        />
-        <CurrentList
-          currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
-          lastBlock={lastBlock}
-          lastAuthor={lastAuthor}
-          next={nextSorted}
-          recentlyOnline={recentlyOnline}
-        />
-      </div>
-    );
+  if (chain_subscribeNewHeads) {
+    lastBlock = formatNumber(chain_subscribeNewHeads.number);
+    lastAuthor = (chain_subscribeNewHeads.author || '').toString();
   }
+
+  return (
+    <div className='staking--Overview'>
+      <Summary
+        allControllers={allControllers}
+        currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
+        lastBlock={lastBlock}
+        lastAuthor={lastAuthor}
+        next={nextSorted}
+      />
+      <CurrentList
+        currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2}
+        lastBlock={lastBlock}
+        lastAuthor={lastAuthor}
+        next={nextSorted}
+        recentlyOnline={recentlyOnline}
+      />
+    </div>
+  );
 }
 
 export default withMulti(

+ 15 - 19
packages/app-storage/src/Queries.tsx

@@ -14,24 +14,20 @@ interface Props extends BareProps {
   value?: QueryTypes[];
 }
 
-export default class Queries extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { onRemove, value } = this.props;
-
-    if (!value || !value.length) {
-      return null;
-    }
-
-    return (
-      <section className='storage--Queries'>
-        {value.map((query): React.ReactNode =>
-          <Query
-            key={query.id}
-            onRemove={onRemove}
-            value={query}
-          />
-        )}
-      </section>
-    );
+export default function Queries ({ onRemove, value }: Props): React.ReactElement<Props> | null {
+  if (!value || !value.length) {
+    return null;
   }
+
+  return (
+    <section className='storage--Queries'>
+      {value.map((query): React.ReactNode =>
+        <Query
+          key={query.id}
+          onRemove={onRemove}
+          value={query}
+        />
+      )}
+    </section>
+  );
 }

+ 27 - 29
packages/app-toolbox/src/Rpc/Results.tsx

@@ -13,35 +13,33 @@ interface Props extends BareProps {
   queue: QueueTx[];
 }
 
-export default class Results extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { queue = [] } = this.props;
+function Results ({ queue = [] }: Props): React.ReactElement<Props> | null {
+  const filtered = queue
+    .filter(({ error, result }): boolean =>
+      !isUndefined(error) || !isUndefined(result)
+    )
+    .reverse();
 
-    const filtered = queue
-      .filter(({ error, result }): boolean =>
-        !isUndefined(error) || !isUndefined(result)
-      )
-      .reverse();
-
-    if (!filtered.length) {
-      return null;
-    }
-
-    return (
-      <section className='rpc--Results'>
-        {filtered.map(({ error, id, result, rpc: { section, method } }): React.ReactNode => (
-          <Output
-            isError={!!error}
-            key={id}
-            label={`${id}: ${section}.${method}`}
-            value={
-              error
-                ? error.message
-                : <pre>{JSON.stringify(result, null, 2)}</pre>
-            }
-          />
-        ))}
-      </section>
-    );
+  if (!filtered.length) {
+    return null;
   }
+
+  return (
+    <section className='rpc--Results'>
+      {filtered.map(({ error, id, result, rpc: { section, method } }): React.ReactNode => (
+        <Output
+          isError={!!error}
+          key={id}
+          label={`${id}: ${section}.${method}`}
+          value={
+            error
+              ? error.message
+              : <pre>{JSON.stringify(result, null, 2)}</pre>
+          }
+        />
+      ))}
+    </section>
+  );
 }
+
+export default Results;

+ 11 - 16
packages/app-toolbox/src/Rpc/index.tsx

@@ -2,7 +2,6 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { BareProps } from '@polkadot/react-components/types';
 import { QueueProps } from '@polkadot/react-components/Status/types';
 
 import React from 'react';
@@ -11,19 +10,15 @@ import { QueueConsumer } from '@polkadot/react-components/Status/Context';
 import Results from './Results';
 import Selection from './Selection';
 
-type Props = BareProps;
-
-export default class RpcApp extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    return (
-      <QueueConsumer>
-        {({ txqueue, queueRpc }: QueueProps): React.ReactNode => (
-          <>
-            <Selection queueRpc={queueRpc} />
-            <Results queue={txqueue} />
-          </>
-        )}
-      </QueueConsumer>
-    );
-  }
+export default function RpcApp (): React.ReactElement {
+  return (
+    <QueueConsumer>
+      {({ txqueue, queueRpc }: QueueProps): React.ReactNode => (
+        <>
+          <Selection queueRpc={queueRpc} />
+          <Results queue={txqueue} />
+        </>
+      )}
+    </QueueConsumer>
+  );
 }

+ 6 - 8
packages/app-transfer/src/index.tsx

@@ -2,14 +2,12 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { AppProps } from '@polkadot/react-components/types';
-
 import React from 'react';
 
-type Props = AppProps;
-
-export default class App extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    return 'Deprecated, now operates via modal';
-  }
+export default function App (): React.ReactElement {
+  return (
+    <>
+      Deprecated, now operates via modal
+    </>
+  );
 }

+ 1 - 1
packages/app-treasury/src/Overview/Proposals.tsx

@@ -28,7 +28,7 @@ interface State {
   proposalIndices: BN[];
 }
 
-class ProposalsBase extends React.PureComponent<Props> {
+class ProposalsBase extends React.PureComponent<Props, State> {
   public state: State = {
     isEmpty: true,
     isProposeOpen: false,

+ 26 - 28
packages/app-treasury/src/Overview/Summary.tsx

@@ -19,35 +19,33 @@ interface Props extends I18nProps {
   treasury_pot?: BN;
 }
 
-class Summary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { treasury_proposalCount = new BN(0), treasury_approvals = [] as BN[], treasury_pot = new BN(0), t } = this.props;
-    const value = treasury_pot
-      ? treasury_pot.toString()
-      : null;
+function Summary (props: Props): React.ReactElement<Props> {
+  const { treasury_proposalCount = new BN(0), treasury_approvals = [] as BN[], treasury_pot = new BN(0), t } = props;
+  const value = treasury_pot
+    ? treasury_pot.toString()
+    : null;
 
-    return (
-      <SummaryBox>
-        <section>
-          <CardSummary label={t('proposals')}>
-            {formatNumber(treasury_proposalCount)}
-          </CardSummary>
-          <CardSummary label={t('approved')}>
-            {treasury_approvals ? treasury_approvals.length : '0'}
-          </CardSummary>
-        </section>
-        <section>
-          <CardSummary label={t('pot')}>
-            {
-              value
-                ? `${formatBalance(value, false)}${treasury_pot.gtn(0) ? formatBalance.calcSi(value).value : ''}`
-                : '-'
-            }
-          </CardSummary>
-        </section>
-      </SummaryBox>
-    );
-  }
+  return (
+    <SummaryBox>
+      <section>
+        <CardSummary label={t('proposals')}>
+          {formatNumber(treasury_proposalCount)}
+        </CardSummary>
+        <CardSummary label={t('approved')}>
+          {treasury_approvals ? treasury_approvals.length : '0'}
+        </CardSummary>
+      </section>
+      <section>
+        <CardSummary label={t('pot')}>
+          {
+            value
+              ? `${formatBalance(value, false)}${treasury_pot.gtn(0) ? formatBalance.calcSi(value).value : ''}`
+              : '-'
+          }
+        </CardSummary>
+      </section>
+    </SummaryBox>
+  );
 }
 
 export default translate(