Skip to content

Commit

Permalink
fix(Pagination): api alignment (#2460)
Browse files Browse the repository at this point in the history
Fixes #2283

---------

Co-authored-by: barsnes <[email protected]>
  • Loading branch information
eirikbacker and Barsnes authored Sep 26, 2024
1 parent d0fad1d commit e9ca9b7
Show file tree
Hide file tree
Showing 17 changed files with 418 additions and 804 deletions.
11 changes: 11 additions & 0 deletions .changeset/angry-planes-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@digdir/designsystemet-css": patch
"@digdir/designsystemet-react": patch
---

Pagination:
- Remove attributes `currentPage` and `totalPages` on `Pagination`
- Replace `Pagination.Root` with `Paginaton`
- Replace `Pagination.Next`, `Pagination.Previous` and `Pagination.Ellipsis` with `Paginaton.Button`
- Make `usePagination` return spreadable props for subcomponents
- Add support for `showPages` and `onChange` in `usePagination`
36 changes: 28 additions & 8 deletions apps/theme/components/Previews/Components/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
Textfield,
ToggleGroup,
Tooltip,
usePagination,
} from '@digdir/designsystemet-react';
import cl from 'clsx/lite';
import { useState } from 'react';
Expand All @@ -34,6 +35,14 @@ import classes from './Components.module.css';

export const Components = () => {
const [radioValue, setRadioValue] = useState('vanilje');
const [currentPage, setCurrentPage] = useState(1);
const pagination = usePagination({
currentPage,
setCurrentPage,
totalPages: 10,
showPages: 7,
});

return (
<div className={classes.components}>
<div className={cl(classes.card, classes.checkbox)}>
Expand Down Expand Up @@ -143,14 +152,25 @@ export const Components = () => {
</Table.Row>
</Table.Body>
</Table>
<Pagination
currentPage={3}
nextLabel='Neste'
onChange={function Ya() {}}
previousLabel='Forrige'
size='sm'
totalPages={6}
/>
<Pagination>
<Pagination.List>
<Pagination.Item>
<Pagination.Button {...pagination.prevButtonProps}>
Forrige
</Pagination.Button>
</Pagination.Item>
{pagination.pages.map(({ itemKey, buttonProps, page }) => (
<Pagination.Item key={itemKey}>
<Pagination.Button {...buttonProps}>{page}</Pagination.Button>
</Pagination.Item>
))}
<Pagination.Item>
<Pagination.Button {...pagination.nextButtonProps}>
Neste
</Pagination.Button>
</Pagination.Item>
</Pagination.List>
</Pagination>
</div>
<div className={cl(classes.card, classes.help)}>
<Heading size='xs' className={classes.helpHeading}>
Expand Down
57 changes: 33 additions & 24 deletions packages/css/pagination.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,42 @@
--dsc-pagination-ellipsis-width: var(--ds-sizing-12);
--dsc-pagination-chevron-size: var(--ds-sizing-6);

display: flex;
flex-wrap: wrap;
gap: var(--dsc-pagination-gap);
list-style: none;
margin: 0;
padding: 0;

/* Style ellipsis When not containing interactive element */
& > li:not(:has(a, button)) {
align-items: center;
& > :is(ol, ul) {
display: flex;
justify-content: center;
min-width: var(--dsc-pagination-ellipsis-width);
}
flex-wrap: wrap;
gap: var(--dsc-pagination-gap);
list-style: none;
margin: 0;
padding: 0;

& > li:first-child > ::before,
& > li:last-child > ::after {
background: currentcolor;
content: '';
height: var(--dsc-pagination-chevron-size);
mask: center/contain no-repeat
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24'%3E%3Cpath d='M9.47 5.97a.75.75 0 0 1 1.06 0l5.5 5.5a.75.75 0 0 1 0 1.06l-5.5 5.5a.75.75 0 1 1-1.06-1.06L14.44 12 9.47 7.03a.75.75 0 0 1 0-1.06'/%3E%3C/svg%3E");
width: var(--dsc-pagination-chevron-size);
}
& > li:first-child > ::before,
& > li:last-child > ::after {
content: '';
background: currentcolor;
height: var(--dsc-pagination-chevron-size);
mask: center/contain no-repeat
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24'%3E%3Cpath d='M9.47 5.97a.75.75 0 0 1 1.06 0l5.5 5.5a.75.75 0 0 1 0 1.06l-5.5 5.5a.75.75 0 1 1-1.06-1.06L14.44 12 9.47 7.03a.75.75 0 0 1 0-1.06'/%3E%3C/svg%3E");
width: var(--dsc-pagination-chevron-size);
}

& > li:first-child > ::before {
rotate: 180deg;
}

/* Makes page buttons square */
& > li:not(:first-child, :last-child) > :is(a, button) {
--dsc-button-padding-inline: var(--dsc-button-padding-block);
}

/* Style as non-interactive ellipsis when empty */
& > li > [aria-hidden='true'] {
color: inherit;
pointer-events: none;

& > li:first-child > ::before {
rotate: 180deg;
&::before {
content: '\2026'; /* ellipsis */
}
}
}

&[data-size='sm'] {
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
data-variant={variant}
ref={ref}
/* don't set type when we use `asChild` */
{...(asChild ? { asChild: true } : { type: 'button' })}
type={asChild ? undefined : 'button'}
/* if consumers set type, our default does not set anything, as `rest` contains this */
{...rest}
>
Expand Down
88 changes: 50 additions & 38 deletions packages/react/src/components/Pagination/Pagination.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Meta, Controls, Primary, Canvas } from '@storybook/blocks';
import { Meta, Controls, Primary, Canvas, ArgTypes } from '@storybook/blocks';

import * as PaginationStories from './Pagination.stories';
import { usePagination } from './usePagination';

<Meta of={PaginationStories} />

Expand All @@ -17,69 +18,80 @@ Vær oppmerksom på:
- Se eksempel [Paginering med lenker](/docs/komponenter-pagination--with-anchor).

<Primary />
<Controls of={PaginationStories.Preview} />
<Controls of={PaginationStories.Preview} include={['aria-label', 'asChild', 'size']} />

## Bruk

### Eksempel på bruk med `Pagination`

`Pagination` er en kontrollert komponent. Det vil si at komponentens tilstand om hvilken side som er aktiv styres utenfra. Når komponenten er på første eller siste side, skjules "forrige"/"neste"-pilene.
`Pagination` er en kontrollert komponent. Det vil si at komponentens tilstand om hvilken side som er aktiv styres utenfra.

```tsx
import { Pagination } from '@digdir/designsystemet-react';

const [currentPage, setCurrentPage] = useState(1);

<Pagination
currentPage={1}
totalPages={10}
onChange={setCurrentPage}
nextLabel='Neste'
previousLabel='Forrige'
itemLabel={(num) => `Side ${num}`}
/>;
```

### Eksempel på bruk med del-komponenter

Er det behov for en tilpasset `Pagination` kan du ta i bruk de enkelte del-kompontenene.

```tsx
<Pagination.Root>
const onChange = (event, page: number) => console.log(`Going to page ${page}`);
const [currentPage, setCurrentPage] = useState(4);
const { pages, prevButtonProps, nextButtonProps, hasNext, hasPrevious } = usePagination({
currentPage,
setCurrentPage,
onChange,
totalPages: 10,
showPages: 7,
});

<Pagination>
<Pagination.List>
<Pagination.Item>
<Pagination.Previous>
<ChevronLeftIcon aria-hidden />
<Pagination.Button aria-label='Forrige side' {...prevButtonProps}>
Forrige
</Pagination.Previous>
</Pagination.Item>

<Pagination.Item>
<Pagination.Button isActive>1</Pagination.Button>
</Pagination.Button>
</Pagination.Item>

{pages.map(({ page, itemKey, buttonProps }) => (
<Pagination.Item key={itemKey}>
<Pagination.Button {...buttonProps} aria-label={`Side ${page}`}>
{page}
</Pagination.Button>
</Pagination.Item>
))}
<Pagination.Item>
<Pagination.Next>
<Pagination.Button aria-label='Neste side' {...nextButtonProps}>
Neste
<ChevronRightIcon aria-hidden />
</Pagination.Next>
</Pagination.Button>
</Pagination.Item>
</Pagination.List>
</Pagination.Root>
</Pagination>
```

## Paginering logikk
## Paginering med `usePagination`

Bruk `usePagination` for paginering-logikk sammen med de enkelt del-komponentene.
Du kan sende inn:

Bruk `usePagination` for paginering logikk sammen med de enkelt del-komponentene for mer avanserte og tilpasset paginering komponent.

`Pagination` er bygget på `usePagination`.
## Props

Bruk `isActive``Pagination.Button` for å indikere hvilken side som er aktiv.
<ArgTypes of={PaginationStories.Preview} include={['currentPage', 'totalPages', 'onChange', 'setCurrentPage', 'showPages']} />

<Canvas of={PaginationStories.UsePagination} />
<br />
I respons får du objektet:

```ts
{
hasPrev: boolean, // Indikererer om det finnes en forrige side
hasNext: boolean, // Indikererer om det finnes en neste side
prevButtonProps: PaginationButtonProps, // Properties du kan spre på forrie-knappen
nextButtonProps: PaginationButtonProps, // Properties du kan spre på neste-knappen
pages: Array<{
page: number | string; // Tall for side, eller tom streng dersom knappen skal være ...
itemKey: string; // key du kan sende til Pagination.Item for å sikre riktig fokus håndtering
buttonProps: PaginationButtonProps; // Properties du kan spre på Pagination.Button
}>
}
```

### Pagenering med lenker

Alle del-komponenter støtter `asChild` som kan brukes til å endre `Pagination.Button` til lenker (`a`).

<Canvas of={PaginationStories.WithAnchor} />

Loading

0 comments on commit e9ca9b7

Please sign in to comment.