-
Notifications
You must be signed in to change notification settings - Fork 59
Lists
MorganeLecurieux edited this page May 18, 2022
·
1 revision
In the app, all the full page lists have the same features:
- Infinite scroll
- Key board navigation (K / J / ⬆️ / ⬇️)
To respect the design conventions, you need to use the elements found in the list style file.
- ListHeader - container for the header
- ListContainer - container for the list items
- BaseListItem - used for skeletons only
- ListItem - used for any list item
- ItemContainer - used only in case there is a dropdown of action in the item
- PopperOpener - used for the dropdown button opener to position it correctly
Items usually redirects to another page (edition/details). To avoid to trigger the redirection while opening the dropwdown, you need to use the following code :
import { ListItem, PopperOpener, ItemContainer } from "~/styles";
const Item = () => {
return (
<ItemContainer>
<ListItem
onClick={() => {
/* TODO */
}}
>
{/* Content of the item */}
<ButtonMock />
</ListItem>
<Popper
opener={
<PopperOpener>
<Button icon="dots-horizontal" variant="quaternary" />
</PopperOpener>
}
>
{/* Popper content */}
</Popper>
</ItemContainer>
);
};
// Placeholder for the popper button that is in aboslute position on top of the item
const ButtonMock = styled.div`
width: 40px;
`;
The infinite scroll is used with a paginated query. Just wrap your list with the InfiniteScroll component as follow :
import { InfiniteScroll } from "~/components/designSystem";
const List = () => {
const { data, error, loading, fetchMore } = useCustomersQuery({
variables: { limit: 20 },
notifyOnNetworkStatusChange: true, // This is needed for the "loading" to be set to "true" while fetching more
});
return (
<InfiniteScroll
onBottom={() => {
const { currentPage = 0, totalPages = 0 } = data?.items?.metadata || {};
currentPage < totalPages &&
!loading &&
fetchMore({
variables: { page: currentPage + 1 },
});
}}
>
<>
{!!list &&
list.map((item) => {
index += 1;
return <Item key={item.id} item={item} />;
})}
{loading &&
[0, 1, 2].map((i) => <Skeletong key={`item-skeleton-${i}`} />)}
</>
</InfiniteScroll>
);
};
Use the useListKeysNavigation hook. It will deal with the navigation automatically.
Then in your component :
const List = () => {
const { onKeyDown } = useListKeysNavigation({
getElmId: (i) => `item-key-${i}`, // identifier used to get each element
})
const list = [] // Elements fetched
return (
<div role="grid" tabIndex={-1} onKeyDown={onKeyDown}>
{/* List header, empty state, error state... */}
{!!list &&
list.map((item) => {
index += 1
return (
<CustomerItem
key={customer.id}
rowId={`item-key-${index}`} // This must be the same structure as the identifier given to useListKeysNavigation
item={item}
/>
)
})}
<div>
)
}
Do not forget then to pass rowId
as the id to the Item so it can be focused onKeyDown.
The tabIndex
is necessary to allow the item to be focused.
const Item = ({ rowId }) => {
return (
<ItemContainer>
<Item
id={rowId}
tabIndex={0}
onClick={() => {}}
>
{/* Item content */}
</ItemContainer>
</Item>
)
}