Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: useLocationsView hook #5908

Open
wants to merge 6 commits into
base: feat-storage-browser/main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ describe('LocationDetailView', () => {
handlePaginateNext,
handlePaginatePrevious,
handleReset: jest.fn(),
range: [0, testResult.length],
});
mockListItemsAction({ result: testResult });

Expand Down Expand Up @@ -329,6 +330,7 @@ describe('LocationDetailView', () => {
handlePaginateNext,
handlePaginatePrevious,
handleReset: jest.fn(),
range: [0, testResult.length],
});
mockListItemsAction({ result: testResult });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
TABLE_HEADER_BUTTON_CLASS_NAME,
TABLE_HEADER_CLASS_NAME,
} from '../../../components/DataTable';
import { useLocationsData } from '../../../context/actions';
import { useControl } from '../../../context/control';
import { LocationAccess } from '../../../context/types';
import { ButtonElement, IconElement } from '../../../context/elements';

Expand Down Expand Up @@ -121,33 +119,26 @@ const getLocationsData = ({
return { columns, rows };
};

export function DataTableControl({
range,
}: {
range: [start: number, end: number];
}): React.JSX.Element | null {
const [{ data, hasError }] = useLocationsData();

const [, handleUpdateState] = useControl('NAVIGATE');
interface DataTableControlProps {
items: LocationAccess[];
handleLocationClick: (location: LocationAccess) => void;
}

export function DataTableControl({
items,
handleLocationClick,
}: DataTableControlProps): React.JSX.Element | null {
const [sortState, setSortState] = React.useState<SortState>({
selection: 'scope',
direction: 'ascending',
});

const [start, end] = range;

const locationsData = React.useMemo(
() =>
getLocationsData({
data: data.result.slice(start, end),
data: items,
sortState,
onLocationClick: (location) => {
handleUpdateState({
type: 'ACCESS_LOCATION',
location,
});
},
onLocationClick: handleLocationClick,
onTableHeaderClick: (location: string) => {
setSortState((prevState) => ({
selection: location,
Expand All @@ -156,8 +147,8 @@ export function DataTableControl({
}));
},
}),
[data.result, handleUpdateState, sortState, start, end]
[items, handleLocationClick, sortState]
);

return hasError ? null : <DataTable data={locationsData} />;
return <DataTable data={locationsData} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { LocationAccess } from '../../../../context/types';

import { DataTableControl } from '../DataTable';

const TEST_RANGE: [number, number] = [0, 100];

const useControlModuleSpy = jest.spyOn(UseControlModule, 'useControl');
const useLocationsDataSpy = jest.spyOn(
UseLocationsDataModule,
Expand All @@ -36,7 +34,9 @@ describe('LocationsViewTableControl', () => {
});

it('renders the table with data', () => {
const { getByText } = render(<DataTableControl range={TEST_RANGE} />);
const { getByText } = render(
<DataTableControl items={mockData} handleLocationClick={jest.fn()} />
);

expect(getByText('Name')).toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
Expand All @@ -46,7 +46,9 @@ describe('LocationsViewTableControl', () => {
});

it('renders the correct icon based on sort state', () => {
const { getByText } = render(<DataTableControl range={TEST_RANGE} />);
const { getByText } = render(
<DataTableControl items={mockData} handleLocationClick={jest.fn()} />
);

const nameTh = screen.getByRole('columnheader', { name: 'Name' });

Expand All @@ -58,17 +60,17 @@ describe('LocationsViewTableControl', () => {
});

it('triggers location click handler when a row is clicked', () => {
const mockHandleUpdateState = jest.fn();
useControlModuleSpy.mockReturnValue([{}, mockHandleUpdateState]);

render(<DataTableControl range={TEST_RANGE} />);
const mockHandleLocationClick = jest.fn();
render(
<DataTableControl
items={mockData}
handleLocationClick={mockHandleLocationClick}
/>
);

const firstRowButton = screen.getByRole('button', { name: 'Location A' });
fireEvent.click(firstRowButton);

expect(mockHandleUpdateState).toHaveBeenCalledWith({
type: 'ACCESS_LOCATION',
location: mockData[0],
});
expect(mockHandleLocationClick).toHaveBeenCalledWith(mockData[0]);
});
});
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
import React from 'react';

import { CLASS_BASE } from '../constants';
import { Controls } from '../Controls';
import { useLocationsData } from '../../context/actions';

import { usePaginate } from '../hooks/usePaginate';
import { listViewHelpers, resolveClassName } from '../utils';

import { resolveClassName } from '../utils';
import { DataTableControl } from './Controls/DataTable';
import { LocationAccess } from '../../context/types';
import { useLocationsView } from './useLocationsView';

export interface LocationsViewProps {
className?: (defaultClassName: string) => string;
}

const DEFAULT_PAGE_SIZE = 100;
export const DEFAULT_LIST_OPTIONS = {
exclude: 'WRITE' as const,
pageSize: DEFAULT_PAGE_SIZE,
};
export const DEFAULT_ERROR_MESSAGE = 'There was an error loading locations.';

const {
Expand Down Expand Up @@ -64,40 +57,15 @@ const LocationsEmptyMessage = () => {
export function LocationsView({
className,
}: LocationsViewProps): React.JSX.Element {
const [{ data, isLoading, hasError }, handleList] = useLocationsData();
const [{ data, isLoading, hasError }, handleAction] = useLocationsView();
const { getProcessedItems, hasMoreData, page } = data;

const { result, nextToken } = data;
const resultCount = result.length;
const hasNextToken = !!nextToken;
const handleLocationClick = (location: LocationAccess) => {
handleAction({ type: 'SELECT_LOCATION', location });
};

// initial load
React.useEffect(() => {
handleList({
options: { ...DEFAULT_LIST_OPTIONS, refresh: true },
});
}, [handleList]);

const onPaginateNext = () =>
handleList({
options: { ...DEFAULT_LIST_OPTIONS, nextToken },
});

const {
currentPage,
handlePaginateNext,
handlePaginatePrevious,
handleReset,
} = usePaginate({ onPaginateNext, pageSize: DEFAULT_PAGE_SIZE });

const { disableNext, disablePrevious, disableRefresh, range } =
listViewHelpers({
currentPage,
hasNextToken,
isLoading,
pageSize: DEFAULT_PAGE_SIZE,
resultCount,
hasError,
});
const disableNext = !hasMoreData || isLoading || hasError;
const disablePrevious = page <= 1 || isLoading || hasError;

return (
<div
Expand All @@ -106,26 +74,30 @@ export function LocationsView({
>
<Title>Home</Title>
<RefreshControl
disableRefresh={disableRefresh}
disableRefresh={isLoading}
handleRefresh={() => {
handleReset();
handleList({
options: { ...DEFAULT_LIST_OPTIONS, refresh: true },
});
handleAction({ type: 'REFRESH' });
}}
/>
<Paginate
currentPage={currentPage}
currentPage={page}
disableNext={disableNext}
disablePrevious={disablePrevious}
handleNext={() => {
handlePaginateNext({ resultCount, hasNextToken });
handleAction({ type: 'PAGINATE_NEXT' });
}}
handlePrevious={() => {
handleAction({ type: 'PAGINATE_PREVIOUS' });
}}
handlePrevious={handlePaginatePrevious}
/>
<LocationsMessage />
<Loading />
<DataTableControl range={range} />
{hasError ? null : (
<DataTableControl
items={getProcessedItems()}
handleLocationClick={handleLocationClick}
/>
)}
<LocationsEmptyMessage />
</div>
);
Expand Down
Loading
Loading