chore(explorer): change asset dialog to details page (#2941)

This commit is contained in:
Art 2023-02-20 15:15:18 +01:00 committed by GitHub
parent 7e957a2841
commit 4b83a10475
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 113 additions and 51 deletions

View File

@ -4,15 +4,6 @@ context('Asset page', { tags: '@regression' }, () => {
describe('Verify elements on page', () => {
before('Navigate to assets page', () => {
cy.visit('/assets');
// Check we have enough enough assets
cy.getAssets().then((assets) => {
assert.isAtLeast(
Object.keys(assets).length,
5,
'Ensuring we have at least 5 assets to test'
);
});
});
it('should be able to see full assets list', () => {
@ -40,7 +31,7 @@ context('Asset page', { tags: '@regression' }, () => {
});
});
it('should open details dialog when clicked on "View details"', () => {
it('should open details page when clicked on "View details"', () => {
cy.getAssets().then((assets) => {
Object.values(assets).forEach((asset) => {
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
@ -49,8 +40,8 @@ context('Asset page', { tags: '@regression' }, () => {
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
.eq(0)
.click();
cy.getByTestId('dialog-content').should('be.visible');
cy.getByTestId('dialog-close').click();
cy.getByTestId('asset-header').should('have.text', asset.name);
cy.go('back');
});
});
});

View File

@ -1,22 +1,35 @@
import { render, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { assetsList } from '../../mocks/assets';
import { AssetsTable } from './assets-table';
describe('AssetsTable', () => {
it('shows loading message on first render', async () => {
const res = render(<AssetsTable data={null} />);
const res = render(
<MemoryRouter>
<AssetsTable data={null} />
</MemoryRouter>
);
expect(await res.findByText('Loading...')).toBeInTheDocument();
});
it('shows no data message if no assets found', async () => {
const res = render(<AssetsTable data={[]} />);
const res = render(
<MemoryRouter>
<AssetsTable data={[]} />
</MemoryRouter>
);
expect(
await res.findByText('This chain has no assets')
).toBeInTheDocument();
});
it('shows a table/list with all the assets', async () => {
const res = render(<AssetsTable data={assetsList} />);
const res = render(
<MemoryRouter>
<AssetsTable data={assetsList} />
</MemoryRouter>
);
await waitFor(() => {
const rowA1 = res.container.querySelector('[row-id="123"]');
expect(rowA1).toBeInTheDocument();

View File

@ -1,5 +1,4 @@
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/react-helpers';
import type { VegaICellRendererParams } from '@vegaprotocol/ui-toolkit';
@ -9,15 +8,14 @@ import { AgGridColumn } from 'ag-grid-react';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints';
import { useNavigate } from 'react-router-dom';
import type { RowClickedEvent } from 'ag-grid-community';
type AssetsTableProps = {
data: AssetFieldsFragment[] | null;
};
export const AssetsTable = ({ data }: AssetsTableProps) => {
const openAssetDetailsDialog = useAssetDetailsDialogStore(
(state) => state.open
);
const navigate = useNavigate();
const ref = useRef<AgGridReact>(null);
const showColumnsOnDesktop = () => {
ref.current?.columnApi.setColumnsVisible(
@ -49,17 +47,23 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
autoHeight: true,
}}
suppressCellFocus={true}
onGridReady={() => {
showColumnsOnDesktop();
onRowClicked={({ data }: RowClickedEvent) => {
navigate(data.id);
}}
>
<AgGridColumn headerName={t('Symbol')} field="symbol" />
<AgGridColumn headerName={t('Name')} field="name" />
<AgGridColumn flex="2" headerName={t('ID')} field="id" />
<AgGridColumn
flex="2"
headerName={t('ID')}
field="id"
hide={window.innerWidth < BREAKPOINT_MD}
/>
<AgGridColumn
colId="type"
headerName={t('Type')}
field="source.__typename"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetTypeMapping[value].value
}
@ -67,6 +71,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
<AgGridColumn
headerName={t('Status')}
field="status"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetStatusMapping[value].value
}
@ -83,28 +88,13 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
value,
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? (
<div className="pb-1">
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value, e.target as HTMLElement);
}}
>
{t('View details')}
</ButtonLink>{' '}
<span className="max-md:hidden">
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(
value,
e.target as HTMLElement,
true
);
}}
>
{t('View JSON')}
</ButtonLink>
</span>
</div>
<ButtonLink
onClick={(e) => {
navigate(value);
}}
>
{t('View details')}
</ButtonLink>
) : (
''
)

View File

@ -5,9 +5,12 @@ import {
useAssetDataProvider,
useAssetDetailsDialogStore,
} from '@vegaprotocol/assets';
import { useNavigate } from 'react-router-dom';
import { Routes } from '../../../routes/route-names';
export type AssetLinkProps = Partial<ComponentProps<typeof ButtonLink>> & {
assetId: string;
asDialog?: boolean;
};
/**
@ -15,17 +18,22 @@ export type AssetLinkProps = Partial<ComponentProps<typeof ButtonLink>> & {
* with a link to the assets modal. If the name does not come back
* it will use the ID instead.
*/
export const AssetLink = ({ assetId, ...props }: AssetLinkProps) => {
export const AssetLink = ({ assetId, asDialog, ...props }: AssetLinkProps) => {
const { data: asset } = useAssetDataProvider(assetId);
const open = useAssetDetailsDialogStore((state) => state.open);
const navigate = useNavigate();
const label = asset?.name ? asset.name : assetId;
return (
<ButtonLink
data-testid="asset-link"
disabled={!asset}
onClick={(e) => {
open(assetId, e.target as HTMLElement);
if (asDialog) {
open(assetId, e.target as HTMLElement);
} else {
navigate(`${Routes.ASSETS}/${asset?.id}`);
}
}}
{...props}
>

View File

@ -0,0 +1,50 @@
import { t } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../components/route-title';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { useParams } from 'react-router-dom';
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { useState } from 'react';
export const AssetPage = () => {
useDocumentTitle(['Assets']);
useScrollToLocation();
const { assetId } = useParams<{ assetId: string }>();
const { data, loading, error } = useAssetDataProvider(assetId || '');
const title = data ? data.name : error ? t('Asset not found') : '';
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
return (
<>
<section className="relative">
<RouteTitle data-testid="asset-header">{title}</RouteTitle>
<AsyncRenderer
noDataMessage={t('Asset not found')}
data={data}
loading={loading}
error={error}
>
<div className="absolute top-0 right-0">
<Button size="xs" onClick={() => setDialogOpen(true)}>
{t('View JSON')}
</Button>
</div>
<div className="h-full relative">
<AssetDetailsTable asset={data as AssetFieldsFragment} />
</div>
</AsyncRenderer>
</section>
<JsonViewerDialog
open={dialogOpen}
onChange={(isOpen) => setDialogOpen(isOpen)}
title={data?.name || ''}
content={data}
/>
</>
);
};

View File

@ -6,7 +6,7 @@ import { useDocumentTitle } from '../../hooks/use-document-title';
import { useAssetsDataProvider } from '@vegaprotocol/assets';
import { AssetsTable } from '../../components/assets/assets-table';
export const Assets = () => {
export const AssetsPage = () => {
useDocumentTitle(['Assets']);
useScrollToLocation();

View File

@ -1 +1,2 @@
export * from './assets';
export * from './assets-page';
export * from './asset-page';

View File

@ -1,4 +1,4 @@
import { Assets } from './assets';
import { AssetPage, AssetsPage } from './assets';
import BlockPage from './blocks';
import Governance from './governance';
import Home from './home';
@ -53,7 +53,16 @@ const assetsRoutes: Route[] = flags.assets
path: Routes.ASSETS,
text: t('Assets'),
name: 'Assets',
element: <Assets />,
children: [
{
index: true,
element: <AssetsPage />,
},
{
path: ':assetId',
element: <AssetPage />,
},
],
},
]
: [];