@navikt/ds-react 0.14.14 → 0.15.0-rc.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/accordion/AccordionHeader.js +3 -3
- package/cjs/button/Button.js +3 -3
- package/cjs/form/checkbox/Checkbox.js +12 -1
- package/cjs/form/checkbox/useCheckbox.js +1 -1
- package/cjs/help-text/HelpText.js +4 -4
- package/cjs/index.js +2 -1
- package/cjs/{accordion-menu/AccordionMenu.js → menu/Menu.js} +7 -7
- package/cjs/{accordion-menu/AccordionMenuCollapsable.js → menu/MenuCollapse.js} +18 -8
- package/cjs/{accordion-menu/AccordionMenuItem.js → menu/MenuItem.js} +2 -2
- package/cjs/menu/MenuItems.js +23 -0
- package/cjs/menu/index.js +8 -0
- package/cjs/menu/package.json +6 -0
- package/cjs/modal/Modal.js +6 -2
- package/cjs/page-header/PageHeader.js +1 -1
- package/cjs/pagination/Pagination.js +51 -0
- package/cjs/pagination/index.js +19 -0
- package/cjs/pagination/package.json +6 -0
- package/cjs/table/ColumnHeader.js +58 -0
- package/cjs/table/Table.js +15 -2
- package/esm/accordion/AccordionHeader.js +3 -3
- package/esm/accordion/AccordionHeader.js.map +1 -1
- package/esm/button/Button.d.ts +1 -1
- package/esm/button/Button.js +3 -3
- package/esm/button/Button.js.map +1 -1
- package/esm/form/checkbox/Checkbox.d.ts +5 -0
- package/esm/form/checkbox/Checkbox.js +12 -1
- package/esm/form/checkbox/Checkbox.js.map +1 -1
- package/esm/form/checkbox/useCheckbox.d.ts +0 -1
- package/esm/form/checkbox/useCheckbox.js +1 -1
- package/esm/form/checkbox/useCheckbox.js.map +1 -1
- package/esm/help-text/HelpText.js +4 -4
- package/esm/help-text/HelpText.js.map +1 -1
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/index.js.map +1 -1
- package/esm/menu/Menu.d.ts +12 -0
- package/esm/{accordion-menu/AccordionMenu.js → menu/Menu.js} +8 -8
- package/esm/menu/Menu.js.map +1 -0
- package/esm/menu/MenuCollapse.d.ts +12 -0
- package/esm/menu/MenuCollapse.js +37 -0
- package/esm/menu/MenuCollapse.js.map +1 -0
- package/esm/menu/MenuItem.d.ts +13 -0
- package/esm/{accordion-menu/AccordionMenuItem.js → menu/MenuItem.js} +3 -3
- package/esm/menu/MenuItem.js.map +1 -0
- package/esm/{accordion-menu → menu}/MenuItems.d.ts +2 -1
- package/esm/menu/MenuItems.js +19 -0
- package/esm/menu/MenuItems.js.map +1 -0
- package/esm/menu/index.d.ts +3 -0
- package/esm/menu/index.js +2 -0
- package/esm/menu/index.js.map +1 -0
- package/esm/modal/Modal.d.ts +3 -0
- package/esm/modal/Modal.js +6 -2
- package/esm/modal/Modal.js.map +1 -1
- package/esm/page-header/PageHeader.js +1 -1
- package/esm/page-header/PageHeader.js.map +1 -1
- package/esm/pagination/Pagination.d.ts +43 -0
- package/esm/pagination/Pagination.js +45 -0
- package/esm/pagination/Pagination.js.map +1 -0
- package/esm/pagination/index.d.ts +2 -0
- package/esm/pagination/index.js +3 -0
- package/esm/pagination/index.js.map +1 -0
- package/esm/table/ColumnHeader.d.ts +16 -0
- package/esm/table/ColumnHeader.js +35 -0
- package/esm/table/ColumnHeader.js.map +1 -0
- package/esm/table/Table.d.ts +16 -0
- package/esm/table/Table.js +15 -2
- package/esm/table/Table.js.map +1 -1
- package/esm/typography/Heading.d.ts +1 -1
- package/package.json +5 -3
- package/src/accordion/AccordionHeader.tsx +4 -3
- package/src/accordion/accordion.stories.tsx +90 -0
- package/src/alert/{stories/alert.stories.tsx → alert.stories.tsx} +2 -2
- package/src/button/Button.tsx +7 -3
- package/src/button/{stories/button.stories.tsx → button.stories.tsx} +12 -1
- package/src/form/checkbox/Checkbox.tsx +18 -1
- package/src/form/checkbox/stories/checkbox.stories.tsx +38 -1
- package/src/form/checkbox/useCheckbox.ts +0 -1
- package/src/help-text/HelpText.tsx +4 -5
- package/src/index.ts +2 -1
- package/src/menu/Menu.tsx +36 -0
- package/src/menu/MenuCollapse.tsx +80 -0
- package/src/{accordion-menu/AccordionMenuItem.tsx → menu/MenuItem.tsx} +6 -6
- package/src/menu/MenuItems.tsx +21 -0
- package/src/menu/index.ts +3 -0
- package/src/menu/stories/menu.stories.mdx +93 -0
- package/src/menu/stories/menu.stories.tsx +139 -0
- package/src/modal/Modal.tsx +11 -0
- package/src/page-header/PageHeader.tsx +1 -5
- package/src/pagination/Pagination.tsx +143 -0
- package/src/pagination/index.ts +2 -0
- package/src/pagination/steps.test.ts +120 -0
- package/src/pagination/stories/pagination.stories.tsx +18 -0
- package/src/table/ColumnHeader.tsx +70 -0
- package/src/table/Table.tsx +30 -2
- package/src/table/stories/people.json +822 -0
- package/src/table/stories/table-async.stories.tsx +166 -0
- package/src/table/stories/table-hot.stories.tsx +377 -0
- package/src/table/stories/table.stories.tsx +35 -2
- package/src/typography/Heading.tsx +1 -1
- package/src/typography/{stories/typography.stories.tsx → typography.stories.tsx} +4 -5
- package/cjs/accordion-menu/MenuItems.js +0 -8
- package/cjs/accordion-menu/index.js +0 -8
- package/cjs/accordion-menu/package.json +0 -6
- package/esm/accordion-menu/AccordionMenu.d.ts +0 -12
- package/esm/accordion-menu/AccordionMenu.js.map +0 -1
- package/esm/accordion-menu/AccordionMenuCollapsable.d.ts +0 -9
- package/esm/accordion-menu/AccordionMenuCollapsable.js +0 -28
- package/esm/accordion-menu/AccordionMenuCollapsable.js.map +0 -1
- package/esm/accordion-menu/AccordionMenuItem.d.ts +0 -13
- package/esm/accordion-menu/AccordionMenuItem.js.map +0 -1
- package/esm/accordion-menu/MenuItems.js +0 -4
- package/esm/accordion-menu/MenuItems.js.map +0 -1
- package/esm/accordion-menu/index.d.ts +0 -3
- package/esm/accordion-menu/index.js +0 -2
- package/esm/accordion-menu/index.js.map +0 -1
- package/src/accordion/stories/accordion.stories.mdx +0 -72
- package/src/accordion/stories/accordion.stories.tsx +0 -92
- package/src/accordion-menu/AccordionMenu.tsx +0 -39
- package/src/accordion-menu/AccordionMenuCollapsable.tsx +0 -45
- package/src/accordion-menu/MenuItems.tsx +0 -13
- package/src/accordion-menu/index.ts +0 -3
- package/src/accordion-menu/stories/accordion-menu.stories.mdx +0 -66
- package/src/accordion-menu/stories/accordion-menu.stories.tsx +0 -141
- package/src/alert/stories/alert.stories.mdx +0 -96
- package/src/button/stories/button.stories.mdx +0 -76
- package/src/typography/stories/index.css +0 -3
- package/src/typography/stories/typography.stories.mdx +0 -83
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import cl from "classnames";
|
|
3
|
+
import { Menu } from "../index";
|
|
4
|
+
import { NavLink, HashRouter as Router } from "react-router-dom";
|
|
5
|
+
import { MenuItemProps } from "../MenuItem";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: "ds-react/menu",
|
|
9
|
+
component: { Menu },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const All = () => (
|
|
13
|
+
<div
|
|
14
|
+
style={{
|
|
15
|
+
background: "white",
|
|
16
|
+
maxWidth: 288,
|
|
17
|
+
}}
|
|
18
|
+
>
|
|
19
|
+
<h2>Only Menu.Items</h2>
|
|
20
|
+
<Menu>
|
|
21
|
+
<Menu.Item href="#leo">Leo</Menu.Item>
|
|
22
|
+
|
|
23
|
+
<Menu.Item href="#justo">Justo</Menu.Item>
|
|
24
|
+
<Menu.Item href="#pariatur" active aria-current="page">
|
|
25
|
+
Pariatur
|
|
26
|
+
</Menu.Item>
|
|
27
|
+
<Menu.Item href="#nulla">Nulla</Menu.Item>
|
|
28
|
+
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
|
29
|
+
<Menu.Item href="#justo">Justo</Menu.Item>
|
|
30
|
+
<Menu.Item href="#pariatur">Pariatur</Menu.Item>
|
|
31
|
+
</Menu>
|
|
32
|
+
|
|
33
|
+
<h2>With Collapse</h2>
|
|
34
|
+
<Menu>
|
|
35
|
+
<Menu.Item href="#leo">Leo</Menu.Item>
|
|
36
|
+
<Menu.Collapse title="Proin">
|
|
37
|
+
<Menu.Item href="#nulla" active>
|
|
38
|
+
Nulla
|
|
39
|
+
</Menu.Item>
|
|
40
|
+
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
|
41
|
+
</Menu.Collapse>
|
|
42
|
+
<Menu.Collapse title="Accumsan">
|
|
43
|
+
<Menu.Item href="#justo" aria-current="page">
|
|
44
|
+
Justo
|
|
45
|
+
</Menu.Item>
|
|
46
|
+
<Menu.Item href="#pariatur" active aria-current="page">
|
|
47
|
+
Pariatur
|
|
48
|
+
</Menu.Item>
|
|
49
|
+
<Menu.Collapse title="Proin">
|
|
50
|
+
<Menu.Item href="#nulla" active>
|
|
51
|
+
Nulla
|
|
52
|
+
</Menu.Item>
|
|
53
|
+
<Menu.Collapse title="Proin">
|
|
54
|
+
<Menu.Item href="#nulla" active>
|
|
55
|
+
Nulla
|
|
56
|
+
</Menu.Item>
|
|
57
|
+
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
|
58
|
+
</Menu.Collapse>
|
|
59
|
+
<Menu.Item href="#luctus">Luctus</Menu.Item>
|
|
60
|
+
</Menu.Collapse>
|
|
61
|
+
</Menu.Collapse>
|
|
62
|
+
<Menu.Item href="#justo" active>
|
|
63
|
+
Justo
|
|
64
|
+
</Menu.Item>
|
|
65
|
+
<Menu.Item href="#pariatur">Pariatur</Menu.Item>
|
|
66
|
+
</Menu>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
export const ReactRouter = () => {
|
|
71
|
+
const MenuItemLink = (props: MenuItemProps & { to: string }) => {
|
|
72
|
+
const { to } = props;
|
|
73
|
+
|
|
74
|
+
const CustomLink = React.useMemo(
|
|
75
|
+
() => (props: MenuItemProps) => (
|
|
76
|
+
<NavLink
|
|
77
|
+
{...props}
|
|
78
|
+
to={to}
|
|
79
|
+
className={({ isActive }) =>
|
|
80
|
+
cl(props.className, {
|
|
81
|
+
"navds-menu-item--active": isActive,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
/>
|
|
85
|
+
),
|
|
86
|
+
[to]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Menu.Item {...props} as={CustomLink}>
|
|
91
|
+
Nivå 2 innrykk 2 rems
|
|
92
|
+
</Menu.Item>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Menu>
|
|
98
|
+
<Menu.Collapse title="Nivå 1 - 3 rems høy">
|
|
99
|
+
<MenuItemLink to="/1">Nivå 2 innrykk 2 rems</MenuItemLink>
|
|
100
|
+
<MenuItemLink to="/2">Subtitle 3</MenuItemLink>
|
|
101
|
+
</Menu.Collapse>
|
|
102
|
+
<Menu.Collapse title="Skjema og søknad">
|
|
103
|
+
<Menu.Collapse title="Satser">
|
|
104
|
+
<MenuItemLink to="/3">
|
|
105
|
+
Nivå 3 - 3 rems innrykk Har du rett til dagpenger når du mottar
|
|
106
|
+
annen økonomisk støtte fra NAV?
|
|
107
|
+
</MenuItemLink>
|
|
108
|
+
</Menu.Collapse>
|
|
109
|
+
<MenuItemLink to="/4">Subtitle 2</MenuItemLink>
|
|
110
|
+
<MenuItemLink to="/5">Subtitle 3</MenuItemLink>
|
|
111
|
+
</Menu.Collapse>
|
|
112
|
+
<Menu.Collapse title="Sakbehandlingstider">
|
|
113
|
+
<Menu.Collapse title="Subtitle 4 Lang tittel på andre nivå lorem ipsum">
|
|
114
|
+
<MenuItemLink to="/6">Subtitle 5</MenuItemLink>
|
|
115
|
+
</Menu.Collapse>
|
|
116
|
+
</Menu.Collapse>
|
|
117
|
+
<MenuItemLink to="/7">Internasjonalt</MenuItemLink>
|
|
118
|
+
<Menu.Collapse title="Meld fra om endringer lang tittel på første nivå">
|
|
119
|
+
<MenuItemLink to="/8">Subtitle 6</MenuItemLink>
|
|
120
|
+
</Menu.Collapse>
|
|
121
|
+
<MenuItemLink to="/9">Nivå 1</MenuItemLink>
|
|
122
|
+
</Menu>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
ReactRouter.decorators = [
|
|
127
|
+
(Story) => (
|
|
128
|
+
<Router>
|
|
129
|
+
<div
|
|
130
|
+
style={{
|
|
131
|
+
background: "white",
|
|
132
|
+
maxWidth: 288,
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<Story />
|
|
136
|
+
</div>
|
|
137
|
+
</Router>
|
|
138
|
+
),
|
|
139
|
+
];
|
package/src/modal/Modal.tsx
CHANGED
|
@@ -33,6 +33,9 @@ export interface ModalProps {
|
|
|
33
33
|
* @default true
|
|
34
34
|
*/
|
|
35
35
|
closeButton?: boolean;
|
|
36
|
+
"aria-labelledby"?: string;
|
|
37
|
+
"aria-describedby"?: string;
|
|
38
|
+
"aria-modal"?: boolean;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
interface ModalComponent
|
|
@@ -56,6 +59,9 @@ const Modal = forwardRef<ReactModal, ModalProps>(
|
|
|
56
59
|
className,
|
|
57
60
|
shouldCloseOnOverlayClick = true,
|
|
58
61
|
closeButton = true,
|
|
62
|
+
"aria-describedby": ariaDescribedBy,
|
|
63
|
+
"aria-labelledby": ariaLabelledBy,
|
|
64
|
+
"aria-modal": ariaModal,
|
|
59
65
|
...rest
|
|
60
66
|
},
|
|
61
67
|
ref
|
|
@@ -81,6 +87,11 @@ const Modal = forwardRef<ReactModal, ModalProps>(
|
|
|
81
87
|
overlayClassName="navds-modal__overlay"
|
|
82
88
|
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
|
|
83
89
|
onRequestClose={(e) => onModalCloseRequest(e)}
|
|
90
|
+
aria={{
|
|
91
|
+
describedby: ariaDescribedBy,
|
|
92
|
+
labelledby: ariaLabelledBy,
|
|
93
|
+
modal: ariaModal,
|
|
94
|
+
}}
|
|
84
95
|
>
|
|
85
96
|
{children}
|
|
86
97
|
{closeButton && (
|
|
@@ -55,11 +55,7 @@ const PageHeader = forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
55
55
|
<div className="navds-page-header__illustration">{illustration}</div>
|
|
56
56
|
)}
|
|
57
57
|
<div className="navds-page-header__wrapper">
|
|
58
|
-
<Heading
|
|
59
|
-
className="navds-page-header__title"
|
|
60
|
-
size="2xlarge"
|
|
61
|
-
level="1"
|
|
62
|
-
>
|
|
58
|
+
<Heading className="navds-page-header__title" size="xlarge" level="1">
|
|
63
59
|
{children}
|
|
64
60
|
</Heading>
|
|
65
61
|
{description && (
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import cl from "classnames";
|
|
3
|
+
import { Back, Next } from "@navikt/ds-icons";
|
|
4
|
+
import { BodyShort } from "..";
|
|
5
|
+
|
|
6
|
+
interface PaginationProps extends React.HTMLAttributes<HTMLElement> {
|
|
7
|
+
/**
|
|
8
|
+
* Current page
|
|
9
|
+
*/
|
|
10
|
+
page: number;
|
|
11
|
+
/**
|
|
12
|
+
* Number of always visible pages before and after the current page.
|
|
13
|
+
* @default 1
|
|
14
|
+
*/
|
|
15
|
+
siblingCount?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Number of always visible pages at the beginning and end.
|
|
18
|
+
* @default 1
|
|
19
|
+
*/
|
|
20
|
+
boundaryCount?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Callback when current page changes
|
|
23
|
+
*/
|
|
24
|
+
onPageChange: (page: number) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Total number of pages
|
|
27
|
+
*/
|
|
28
|
+
count: number;
|
|
29
|
+
/**
|
|
30
|
+
* Changes padding, height and font-size
|
|
31
|
+
* @default "medium"
|
|
32
|
+
*/
|
|
33
|
+
size?: "medium" | "small";
|
|
34
|
+
/**
|
|
35
|
+
* Display text alongside "previous" and "next" icons
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
prevNextTexts?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const getSteps = ({
|
|
42
|
+
page,
|
|
43
|
+
count,
|
|
44
|
+
boundaryCount = 1,
|
|
45
|
+
siblingCount = 1,
|
|
46
|
+
}) => {
|
|
47
|
+
const range = (start: number, end: number) =>
|
|
48
|
+
Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
|
49
|
+
|
|
50
|
+
if (count <= (boundaryCount + siblingCount) * 2 + 3) return range(1, count);
|
|
51
|
+
|
|
52
|
+
const startPages = range(1, boundaryCount);
|
|
53
|
+
const endPages = range(count - boundaryCount + 1, count);
|
|
54
|
+
|
|
55
|
+
const siblingsStart = Math.max(
|
|
56
|
+
Math.min(page - siblingCount, count - boundaryCount - siblingCount * 2 - 1),
|
|
57
|
+
boundaryCount + 2
|
|
58
|
+
);
|
|
59
|
+
const siblingsEnd = siblingsStart + siblingCount * 2;
|
|
60
|
+
|
|
61
|
+
return [
|
|
62
|
+
...startPages,
|
|
63
|
+
siblingsStart - (startPages[startPages.length - 1] ?? 0) === 2
|
|
64
|
+
? siblingsStart - 1
|
|
65
|
+
: "ellipsis",
|
|
66
|
+
...range(siblingsStart, siblingsEnd),
|
|
67
|
+
(endPages[0] ?? count + 1) - siblingsEnd === 2
|
|
68
|
+
? siblingsEnd + 1
|
|
69
|
+
: "ellipsis",
|
|
70
|
+
...endPages,
|
|
71
|
+
];
|
|
72
|
+
};
|
|
73
|
+
const Pagination = ({
|
|
74
|
+
page,
|
|
75
|
+
onPageChange,
|
|
76
|
+
count,
|
|
77
|
+
siblingCount,
|
|
78
|
+
boundaryCount,
|
|
79
|
+
className,
|
|
80
|
+
size = "medium",
|
|
81
|
+
prevNextTexts = false,
|
|
82
|
+
}: PaginationProps) => {
|
|
83
|
+
return (
|
|
84
|
+
<nav
|
|
85
|
+
className={cl("navds-pagination", `navds-pagination--${size}`, className)}
|
|
86
|
+
>
|
|
87
|
+
<ul className="navds-pagination__list">
|
|
88
|
+
<li>
|
|
89
|
+
<button
|
|
90
|
+
className="navds-pagination__previous"
|
|
91
|
+
disabled={page === 1}
|
|
92
|
+
onClick={() => onPageChange(page - 1)}
|
|
93
|
+
>
|
|
94
|
+
<Back
|
|
95
|
+
className="navds-pagination__previous-icon"
|
|
96
|
+
title={prevNextTexts ? undefined : "Tilbake"}
|
|
97
|
+
role={prevNextTexts ? "presentation" : undefined}
|
|
98
|
+
/>
|
|
99
|
+
{prevNextTexts && <BodyShort size={size}>Tilbake</BodyShort>}
|
|
100
|
+
</button>
|
|
101
|
+
</li>
|
|
102
|
+
{getSteps({ page, count, siblingCount, boundaryCount }).map(
|
|
103
|
+
(step, i) => {
|
|
104
|
+
const n = Number(step);
|
|
105
|
+
return isNaN(n) ? (
|
|
106
|
+
<li className="navds-pagination__ellipsis" key={`${step}${i}`}>
|
|
107
|
+
<BodyShort size={size}>...</BodyShort>
|
|
108
|
+
</li>
|
|
109
|
+
) : (
|
|
110
|
+
<li key={step}>
|
|
111
|
+
<BodyShort
|
|
112
|
+
size={size}
|
|
113
|
+
as="button"
|
|
114
|
+
className="navds-pagination__item"
|
|
115
|
+
onClick={() => onPageChange(n)}
|
|
116
|
+
aria-current={page === n ? true : undefined}
|
|
117
|
+
>
|
|
118
|
+
{n}
|
|
119
|
+
</BodyShort>
|
|
120
|
+
</li>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
)}
|
|
124
|
+
<li>
|
|
125
|
+
<button
|
|
126
|
+
className="navds-pagination__next"
|
|
127
|
+
disabled={page === count}
|
|
128
|
+
onClick={() => onPageChange(page + 1)}
|
|
129
|
+
>
|
|
130
|
+
{prevNextTexts && <BodyShort size={size}>Neste</BodyShort>}
|
|
131
|
+
<Next
|
|
132
|
+
className="navds-pagination__next-icon"
|
|
133
|
+
title={prevNextTexts ? undefined : "Neste"}
|
|
134
|
+
role={prevNextTexts ? "presentation" : undefined}
|
|
135
|
+
/>
|
|
136
|
+
</button>
|
|
137
|
+
</li>
|
|
138
|
+
</ul>
|
|
139
|
+
</nav>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export default Pagination;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import faker from "faker";
|
|
2
|
+
import { getSteps } from "./Pagination";
|
|
3
|
+
|
|
4
|
+
describe("getSteps", () => {
|
|
5
|
+
it("lists all pages when count is <= 7", () => {
|
|
6
|
+
const count = faker.datatype.number({ min: 1, max: 7 });
|
|
7
|
+
expect(getSteps({ page: 1, count })).toEqual(
|
|
8
|
+
Array.from({ length: count }, (_, i) => i + 1)
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("has an end ellipsis when count >= 8", () => {
|
|
13
|
+
const count = faker.datatype.number({ min: 8 });
|
|
14
|
+
const page = faker.datatype.number({ min: 1, max: 4 });
|
|
15
|
+
expect(
|
|
16
|
+
getSteps({
|
|
17
|
+
page,
|
|
18
|
+
count,
|
|
19
|
+
})
|
|
20
|
+
).toEqual([1, 2, 3, 4, 5, "ellipsis", count]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("has a start ellipsis when count - page >= 3", () => {
|
|
24
|
+
const count = faker.datatype.number({ min: 8 });
|
|
25
|
+
const page = faker.datatype.number({ min: count - 3, max: count });
|
|
26
|
+
expect(
|
|
27
|
+
getSteps({
|
|
28
|
+
page,
|
|
29
|
+
count,
|
|
30
|
+
})
|
|
31
|
+
).toEqual([
|
|
32
|
+
1,
|
|
33
|
+
"ellipsis",
|
|
34
|
+
count - 4,
|
|
35
|
+
count - 3,
|
|
36
|
+
count - 2,
|
|
37
|
+
count - 1,
|
|
38
|
+
count,
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("has start & end ellipsis when count is high", () => {
|
|
43
|
+
const count = faker.datatype.number({ min: 9 });
|
|
44
|
+
const page = faker.datatype.number({ min: 5, max: count - 4 });
|
|
45
|
+
expect(
|
|
46
|
+
getSteps({
|
|
47
|
+
page,
|
|
48
|
+
count,
|
|
49
|
+
})
|
|
50
|
+
).toEqual([1, "ellipsis", page - 1, page, page + 1, "ellipsis", count]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("can have a reduced siblingCount", () => {
|
|
54
|
+
const count = faker.datatype.number({ min: 7 });
|
|
55
|
+
const page = faker.datatype.number({ min: 4, max: count - 3 });
|
|
56
|
+
expect(
|
|
57
|
+
getSteps({
|
|
58
|
+
page,
|
|
59
|
+
count,
|
|
60
|
+
siblingCount: 0,
|
|
61
|
+
})
|
|
62
|
+
).toEqual([1, "ellipsis", page, "ellipsis", count]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("can have an increased siblingCount", () => {
|
|
66
|
+
const count = faker.datatype.number({ min: 11 });
|
|
67
|
+
const page = faker.datatype.number({ min: 6, max: count - 5 });
|
|
68
|
+
expect(
|
|
69
|
+
getSteps({
|
|
70
|
+
page,
|
|
71
|
+
count,
|
|
72
|
+
siblingCount: 2,
|
|
73
|
+
})
|
|
74
|
+
).toEqual([
|
|
75
|
+
1,
|
|
76
|
+
"ellipsis",
|
|
77
|
+
page - 2,
|
|
78
|
+
page - 1,
|
|
79
|
+
page,
|
|
80
|
+
page + 1,
|
|
81
|
+
page + 2,
|
|
82
|
+
"ellipsis",
|
|
83
|
+
count,
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("can have an reduced boundaryCount", () => {
|
|
88
|
+
const count = faker.datatype.number({ min: 7 });
|
|
89
|
+
const page = faker.datatype.number({ min: 4, max: count - 3 });
|
|
90
|
+
expect(
|
|
91
|
+
getSteps({
|
|
92
|
+
page,
|
|
93
|
+
count,
|
|
94
|
+
boundaryCount: 0,
|
|
95
|
+
})
|
|
96
|
+
).toEqual(["ellipsis", page - 1, page, page + 1, "ellipsis"]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("can have an increased boundaryCount", () => {
|
|
100
|
+
const count = faker.datatype.number({ min: 11 });
|
|
101
|
+
const page = faker.datatype.number({ min: 6, max: count - 5 });
|
|
102
|
+
expect(
|
|
103
|
+
getSteps({
|
|
104
|
+
page,
|
|
105
|
+
count,
|
|
106
|
+
boundaryCount: 2,
|
|
107
|
+
})
|
|
108
|
+
).toEqual([
|
|
109
|
+
1,
|
|
110
|
+
2,
|
|
111
|
+
"ellipsis",
|
|
112
|
+
page - 1,
|
|
113
|
+
page,
|
|
114
|
+
page + 1,
|
|
115
|
+
"ellipsis",
|
|
116
|
+
count - 1,
|
|
117
|
+
count,
|
|
118
|
+
]);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import Pagination from "../Pagination";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ds-react/pagination",
|
|
6
|
+
component: Pagination,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const All = (props) => {
|
|
10
|
+
const [page, setPage] = useState(props.page);
|
|
11
|
+
return <Pagination {...props} page={page} onPageChange={setPage} />;
|
|
12
|
+
};
|
|
13
|
+
All.args = {
|
|
14
|
+
page: 1,
|
|
15
|
+
count: 8,
|
|
16
|
+
siblingCount: 1,
|
|
17
|
+
boundaryCount: 1,
|
|
18
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React, { forwardRef, useContext } from "react";
|
|
2
|
+
import { TableContext } from "..";
|
|
3
|
+
import { Down, Up } from "@navikt/ds-icons";
|
|
4
|
+
import HeaderCell from "./HeaderCell";
|
|
5
|
+
|
|
6
|
+
interface ColumnHeaderProps extends React.HTMLAttributes<HTMLTableCellElement> {
|
|
7
|
+
/**
|
|
8
|
+
* Column is sortable
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
sortable?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Key to sort by
|
|
14
|
+
*/
|
|
15
|
+
sortKey?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ColumnHeaderType
|
|
19
|
+
extends React.ForwardRefExoticComponent<
|
|
20
|
+
ColumnHeaderProps & React.RefAttributes<HTMLTableCellElement>
|
|
21
|
+
> {}
|
|
22
|
+
|
|
23
|
+
const ColumnHeader: ColumnHeaderType = forwardRef(
|
|
24
|
+
({ className, children, sortable = false, sortKey, ...rest }, ref) => {
|
|
25
|
+
const context = useContext(TableContext);
|
|
26
|
+
|
|
27
|
+
if (sortable && !sortKey) {
|
|
28
|
+
console.warn("ColumnHeader with `sortable=true` must have a sortKey.");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<HeaderCell
|
|
33
|
+
scope="col"
|
|
34
|
+
ref={ref}
|
|
35
|
+
className={className}
|
|
36
|
+
aria-sort={
|
|
37
|
+
sortable
|
|
38
|
+
? context?.sort?.orderBy === sortKey
|
|
39
|
+
? context?.sort?.direction
|
|
40
|
+
: "none"
|
|
41
|
+
: undefined
|
|
42
|
+
}
|
|
43
|
+
{...rest}
|
|
44
|
+
>
|
|
45
|
+
{sortable ? (
|
|
46
|
+
<button
|
|
47
|
+
className="navds-table__sort-button"
|
|
48
|
+
onClick={
|
|
49
|
+
sortable && sortKey
|
|
50
|
+
? () => context?.onSortChange?.(sortKey)
|
|
51
|
+
: undefined
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
{context?.sort?.orderBy === sortKey &&
|
|
56
|
+
context?.sort?.direction === "descending" ? (
|
|
57
|
+
<Down aria-label="sorter synkende" />
|
|
58
|
+
) : (
|
|
59
|
+
<Up aria-label="sorter stigende" />
|
|
60
|
+
)}
|
|
61
|
+
</button>
|
|
62
|
+
) : (
|
|
63
|
+
children
|
|
64
|
+
)}
|
|
65
|
+
</HeaderCell>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
export default ColumnHeader;
|
package/src/table/Table.tsx
CHANGED
|
@@ -3,9 +3,15 @@ import cl from "classnames";
|
|
|
3
3
|
import Header, { HeaderType } from "./Header";
|
|
4
4
|
import Body, { BodyType } from "./Body";
|
|
5
5
|
import Row, { RowType } from "./Row";
|
|
6
|
+
import ColumnHeader, { ColumnHeaderType } from "./ColumnHeader";
|
|
6
7
|
import HeaderCell, { HeaderCellType } from "./HeaderCell";
|
|
7
8
|
import DataCell, { DataCellType } from "./DataCell";
|
|
8
9
|
|
|
10
|
+
export interface SortState {
|
|
11
|
+
orderBy: string;
|
|
12
|
+
direction: "ascending" | "descending";
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
export interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
|
|
10
16
|
/**
|
|
11
17
|
* Changes padding
|
|
@@ -17,6 +23,14 @@ export interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
|
|
|
17
23
|
* @default false
|
|
18
24
|
*/
|
|
19
25
|
zebraStripes?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Sort state
|
|
28
|
+
*/
|
|
29
|
+
sort?: SortState;
|
|
30
|
+
/**
|
|
31
|
+
* Callback whens ort state changes
|
|
32
|
+
*/
|
|
33
|
+
onSortChange?: (sortKey?: string) => void;
|
|
20
34
|
}
|
|
21
35
|
|
|
22
36
|
export interface TableType
|
|
@@ -28,17 +42,30 @@ export interface TableType
|
|
|
28
42
|
Row: RowType;
|
|
29
43
|
DataCell: DataCellType;
|
|
30
44
|
HeaderCell: HeaderCellType;
|
|
45
|
+
ColumnHeader: ColumnHeaderType;
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
export interface TableContextProps {
|
|
34
49
|
size: "medium" | "small";
|
|
50
|
+
onSortChange?: (sortKey: string) => void;
|
|
51
|
+
sort?: SortState;
|
|
35
52
|
}
|
|
36
53
|
|
|
37
54
|
export const TableContext = createContext<TableContextProps | null>(null);
|
|
38
55
|
|
|
39
56
|
const Table = forwardRef(
|
|
40
|
-
(
|
|
41
|
-
|
|
57
|
+
(
|
|
58
|
+
{
|
|
59
|
+
className,
|
|
60
|
+
zebraStripes = false,
|
|
61
|
+
size = "medium",
|
|
62
|
+
onSortChange,
|
|
63
|
+
sort,
|
|
64
|
+
...rest
|
|
65
|
+
},
|
|
66
|
+
ref
|
|
67
|
+
) => (
|
|
68
|
+
<TableContext.Provider value={{ size, onSortChange, sort }}>
|
|
42
69
|
<table
|
|
43
70
|
{...rest}
|
|
44
71
|
ref={ref}
|
|
@@ -53,6 +80,7 @@ const Table = forwardRef(
|
|
|
53
80
|
Table.Header = Header;
|
|
54
81
|
Table.Body = Body;
|
|
55
82
|
Table.Row = Row;
|
|
83
|
+
Table.ColumnHeader = ColumnHeader;
|
|
56
84
|
Table.HeaderCell = HeaderCell;
|
|
57
85
|
Table.DataCell = DataCell;
|
|
58
86
|
|