From 037255cf852b8408d2b2bf78e70f4db6fd827b53 Mon Sep 17 00:00:00 2001 From: Eirik Backer Date: Fri, 13 Sep 2024 12:43:16 +0200 Subject: [PATCH] fix(Pagination): start converting to new css principles (#2395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Part of #2295 - Wraps pagination for now, but should be looked more into #2396 - Skipping #2283 for now to avoid too large PR - Renames `Pagination.Content` to `Pagination.List` following #2221 - Fixes so `Button` with `hidden` attribute is actually hidden ☺️ --- .changeset/beige-grapes-report.md | 6 ++ packages/css/button.css | 37 ++++--- packages/css/pagination.css | 101 +++++++----------- .../components/Breadcrumbs/BreadcrumbsNav.tsx | 3 +- .../src/components/Pagination/Pagination.mdx | 4 +- .../Pagination/Pagination.stories.tsx | 62 +++-------- .../components/Pagination/Pagination.test.tsx | 17 +-- .../src/components/Pagination/Pagination.tsx | 42 +++----- .../Pagination/PaginationButton.tsx | 4 +- .../Pagination/PaginationContent.tsx | 32 ------ .../Pagination/PaginationEllipsis.tsx | 10 +- .../components/Pagination/PaginationItem.tsx | 8 +- .../components/Pagination/PaginationList.tsx | 29 +++++ .../Pagination/PaginationNextPrev.tsx | 52 +++++---- .../components/Pagination/PaginationRoot.tsx | 24 +++-- .../react/src/components/Pagination/index.ts | 33 +++--- 16 files changed, 199 insertions(+), 265 deletions(-) create mode 100644 .changeset/beige-grapes-report.md delete mode 100644 packages/react/src/components/Pagination/PaginationContent.tsx create mode 100644 packages/react/src/components/Pagination/PaginationList.tsx diff --git a/.changeset/beige-grapes-report.md b/.changeset/beige-grapes-report.md new file mode 100644 index 0000000000..6bf3d4485c --- /dev/null +++ b/.changeset/beige-grapes-report.md @@ -0,0 +1,6 @@ +--- +"@digdir/designsystemet-css": patch +"@digdir/designsystemet-react": patch +--- + +Pagination: Use data attrs instead of class names diff --git a/packages/css/button.css b/packages/css/button.css index 1fe768ecae..4509901a52 100644 --- a/packages/css/button.css +++ b/packages/css/button.css @@ -16,7 +16,6 @@ box-sizing: border-box; color: var(--dsc-button-color); cursor: pointer; - display: flex; font-family: inherit; gap: var(--dsc-button-gap); justify-content: center; @@ -27,14 +26,18 @@ text-align: inherit; text-decoration: none; - &[data-size="sm"] { + &:not([hidden]) { + display: flex; + } + + &[data-size='sm'] { --dsc-button-gap: var(--ds-sizing-1); --dsc-button-padding-block: var(--ds-spacing-2); --dsc-button-padding-inline: var(--ds-spacing-3); --dsc-button-size: var(--ds-sizing-10); } - &[data-size="lg"] { + &[data-size='lg'] { --dsc-button-padding-block: var(--ds-spacing-3); --dsc-button-padding-inline: var(--ds-spacing-5); --dsc-button-size: var(--ds-sizing-14); @@ -52,7 +55,7 @@ /** * Variants */ - &[data-variant="secondary"] { + &[data-variant='secondary'] { --dsc-button-background--active: var(--ds-color-accent-surface-hover); --dsc-button-background--hover: var(--ds-color-accent-surface-default); --dsc-button-background: transparent; @@ -62,7 +65,7 @@ --dsc-button-color: var(--ds-color-accent-text-subtle); } - &[data-variant="tertiary"] { + &[data-variant='tertiary'] { --dsc-button-background--active: var(--ds-color-accent-surface-hover); --dsc-button-background--hover: var(--ds-color-accent-surface-default); --dsc-button-background: transparent; @@ -75,14 +78,14 @@ /** * Colors */ - &[data-color="neutral"] { + &[data-color='neutral'] { --dsc-button-background--active: var(--ds-color-neutral-base-active); --dsc-button-background--hover: var(--ds-color-neutral-base-hover); --dsc-button-background: var(--ds-color-neutral-base-default); --dsc-button-color: var(--ds-color-neutral-contrast-default); } - &[data-color="neutral"][data-variant="secondary"] { + &[data-color='neutral'][data-variant='secondary'] { --dsc-button-background--active: var(--ds-color-neutral-surface-hover); --dsc-button-background--hover: var(--ds-color-neutral-surface-default); --dsc-button-background: transparent; @@ -92,7 +95,7 @@ --dsc-button-color: var(--ds-color-neutral-text-subtle); } - &[data-color="neutral"][data-variant="tertiary"] { + &[data-color='neutral'][data-variant='tertiary'] { --dsc-button-background--active: var(--ds-color-neutral-surface-hover); --dsc-button-background--hover: var(--ds-color-neutral-surface-default); --dsc-button-background: transparent; @@ -101,14 +104,14 @@ --dsc-button-color: var(--ds-color-neutral-text-subtle); } - &[data-color="danger"] { + &[data-color='danger'] { --dsc-button-background--active: var(--ds-color-danger-base-active); --dsc-button-background--hover: var(--ds-color-danger-base-hover); --dsc-button-background: var(--ds-color-danger-base-default); --dsc-button-color: var(--ds-color-danger-contrast-default); } - &[data-color="danger"][data-variant="secondary"] { + &[data-color='danger'][data-variant='secondary'] { --dsc-button-background--active: var(--ds-color-danger-surface-hover); --dsc-button-background--hover: var(--ds-color-danger-surface-default); --dsc-button-background: transparent; @@ -118,7 +121,7 @@ --dsc-button-color: var(--ds-color-danger-text-subtle); } - &[data-color="danger"][data-variant="tertiary"] { + &[data-color='danger'][data-variant='tertiary'] { --dsc-button-background--active: var(--ds-color-danger-surface-hover); --dsc-button-background--hover: var(--ds-color-danger-surface-default); --dsc-button-background: transparent; @@ -131,13 +134,14 @@ * States */ - @media (hover: hover) and (pointer: fine) { /* Only use hover for non-touch devices to prevent sticky-hovering */ - &:not(:disabled, [aria-disabled="true"], [aria-busy="true"]):hover { + @media (hover: hover) and (pointer: fine) { + /* Only use hover for non-touch devices to prevent sticky-hovering */ + &:not(:disabled, [aria-disabled='true'], [aria-busy='true']):hover { background: var(--dsc-button-background--hover); } } - &[aria-busy="true"] { + &[aria-busy='true'] { cursor: progress; } @@ -145,13 +149,12 @@ box-shadow: var(--dsc-focus-boxShadow); } - &:is(:disabled, [aria-disabled="true"]) { + &:is(:disabled, [aria-disabled='true']) { cursor: not-allowed; opacity: var(--ds-disabled-opacity); } - &:not(:disabled, [aria-disabled="true"], [aria-busy="true"]):active { + &:not(:disabled, [aria-disabled='true'], [aria-busy='true']):active { background-color: var(--dsc-button-background--active); } } - diff --git a/packages/css/pagination.css b/packages/css/pagination.css index d2114df253..c7f55704f9 100644 --- a/packages/css/pagination.css +++ b/packages/css/pagination.css @@ -1,67 +1,48 @@ .ds-pagination { - --dsc-pagination-listitem-margin: var(--ds-spacing-4); + --dsc-pagination-gap: var(--ds-spacing-4); --dsc-pagination-ellipsis-width: var(--ds-sizing-12); - --dsc-pagination-chevron-margin: var(--ds-spacing-2); + --dsc-pagination-chevron-size: var(--ds-sizing-6); - list-style-type: none; display: flex; - padding: 0; + flex-wrap: wrap; + gap: var(--dsc-pagination-gap); + list-style: none; margin: 0; - position: relative; - width: fit-content; -} - -.ds-pagination--hidden { - visibility: hidden; -} - -.ds-pagination--sm { - --dsc-pagination-chevron-margin: var(--ds-spacing-2); -} - -.ds-pagination--md { - --dsc-pagination-chevron-margin: var(--ds-spacing-2); -} - -.ds-pagination--lg { - --dsc-pagination-chevron-margin: var(--ds-spacing-2); -} - -.ds-pagination li:first-child { - margin-right: var(--dsc-pagination-chevron-margin); -} - -.ds-pagination li:last-child { - margin-left: var(--dsc-pagination-chevron-margin); -} - -.ds-pagination__item { - flex: 1; - margin-right: var(--dsc-pagination-listitem-margin); -} - -.ds-pagination--sm .ds-pagination__item { - --dsc-pagination-listitem-margin: var(--ds-spacing-2); - --dsc-pagination-ellipsis-width: var(--ds-sizing-10); -} - -.ds-pagination--md .ds-pagination__item { - --dsc-pagination-listitem-margin: var(--ds-spacing-4); - --dsc-pagination-ellipsis-width: var(--ds-sizing-12); -} - -.ds-pagination--lg .ds-pagination__item { - --dsc-pagination-listitem-margin: var(--ds-spacing-6); - --dsc-pagination-ellipsis-width: var(--ds-sizing-14); -} - -.ds-pagination__item.ds-pagination--compact { - --dsc-pagination-listitem-margin: var(--ds-spacing-0); - --dsc-pagination-chevron-margin: var(--ds-spacing-1); -} + padding: 0; -.ds-pagination__ellipsis { - margin-top: 0.5em; - text-align: center; - width: var(--dsc-pagination-ellipsis-width); + /* Style ellipsis When not containing interactive element */ + & > li:not(:has(a, button)) { + align-items: center; + display: flex; + justify-content: center; + min-width: var(--dsc-pagination-ellipsis-width); + } + + & > 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 { + rotate: 180deg; + } + + &[data-size='sm'] { + --dsc-pagination-gap: var(--ds-spacing-2); + --dsc-pagination-ellipsis-width: var(--ds-sizing-10); + } + + &[data-size='lg'] { + --dsc-pagination-gap: var(--ds-spacing-6); + --dsc-pagination-ellipsis-width: var(--ds-sizing-14); + } + + &[data-compact] { + --dsc-pagination-gap: var(--ds-spacing-0); + } } diff --git a/packages/react/src/components/Breadcrumbs/BreadcrumbsNav.tsx b/packages/react/src/components/Breadcrumbs/BreadcrumbsNav.tsx index 694ae342f3..7b050ea7d0 100644 --- a/packages/react/src/components/Breadcrumbs/BreadcrumbsNav.tsx +++ b/packages/react/src/components/Breadcrumbs/BreadcrumbsNav.tsx @@ -2,7 +2,8 @@ import cl from 'clsx/lite'; import { type HTMLAttributes, forwardRef } from 'react'; export type BreadcrumbsNavProps = { - /** Sets the text label for Breadcrumbs area + /** + * Sets the screen reader label for the Breadcrumbs area * @default 'Du er her' */ 'aria-label'?: string; diff --git a/packages/react/src/components/Pagination/Pagination.mdx b/packages/react/src/components/Pagination/Pagination.mdx index 49c0d2d030..9ce9ed07d5 100644 --- a/packages/react/src/components/Pagination/Pagination.mdx +++ b/packages/react/src/components/Pagination/Pagination.mdx @@ -46,7 +46,7 @@ Er det behov for en tilpasset `Pagination` kan du ta i bruk de enkelte del-kompo ```tsx - + @@ -64,7 +64,7 @@ Er det behov for en tilpasset `Pagination` kan du ta i bruk de enkelte del-kompo - + ``` diff --git a/packages/react/src/components/Pagination/Pagination.stories.tsx b/packages/react/src/components/Pagination/Pagination.stories.tsx index 7547ab021f..d73b7075ed 100644 --- a/packages/react/src/components/Pagination/Pagination.stories.tsx +++ b/packages/react/src/components/Pagination/Pagination.stories.tsx @@ -1,4 +1,3 @@ -import { ChevronLeftIcon, ChevronRightIcon } from '@navikt/aksel-icons'; import type { Meta, StoryFn } from '@storybook/react'; import { useEffect, useState } from 'react'; @@ -17,17 +16,11 @@ export const Preview: StoryFn = (args) => { }, [args.currentPage]); return ( - <> - - + ); }; - Preview.args = { + 'aria-label': 'Sidenavigering', size: 'md', nextLabel: 'Neste', previousLabel: 'Forrige', @@ -54,16 +47,13 @@ export const UsePagination: StoryFn = (args) => { }); return ( - - + + - Forrige @@ -85,17 +75,11 @@ export const UsePagination: StoryFn = (args) => { ))} - + Neste - - + ); }; @@ -108,20 +92,11 @@ export const WithAnchor: StoryFn = (args) => { }); return ( - - + + - - - - Forrige - + + Forrige @@ -142,20 +117,11 @@ export const WithAnchor: StoryFn = (args) => { ))} - - - Neste - - + + Neste - + ); }; diff --git a/packages/react/src/components/Pagination/Pagination.test.tsx b/packages/react/src/components/Pagination/Pagination.test.tsx index 2903992295..0720e46024 100644 --- a/packages/react/src/components/Pagination/Pagination.test.tsx +++ b/packages/react/src/components/Pagination/Pagination.test.tsx @@ -156,7 +156,7 @@ describe('Pagination', () => { const renderWithRoot = (props: PaginationRootProps) => { renderRtl( - + Forrige @@ -181,20 +181,23 @@ const renderWithRoot = (props: PaginationRootProps) => { Neste - + , ); }; describe('Pagination.Root', () => { it('should render correctly with default props', () => { - renderWithRoot({}); + renderWithRoot({ + 'aria-label': 'pagination', + }); expect(screen.getByRole('navigation')).toBeInTheDocument(); }); it('should render correctly with custom props', () => { renderWithRoot({ + 'aria-label': 'pagination', size: 'lg', compact: true, }); @@ -202,7 +205,9 @@ describe('Pagination.Root', () => { }); it('should render all children correctly', () => { - renderWithRoot({}); + renderWithRoot({ + 'aria-label': 'pagination', + }); expect(screen.getByText('Forrige')).toBeInTheDocument(); expect(screen.getByText('Neste')).toBeInTheDocument(); @@ -217,7 +222,7 @@ describe('Pagination.Root', () => { describe('Pagination.Button', () => { it('should render correctly with default props', () => { renderRtl( - + 1 , ); @@ -227,7 +232,7 @@ describe('Pagination.Button', () => { it('should render as anchor when asChild is true', () => { renderRtl( - + 1 diff --git a/packages/react/src/components/Pagination/Pagination.tsx b/packages/react/src/components/Pagination/Pagination.tsx index cc0446f341..1c61530ae3 100644 --- a/packages/react/src/components/Pagination/Pagination.tsx +++ b/packages/react/src/components/Pagination/Pagination.tsx @@ -1,12 +1,10 @@ -import { ChevronLeftIcon, ChevronRightIcon } from '@navikt/aksel-icons'; -import cl from 'clsx/lite'; import { forwardRef } from 'react'; import type { HTMLAttributes } from 'react'; import { PaginationButton } from './PaginationButton'; -import { PaginationContent } from './PaginationContent'; import { PaginationEllipsis } from './PaginationEllipsis'; import { PaginationItem } from './PaginationItem'; +import { PaginationList } from './PaginationList'; import { PaginationNext, PaginationPrevious } from './PaginationNextPrev'; import { PaginationRoot } from './PaginationRoot'; import { usePagination } from './usePagination'; @@ -42,8 +40,8 @@ const iconSize = { lg: '2rem', }; -const Pagination = forwardRef( - ( +export const Pagination = forwardRef( + function Pagination( { nextLabel = '', previousLabel = '', @@ -57,7 +55,7 @@ const Pagination = forwardRef( ...rest }: PaginationProps, ref, - ) => { + ) { const { pages, showNextPage, showPreviousPage } = usePagination({ compact, currentPage, @@ -66,22 +64,19 @@ const Pagination = forwardRef( return ( - + { - onChange(currentPage - 1); - }} aria-label={previousLabel} + disabled={!showPreviousPage} + onClick={() => onChange(currentPage - 1)} > - {!hideLabels && previousLabel} @@ -92,11 +87,9 @@ const Pagination = forwardRef( ) : ( { - onChange(page); - }} + isActive={currentPage === page} + onClick={() => onChange(page)} > {page} @@ -106,21 +99,14 @@ const Pagination = forwardRef( { - onChange(currentPage + 1); - }} - className={cl(!showNextPage && 'ds-pagination--hidden')} + disabled={!showNextPage} + onClick={() => onChange(currentPage + 1)} > {!hideLabels && nextLabel} - - + ); }, ); - -Pagination.displayName = 'Pagination'; - -export { Pagination }; diff --git a/packages/react/src/components/Pagination/PaginationButton.tsx b/packages/react/src/components/Pagination/PaginationButton.tsx index 0d60b13b31..329248d801 100644 --- a/packages/react/src/components/Pagination/PaginationButton.tsx +++ b/packages/react/src/components/Pagination/PaginationButton.tsx @@ -21,10 +21,10 @@ export const PaginationButton = forwardRef< return (