Browse Source

Functional components (apps, react-components) (#1564)

* Functional components (apps, react-components)

* move consts
kwingram25 5 years ago
parent
commit
cdf1d88de5
54 changed files with 1494 additions and 1681 deletions
  1. 20 23
      packages/apps/src/SideBar/NodeInfo.tsx
  2. 12 16
      packages/apps/src/TopBar.tsx
  3. 44 47
      packages/apps/src/overlays/Connecting.tsx
  4. 18 30
      packages/react-components/src/ActionItem.tsx
  5. 19 22
      packages/react-components/src/AddressCard.tsx
  6. 195 209
      packages/react-components/src/AddressInfo.tsx
  7. 68 76
      packages/react-components/src/AddressMini.tsx
  8. 1 1
      packages/react-components/src/AddressRow.tsx
  9. 12 16
      packages/react-components/src/Available.tsx
  10. 33 32
      packages/react-components/src/Balance.tsx
  11. 33 32
      packages/react-components/src/Bonded.tsx
  12. 15 19
      packages/react-components/src/Bubble.tsx
  13. 12 16
      packages/react-components/src/Button/Or.tsx
  14. 61 64
      packages/react-components/src/Call.tsx
  15. 6 10
      packages/react-components/src/Card.tsx
  16. 44 47
      packages/react-components/src/CardSummary.tsx
  17. 10 13
      packages/react-components/src/ChainImg.tsx
  18. 9 13
      packages/react-components/src/Chart/Base.tsx
  19. 37 40
      packages/react-components/src/Chart/Doughnut.tsx
  20. 6 10
      packages/react-components/src/Columar.tsx
  21. 39 43
      packages/react-components/src/CopyButton.tsx
  22. 20 23
      packages/react-components/src/CryptoType.tsx
  23. 21 24
      packages/react-components/src/Event.tsx
  24. 6 10
      packages/react-components/src/FilterOverlay.tsx
  25. 103 111
      packages/react-components/src/Forget.tsx
  26. 6 10
      packages/react-components/src/InfoForInput.tsx
  27. 23 27
      packages/react-components/src/InputBalance.tsx
  28. 23 24
      packages/react-components/src/InputConsts/SelectKey.tsx
  29. 13 17
      packages/react-components/src/InputConsts/SelectSection.tsx
  30. 9 13
      packages/react-components/src/InputError.tsx
  31. 21 24
      packages/react-components/src/InputExtrinsic/SelectMethod.tsx
  32. 13 17
      packages/react-components/src/InputExtrinsic/SelectSection.tsx
  33. 22 24
      packages/react-components/src/InputRpc/SelectMethod.tsx
  34. 13 17
      packages/react-components/src/InputRpc/SelectSection.tsx
  35. 23 26
      packages/react-components/src/InputStorage/SelectKey.tsx
  36. 13 17
      packages/react-components/src/InputStorage/SelectSection.tsx
  37. 1 1
      packages/react-components/src/InputTags.tsx
  38. 10 14
      packages/react-components/src/Label.tsx
  39. 24 28
      packages/react-components/src/Labelled.tsx
  40. 17 20
      packages/react-components/src/LinkPolkascan.tsx
  41. 49 51
      packages/react-components/src/Messages.tsx
  42. 12 16
      packages/react-components/src/Nonce.tsx
  43. 26 30
      packages/react-components/src/Output.tsx
  44. 22 25
      packages/react-components/src/Params/Call.tsx
  45. 22 24
      packages/react-components/src/Params/Extrinsic.tsx
  46. 22 23
      packages/react-components/src/Params/Proposal.tsx
  47. 32 35
      packages/react-components/src/Progress.tsx
  48. 63 66
      packages/react-components/src/ProposedAction.tsx
  49. 16 20
      packages/react-components/src/Static.tsx
  50. 109 112
      packages/react-components/src/Status/index.tsx
  51. 6 10
      packages/react-components/src/SummaryBox.tsx
  52. 19 20
      packages/react-components/src/Tabs.tsx
  53. 20 22
      packages/react-components/src/Toggle.tsx
  54. 1 1
      packages/react-components/src/TxButton.tsx

+ 20 - 23
packages/apps/src/SideBar/NodeInfo.tsx

@@ -15,34 +15,31 @@ interface Props extends ApiProps, BareProps {}
 // eslint-disable-next-line @typescript-eslint/no-var-requires
 const pkgJson = require('../../package.json');
 
-class NodeInfo extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { api, className } = this.props;
-    const uiInfo = `apps v${pkgJson.version}`;
+const uiInfo = `apps v${pkgJson.version}`;
 
-    return (
-      <div className={className}>
-        {this.renderNode()}
-        <div>{api.libraryInfo.replace('@polkadot/', '')}</div>
-        <div>{uiInfo}</div>
-      </div>
-    );
+function renderNode ({ isApiReady }: Props): React.ReactNode {
+  if (!isApiReady) {
+    return null;
   }
 
-  private renderNode (): React.ReactNode {
-    const { isApiReady } = this.props;
+  return (
+    <div>
+      <NodeName />&nbsp;
+      <NodeVersion label='v' />
+    </div>
+  );
+}
 
-    if (!isApiReady) {
-      return null;
-    }
+function NodeInfo (props: Props): React.ReactElement<Props> {
+  const { api, className } = props;
 
-    return (
-      <div>
-        <NodeName />&nbsp;
-        <NodeVersion label='v' />
-      </div>
-    );
-  }
+  return (
+    <div className={className}>
+      {renderNode(props)}
+      <div>{api.libraryInfo.replace('@polkadot/', '')}</div>
+      <div>{uiInfo}</div>
+    </div>
+  );
 }
 
 export default withApi(

+ 12 - 16
packages/apps/src/TopBar.tsx

@@ -12,23 +12,19 @@ interface Props {
   className?: string;
 }
 
-class TopBar extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className } = this.props;
-
-    return (
-      <div className={className}>
-        <div>
-          <NodeName />&nbsp;
-          <NodeVersion label='v' />
-        </div>
-        <div>
-          <Chain />&nbsp;
-          <BestNumber label='#' />
-        </div>
+function TopBar ({ className }: Props): React.ReactElement<Props> {
+  return (
+    <div className={className}>
+      <div>
+        <NodeName />&nbsp;
+        <NodeVersion label='v' />
       </div>
-    );
-  }
+      <div>
+        <Chain />&nbsp;
+        <BestNumber label='#' />
+      </div>
+    </div>
+  );
 }
 
 export default styled(TopBar)`

+ 44 - 47
packages/apps/src/overlays/Connecting.tsx

@@ -16,60 +16,57 @@ import BaseOverlay from './Base';
 type Props = I18nProps & ApiProps;
 
 const isFirefox = typeof (window as any).InstallTrigger !== 'undefined';
+const wsUrl = settings.apiUrl;
+const isWs = wsUrl.startsWith('ws://');
+const isWsRemote = wsUrl.includes('127.0.0.1');
+const isHttps = window.location.protocol.startsWith('https:');
 
-class Connecting extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    return this.renderExtension() || this.renderConnecting();
+function renderExtension ({ className, isWaitingInjected, t }: Props): React.ReactNode {
+  if (!isWaitingInjected) {
+    return null;
   }
 
-  private renderExtension (): React.ReactNode {
-    const { className, isWaitingInjected, t } = this.props;
-
-    if (!isWaitingInjected) {
-      return null;
-    }
+  return (
+    <BaseOverlay
+      className={className}
+      icon='puzzle'
+    >
+      <div>{t('Waiting for authorization from the extension. Please open the installed extension and approve or reject access.')}</div>
+    </BaseOverlay>
+  );
+}
 
-    return (
-      <BaseOverlay
-        className={className}
-        icon='puzzle'
-      >
-        <div>{t('Waiting for authorization from the extension. Please open the installed extension and approve or reject access.')}</div>
-      </BaseOverlay>
-    );
+function renderConnecting ({ className, isApiConnected, t }: Props): React.ReactNode {
+  if (isApiConnected) {
+    return null;
   }
 
-  private renderConnecting (): React.ReactNode {
-    const { className, isApiConnected, t } = this.props;
-
-    if (isApiConnected) {
-      return null;
-    }
-
-    const wsUrl = settings.apiUrl;
-    const isWs = wsUrl.startsWith('ws://');
-    const isWsRemote = wsUrl.includes('127.0.0.1');
-    const isHttps = window.location.protocol.startsWith('https:');
+  return (
+    <BaseOverlay
+      className={className}
+      icon='globe'
+    >
+      <div>{t('You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.')}</div>
+      {
+        isFirefox && isWs
+          ? <div>{t('With the Firefox browser connecting to insecure WebSockets ({{wsUrl}}) will fail due to the browser not allowing localhost access from a secure site.', { replace: { wsUrl } })}</div>
+          : undefined
+      }
+      {
+        isWs && isWsRemote && isHttps
+          ? <div>{t('You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure \'wss\' endpoint.', { replace: { wsUrl } })}</div>
+          : undefined
+      }
+    </BaseOverlay>
+  );
+}
 
-    return (
-      <BaseOverlay
-        className={className}
-        icon='globe'
-      >
-        <div>{t('You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.')}</div>
-        {
-          isFirefox && isWs
-            ? <div>{t('With the Firefox browser connecting to insecure WebSockets ({{wsUrl}}) will fail due to the browser not allowing localhost access from a secure site.', { replace: { wsUrl } })}</div>
-            : undefined
-        }
-        {
-          isWs && isWsRemote && isHttps
-            ? <div>{t('You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure \'wss\' endpoint.', { replace: { wsUrl } })}</div>
-            : undefined
-        }
-      </BaseOverlay>
-    );
-  }
+function Connecting (props: Props): React.ReactElement<Props> {
+  return (
+    <>
+      {renderExtension(props) || renderConnecting(props)}
+    </>
+  );
 }
 
 export default withMulti(

+ 18 - 30
packages/react-components/src/ActionItem.tsx

@@ -28,37 +28,25 @@ export const styles = `
   ${proposedActionStyles}
 `;
 
-class ActionItem extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, children, accessory } = this.props;
-
-    return (
-      <Card className={className}>
-        <div className='ui--Row'>
-          <div className='ui--Row-base'>
-            <div className='ui--Row-details'>
-              {this.renderProposal()}
-            </div>
-            {accessory}
+function ActionItem ({ className, children, accessory, idNumber, proposal, expandNested }: Props): React.ReactElement<Props> {
+  return (
+    <Card className={className}>
+      <div className='ui--Row'>
+        <div className='ui--Row-base'>
+          <div className='ui--Row-details'>
+            <ProposedAction
+              idNumber={idNumber}
+              proposal={proposal}
+              withLinks={expandNested}
+              expandNested={expandNested}
+            />
           </div>
-          {children}
+          {accessory}
         </div>
-      </Card>
-    );
-  }
-
-  private renderProposal (): React.ReactNode {
-    const { idNumber, proposal, expandNested } = this.props;
-
-    return (
-      <ProposedAction
-        idNumber={idNumber}
-        proposal={proposal}
-        withLinks={expandNested}
-        expandNested={expandNested}
-      />
-    );
-  }
+        {children}
+      </div>
+    </Card>
+  );
 }
 
-export default withRouter(styled(ActionItem as React.ComponentClass<Props>)`${styles}`);
+export default withRouter(styled(ActionItem)`${styles}`);

+ 19 - 22
packages/react-components/src/AddressCard.tsx

@@ -20,30 +20,27 @@ interface Props extends AddressProps {
   withExplorer?: boolean;
 }
 
-class AddressCard extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, value, withExplorer } = this.props;
-
-    return (
-      <Card className={className}>
-        <AddressRow
-          {...this.props}
-          className='ui--AddressCard-AddressRow'
-        />
-        {withExplorer && (
-          <div className='ui--AddressCard-explorer'>
-            <LinkPolkascan
-              className='ui--AddressCard-exporer-link'
-              data={value}
-            />
-          </div>
-        )}
-      </Card>
-    );
-  }
+function AddressCard (props: Props): React.ReactElement<Props> {
+  const { className, value, withExplorer } = props;
+  return (
+    <Card className={className}>
+      <AddressRow
+        {...props}
+        className='ui--AddressCard-AddressRow'
+      />
+      {withExplorer && (
+        <div className='ui--AddressCard-explorer'>
+          <LinkPolkascan
+            className='ui--AddressCard-exporer-link'
+            data={value}
+          />
+        </div>
+      )}
+    </Card>
+  );
 }
 
-export default translate(styled(AddressCard as React.ComponentClass<Props>)`
+export default translate(styled(AddressCard)`
   display: flex;
   flex-direction: column;
   justify-content: space-between;

+ 195 - 209
packages/react-components/src/AddressInfo.tsx

@@ -49,239 +49,225 @@ type Props = BareProps & I18nProps & {
   withValidatorPrefs?: boolean | ValidatorPrefsType;
 };
 
-class AddressInfo extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className } = this.props;
-
-    return (
-      <div className={className}>
-        <div className='column'>
-          {this.renderBalances()}
-          {this.renderHexSessionId()}
-          {this.renderValidatorPrefs()}
-          {this.renderRewardDestination()}
-        </div>
-
-        {this.renderExtended()}
-        {children && (
-          <div className='column'>
-            {children}
-          </div>
-        )}
-      </div>
-    );
+// either true (filtered above already) or [own, ...all extras bonded funds from nominators]
+function renderBonded ({ staking_info, t }: Props, bonded: true | BN[]): React.ReactNode {
+  let value;
+
+  if (Array.isArray(bonded)) {
+    // Get the sum of funds bonded by any nominator (if available)
+    const extras = bonded.filter((_, index): boolean => index !== 0);
+    const extra = extras.reduce((total, value): BN => total.add(value), new BN(0)).gtn(0)
+      ? `(+${extras.map((bonded): string => formatBalance(bonded)).join(', ')})`
+      : '';
+
+    value = `${formatBalance(bonded[0])} ${extra}`;
+  } else if (staking_info && staking_info.stakingLedger && staking_info.accountId.eq(staking_info.stashId)) {
+    value = formatBalance(staking_info.stakingLedger.active);
   }
 
-  private renderBalances (): React.ReactNode {
-    const { balances_all, staking_info, t, withBalance = true } = this.props;
-    const balanceDisplay = withBalance === true
-      ? { available: true, bonded: true, free: true, redeemable: true, unlocking: true }
-      : withBalance || undefined;
+  return value
+    ? (
+      <>
+        <Label label={t('bonded')} />
+        <div className='result'>{value}</div>
+      </>
+    )
+    : undefined;
+}
 
-    if (!balanceDisplay || !balances_all) {
-      return null;
-    }
+function renderExtended ({ balances_all, t, address, withExtended }: Props): React.ReactNode {
+  const extendedDisplay = withExtended === true
+    ? { crypto: true, nonce: true }
+    : withExtended || undefined;
 
-    return (
-      <>
-        {balanceDisplay.free && (
-          <>
-            <Label label={t('total')} />
-            <div className='result'>{formatBalance(balances_all.freeBalance)}</div>
-          </>
-        )}
-        {balanceDisplay.available && (
-          <>
-            <Label label={t('available')} />
-            <div className='result'>{formatBalance(balances_all.availableBalance)}</div>
-          </>
-        )}
-        {balanceDisplay.bonded && this.renderBonded(balanceDisplay.bonded)}
-        {balanceDisplay.redeemable && staking_info && staking_info.redeemable && staking_info.redeemable.gtn(0) && (
-          <>
-            <Label label={t('redeemable')} />
-            <div className='result'>
-              {formatBalance(staking_info.redeemable)}
-              {this.renderRedeemButton()}
-            </div>
-          </>
-        )}
-        {balanceDisplay.unlocking && staking_info && staking_info.unlocking && (
-          <>
-            <Label label={t('unbonding')} />
-            <div className='result'>
-              {this.renderUnlocking()}
-            </div>
-          </>
-        )}
-    </>
-    );
+  if (!extendedDisplay || !balances_all) {
+    return null;
   }
 
-  // either true (filtered above already) or [own, ...all extras bonded funds from nominators]
-  private renderBonded (bonded: true | BN[]): React.ReactNode {
-    const { staking_info, t } = this.props;
-    let value;
-
-    if (Array.isArray(bonded)) {
-      // Get the sum of funds bonded by any nominator (if available)
-      const extras = bonded.filter((value, index): boolean => index !== 0);
-      const extra = extras.reduce((total, value): BN => total.add(value), new BN(0)).gtn(0)
-        ? `(+${extras.map((bonded): string => formatBalance(bonded)).join(', ')})`
-        : '';
-
-      value = `${formatBalance(bonded[0])} ${extra}`;
-    } else if (staking_info && staking_info.stakingLedger && staking_info.accountId.eq(staking_info.stashId)) {
-      value = formatBalance(staking_info.stakingLedger.active);
-    }
-
-    return value
-      ? (
+  return (
+    <div className='column'>
+      {extendedDisplay.nonce && (
         <>
-          <Label label={t('bonded')} />
-          <div className='result'>{value}</div>
+          <Label label={t('transactions')} />
+          <div className='result'>{formatNumber(balances_all.accountNonce)}</div>
         </>
-      )
-      : undefined;
-  }
-
-  private renderExtended (): React.ReactNode {
-    const { balances_all, t, address, withExtended } = this.props;
-    const extendedDisplay = withExtended === true
-      ? { crypto: true, nonce: true }
-      : withExtended || undefined;
-
-    if (!extendedDisplay || !balances_all) {
-      return null;
-    }
+      )}
+      {extendedDisplay.crypto && (
+        <>
+          <Label label={t('crypto type')} />
+          <CryptoType
+            accountId={address}
+            className='result'
+          />
+        </>
+      )}
+    </div>
+  );
+}
 
-    return (
-      <div className='column'>
-        {extendedDisplay.nonce && (
-          <>
-            <Label label={t('transactions')} />
-            <div className='result'>{formatNumber(balances_all.accountNonce)}</div>
-          </>
-        )}
-        {extendedDisplay.crypto && (
-          <>
-            <Label label={t('crypto type')} />
-            <CryptoType
-              accountId={address}
-              className='result'
-            />
-          </>
-        )}
-      </div>
-    );
+function renderHexSessionId ({ t, withHexSessionId = null }: Props): React.ReactNode {
+  if (!withHexSessionId) {
+    return null;
   }
 
-  private renderHexSessionId (): React.ReactNode {
-    const { t, withHexSessionId = null } = this.props;
+  return (
+    <>
+      <Label label={t('session keys')} />
+      <div className='result'>{withHexSessionId}</div>
+    </>
+  );
+}
 
-    if (!withHexSessionId) {
-      return null;
-    }
+function renderRedeemButton ({ staking_info, t }: Props): React.ReactNode {
+  return (staking_info && staking_info.controllerId && (
+    <TxButton
+      accountId={staking_info.controllerId.toString()}
+      className='iconButton'
+      icon='lock'
+      size='small'
+      isPrimary
+      key='unlock'
+      params={[]}
+      tooltip={t('Redeem these funds')}
+      tx='staking.withdrawUnbonded'
+    />
+  ));
+}
 
-    return (
-      <>
-        <Label label={t('session keys')} />
-        <div className='result'>{withHexSessionId}</div>
-      </>
-    );
+function renderRewardDestination ({ staking_info, withRewardDestination, t }: Props): React.ReactNode {
+  if (!withRewardDestination) {
+    return null;
   }
 
-  private renderRewardDestination (): React.ReactNode {
-    const { staking_info, withRewardDestination, t } = this.props;
+  return (
+    staking_info &&
+    staking_info.rewardDestination &&
+    <>
+      <Label label={t('reward destination')} />
+      <div className='result'>{staking_info.rewardDestination.toString().toLowerCase()}</div>
+    </>
+  );
+}
 
-    if (!withRewardDestination) {
-      return null;
-    }
+function renderUnlocking ({ staking_info, t }: Props): React.ReactNode {
+  return (
+    staking_info &&
+    staking_info.unlocking &&
+    staking_info.unlocking.map(({ remainingBlocks, value }, index): React.ReactNode => (
+      <div key={index}>
+        {formatBalance(value)}
+        <Icon
+          name='info circle'
+          data-tip
+          data-for={`unlocking-trigger-${index}`}
+        />
+        <Tooltip
+          text={t('{{remainingBlocks}} blocks left', { replace: { remainingBlocks } })}
+          trigger={`unlocking-trigger-${index}`}
+        />
+      </div>
+    ))
+  );
+}
 
-    return (
-      staking_info &&
-      staking_info.rewardDestination &&
-      <>
-        <Label label={t('reward destination')} />
-        <div className='result'>{staking_info.rewardDestination.toString().toLowerCase()}</div>
-      </>
-    );
-  }
+function renderValidatorPrefs ({ staking_info, t, withValidatorPrefs = false }: Props): React.ReactNode {
+  const validatorPrefsDisplay = withValidatorPrefs === true
+    ? { unstakeThreshold: true, validatorPayment: true }
+    : withValidatorPrefs;
 
-  private renderRedeemButton (): React.ReactNode {
-    const { staking_info, t } = this.props;
-
-    return (staking_info && staking_info.controllerId && (
-      <TxButton
-        accountId={staking_info.controllerId.toString()}
-        className='iconButton'
-        icon='lock'
-        size='small'
-        isPrimary
-        key='unlock'
-        params={[]}
-        tooltip={t('Redeem these funds')}
-        tx='staking.withdrawUnbonded'
-      />
-    ));
+  if (!validatorPrefsDisplay || !staking_info || !staking_info.validatorPrefs) {
+    return null;
   }
 
-  private renderUnlocking (): React.ReactNode {
-    const { staking_info, t } = this.props;
-
-    return (
-      staking_info &&
-      staking_info.unlocking &&
-      staking_info.unlocking.map(({ remainingBlocks, value }, index): React.ReactNode => (
-        <div key={index}>
-          {formatBalance(value)}
-          <Icon
-            name='info circle'
-            data-tip
-            data-for={`unlocking-trigger-${index}`}
-          />
-          <Tooltip
-            text={t('{{remainingBlocks}} blocks left', { replace: { remainingBlocks } })}
-            trigger={`unlocking-trigger-${index}`}
-          />
-        </div>
-      ))
-    );
+  // start with a spacer
+  return (
+    <>
+      <div className='spacer'></div>
+      {validatorPrefsDisplay.unstakeThreshold && (staking_info.validatorPrefs as ValidatorPrefs0to145).unstakeThreshold && (
+        <>
+          <Label label={t('unstake threshold')} />
+          <div className='result'>
+            {(staking_info.validatorPrefs as ValidatorPrefs0to145).unstakeThreshold.toString()}
+          </div>
+        </>
+      )}
+      {validatorPrefsDisplay.validatorPayment && staking_info.validatorPrefs.validatorPayment && (
+        <>
+          <Label label={t('commision')} />
+          <div className='result'>{
+            formatBalance(staking_info.validatorPrefs.validatorPayment)
+          }</div>
+        </>
+      )}
+    </>
+  );
+}
+
+function renderBalances (props: Props): React.ReactNode {
+  const { balances_all, staking_info, t, withBalance = true } = props;
+  const balanceDisplay = withBalance === true
+    ? { available: true, bonded: true, free: true, redeemable: true, unlocking: true }
+    : withBalance || undefined;
+
+  if (!balanceDisplay || !balances_all) {
+    return null;
   }
 
-  private renderValidatorPrefs (): React.ReactNode {
-    const { staking_info, t, withValidatorPrefs = false } = this.props;
-    const validatorPrefsDisplay = withValidatorPrefs === true
-      ? { unstakeThreshold: true, validatorPayment: true }
-      : withValidatorPrefs;
+  return (
+    <>
+      {balanceDisplay.free && (
+        <>
+          <Label label={t('total')} />
+          <div className='result'>{formatBalance(balances_all.freeBalance)}</div>
+        </>
+      )}
+      {balanceDisplay.available && (
+        <>
+          <Label label={t('available')} />
+          <div className='result'>{formatBalance(balances_all.availableBalance)}</div>
+        </>
+      )}
+      {balanceDisplay.bonded && renderBonded(props, balanceDisplay.bonded)}
+      {balanceDisplay.redeemable && staking_info && staking_info.redeemable && staking_info.redeemable.gtn(0) && (
+        <>
+          <Label label={t('redeemable')} />
+          <div className='result'>
+            {formatBalance(staking_info.redeemable)}
+            {renderRedeemButton(props)}
+          </div>
+        </>
+      )}
+      {balanceDisplay.unlocking && staking_info && staking_info.unlocking && (
+        <>
+          <Label label={t('unbonding')} />
+          <div className='result'>
+            {renderUnlocking(props)}
+          </div>
+        </>
+      )}
+  </>
+  );
+}
 
-    if (!validatorPrefsDisplay || !staking_info || !staking_info.validatorPrefs) {
-      return null;
-    }
+function AddressInfo (props: Props): React.ReactElement<Props> {
+  const { className, children } = props;
+  return (
+    <div className={className}>
+      <div className='column'>
+        {renderBalances(props)}
+        {renderHexSessionId(props)}
+        {renderValidatorPrefs(props)}
+        {renderRewardDestination(props)}
+      </div>
 
-    // start with a spacer
-    return (
-      <>
-        <div className='spacer'></div>
-        {validatorPrefsDisplay.unstakeThreshold && (staking_info.validatorPrefs as ValidatorPrefs0to145).unstakeThreshold && (
-          <>
-            <Label label={t('unstake threshold')} />
-            <div className='result'>
-              {(staking_info.validatorPrefs as ValidatorPrefs0to145).unstakeThreshold.toString()}
-            </div>
-          </>
-        )}
-        {validatorPrefsDisplay.validatorPayment && staking_info.validatorPrefs.validatorPayment && (
-          <>
-            <Label label={t('commision')} />
-            <div className='result'>{
-              formatBalance(staking_info.validatorPrefs.validatorPayment)
-            }</div>
-          </>
-        )}
-      </>
-    );
-  }
+      {renderExtended(props)}
+      {children && (
+        <div className='column'>
+          {children}
+        </div>
+      )}
+    </div>
+  );
 }
 
 export default withMulti(

+ 68 - 76
packages/react-components/src/AddressMini.tsx

@@ -29,94 +29,86 @@ interface Props extends BareProps {
   withBonded?: boolean;
 }
 
-class AddressMini extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, iconInfo, isPadded = true, style, value } = this.props;
+function renderAddressOrName ({ isShort = true, withAddress = true, type }: Props, address: string): React.ReactNode {
+  if (!withAddress) {
+    return null;
+  }
 
-    if (!value) {
-      return null;
-    }
+  const name = getAddressName(address, type);
+
+  return (
+    <div className={`ui--AddressMini-address ${name ? 'withName' : 'withAddr'}`}>{
+      name || (
+        isShort
+          ? toShortAddress(address)
+          : address
+      )
+    }</div>
+  );
+}
 
-    const address = value.toString();
-
-    return (
-      <div
-        className={classes('ui--AddressMini', isPadded ? 'padded' : '', className)}
-        style={style}
-      >
-        <div className='ui--AddressMini-info'>
-          {this.renderAddressOrName(address)}
-          {children}
-        </div>
-        <div className='ui--AddressMini-icon'>
-          <IdentityIcon
-            size={24}
-            value={address}
-          />
-          {iconInfo && (
-            <div className='ui--AddressMini-icon-info'>
-              {iconInfo}
-            </div>
-          )}
-        </div>
-        <div className='ui--AddressMini-balances'>
-          {this.renderBalance()}
-          {this.renderBonded()}
-        </div>
-      </div>
-    );
+function renderBalance ({ balance, value, withBalance = false }: Props): React.ReactNode {
+  if (!withBalance || !value) {
+    return null;
   }
 
-  private renderAddressOrName (address: string): React.ReactNode {
-    const { isShort = true, withAddress = true, type } = this.props;
-
-    if (!withAddress) {
-      return null;
-    }
+  return (
+    <BalanceDisplay
+      balance={balance}
+      params={value}
+    />
+  );
+}
 
-    const name = getAddressName(address, type);
-
-    return (
-      <div className={`ui--AddressMini-address ${name ? 'withName' : 'withAddr'}`}>{
-        name || (
-          isShort
-            ? toShortAddress(address)
-            : address
-        )
-      }</div>
-    );
+function renderBonded ({ bonded, value, withBonded = false }: Props): React.ReactNode {
+  if (!withBonded || !value) {
+    return null;
   }
 
-  private renderBalance (): React.ReactNode {
-    const { balance, value, withBalance = false } = this.props;
+  return (
+    <BondedDisplay
+      bonded={bonded}
+      label=''
+      params={value}
+    />
+  );
+}
 
-    if (!withBalance || !value) {
-      return null;
-    }
+function AddressMini (props: Props): React.ReactElement<Props> | null {
+  const { children, className, iconInfo, isPadded = true, style, value } = props;
 
-    return (
-      <BalanceDisplay
-        balance={balance}
-        params={value}
-      />
-    );
+  if (!value) {
+    return null;
   }
 
-  private renderBonded (): React.ReactNode {
-    const { bonded, value, withBonded = false } = this.props;
-
-    if (!withBonded || !value) {
-      return null;
-    }
+  const address = value.toString();
 
-    return (
-      <BondedDisplay
-        bonded={bonded}
-        label=''
-        params={value}
-      />
-    );
-  }
+  return (
+    <div
+      className={classes('ui--AddressMini', isPadded ? 'padded' : '', className)}
+      style={style}
+    >
+      <div className='ui--AddressMini-info'>
+        {renderAddressOrName(props, address)}
+        {children}
+      </div>
+      <div className='ui--AddressMini-icon'>
+        <IdentityIcon
+          size={24}
+          value={address}
+        />
+        {iconInfo && (
+          <div className='ui--AddressMini-icon-info'>
+            {iconInfo}
+          </div>
+        )}
+      </div>
+      <div className='ui--AddressMini-balances'>
+        {renderBalance(props)}
+        {renderBonded(props)}
+      </div>
+    </div>
+  );
 }
 
 export default styled(AddressMini)`

+ 1 - 1
packages/react-components/src/AddressRow.tsx

@@ -269,7 +269,7 @@ export {
 };
 
 export default withMulti(
-  styled(AddressRow as React.ComponentClass<Props>)`
+  styled(AddressRow as React.ComponentClass<Props, State>)`
     ${styles}
   `,
   translate,

+ 12 - 16
packages/react-components/src/Available.tsx

@@ -15,21 +15,17 @@ export interface Props extends BareProps {
   params?: AccountId | AccountIndex | Address | string | Uint8Array | null;
 }
 
-export default class AvailableDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { params, className, label, style } = this.props;
-
-    if (!params) {
-      return null;
-    }
-
-    return (
-      <Available
-        className={classes('ui--Available', className)}
-        label={label}
-        params={params}
-        style={style}
-      />
-    );
+export default function AvailableDisplay ({ params, className, label, style }: Props): React.ReactElement<Props> | null {
+  if (!params) {
+    return null;
   }
+
+  return (
+    <Available
+      className={classes('ui--Available', className)}
+      label={label}
+      params={params}
+      style={style}
+    />
+  );
 }

+ 33 - 32
packages/react-components/src/Balance.tsx

@@ -19,46 +19,47 @@ export interface Props extends BareProps {
   withLabel?: boolean;
 }
 
-export default class BalanceDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { balance, className, label, params, style } = this.props;
+function renderProvided ({ balance, className, label, style }: Props): React.ReactNode {
+  let value = `${formatBalance(Array.isArray(balance) ? balance[0] : balance)}`;
 
-    if (!params) {
-      return null;
-    }
+  if (Array.isArray(balance)) {
+    const totals = balance.filter((_, index): boolean => index !== 0);
+    const total = totals.reduce((total, value): BN => total.add(value), new BN(0)).gtn(0)
+      ? `(+${totals.map((balance): string => formatBalance(balance)).join(', ')})`
+      : '';
 
-    return balance
-      ? this.renderProvided()
-      : (
-        <Balance
-          className={classes('ui--Balance', className)}
-          label={label}
-          params={params}
-          style={style}
-        />
-      );
+    value = `${value}  ${total}`;
   }
 
-  private renderProvided (): React.ReactNode {
-    const { balance, className, label, style } = this.props;
-    let value = `${formatBalance(Array.isArray(balance) ? balance[0] : balance)}`;
+  return (
+    <div
+      className={classes('ui--Balance', className)}
+      style={style}
+    >
+      {label}{value}
+    </div>
+  );
+}
 
-    if (Array.isArray(balance)) {
-      const totals = balance.filter((value, index): boolean => index !== 0);
-      const total = totals.reduce((total, value): BN => total.add(value), new BN(0)).gtn(0)
-        ? `(+${totals.map((balance): string => formatBalance(balance)).join(', ')})`
-        : '';
+export default function BalanceDisplay (props: Props): React.ReactElement<Props> | null {
+  const { balance, className, label, params, style } = props;
 
-      value = `${value}  ${total}`;
-    }
+  if (!params) {
+    return null;
+  }
 
-    return (
-      <div
+  return balance
+    ? (
+      <>
+        {renderProvided(props)}
+      </>
+    )
+    : (
+      <Balance
         className={classes('ui--Balance', className)}
+        label={label}
+        params={params}
         style={style}
-      >
-        {label}{value}
-      </div>
+      />
     );
-  }
 }

+ 33 - 32
packages/react-components/src/Bonded.tsx

@@ -19,46 +19,47 @@ export interface Props extends BareProps {
   withLabel?: boolean;
 }
 
-export default class BondedDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { bonded, params, className, label, style } = this.props;
+function renderProvided ({ bonded, className, label, style }: Props): React.ReactNode {
+  let value = `${formatBalance(Array.isArray(bonded) ? bonded[0] : bonded)}`;
 
-    if (!params) {
-      return null;
-    }
+  if (Array.isArray(bonded)) {
+    const totals = bonded.filter((_, index): boolean => index !== 0);
+    const total = totals.reduce((total, value): BN => total.add(value), new BN(0)).gtn(0)
+      ? `(+${totals.map((bonded): string => formatBalance(bonded)).join(', ')})`
+      : '';
 
-    return bonded
-      ? this.renderProvided()
-      : (
-        <Bonded
-          className={classes('ui--Bonded', className)}
-          label={label}
-          params={params}
-          style={style}
-        />
-      );
+    value = `${value}  ${total}`;
   }
 
-  private renderProvided (): React.ReactNode {
-    const { bonded, className, label, style } = this.props;
-    let value = `${formatBalance(Array.isArray(bonded) ? bonded[0] : bonded)}`;
+  return (
+    <div
+      className={classes('ui--Bonded', className)}
+      style={style}
+    >
+      {label}{value}
+    </div>
+  );
+}
 
-    if (Array.isArray(bonded)) {
-      const totals = bonded.filter((value, index): boolean => index !== 0);
-      const total = totals.reduce((total, value): BN => total.add(value), new BN(0)).gtn(0)
-        ? `(+${totals.map((bonded): string => formatBalance(bonded)).join(', ')})`
-        : '';
+export default function BondedDisplay (props: Props): React.ReactElement<Props> | null {
+  const { bonded, params, className, label, style } = props;
 
-      value = `${value}  ${total}`;
-    }
+  if (!params) {
+    return null;
+  }
 
-    return (
-      <div
+  return bonded
+    ? (
+        <>
+          {renderProvided(props)}
+        </>
+    )
+    : (
+      <Bonded
         className={classes('ui--Bonded', className)}
+        label={label}
+        params={params}
         style={style}
-      >
-        {label}{value}
-      </div>
+      />
     );
-  }
 }

+ 15 - 19
packages/react-components/src/Bubble.tsx

@@ -20,25 +20,21 @@ interface Props extends BareProps {
   label?: React.ReactNode;
 }
 
-class Bubble extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { color, children, className, icon, label } = this.props;
-
-    return (
-      <SUILabel
-        className={classes(`theme--${settings.uiTheme}`, 'ui--Bubble', className)}
-        color={color}
-      >
-        <div className='ui--Bubble-header'>
-          {icon && <Icon name={icon} size='large' />}
-          {label && <div className='text'>{label}</div>}
-        </div>
-        <div className='ui--Bubble-children'>
-          {children}
-        </div>
-      </SUILabel>
-    );
-  }
+function Bubble ({ color, children, className, icon, label }: Props): React.ReactElement<Props> {
+  return (
+    <SUILabel
+      className={classes(`theme--${settings.uiTheme}`, 'ui--Bubble', className)}
+      color={color}
+    >
+      <div className='ui--Bubble-header'>
+        {icon && <Icon name={icon} size='large' />}
+        {label && <div className='text'>{label}</div>}
+      </div>
+      <div className='ui--Bubble-children'>
+        {children}
+      </div>
+    </SUILabel>
+  );
 }
 
 export default styled(Bubble)`

+ 12 - 16
packages/react-components/src/Button/Or.tsx

@@ -14,22 +14,18 @@ interface Props extends I18nProps {
   text?: string;
 }
 
-class ButtonOr extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, style, t, text } = this.props;
-
-    return (
-      <SUIButton.Or
-        className={className}
-        style={style}
-        label={
-          isUndefined(text)
-            ? t('or')
-            : text
-        }
-      />
-    );
-  }
+function ButtonOr ({ className, style, t, text }: Props): React.ReactElement<Props> {
+  return (
+    <SUIButton.Or
+      className={className}
+      style={style}
+      label={
+        isUndefined(text)
+          ? t('or')
+          : text
+      }
+    />
+  );
 }
 
 export default translate(ButtonOr);

+ 61 - 64
packages/react-components/src/Call.tsx

@@ -33,71 +33,68 @@ const Wrapper = styled.div`
   }
 `;
 
-class Call extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, style, mortality, tip, value, withHash, t } = this.props;
-    const params = GenericCall.filterOrigin(value.meta).map(({ name, type }): { name: string; type: TypeDef } => ({
-      name: name.toString(),
-      type: getTypeDef(type.toString())
-    }));
-    const values = value.args.map((value): { isValid: boolean; value: Codec } => ({
-      isValid: true,
-      value
-    }));
-    const hash = withHash
-      ? (value as IExtrinsic).hash
-      : null;
+function Call ({ children, className, style, mortality, tip, value, withHash, t }: Props): React.ReactElement<Props> {
+  const params = GenericCall.filterOrigin(value.meta).map(({ name, type }): { name: string; type: TypeDef } => ({
+    name: name.toString(),
+    type: getTypeDef(type.toString())
+  }));
+  const values = value.args.map((value): { isValid: boolean; value: Codec } => ({
+    isValid: true,
+    value
+  }));
+  const hash = withHash
+    ? (value as IExtrinsic).hash
+    : null;
 
-    return (
-      <Wrapper
-        className={classes('ui--Extrinsic', className)}
-        style={style}
-      >
-        {children}
-        {
-          hash
-            ? (
-              <Static
-                className='hash'
-                label={t('extrinsic hash')}
-              >
-                {hash.toHex()}
-              </Static>
-            )
-            : null
-        }
-        {
-          mortality
-            ? (
-              <Static
-                className='mortality'
-                label={t('lifetime')}
-              >
-                {mortality}
-              </Static>
-            )
-            : null
-        }
-        {
-          (tip && tip.gtn(0))
-            ? (
-              <Static
-                className='tip'
-                label={t('tip')}
-              >
-                {formatBalance(tip)}
-              </Static>
-            )
-            : null
-        }
-        <Params
-          isDisabled
-          params={params}
-          values={values}
-        />
-      </Wrapper>
-    );
-  }
+  return (
+    <Wrapper
+      className={classes('ui--Extrinsic', className)}
+      style={style}
+    >
+      {children}
+      {
+        hash
+          ? (
+            <Static
+              className='hash'
+              label={t('extrinsic hash')}
+            >
+              {hash.toHex()}
+            </Static>
+          )
+          : null
+      }
+      {
+        mortality
+          ? (
+            <Static
+              className='mortality'
+              label={t('lifetime')}
+            >
+              {mortality}
+            </Static>
+          )
+          : null
+      }
+      {
+        (tip && tip.gtn(0))
+          ? (
+            <Static
+              className='tip'
+              label={t('tip')}
+            >
+              {formatBalance(tip)}
+            </Static>
+          )
+          : null
+      }
+      <Params
+        isDisabled
+        params={params}
+        values={values}
+      />
+    </Wrapper>
+  );
 }
 
 export default translate(Call);

+ 6 - 10
packages/react-components/src/Card.tsx

@@ -15,16 +15,12 @@ interface Props {
   withBottomMargin?: boolean;
 }
 
-class Card extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, isError, isSuccess, withBottomMargin } = this.props;
-
-    return (
-      <article className={classes('ui--Card', className, (isError && !isSuccess) && 'error', (!isError && isSuccess) && 'success', withBottomMargin && 'withBottomMargin')}>
-        {children}
-      </article>
-    );
-  }
+function Card ({ children, className, isError, isSuccess, withBottomMargin }: Props): React.ReactElement<Props> {
+  return (
+    <article className={classes('ui--Card', className, (isError && !isSuccess) && 'error', (!isError && isSuccess) && 'success', withBottomMargin && 'withBottomMargin')}>
+      {children}
+    </article>
+  );
 }
 
 export default styled(Card)`

+ 44 - 47
packages/react-components/src/CardSummary.tsx

@@ -28,54 +28,51 @@ interface Props extends BareProps {
   progress?: ProgressProps;
 }
 
-class CardSummary extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, help, label, progress } = this.props;
-    const value = progress && progress.value;
-    const total = progress && progress.total;
-    const left = progress && !isUndefined(value) && !isUndefined(total) && value.gten(0) && total.gtn(0)
-      ? (
-        value.gt(total)
-          ? `>${
-            progress.isPercent
-              ? '100'
-              : formatNumber(total)
-          }`
-          : (
-            progress.isPercent
-              ? value.muln(100).div(total).toString()
-              : formatNumber(value)
-          )
-      )
-      : undefined;
-
-    if (progress && isUndefined(left)) {
-      return null;
-    }
-
-    return (
-      <article className={className}>
-        <Labelled
-          help={help}
-          isSmall
-          label={label}
-        >
-          {children}{
-            progress && !progress.hideValue && (
-              !left || isUndefined(progress.total)
-                ? '-'
-                : `${left}${progress.isPercent ? '' : '/'}${
-                  progress.isPercent
-                    ? '%'
-                    : formatNumber(progress.total)
-                }`
-            )
-          }
-          {progress && <Progress {...progress} />}
-        </Labelled>
-      </article>
-    );
+function CardSummary ({ children, className, help, label, progress }: Props): React.ReactElement<Props> | null {
+  const value = progress && progress.value;
+  const total = progress && progress.total;
+  const left = progress && !isUndefined(value) && !isUndefined(total) && value.gten(0) && total.gtn(0)
+    ? (
+      value.gt(total)
+        ? `>${
+          progress.isPercent
+            ? '100'
+            : formatNumber(total)
+        }`
+        : (
+          progress.isPercent
+            ? value.muln(100).div(total).toString()
+            : formatNumber(value)
+        )
+    )
+    : undefined;
+
+  if (progress && isUndefined(left)) {
+    return null;
   }
+
+  return (
+    <article className={className}>
+      <Labelled
+        help={help}
+        isSmall
+        label={label}
+      >
+        {children}{
+          progress && !progress.hideValue && (
+            !left || isUndefined(progress.total)
+              ? '-'
+              : `${left}${progress.isPercent ? '' : '/'}${
+                progress.isPercent
+                  ? '%'
+                  : formatNumber(progress.total)
+              }`
+          )
+        }
+        {progress && <Progress {...progress} />}
+      </Labelled>
+    </article>
+  );
 }
 
 export default styled(CardSummary)`

+ 10 - 13
packages/react-components/src/ChainImg.tsx

@@ -55,20 +55,17 @@ interface Props extends ApiProps {
   onClick?: () => any;
 }
 
-class ChainImg extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, injectedLogoChain, injectedLogoNode, logo = '', onClick } = this.props;
-    const img = LOGOS[logo] || injectedLogoChain || injectedLogoNode || EMPTY;
+function ChainImg ({ className, injectedLogoChain, injectedLogoNode, logo = '', onClick }: Props): React.ReactElement<Props> {
+  const img = LOGOS[logo] || injectedLogoChain || injectedLogoNode || EMPTY;
 
-    return (
-      <img
-        alt='chain logo'
-        className={className}
-        onClick={onClick}
-        src={img}
-      />
-    );
-  }
+  return (
+    <img
+      alt='chain logo'
+      className={className}
+      onClick={onClick}
+      src={img}
+    />
+  );
 }
 
 export default withMulti(

+ 9 - 13
packages/react-components/src/Chart/Base.tsx

@@ -21,17 +21,13 @@ const Wrapper = styled.div`
   width: 15vw;
 `;
 
-export default class BaseChart extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, style } = this.props;
-
-    return (
-      <Wrapper
-        className={classes('ui--Chart', className)}
-        style={style}
-      >
-        {children}
-      </Wrapper>
-    );
-  }
+export default function BaseChart ({ children, className, style }: Props): React.ReactElement<Props> {
+  return (
+    <Wrapper
+      className={classes('ui--Chart', className)}
+      style={style}
+    >
+      {children}
+    </Wrapper>
+  );
 }

+ 37 - 40
packages/react-components/src/Chart/Doughnut.tsx

@@ -29,46 +29,43 @@ interface Options {
   labels: string[];
 }
 
-export default class ChartDoughnut extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, size = 100, style, values } = this.props;
-    const options: Options = {
-      colorNormal: [],
-      colorHover: [],
-      data: [],
-      labels: []
-    };
+export default function ChartDoughnut ({ className, size = 100, style, values }: Props): React.ReactElement<Props> {
+  const options: Options = {
+    colorNormal: [],
+    colorHover: [],
+    data: [],
+    labels: []
+  };
 
-    // FIXME Classic case of kicking the can down the road, i.e. don't expend energy
-    // when stuff are not used. This was replaced by the HorizBar as the only Chart
-    // in actual use (by Referendum). However the below is not optimal, and gets re-
-    // calculated on each render. If this component is put back in use, look at
-    // getDerivedStateFromProps in HorizBar (the logic is the same for chartData)
-    values.forEach(({ colors: [normalColor = '#00f', hoverColor], label, value }): void => {
-      options.colorNormal.push(normalColor);
-      options.colorHover.push(hoverColor || normalColor);
-      options.data.push(bnToBn(value).toNumber());
-      options.labels.push(label);
-    });
+  // FIXME Classic case of kicking the can down the road, i.e. don't expend energy
+  // when stuff are not used. This was replaced by the HorizBar as the only Chart
+  // in actual use (by Referendum). However the below is not optimal, and gets re-
+  // calculated on each render. If this component is put back in use, look at
+  // getDerivedStateFromProps in HorizBar (the logic is the same for chartData)
+  values.forEach(({ colors: [normalColor = '#00f', hoverColor], label, value }): void => {
+    options.colorNormal.push(normalColor);
+    options.colorHover.push(hoverColor || normalColor);
+    options.data.push(bnToBn(value).toNumber());
+    options.labels.push(label);
+  });
 
-    return (
-      <Base
-        className={className}
-        style={style}
-      >
-        <Doughnut
-          data={{
-            labels: options.labels,
-            datasets: [{
-              data: options.data,
-              backgroundColor: options.colorNormal,
-              hoverBackgroundColor: options.colorHover
-            }]
-          }}
-          height={size}
-          width={size}
-        />
-      </Base>
-    );
-  }
+  return (
+    <Base
+      className={className}
+      style={style}
+    >
+      <Doughnut
+        data={{
+          labels: options.labels,
+          datasets: [{
+            data: options.data,
+            backgroundColor: options.colorNormal,
+            hoverBackgroundColor: options.colorHover
+          }]
+        }}
+        height={size}
+        width={size}
+      />
+    </Base>
+  );
 }

+ 6 - 10
packages/react-components/src/Columar.tsx

@@ -10,16 +10,12 @@ interface Props {
   className?: string;
 }
 
-class Columar extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className } = this.props;
-
-    return (
-      <div className={`ui--Columnar ${className}`}>
-        {children}
-      </div>
-    );
-  }
+function Columar ({ children, className }: Props): React.ReactElement<Props> {
+  return (
+    <div className={`ui--Columnar ${className}`}>
+      {children}
+    </div>
+  );
 }
 
 export default styled(Columar)`

+ 39 - 43
packages/react-components/src/CopyButton.tsx

@@ -24,35 +24,8 @@ type InnerProps = Props & I18nProps & {
   queueAction: QueueAction$Add;
 };
 
-class CopyButtonInner extends React.PureComponent<InnerProps> {
-  public render (): React.ReactNode {
-    const { children, className, icon = 'copy', value } = this.props;
-
-    return (
-      <div className={className}>
-        <CopyToClipboard
-          onCopy={this.onCopy}
-          text={value}
-        >
-          <div>
-            {children}
-            <span className='copySpan'>
-              <Button
-                className='iconButton'
-                icon={icon}
-                size='mini'
-                isPrimary
-              />
-            </span>
-          </div>
-        </CopyToClipboard>
-      </div>
-    );
-  }
-
-  private onCopy = (): void => {
-    const { isAddress = false, queueAction, t, value } = this.props;
-
+function onCopy ({ isAddress = false, queueAction, t, value }: InnerProps): () => void {
+  return (): void => {
     if (isAddress && queueAction) {
       queueAction({
         account: value,
@@ -61,24 +34,47 @@ class CopyButtonInner extends React.PureComponent<InnerProps> {
         message: t('address copied')
       });
     }
-  }
+  };
+}
+
+function CopyButtonInner (props: InnerProps): React.ReactElement<InnerProps> {
+  const { children, className, icon = 'copy', value } = props;
+
+  return (
+    <div className={className}>
+      <CopyToClipboard
+        onCopy={onCopy(props)}
+        text={value}
+      >
+        <div>
+          {children}
+          <span className='copySpan'>
+            <Button
+              className='iconButton'
+              icon={icon}
+              size='mini'
+              isPrimary
+            />
+          </span>
+        </div>
+      </CopyToClipboard>
+    </div>
+  );
 }
 
 const CopyButtonI18n = translate(CopyButtonInner);
 
-class CopyButton extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    return (
-      <QueueConsumer>
-        {({ queueAction }): React.ReactNode => (
-          <CopyButtonI18n
-            {...this.props}
-            queueAction={queueAction}
-          />
-        )}
-      </QueueConsumer>
-    );
-  }
+function CopyButton (props: Props): React.ReactElement<Props> {
+  return (
+    <QueueConsumer>
+      {({ queueAction }): React.ReactNode => (
+        <CopyButtonI18n
+          {...props}
+          queueAction={queueAction}
+        />
+      )}
+    </QueueConsumer>
+  );
 }
 
 export default styled(CopyButton)`

+ 20 - 23
packages/react-components/src/CryptoType.tsx

@@ -15,29 +15,26 @@ interface Props extends BareProps {
   label?: string;
 }
 
-export default class CryptoType extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { accountId, className, label = '' } = this.props;
-    let type = 'unknown';
-
-    try {
-      const current = accountId
-        ? keyring.getPair(accountId.toString())
-        : null;
-
-      if (current) {
-        type = current.meta.isInjected
-          ? 'injected'
-          : current.type;
-      }
-    } catch (error) {
-      // cannot determine, keep unknown
+export default function CryptoType ({ accountId, className, label = '' }: Props): React.ReactElement<Props> {
+  let type = 'unknown';
+
+  try {
+    const current = accountId
+      ? keyring.getPair(accountId.toString())
+      : null;
+
+    if (current) {
+      type = current.meta.isInjected
+        ? 'injected'
+        : current.type;
     }
-
-    return (
-      <div className={classes('ui--CryptoType', className)}>
-        {label}{type}
-      </div>
-    );
+  } catch (error) {
+    // cannot determine, keep unknown
   }
+
+  return (
+    <div className={classes('ui--CryptoType', className)}>
+      {label}{type}
+    </div>
+  );
 }

+ 21 - 24
packages/react-components/src/Event.tsx

@@ -17,29 +17,26 @@ export interface Props extends BareProps {
   value: Event;
 }
 
-export default class EventDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, style, value } = this.props;
-    const params = value.typeDef.map(({ type }): { type: TypeDef } => ({
-      type: getTypeDef(type)
-    }));
-    const values = value.data.map((value): { isValid: boolean; value: Codec } => ({
-      isValid: true,
-      value
-    }));
+export default function EventDisplay ({ children, className, style, value }: Props): React.ReactElement<Props> {
+  const params = value.typeDef.map(({ type }): { type: TypeDef } => ({
+    type: getTypeDef(type)
+  }));
+  const values = value.data.map((value): { isValid: boolean; value: Codec } => ({
+    isValid: true,
+    value
+  }));
 
-    return (
-      <div
-        className={classes('ui--Event', className)}
-        style={style}
-      >
-        {children}
-        <Params
-          isDisabled
-          params={params}
-          values={values}
-        />
-      </div>
-    );
-  }
+  return (
+    <div
+      className={classes('ui--Event', className)}
+      style={style}
+    >
+      {children}
+      <Params
+        isDisabled
+        params={params}
+        values={values}
+      />
+    </div>
+  );
 }

+ 6 - 10
packages/react-components/src/FilterOverlay.tsx

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

+ 103 - 111
packages/react-components/src/Forget.tsx

@@ -19,124 +19,116 @@ interface Props extends I18nProps {
   onForget: () => void;
 }
 
-class Forget extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { onClose } = this.props;
-    return (
-      <Modal
-        className='app--accounts-Modal'
-        dimmer='inverted'
-        onClose={onClose}
-        open
-      >
-        <Modal.Header>
-          {this.headerText()}
-        </Modal.Header>
-        <Modal.Content>
-          {this.renderContent()}
-        </Modal.Content>
-        {this.renderButtons()}
-      </Modal>
-    );
+function getContent ({ mode = 'account', t }: Props): React.ReactNode {
+  switch (mode) {
+    case 'account':
+      return (
+        <>
+          <p>{t('You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.')}</p>
+          <p>{t('This operaion does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.')}</p>
+        </>
+      );
+    case 'address':
+      return (
+        <>
+          <p>{t('You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.')}</p>
+          <p>{t('This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.')}</p>
+        </>
+      );
+    case 'contract':
+      return (
+        <>
+          <p>{t('You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract\'s address in the Instantiate tab.')}</p>
+          <p>{t('This operaion does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.')}</p>
+        </>
+      );
+    case 'code':
+      return (
+        <>
+          <p>{t('You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.')}</p>
+          <p>{t('This operaion does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.')}</p>
+        </>
+      );
   }
+}
 
-  private headerText = (): string => {
-    const { mode = 'account', t } = this.props;
-    switch (mode) {
-      case 'account':
-        return t('Confirm account removal');
-      case 'address':
-        return t('Confirm address removal');
-      case 'contract':
-        return t('Confirm contract removal');
-      case 'code':
-        return t('Confirm code removal');
-    }
-  }
-
-  private content = (): React.ReactNode => {
-    const { mode = 'account', t } = this.props;
-
-    switch (mode) {
-      case 'account':
-        return (
-          <>
-            <p>{t('You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.')}</p>
-            <p>{t('This operaion does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.')}</p>
-          </>
-        );
-      case 'address':
-        return (
-          <>
-            <p>{t('You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.')}</p>
-            <p>{t('This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.')}</p>
-          </>
-        );
-      case 'contract':
-        return (
-          <>
-            <p>{t('You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract\'s address in the Instantiate tab.')}</p>
-            <p>{t('This operaion does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.')}</p>
-          </>
-        );
-      case 'code':
-        return (
-          <>
-            <p>{t('You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.')}</p>
-            <p>{t('This operaion does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.')}</p>
-          </>
-        );
-    }
+function getHeaderText ({ mode = 'account', t }: Props): string {
+  switch (mode) {
+    case 'account':
+      return t('Confirm account removal');
+    case 'address':
+      return t('Confirm address removal');
+    case 'contract':
+      return t('Confirm contract removal');
+    case 'code':
+      return t('Confirm code removal');
   }
+}
 
-  private renderButtons (): React.ReactNode {
-    const { onClose, onForget, t } = this.props;
+function renderButtons ({ onClose, onForget, t }: Props): React.ReactNode {
+  return (
+    <Modal.Actions>
+      <Button.Group>
+        <Button
+          isNegative
+          onClick={onClose}
+          label={t('Cancel')}
+        />
+        <Button.Or />
+        <Button
+          isPrimary
+          onClick={onForget}
+          label={t('Forget')}
+        />
+      </Button.Group>
+    </Modal.Actions>
+  );
+}
 
-    return (
-      <Modal.Actions>
-        <Button.Group>
-          <Button
-            isNegative
-            onClick={onClose}
-            label={t('Cancel')}
-          />
-          <Button.Or />
-          <Button
-            isPrimary
-            onClick={onForget}
-            label={t('Forget')}
-          />
-        </Button.Group>
-      </Modal.Actions>
-    );
+function renderContent (props: Props): React.ReactNode {
+  const { address, code, mode = 'account' } = props;
+  switch (mode) {
+    case 'account':
+    case 'address':
+    case 'contract':
+      return (
+        <AddressRow
+          isInline
+          value={address || ''}
+        >
+          {getContent(props)}
+        </AddressRow>
+      );
+    case 'code':
+      return (
+        <CodeRow
+          isInline
+          code={code || ''}
+        >
+          {getContent(props)}
+        </CodeRow>
+      );
   }
+}
 
-  private renderContent (): React.ReactNode {
-    const { address, code, mode = 'account' } = this.props;
-
-    switch (mode) {
-      case 'account':
-      case 'address':
-      case 'contract':
-        return (
-          <AddressRow
-            isInline
-            value={address || ''}
-          >
-            {this.content()}
-          </AddressRow>
-        );
-      case 'code':
-        return (
-          <CodeRow
-            isInline
-            code={code || ''}
-          >
-            {this.content()}
-          </CodeRow>
-        );
-    }
-  }
+function Forget (props: Props): React.ReactElement<Props> {
+  const { onClose } = props;
+  return (
+    <Modal
+      className='app--accounts-Modal'
+      dimmer='inverted'
+      onClose={onClose}
+      open
+    >
+      <Modal.Header>
+        {getHeaderText(props)}
+      </Modal.Header>
+      <Modal.Content>
+        {renderContent(props)}
+      </Modal.Content>
+      {renderButtons(props)}
+    </Modal>
+  );
 }
 
 export default translate(Forget);

+ 6 - 10
packages/react-components/src/InfoForInput.tsx

@@ -35,14 +35,10 @@ const Wrapper = styled.div`
   }
 `;
 
-export default class InfoForInput extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className, type = 'info' } = this.props;
-
-    return (
-      <Labelled>
-        <Wrapper className={classes(className, type)}>{children}</Wrapper>
-      </Labelled>
-    );
-  }
+export default function InfoForInput ({ children, className, type = 'info' }: Props): React.ReactElement<Props> {
+  return (
+    <Labelled>
+      <Wrapper className={classes(className, type)}>{children}</Wrapper>
+    </Labelled>
+  );
 }

+ 23 - 27
packages/react-components/src/InputBalance.tsx

@@ -28,31 +28,27 @@ interface Props extends BareProps {
 
 const DEFAULT_BITLENGTH = BitLengthOption.CHAIN_SPEC as BitLength;
 
-export default class InputBalance extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { autoFocus, className, defaultValue, help, isDisabled, isError, label, maxValue, onChange, onEnter, placeholder, style, value, withEllipsis, withLabel, withMax } = this.props;
-
-    return (
-      <InputNumber
-        autoFocus={autoFocus}
-        className={className}
-        bitLength={DEFAULT_BITLENGTH}
-        defaultValue={defaultValue}
-        help={help}
-        isDisabled={isDisabled}
-        isError={isError}
-        isSi
-        label={label}
-        maxValue={maxValue}
-        onChange={onChange}
-        onEnter={onEnter}
-        placeholder={placeholder}
-        style={style}
-        value={value}
-        withEllipsis={withEllipsis}
-        withLabel={withLabel}
-        withMax={withMax}
-      />
-    );
-  }
+export default function InputBalance ({ autoFocus, className, defaultValue, help, isDisabled, isError, label, maxValue, onChange, onEnter, placeholder, style, value, withEllipsis, withLabel, withMax }: Props): React.ReactElement<Props> {
+  return (
+    <InputNumber
+      autoFocus={autoFocus}
+      className={className}
+      bitLength={DEFAULT_BITLENGTH}
+      defaultValue={defaultValue}
+      help={help}
+      isDisabled={isDisabled}
+      isError={isError}
+      isSi
+      label={label}
+      maxValue={maxValue}
+      onChange={onChange}
+      onEnter={onEnter}
+      placeholder={placeholder}
+      style={style}
+      value={value}
+      withEllipsis={withEllipsis}
+      withLabel={withLabel}
+      withMax={withMax}
+    />
+  );
 }

+ 23 - 24
packages/react-components/src/InputConsts/SelectKey.tsx

@@ -20,37 +20,36 @@ type Props = ApiProps & BareProps & {
   value: ConstValueBase;
 };
 
-class SelectKey extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, isError, onChange, options, style, value } = this.props;
-
-    if (!options.length) {
-      return null;
-    }
-
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Items', className)}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        transform={this.transform}
-        value={value.method}
-        withLabel={false}
-      />
-    );
-  }
-
-  private transform = (method: string): ConstValueBase => {
-    const { value } = this.props;
+function transform ({ value }: Props): (method: string) => ConstValueBase {
+  return (method: string): ConstValueBase => {
     const section = value.section;
 
     return {
       method,
       section
     };
+  };
+}
+
+function SelectKey (props: Props): React.ReactElement<Props> | null {
+  const { className, isError, onChange, options, style, value } = props;
+
+  if (!options.length) {
+    return null;
   }
+
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Items', className)}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      transform={transform(props)}
+      value={value.method}
+      withLabel={false}
+    />
+  );
 }
 
 export default withApi(SelectKey);

+ 13 - 17
packages/react-components/src/InputConsts/SelectSection.tsx

@@ -20,21 +20,17 @@ interface Props extends BareProps {
   value: ConstValueBase;
 }
 
-export default class SelectSection extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, defaultValue, isError, onChange, options, style, value: { section } } = this.props;
-
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Sections', className)}
-        defaultValue={defaultValue}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        value={section}
-        withLabel={false}
-      />
-    );
-  }
+export default function SelectSection ({ className, defaultValue, isError, onChange, options, style, value: { section } }: Props): React.ReactElement<Props> {
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Sections', className)}
+      defaultValue={defaultValue}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      value={section}
+      withLabel={false}
+    />
+  );
 }

+ 9 - 13
packages/react-components/src/InputError.tsx

@@ -17,17 +17,13 @@ const defaultLabel: React.ReactNode = (
   <div>&nbsp;</div>
 );
 
-export default class InputError extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, label = defaultLabel, style } = this.props;
-
-    return (
-      <div
-        className={classes('ui--InputError', className)}
-        style={style}
-      >
-        <Label color='red' pointing='left'>{label}</Label>
-      </div>
-    );
-  }
+export default function InputError ({ className, label = defaultLabel, style }: Props): React.ReactElement<Props> {
+  return (
+    <div
+      className={classes('ui--InputError', className)}
+      style={style}
+    >
+      <Label color='red' pointing='left'>{label}</Label>
+    </div>
+  );
 }

+ 21 - 24
packages/react-components/src/InputExtrinsic/SelectMethod.tsx

@@ -20,31 +20,28 @@ interface Props extends BareProps {
   value: CallFunction;
 }
 
-export default class SelectMethod extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, isError, onChange, options, style, value } = this.props;
-
-    if (!options.length) {
-      return null;
-    }
+function transform ({ api, value }: Props): (method: string) => CallFunction {
+  return (method: string): CallFunction => {
+    return api.tx[value.section][method];
+  };
+}
 
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Items', className)}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        transform={this.transform}
-        value={value.method}
-        withLabel={false}
-      />
-    );
+export default function SelectMethod (props: Props): React.ReactElement<Props> | null {
+  const { className, isError, onChange, options, style, value } = props;
+  if (!options.length) {
+    return null;
   }
 
-  private transform = (method: string): CallFunction => {
-    const { api, value } = this.props;
-
-    return api.tx[value.section][method];
-  }
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Items', className)}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      transform={transform(props)}
+      value={value.method}
+      withLabel={false}
+    />
+  );
 }

+ 13 - 17
packages/react-components/src/InputExtrinsic/SelectSection.tsx

@@ -19,21 +19,17 @@ interface Props extends BareProps {
   value: CallFunction;
 }
 
-export default class SelectSection extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, defaultValue, isError, onChange, options, style, value } = this.props;
-
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Sections', className)}
-        defaultValue={defaultValue}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        value={value.section}
-        withLabel={false}
-      />
-    );
-  }
+export default function SelectSection ({ className, defaultValue, isError, onChange, options, style, value }: Props): React.ReactElement<Props> {
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Sections', className)}
+      defaultValue={defaultValue}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      value={value.section}
+      withLabel={false}
+    />
+  );
 }

+ 22 - 24
packages/react-components/src/InputRpc/SelectMethod.tsx

@@ -20,31 +20,29 @@ interface Props extends BareProps {
   value: RpcMethod;
 }
 
-export default class SelectMethod extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, isError, onChange, options, style, value } = this.props;
-
-    if (!options.length) {
-      return null;
-    }
-
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Items', className)}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        transform={this.transform}
-        value={value.method}
-        withLabel={false}
-      />
-    );
-  }
+function transform ({ value }: Props): (method: string) => RpcMethod {
+  return function (method: string): RpcMethod {
+    return map[value.section].methods[method];
+  };
+}
 
-  private transform = (method: string): RpcMethod => {
-    const { value } = this.props;
+export default function SelectMethod (props: Props): React.ReactElement<Props> | null {
+  const { className, isError, onChange, options, style, value } = props;
 
-    return map[value.section].methods[method];
+  if (!options.length) {
+    return null;
   }
+
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Items', className)}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      transform={transform(props)}
+      value={value.method}
+      withLabel={false}
+    />
+  );
 }

+ 13 - 17
packages/react-components/src/InputRpc/SelectSection.tsx

@@ -19,21 +19,17 @@ interface Props extends BareProps {
   value: RpcMethod;
 }
 
-export default class SelectSection extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, defaultValue, isError, onChange, options, style, value } = this.props;
-
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Sections', className)}
-        defaultValue={defaultValue}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        value={value.section}
-        withLabel={false}
-      />
-    );
-  }
+export default function SelectSection ({ className, defaultValue, isError, onChange, options, style, value }: Props): React.ReactElement<Props> {
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Sections', className)}
+      defaultValue={defaultValue}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      value={value.section}
+      withLabel={false}
+    />
+  );
 }

+ 23 - 26
packages/react-components/src/InputStorage/SelectKey.tsx

@@ -20,36 +20,33 @@ type Props = ApiProps & BareProps & {
   value: StorageEntryPromise;
 };
 
-class SelectKey extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, isError, onChange, options, style, value } = this.props;
-
-    if (!options.length) {
-      return null;
-    }
-
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Items', className)}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        transform={this.transform}
-        value={value.creator.method}
-        withLabel={false}
-      />
-    );
-  }
-
-  private transform = (method: string): StorageEntryPromise => {
-    const { api, value } = this.props;
-
-    // We should not get to the fallback, but ... https://github.com/polkadot-js/apps/issues/1375
+function transform ({ api, value }: Props): (method: string) => StorageEntryPromise {
+  return function (method: string): StorageEntryPromise {
     return api.query[value.creator.section]
       ? api.query[value.creator.section][method]
       : value;
+  };
+}
+
+function SelectKey (props: Props): React.ReactElement<Props> | null {
+  const { className, isError, onChange, options, style, value } = props;
+
+  if (!options.length) {
+    return null;
   }
+
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Items', className)}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      transform={transform(props)}
+      value={value.creator.method}
+      withLabel={false}
+    />
+  );
 }
 
 export default withApi(SelectKey);

+ 13 - 17
packages/react-components/src/InputStorage/SelectSection.tsx

@@ -19,21 +19,17 @@ interface Props extends BareProps {
   value: StorageEntryPromise;
 }
 
-export default class SelectSection extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, defaultValue, isError, onChange, options, style, value: { creator: { section } } } = this.props;
-
-    return (
-      <Dropdown
-        className={classes('ui--DropdownLinked-Sections', className)}
-        defaultValue={defaultValue}
-        isError={isError}
-        onChange={onChange}
-        options={options}
-        style={style}
-        value={section}
-        withLabel={false}
-      />
-    );
-  }
+export default function SelectSection ({ className, defaultValue, isError, onChange, options, style, value: { creator: { section } } }: Props): React.ReactElement<Props> {
+  return (
+    <Dropdown
+      className={classes('ui--DropdownLinked-Sections', className)}
+      defaultValue={defaultValue}
+      isError={isError}
+      onChange={onChange}
+      options={options}
+      style={style}
+      value={section}
+      withLabel={false}
+    />
+  );
 }

+ 1 - 1
packages/react-components/src/InputTags.tsx

@@ -46,7 +46,7 @@ function saveTags (tags: string[]): void {
 
 const tags = loadTags();
 
-export default class InputTags extends React.PureComponent<Props> {
+export default class InputTags extends React.PureComponent<Props, State> {
   public state: State = {
     options: tags.map((value): { key: string; text: string; value: string } => ({
       key: value, text: value, value

+ 10 - 14
packages/react-components/src/Label.tsx

@@ -14,18 +14,14 @@ interface Props extends BareProps {
   withEllipsis?: boolean;
 }
 
-export default class Label extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, help, label, withEllipsis } = this.props;
-
-    return (
-      <label className={className}>
-        {
-          withEllipsis
-            ? <div className='withEllipsis'>{label}</div>
-            : label
-        }{help && <LabelHelp help={help} />}
-      </label>
-    );
-  }
+export default function Label ({ className, help, label, withEllipsis }: Props): React.ReactElement<Props> {
+  return (
+    <label className={className}>
+      {
+        withEllipsis
+          ? <div className='withEllipsis'>{label}</div>
+          : label
+      }{help && <LabelHelp help={help} />}
+    </label>
+  );
 }

+ 24 - 28
packages/react-components/src/Labelled.tsx

@@ -116,35 +116,31 @@ const Wrapper = styled.div`
   }
 `;
 
-export default class Labelled extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, children, help, isSmall, isHidden, label = defaultLabel, labelExtra, style, withEllipsis, withLabel = true } = this.props;
-
-    if (isHidden) {
-      return null;
-    } else if (!withLabel) {
-      return (
-        <div className={className}>{children}</div>
-      );
-    }
-
+export default function Labelled ({ className, children, help, isSmall, isHidden, label = defaultLabel, labelExtra, style, withEllipsis, withLabel = true }: Props): React.ReactElement<Props> | null {
+  if (isHidden) {
+    return null;
+  } else if (!withLabel) {
     return (
-      <Wrapper
-        className={classes('ui--Labelled', isSmall ? 'label-small' : '', className)}
-        style={style}
-      >
-        <label>
-          {
-            withEllipsis
-              ? <div className='withEllipsis'>{label}</div>
-              : label
-          }{help && <LabelHelp help={help} />}
-        </label>
-        {labelExtra && <div className='labelExtra'>{labelExtra}</div>}
-        <div className='ui--Labelled-content'>
-          {children}
-        </div>
-      </Wrapper>
+      <div className={className}>{children}</div>
     );
   }
+
+  return (
+    <Wrapper
+      className={classes('ui--Labelled', isSmall ? 'label-small' : '', className)}
+      style={style}
+    >
+      <label>
+        {
+          withEllipsis
+            ? <div className='withEllipsis'>{label}</div>
+            : label
+        }{help && <LabelHelp help={help} />}
+      </label>
+      {labelExtra && <div className='labelExtra'>{labelExtra}</div>}
+      <div className='ui--Labelled-content'>
+        {children}
+      </div>
+    </Wrapper>
+  );
 }

+ 17 - 20
packages/react-components/src/LinkPolkascan.tsx

@@ -33,28 +33,25 @@ const TYPES: Record<string, string> = {
   extrinsic: '/system/extrinsic/'
 };
 
-class LinkPolkascan extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, currentChain, data, t, type } = this.props;
-    const extChain = CHAINS[currentChain];
-    const extType = TYPES[type];
+function LinkPolkascan ({ className, currentChain, data, t, type }: Props): React.ReactElement<Props> | null {
+  const extChain = CHAINS[currentChain];
+  const extType = TYPES[type];
 
-    if (!extChain || !extType) {
-      return null;
-    }
-
-    return (
-      <div className={className}>
-        <a
-          href={`${BASE}${extChain}${extType}${data}`}
-          rel='noopener noreferrer'
-          target='_blank'
-        >
-          {t('View this {{type}} on Polkascan.io', { replace: { type } })}
-        </a>
-      </div>
-    );
+  if (!extChain || !extType) {
+    return null;
   }
+
+  return (
+    <div className={className}>
+      <a
+        href={`${BASE}${extChain}${extType}${data}`}
+        rel='noopener noreferrer'
+        target='_blank'
+      >
+        {t('View this {{type}} on Polkascan.io', { replace: { type } })}
+      </a>
+    </div>
+  );
 }
 
 export default withMulti(

+ 49 - 51
packages/react-components/src/Messages.tsx

@@ -21,57 +21,9 @@ export interface Props extends I18nProps {
   onSelect?: (callAddress?: string, callMethod?: string) => void;
 }
 
-class Messages extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, contractAbi: { abi: { messages } }, isLabelled, isRemovable, onRemove = (): void => { /* . */ }, onSelect, t } = this.props;
-
-    return (
-      <div className={classes(className, 'ui--Messages', isLabelled && 'labelled', onSelect && 'select')}>
-        {messages.map((_, index): React.ReactNode => {
-          return this.renderMessage(index);
-        })}
-        {isRemovable && (
-          <Button
-            className='iconButton'
-            icon='remove'
-            onClick={onRemove}
-            size='tiny'
-            isNegative
-            tooltip={t('Remove ABI')}
-          />
-        )}
-      </div>
-    );
-  }
-
-  private renderMessage (index: number): React.ReactNode {
-    const { contractAbi: { abi: { messages } }, onSelect } = this.props;
-
-    if (!messages[index]) {
-      return null;
-    }
-
-    const { args, name, return_type: returnType } = messages[index];
-
-    return (
-      <Button
-        key={name}
-        className={classes('message', !onSelect && 'exempt-hover')}
-        isDisabled={!onSelect}
-        onClick={this.onSelect(index)}
-        isPrimary={!!onSelect}
-      >
-        {name}
-        (
-        {args.map(({ name, type }): string => `${name}: ${type}`).join(', ')}
-        )
-        {returnType && `: ${returnType}`}
-      </Button>
-    );
-  }
-
-  private onSelect = (index: number): () => void => (): void => {
-    const { address: callAddress, contractAbi: { abi: { messages } }, onSelect } = this.props;
+function onSelect (props: Props, index: number): () => void {
+  return function (): void {
+    const { address: callAddress, contractAbi: { abi: { messages } }, onSelect } = props;
 
     if (!callAddress || !messages || !messages[index]) {
       return;
@@ -80,7 +32,53 @@ class Messages extends React.PureComponent<Props> {
     const { name: callMethod } = messages[index];
 
     onSelect && onSelect(callAddress, callMethod);
+  };
+}
+
+function renderMessage (props: Props, index: number): React.ReactNode {
+  const { contractAbi: { abi: { messages } }, onSelect: onSelectProp } = props;
+  if (!messages[index]) {
+    return null;
   }
+
+  const { args, name, return_type: returnType } = messages[index];
+
+  return (
+    <Button
+      key={name}
+      className={classes('message', !onSelect && 'exempt-hover')}
+      isDisabled={!onSelectProp}
+      onClick={onSelect(props, index)}
+      isPrimary={!!onSelectProp}
+    >
+      {name}
+      (
+      {args.map(({ name, type }): string => `${name}: ${type}`).join(', ')}
+      )
+      {returnType && `: ${returnType}`}
+    </Button>
+  );
+}
+
+function Messages (props: Props): React.ReactElement<Props> {
+  const { className, contractAbi: { abi: { messages } }, isLabelled, isRemovable, onRemove = (): void => { /* . */ }, onSelect, t } = props;
+  return (
+    <div className={classes(className, 'ui--Messages', isLabelled && 'labelled', onSelect && 'select')}>
+      {messages.map((_, index): React.ReactNode => {
+        return renderMessage(props, index);
+      })}
+      {isRemovable && (
+        <Button
+          className='iconButton'
+          icon='remove'
+          onClick={onRemove}
+          size='tiny'
+          isNegative
+          tooltip={t('Remove ABI')}
+        />
+      )}
+    </div>
+  );
 }
 
 export default translate(styled(Messages)`

+ 12 - 16
packages/react-components/src/Nonce.tsx

@@ -15,21 +15,17 @@ export interface Props extends BareProps {
   params?: AccountId | AccountIndex | Address | string | Uint8Array | null;
 }
 
-export default class NonceDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, label, params, style } = this.props;
-
-    if (!params) {
-      return null;
-    }
-
-    return (
-      <Nonce
-        className={classes('ui--Nonce', className)}
-        label={label}
-        params={params.toString()}
-        style={style}
-      />
-    );
+export default function NonceDisplay ({ className, label, params, style }: Props): React.ReactElement<Props> | null {
+  if (!params) {
+    return null;
   }
+
+  return (
+    <Nonce
+      className={classes('ui--Nonce', className)}
+      label={label}
+      params={params.toString()}
+      style={style}
+    />
+  );
 }

+ 26 - 30
packages/react-components/src/Output.tsx

@@ -22,34 +22,30 @@ interface Props extends BareProps {
   withLabel?: boolean;
 }
 
-export default class Output extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, children, help, isError, isHidden, isMonospace, label, style, value, withCopy = false, withLabel } = this.props;
-
-    return (
-      <Labelled
-        className={className}
-        help={help}
-        isHidden={isHidden}
-        label={label}
-        style={style}
-        withLabel={withLabel}
-      >
-        <div className={classes('ui--output', isError && 'error', isMonospace && 'monospace')}>
-          {value}
-          {children}
-          {
-            withCopy
-              ? (
-                <CopyButton
-                  className='ui--output-button'
-                  value={value}
-                />
-              )
-              : null
-          }
-        </div>
-      </Labelled>
-    );
-  }
+export default function Output ({ className, children, help, isError, isHidden, isMonospace, label, style, value, withCopy = false, withLabel }: Props): React.ReactElement<Props> {
+  return (
+    <Labelled
+      className={className}
+      help={help}
+      isHidden={isHidden}
+      label={label}
+      style={style}
+      withLabel={withLabel}
+    >
+      <div className={classes('ui--output', isError && 'error', isMonospace && 'monospace')}>
+        {value}
+        {children}
+        {
+          withCopy
+            ? (
+              <CopyButton
+                className='ui--output-button'
+                value={value}
+              />
+            )
+            : null
+        }
+      </div>
+    </Labelled>
+  );
 }

+ 22 - 25
packages/react-components/src/Params/Call.tsx

@@ -13,32 +13,29 @@ import Extrinsic from './Extrinsic';
 
 type Props = ApiProps & BaseProps;
 
-class Call extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { apiDefaultTx, api, className, isDisabled, isError, label, onChange, onEnter, style, withLabel } = this.props;
-    const defaultValue = ((): SubmittableExtrinsicFunction<'promise'> => {
-      try {
-        return api.tx.balances.transfer;
-      } catch (error) {
-        return apiDefaultTx;
-      }
-    })();
+function Call ({ apiDefaultTx, api, className, isDisabled, isError, label, onChange, onEnter, style, withLabel }: Props): React.ReactElement<Props> {
+  const defaultValue = ((): SubmittableExtrinsicFunction<'promise'> => {
+    try {
+      return api.tx.balances.transfer;
+    } catch (error) {
+      return apiDefaultTx;
+    }
+  })();
 
-    return (
-      <Extrinsic
-        className={className}
-        defaultValue={defaultValue}
-        isDisabled={isDisabled}
-        isError={isError}
-        isPrivate={false}
-        label={label}
-        onChange={onChange}
-        onEnter={onEnter}
-        style={style}
-        withLabel={withLabel}
-      />
-    );
-  }
+  return (
+    <Extrinsic
+      className={className}
+      defaultValue={defaultValue}
+      isDisabled={isDisabled}
+      isError={isError}
+      isPrivate={false}
+      label={label}
+      onChange={onChange}
+      onEnter={onEnter}
+      style={style}
+      withLabel={withLabel}
+    />
+  );
 }
 
 export default withApi(Call);

+ 22 - 24
packages/react-components/src/Params/Extrinsic.tsx

@@ -22,32 +22,30 @@ interface Props extends BareProps {
   withLabel?: boolean;
 }
 
-export default class ExtrinsicDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, defaultValue, isDisabled, isError, isPrivate, label, onEnter, style, withLabel } = this.props;
-
-    return (
-      <BaseExtrinsic
-        className={className}
-        defaultValue={defaultValue}
-        isDisabled={isDisabled}
-        isError={isError}
-        isPrivate={isPrivate}
-        label={label}
-        onChange={this.onChange}
-        onEnter={onEnter}
-        style={style}
-        withLabel={withLabel}
-      />
-    );
-  }
-
-  private onChange = (method: Call): void => {
-    const { onChange } = this.props;
-
+function onChange ({ onChange }: Props): (_: Call) => void {
+  return function (method: Call): void {
     onChange && onChange({
       isValid: !!method,
       value: method
     });
-  }
+  };
+}
+
+export default function ExtrinsicDisplay (props: Props): React.ReactElement<Props> {
+  const { className, defaultValue, isDisabled, isError, isPrivate, label, onEnter, style, withLabel } = props;
+
+  return (
+    <BaseExtrinsic
+      className={className}
+      defaultValue={defaultValue}
+      isDisabled={isDisabled}
+      isError={isError}
+      isPrivate={isPrivate}
+      label={label}
+      onChange={onChange(props)}
+      onEnter={onEnter}
+      style={style}
+      withLabel={withLabel}
+    />
+  );
 }

+ 22 - 23
packages/react-components/src/Params/Proposal.tsx

@@ -13,28 +13,8 @@ import ExtrinsicDisplay from './Extrinsic';
 
 type Props = ApiProps & BaseProps;
 
-class ProposalDisplay extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { apiDefaultTxSudo, className, isDisabled, isError, label, onEnter, style, withLabel } = this.props;
-
-    return (
-      <ExtrinsicDisplay
-        className={className}
-        defaultValue={apiDefaultTxSudo}
-        isDisabled={isDisabled}
-        isError={isError}
-        isPrivate
-        label={label}
-        onChange={this.onChange}
-        onEnter={onEnter}
-        style={style}
-        withLabel={withLabel}
-      />
-    );
-  }
-
-  private onChange = ({ isValid, value }: RawParam): void => {
-    const { onChange } = this.props;
+function onChange ({ onChange }: Props): (_: RawParam) => void {
+  return function ({ isValid, value }: RawParam): void {
     let proposal = null;
 
     if (isValid && value) {
@@ -45,7 +25,26 @@ class ProposalDisplay extends React.PureComponent<Props> {
       isValid,
       value: proposal
     });
-  }
+  };
+}
+
+function ProposalDisplay (props: Props): React.ReactElement<Props> {
+  const { apiDefaultTxSudo, className, isDisabled, isError, label, onEnter, style, withLabel } = props;
+
+  return (
+    <ExtrinsicDisplay
+      className={className}
+      defaultValue={apiDefaultTxSudo}
+      isDisabled={isDisabled}
+      isError={isError}
+      isPrivate
+      label={label}
+      onChange={onChange(props)}
+      onEnter={onEnter}
+      style={style}
+      withLabel={withLabel}
+    />
+  );
 }
 
 export default withApi(ProposalDisplay);

+ 32 - 35
packages/react-components/src/Progress.tsx

@@ -22,45 +22,42 @@ interface Props extends BareProps {
   value?: UInt | BN | number;
 }
 
-export default class Progress extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, color = 'blue', percent, total, style, value } = this.props;
-    let calculated: number | undefined;
-    const _total = bnToBn(total);
-    const _value = bnToBn(value);
-
-    if (_total.gtn(0)) {
-      calculated = 100.0 * _value.toNumber() / _total.toNumber();
-    } else {
-      calculated = isBn(percent) ? percent.toNumber() : percent;
-    }
+export default function Progress ({ className, color = 'blue', percent, total, style, value }: Props): React.ReactElement<Props> | null {
+  let calculated: number | undefined;
+  const _total = bnToBn(total);
+  const _value = bnToBn(value);
+
+  if (_total.gtn(0)) {
+    calculated = 100.0 * _value.toNumber() / _total.toNumber();
+  } else {
+    calculated = isBn(percent) ? percent.toNumber() : percent;
+  }
 
-    if (isUndefined(calculated) || calculated < 0) {
-      return null;
-    }
+  if (isUndefined(calculated) || calculated < 0) {
+    return null;
+  }
 
-    let rainbow: BaseColors;
+  let rainbow: BaseColors;
 
-    if (color === 'auto' || color === 'autoReverse') {
-      if (calculated > 66.6) {
-        rainbow = color === 'auto' ? 'green' : 'red';
-      } else if (calculated > 33.3) {
-        rainbow = 'orange';
-      } else {
-        rainbow = color === 'auto' ? 'red' : 'green';
-      }
+  if (color === 'auto' || color === 'autoReverse') {
+    if (calculated > 66.6) {
+      rainbow = color === 'auto' ? 'green' : 'red';
+    } else if (calculated > 33.3) {
+      rainbow = 'orange';
     } else {
-      rainbow = color;
+      rainbow = color === 'auto' ? 'red' : 'green';
     }
-
-    return (
-      <SUIProgress
-        className={classes('ui--Progress', className)}
-        color={rainbow}
-        percent={calculated}
-        size='tiny'
-        style={style}
-      />
-    );
+  } else {
+    rainbow = color;
   }
+
+  return (
+    <SUIProgress
+      className={classes('ui--Progress', className)}
+      color={rainbow}
+      percent={calculated}
+      size='tiny'
+      style={style}
+    />
+  );
 }

+ 63 - 66
packages/react-components/src/ProposedAction.tsx

@@ -40,82 +40,79 @@ export const styles = `
   }
 `;
 
-class ProposedAction extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { asInset, insetProps, isCollapsible, proposal, withLinks, expandNested } = this.props;
+function ProposedAction (props: Props): React.ReactElement<Props> {
+  const { asInset, insetProps, isCollapsible, proposal, withLinks, expandNested } = props;
+  const idNumber = typeof props.idNumber === 'string'
+    ? props.idNumber
+    : formatNumber(props.idNumber);
 
-    const idNumber = typeof this.props.idNumber === 'string'
-      ? this.props.idNumber
-      : formatNumber(this.props.idNumber);
+  if (!proposal) {
+    return (
+      <h3>#{idNumber}</h3>
+    );
+  }
 
-    if (!proposal) {
-      return (
-        <h3>#{idNumber}</h3>
-      );
-    }
+  const { meta, method, section } = GenericCall.findFunction(proposal.callIndex);
 
-    const { meta, method, section } = GenericCall.findFunction(proposal.callIndex);
+  const header = `#${idNumber}: ${section}.${method}`;
+  const documentation = (meta && meta.documentation)
+    ? (
+      <summary>{meta.documentation.join(' ')}</summary>
+    )
+    : null;
+  const params = (isTreasuryProposalVote(proposal) && expandNested) ? (
+    <TreasuryProposal
+      className='ui--ProposedAction-extrinsic'
+      asInset={withLinks}
+      insetProps={{
+        withTopMargin: true,
+        withBottomMargin: true,
+        ...(withLinks ? { href: '/treasury' } : {})
+      }}
+      proposalId={proposal.args[0].toString()}
+    />
+  ) : (
+    <Call
+      className='ui--ProposedAction-extrinsic'
+      value={proposal}
+    />
+  );
 
-    const header = `#${idNumber}: ${section}.${method}`;
-    const documentation = (meta && meta.documentation)
-      ? (
-        <summary>{meta.documentation.join(' ')}</summary>
-      )
-      : null;
-    const params = (isTreasuryProposalVote(proposal) && expandNested) ? (
-      <TreasuryProposal
-        className='ui--ProposedAction-extrinsic'
-        asInset={withLinks}
-        insetProps={{
-          withTopMargin: true,
-          withBottomMargin: true,
-          ...(withLinks ? { href: '/treasury' } : {})
-        }}
-        proposalId={proposal.args[0].toString()}
-      />
-    ) : (
-      <Call
-        className='ui--ProposedAction-extrinsic'
-        value={proposal}
-      />
+  if (asInset) {
+    return (
+      <Inset
+        header={header}
+        isCollapsible
+        {...insetProps}
+      >
+        <>
+          {documentation}
+          {params}
+        </>
+      </Inset>
     );
+  }
 
-    if (asInset) {
-      return (
-        <Inset
-          header={header}
-          isCollapsible
-          {...insetProps}
-        >
-          <>
+  return (
+    <>
+      <h3>{header}</h3>
+      {isCollapsible
+        ? (
+          <details>
             {documentation}
             {params}
-          </>
-        </Inset>
-      );
-    }
-
-    return (
-      <>
-        <h3>{header}</h3>
-        {isCollapsible
-          ? (
+          </details>
+        )
+        : (
+          <>
             <details>
               {documentation}
-              {params}
             </details>
-          )
-          : (
-            <>
-              <details>
-                {documentation}
-              </details>
-              {params}
-            </>
-          )}
-      </>
-    );
-  }
+            {params}
+          </>
+        )}
+    </>
+  );
 }
 
-export default styled(ProposedAction as React.ComponentClass<Props>)`${styles}`;
+export default styled(ProposedAction)`${styles}`;

+ 16 - 20
packages/react-components/src/Static.tsx

@@ -20,24 +20,20 @@ interface Props extends BareProps {
   withLabel?: boolean;
 }
 
-export default class Static extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, children, defaultValue, help, isHidden, label, style, value, withLabel } = this.props;
-
-    return (
-      <Labelled
-        className={className}
-        help={help}
-        isHidden={isHidden}
-        label={label}
-        style={style}
-        withLabel={withLabel}
-      >
-        <div className='ui--Static ui dropdown selection disabled'>
-          {value || defaultValue}
-          {children}
-        </div>
-      </Labelled>
-    );
-  }
+export default function Static ({ className, children, defaultValue, help, isHidden, label, style, value, withLabel }: Props): React.ReactElement<Props> {
+  return (
+    <Labelled
+      className={className}
+      help={help}
+      isHidden={isHidden}
+      label={label}
+      style={style}
+      withLabel={withLabel}
+    >
+      <div className='ui--Static ui dropdown selection disabled'>
+        {value || defaultValue}
+        {children}
+      </div>
+    </Labelled>
+  );
 }

+ 109 - 112
packages/react-components/src/Status/index.tsx

@@ -89,140 +89,137 @@ const Wrapper = styled.div`
   }
 `;
 
-class Status extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { stqueue = [], txqueue = [] } = this.props;
-    const allst: QueueStatus[] = stqueue.filter(({ isCompleted }): boolean => !isCompleted);
-    const alltx: QueueTx[] = txqueue.filter(({ status }): boolean =>
-      !['completed', 'incomplete'].includes(status)
-    );
-
-    if (!allst.length && !alltx.length) {
-      return null;
-    }
+function iconName (status: string): any {
+  switch (status) {
+    case 'error':
+      return 'ban';
+
+    case 'event':
+      return 'assistive listening devices';
 
-    return (
-      <Wrapper className='ui--Status'>
-        {alltx.map(this.renderItem)}
-        {allst.map(this.renderStatus)}
-      </Wrapper>
-    );
+    case 'received':
+      return 'telegram plane';
+
+    default:
+      return 'check';
   }
+}
+
+function signerIconName (status: QueueTxStatus): any {
+  switch (status) {
+    case 'cancelled':
+      return 'ban';
+
+    case 'completed':
+    case 'finalized':
+    case 'sent':
+      return 'check';
+
+    case 'dropped':
+    case 'invalid':
+    case 'usurped':
+      return 'arrow down';
+
+    case 'error':
+      return 'warning sign';
 
-  private renderStatus = ({ account, action, id, message, removeItem, status }: QueueStatus): React.ReactNode => {
-    const addressRendered = account
-      ? <AddressMini value={account} />
-      : undefined;
-
-    return (
-      <div
-        className={classes('item', status)}
-        onClick={removeItem}
-        key={id}
-      >
-        <div className='wrapper'>
-          <div className='container'>
-            <div className='desc'>
-              <div className='header'>
-                {action}
-              </div>
-              {addressRendered}
-              <div className='status'>
-                {message}
-              </div>
+    case 'queued':
+      return 'random';
+
+    default:
+      return 'spinner';
+  }
+}
+
+function renderStatus ({ account, action, id, message, removeItem, status }: QueueStatus): React.ReactNode {
+  const addressRendered = account
+    ? <AddressMini value={account} />
+    : undefined;
+
+  return (
+    <div
+      className={classes('item', status)}
+      onClick={removeItem}
+      key={id}
+    >
+      <div className='wrapper'>
+        <div className='container'>
+          <div className='desc'>
+            <div className='header'>
+              {action}
             </div>
-            <div className='short'>
-              <Icon name={this.iconName(status)} />
+            {addressRendered}
+            <div className='status'>
+              {message}
             </div>
           </div>
+          <div className='short'>
+            <Icon name={iconName(status)} />
+          </div>
         </div>
       </div>
-    );
-  }
+    </div>
+  );
+}
 
-  private renderItem = ({ id, extrinsic, error, removeItem, rpc, status }: QueueTx): React.ReactNode => {
-    let { method, section } = rpc;
+function renderItem ({ id, extrinsic, error, removeItem, rpc, status }: QueueTx): React.ReactNode {
+  let { method, section } = rpc;
 
-    if (extrinsic) {
-      const found = GenericCall.findFunction(extrinsic.callIndex);
+  if (extrinsic) {
+    const found = GenericCall.findFunction(extrinsic.callIndex);
 
-      if (found.section !== 'unknown') {
-        method = found.method;
-        section = found.section;
-      }
+    if (found.section !== 'unknown') {
+      method = found.method;
+      section = found.section;
     }
+  }
 
-    const icon = this.signerIconName(status);
-
-    return (
-      <div
-        className={classes('item', status)}
-        onClick={removeItem}
-        key={id}
-      >
-        <div className='wrapper'>
-          <div className='container'>
-            <div className='desc'>
-              <div className='header'>
-                {section}.{method}
-              </div>
-              <div className='status'>
-                {error ? error.message : status}
-              </div>
+  const icon = signerIconName(status);
+
+  return (
+    <div
+      className={classes('item', status)}
+      onClick={removeItem}
+      key={id}
+    >
+      <div className='wrapper'>
+        <div className='container'>
+          <div className='desc'>
+            <div className='header'>
+              {section}.{method}
             </div>
-            <div className='short'>
-              <Icon
-                loading={icon === 'spinner'}
-                name={icon}
-              />
+            <div className='status'>
+              {error ? error.message : status}
             </div>
           </div>
+          <div className='short'>
+            <Icon
+              loading={icon === 'spinner'}
+              name={icon}
+            />
+          </div>
         </div>
       </div>
-    );
-  }
-
-  private iconName = (status: string): any => {
-    switch (status) {
-      case 'error':
-        return 'ban';
-
-      case 'event':
-        return 'assistive listening devices';
+    </div>
+  );
+}
 
-      case 'received':
-        return 'telegram plane';
+function Status ({ stqueue = [], txqueue = [] }: Props): React.ReactElement<Props> | null {
+  const allst: QueueStatus[] = stqueue.filter(({ isCompleted }): boolean => !isCompleted);
+  const alltx: QueueTx[] = txqueue.filter(({ status }): boolean =>
+    !['completed', 'incomplete'].includes(status)
+  );
 
-      default:
-        return 'check';
-    }
+  if (!allst.length && !alltx.length) {
+    return null;
   }
 
-  private signerIconName = (status: QueueTxStatus): any => {
-    switch (status) {
-      case 'cancelled':
-        return 'ban';
-
-      case 'completed':
-      case 'finalized':
-      case 'sent':
-        return 'check';
-
-      case 'dropped':
-      case 'invalid':
-      case 'usurped':
-        return 'arrow down';
-
-      case 'error':
-        return 'warning sign';
-
-      case 'queued':
-        return 'random';
-
-      default:
-        return 'spinner';
-    }
-  }
+  return (
+    <Wrapper className='ui--Status'>
+      {alltx.map(renderItem)}
+      {allst.map(renderStatus)}
+    </Wrapper>
+  );
 }
 
 export default translate(Status);

+ 6 - 10
packages/react-components/src/SummaryBox.tsx

@@ -12,16 +12,12 @@ interface Props extends BareProps {
   className?: string;
 }
 
-class SummaryBox extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { children, className } = this.props;
-
-    return (
-      <div className={className}>
-        {children}
-      </div>
-    );
-  }
+function SummaryBox ({ children, className }: Props): React.ReactElement<Props> {
+  return (
+    <div className={className}>
+      {children}
+    </div>
+  );
 }
 
 export default styled(SummaryBox)`

+ 19 - 20
packages/react-components/src/Tabs.tsx

@@ -33,25 +33,8 @@ interface Props extends BareProps {
   isSequence?: boolean;
 }
 
-export default class Tabs extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, hidden = [], items, style } = this.props;
-    return (
-      <div
-        className={classes('ui--Menu ui menu tabular', className)}
-        style={style}
-      >
-        {
-          items
-            .filter(({ name }): boolean => !hidden.includes(name))
-            .map(this.renderItem)
-        }
-      </div>
-    );
-  }
-
-  private renderItem = ({ hasParams, isRoot, name, text, ...tab }: TabItem, index: number): React.ReactNode => {
-    const { basePath, isSequence, items } = this.props;
+function renderItem ({ basePath, isSequence, items }: Props): (tabItem: TabItem, index: number) => React.ReactNode {
+  return function Tab ({ hasParams, isRoot, name, text, ...tab }: TabItem, index: number): React.ReactNode {
     const to = isRoot
       ? basePath
       : `${basePath}/${name}`;
@@ -75,5 +58,21 @@ export default class Tabs extends React.PureComponent<Props> {
         )}
       </React.Fragment>
     );
-  }
+  };
+}
+
+export default function Tabs (props: Props): React.ReactElement<Props> {
+  const { className, hidden = [], items, style } = props;
+  return (
+    <div
+      className={classes('ui--Menu ui menu tabular', className)}
+      style={style}
+    >
+      {
+        items
+          .filter(({ name }): boolean => !hidden.includes(name))
+          .map(renderItem(props))
+      }
+    </div>
+  );
 }

+ 20 - 22
packages/react-components/src/Toggle.tsx

@@ -17,29 +17,27 @@ interface Props extends BareProps {
   value?: boolean;
 }
 
-class Toggle extends React.PureComponent<Props> {
-  public render (): React.ReactNode {
-    const { className, asSwitch = true, defaultValue, isDisabled, value, label } = this.props;
-
-    return (
-      <div className={className}>
-        <label>{label}</label>
-        <SUICheckbox
-          checked={value}
-          disabled={isDisabled}
-          defaultChecked={defaultValue}
-          onChange={this.onChange}
-          toggle={asSwitch}
-        />
-      </div>
-    );
-  }
-
-  private onChange = (_: React.FormEvent<HTMLInputElement>, { checked }: any): void => {
-    const { onChange } = this.props;
-
+function onChange ({ onChange }: Props): (_: React.FormEvent<HTMLInputElement>, __: any) => void {
+  return function (_: React.FormEvent<HTMLInputElement>, { checked }: any): void {
     onChange && onChange(checked);
-  }
+  };
+}
+
+function Toggle (props: Props): React.ReactElement<Props> {
+  const { className, asSwitch = true, defaultValue, isDisabled, value, label } = props;
+
+  return (
+    <div className={className}>
+      <label>{label}</label>
+      <SUICheckbox
+        checked={value}
+        disabled={isDisabled}
+        defaultChecked={defaultValue}
+        onChange={onChange(props)}
+        toggle={asSwitch}
+      />
+    </div>
+  );
 }
 
 export default styled(Toggle)`

+ 1 - 1
packages/react-components/src/TxButton.tsx

@@ -140,7 +140,7 @@ class TxButtonInner extends React.PureComponent<InnerProps> {
   }
 }
 
-class TxButton extends React.PureComponent<Props> {
+class TxButton extends React.PureComponent<Props, State> {
   protected button: any = React.createRef();
 
   public render (): React.ReactNode {