Added a component to render fetched data to include loading and error states. Added to all instances of useFetch

This commit is contained in:
sam-keen 2022-03-15 11:32:15 +00:00
parent 061388938c
commit 477e3faa6e
8 changed files with 92 additions and 52 deletions

View File

@ -6,11 +6,15 @@ import { Table, TableRow, TableCell } from '../table';
interface BlockProps {
block: BlockMeta;
className?: string;
}
export const BlockData = ({ block }: BlockProps) => {
export const BlockData = ({ block, className }: BlockProps) => {
return (
<Table aria-label={`Data for block ${block.header?.height}`}>
<Table
aria-label={`Data for block ${block.header?.height}`}
className={className}
>
<TableRow dataTestId="block-row" modifier="background">
<TableCell
dataTestId="block-height"

View File

@ -0,0 +1,27 @@
import React from 'react';
import { StatusMessage } from '../status-message';
interface RenderFetchedProps {
children: React.ReactNode;
error: Error | undefined;
loading: boolean | undefined;
className?: string;
}
export const RenderFetched = ({
error,
loading,
children,
className,
}: RenderFetchedProps) => {
if (loading) {
return <StatusMessage className={className}>Loading...</StatusMessage>;
}
if (error) {
return <StatusMessage className={className}>Error: {error}</StatusMessage>;
}
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
};

View File

@ -7,6 +7,6 @@ interface StatusMessageProps {
}
export const StatusMessage = ({ children, className }: StatusMessageProps) => {
const classes = classnames('font-alpha', 'text-h4', 'mb-28', className);
const classes = classnames('font-alpha text-h4 mb-28', className);
return <h3 className={classes}>{children}</h3>;
};

View File

@ -8,9 +8,12 @@ interface TxsProps {
className?: string;
}
export const TxsData = ({ data, className }: TxsProps) => {
export const BlockTxsData = ({ data, className }: TxsProps) => {
if (!data?.result) {
return <div className={className}>Awaiting block data</div>;
// Data for the block has already been fetched at this point, so no errors
// or loading to deal with. This is specifically the case
// where the data object is not undefined, but lacks a result.
return <div className={className}>No data</div>;
}
return (
@ -21,7 +24,7 @@ export const TxsData = ({ data, className }: TxsProps) => {
{data.result?.block_metas?.map((block, index) => {
return (
<li key={index} data-testid="block-row">
<BlockData block={block} />
<BlockData block={block} className="mb-12" />
<TxsPerBlock blockHeight={block.header.height} />
</li>
);

View File

@ -1,4 +1,4 @@
export { TxDetails } from './id/tx-details';
export { TxContent } from './id/tx-content';
export { TxList } from './pending/tx-list';
export { TxsData } from './home/txs-data';
export { BlockTxsData } from './home/block-txs-data';

View File

@ -2,6 +2,7 @@ import useFetch from '../../../hooks/use-fetch';
import { ChainExplorerTxResponse } from '../../../routes/types/chain-explorer-response';
import { DATA_SOURCES } from '../../../config';
import { Link } from 'react-router-dom';
import { RenderFetched } from '../../render-fetched';
import { TruncateInline } from '../../truncate/truncate';
interface TxsPerBlockProps {
@ -12,7 +13,7 @@ const truncateLength = 14;
export const TxsPerBlock = ({ blockHeight }: TxsPerBlockProps) => {
const {
state: { data: decodedBlockData },
state: { data: decodedBlockData, loading, error },
} = useFetch<ChainExplorerTxResponse[]>(DATA_SOURCES.chainExplorerUrl, {
method: 'POST',
mode: 'cors',
@ -27,6 +28,7 @@ export const TxsPerBlock = ({ blockHeight }: TxsPerBlockProps) => {
});
return (
<RenderFetched error={error} loading={loading} className="text-body-large">
<div className="overflow-x-auto whitespace-nowrap mb-28">
<table className="w-full">
<thead>
@ -38,7 +40,7 @@ export const TxsPerBlock = ({ blockHeight }: TxsPerBlockProps) => {
</thead>
<tbody>
{decodedBlockData &&
decodedBlockData.map(({ TxHash, PubKey, Type }, index) => {
decodedBlockData.map(({ TxHash, PubKey, Type }) => {
return (
<tr key={TxHash}>
<td>
@ -66,5 +68,6 @@ export const TxsPerBlock = ({ blockHeight }: TxsPerBlockProps) => {
</tbody>
</table>
</div>
</RenderFetched>
);
};

View File

@ -2,7 +2,7 @@ import { DATA_SOURCES } from '../../../config';
import useFetch from '../../../hooks/use-fetch';
import { TendermintBlockchainResponse } from '../tendermint-blockchain-response';
import { RouteTitle } from '../../../components/route-title';
import { StatusMessage } from '../../../components/status-message';
import { RenderFetched } from '../../../components/render-fetched';
import { BlocksData, BlocksRefetch } from '../../../components/blocks';
import { JumpToBlock } from '../../../components/jump-to-block';
@ -17,14 +17,12 @@ const Blocks = () => {
return (
<section>
<RouteTitle>Blocks</RouteTitle>
{loading && <StatusMessage>Loading...</StatusMessage>}
{error && <StatusMessage>Error: {error}</StatusMessage>}
{data && (
<RenderFetched error={error} loading={loading}>
<>
<BlocksRefetch refetch={refetch} />
<BlocksData data={data} className="mb-28" />
</>
)}
</RenderFetched>
<JumpToBlock />
</section>
);

View File

@ -3,12 +3,13 @@ import { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain
import { DATA_SOURCES } from '../../../config';
import { RouteTitle } from '../../../components/route-title';
import { BlocksRefetch } from '../../../components/blocks';
import { TxsData } from '../../../components/txs';
import { RenderFetched } from '../../../components/render-fetched';
import { BlockTxsData } from '../../../components/txs';
import { JumpToBlock } from '../../../components/jump-to-block';
const Txs = () => {
const {
state: { data },
state: { data, error, loading },
refetch,
} = useFetch<TendermintBlockchainResponse>(
`${DATA_SOURCES.tendermintUrl}/blockchain`
@ -17,8 +18,12 @@ const Txs = () => {
return (
<section>
<RouteTitle>Transactions</RouteTitle>
<RenderFetched error={error} loading={loading}>
<>
<BlocksRefetch refetch={refetch} />
<TxsData data={data} />
<BlockTxsData data={data} />
</>
</RenderFetched>
<JumpToBlock />
</section>
);