@navikt/ds-react 4.7.4 → 4.9.0

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.
Files changed (45) hide show
  1. package/_docs.json +389 -1
  2. package/cjs/index.js +1 -0
  3. package/cjs/layout/stack/HStack.js +51 -0
  4. package/cjs/layout/stack/Spacer.js +21 -0
  5. package/cjs/layout/stack/Stack.js +51 -0
  6. package/cjs/layout/stack/VStack.js +51 -0
  7. package/cjs/layout/stack/index.js +9 -0
  8. package/cjs/layout/stack/package.json +6 -0
  9. package/cjs/layout/utilities/css.js +17 -0
  10. package/cjs/table/ExpandableRow.js +20 -8
  11. package/esm/index.d.ts +1 -0
  12. package/esm/index.js +1 -0
  13. package/esm/index.js.map +1 -1
  14. package/esm/layout/stack/HStack.d.ts +24 -0
  15. package/esm/layout/stack/HStack.js +26 -0
  16. package/esm/layout/stack/HStack.js.map +1 -0
  17. package/esm/layout/stack/Spacer.d.ts +14 -0
  18. package/esm/layout/stack/Spacer.js +15 -0
  19. package/esm/layout/stack/Spacer.js.map +1 -0
  20. package/esm/layout/stack/Stack.d.ts +26 -0
  21. package/esm/layout/stack/Stack.js +23 -0
  22. package/esm/layout/stack/Stack.js.map +1 -0
  23. package/esm/layout/stack/VStack.d.ts +24 -0
  24. package/esm/layout/stack/VStack.js +26 -0
  25. package/esm/layout/stack/VStack.js.map +1 -0
  26. package/esm/layout/stack/index.d.ts +3 -0
  27. package/esm/layout/stack/index.js +4 -0
  28. package/esm/layout/stack/index.js.map +1 -0
  29. package/esm/layout/utilities/css.d.ts +10 -0
  30. package/esm/layout/utilities/css.js +14 -0
  31. package/esm/layout/utilities/css.js.map +1 -0
  32. package/esm/table/ExpandableRow.d.ts +6 -1
  33. package/esm/table/ExpandableRow.js +20 -8
  34. package/esm/table/ExpandableRow.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/index.ts +1 -0
  37. package/src/layout/stack/HStack.tsx +30 -0
  38. package/src/layout/stack/Spacer.tsx +15 -0
  39. package/src/layout/stack/Stack.tsx +76 -0
  40. package/src/layout/stack/VStack.tsx +30 -0
  41. package/src/layout/stack/index.ts +3 -0
  42. package/src/layout/stack/stack.stories.tsx +161 -0
  43. package/src/layout/utilities/css.ts +56 -0
  44. package/src/table/ExpandableRow.tsx +28 -8
  45. package/src/table/stories/table-expandable.stories.tsx +62 -1
@@ -0,0 +1,161 @@
1
+ import React from "react";
2
+ import type { Meta } from "@storybook/react";
3
+ import { HStack, VStack, Spacer } from ".";
4
+
5
+ export default {
6
+ title: "ds-react/Stack",
7
+ component: HStack,
8
+ } satisfies Meta<typeof HStack>;
9
+
10
+ export const Horizontal = {
11
+ render: () => (
12
+ <HStack gap="4">
13
+ <Placeholders count={4} />
14
+ </HStack>
15
+ ),
16
+ };
17
+
18
+ export const Spacing = {
19
+ render: () => (
20
+ <div style={{ height: "80vh", display: "flex" }}>
21
+ <VStack gap="8">
22
+ <Spacer />
23
+ <HStack gap="4">
24
+ <Placeholders count={1} />
25
+ <Spacer />
26
+ <Placeholders count={1} />
27
+ </HStack>
28
+ <HStack gap="4">
29
+ <Placeholders count={1} />
30
+ <Placeholders count={1} />
31
+ </HStack>
32
+ <HStack gap="4">
33
+ <Placeholders count={2} />
34
+ </HStack>
35
+ </VStack>
36
+ </div>
37
+ ),
38
+ parameters: {
39
+ layout: "fullscreen",
40
+ },
41
+ };
42
+
43
+ export const Vertical = {
44
+ render: () => (
45
+ <VStack gap="4">
46
+ <Placeholders count={4} />
47
+ </VStack>
48
+ ),
49
+ };
50
+
51
+ export const VerticalDemo = {
52
+ render: () => (
53
+ <VStack gap="2">
54
+ <VStack>
55
+ <Placeholders count={4} />
56
+ </VStack>
57
+ <Placeholders count={4} />
58
+ <VStack>
59
+ <Placeholders count={4} />
60
+ </VStack>
61
+ </VStack>
62
+ ),
63
+ };
64
+
65
+ export const VerticalAlign = {
66
+ render: () => (
67
+ <VStack gap="4">
68
+ <VStack align="start">
69
+ <Placeholders count={2} />
70
+ </VStack>
71
+ <VStack align="center">
72
+ <Placeholders count={2} />
73
+ </VStack>
74
+ <VStack align="end">
75
+ <Placeholders count={2} />
76
+ </VStack>
77
+ </VStack>
78
+ ),
79
+ parameters: {
80
+ layout: "fullscreen",
81
+ },
82
+ };
83
+
84
+ export const OverrideComponent = {
85
+ render: () => (
86
+ <VStack gap="4" as="form" onSubmit={(e) => e.preventDefault()}>
87
+ <Placeholders count={4} />
88
+ </VStack>
89
+ ),
90
+ };
91
+
92
+ export const Responsive = {
93
+ render: () => (
94
+ <VStack gap={{ xs: "1", sm: "3", md: "6", lg: "10", xl: "16" }}>
95
+ <Placeholders count={4} />
96
+ </VStack>
97
+ ),
98
+ };
99
+
100
+ export const Nested = {
101
+ render: () => (
102
+ <VStack gap="16">
103
+ <Placeholders count={2}>
104
+ <VStack gap="4">
105
+ <Placeholders count={2} color="gray" />
106
+ </VStack>
107
+ </Placeholders>
108
+ </VStack>
109
+ ),
110
+ };
111
+
112
+ export const DividerDemo = {
113
+ render: () => (
114
+ <div style={{ height: "80vh", width: "40rem" }}>
115
+ <VStack gap={{ xs: "2", md: "6", lg: "12" }}>
116
+ <HStack gap={{ xs: "2", md: "6", lg: "12" }}>
117
+ <Placeholders count={1} />
118
+ <Spacer />
119
+ <Placeholders count={1} />
120
+ </HStack>
121
+ <hr
122
+ style={{
123
+ border: "none",
124
+ borderBottom: "1px solid var(--a-border-divider)",
125
+ margin: 0,
126
+ }}
127
+ />
128
+ <HStack gap={{ xs: "2", md: "6", lg: "12" }}>
129
+ <Placeholders count={2} />
130
+ </HStack>
131
+ </VStack>
132
+ </div>
133
+ ),
134
+ };
135
+
136
+ function Placeholders({
137
+ count,
138
+ children,
139
+ color,
140
+ }: {
141
+ count: number;
142
+ children?: React.ReactNode;
143
+ color?: string;
144
+ }) {
145
+ return (
146
+ <>
147
+ {Array.from({ length: count }, (_, i) => (
148
+ <div
149
+ key={i}
150
+ style={{
151
+ backgroundColor: color ?? "var(--a-purple-200)",
152
+ height: children ? "" : "3rem",
153
+ width: children ? "" : "3rem",
154
+ }}
155
+ >
156
+ {children}
157
+ </div>
158
+ ))}
159
+ </>
160
+ );
161
+ }
@@ -0,0 +1,56 @@
1
+ export type BreakpointsAlias = "xs" | "sm" | "md" | "lg" | "xl";
2
+ export type SpacingScale =
3
+ | "0"
4
+ | "05"
5
+ | "1"
6
+ | "2"
7
+ | "3"
8
+ | "4"
9
+ | "5"
10
+ | "6"
11
+ | "7"
12
+ | "8"
13
+ | "9"
14
+ | "10"
15
+ | "11"
16
+ | "12"
17
+ | "14"
18
+ | "16"
19
+ | "18"
20
+ | "20"
21
+ | "24"
22
+ | "32";
23
+
24
+ export type ResponsiveProp<T> =
25
+ | T
26
+ | {
27
+ // eslint-disable-next-line no-unused-vars
28
+ [Breakpoint in BreakpointsAlias]?: T;
29
+ };
30
+
31
+ export function getResponsiveProps(
32
+ componentName: string,
33
+ componentProp: string,
34
+ tokenSubgroup: string,
35
+ responsiveProp?:
36
+ | string
37
+ | {
38
+ // eslint-disable-next-line no-unused-vars
39
+ [Breakpoint in BreakpointsAlias]?: string;
40
+ }
41
+ ) {
42
+ if (!responsiveProp) return {};
43
+
44
+ if (typeof responsiveProp === "string") {
45
+ return {
46
+ [`--ac-${componentName}-${componentProp}-xs`]: `var(--a-${tokenSubgroup}-${responsiveProp})`,
47
+ };
48
+ }
49
+
50
+ return Object.fromEntries(
51
+ Object.entries(responsiveProp).map(([breakpointAlias, aliasOrScale]) => [
52
+ `--ac-${componentName}-${componentProp}-${breakpointAlias}`,
53
+ `var(--a-${tokenSubgroup}-${aliasOrScale})`,
54
+ ])
55
+ );
56
+ }
@@ -31,10 +31,15 @@ export interface ExpandableRowProps extends Omit<RowProps, "content"> {
31
31
  */
32
32
  onOpenChange?: (open: boolean) => void;
33
33
  /**
34
- * Disable expansio
34
+ * Disable expansion. shadeOnHover will not be visible.
35
35
  * @default false
36
36
  */
37
37
  expansionDisabled?: boolean;
38
+ /**
39
+ * Makes the whole row clickable
40
+ * @default false
41
+ */
42
+ expandOnRowClick?: boolean;
38
43
  /**
39
44
  * The width of the expanded row's internal cell
40
45
  * @default 999
@@ -58,6 +63,7 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
58
63
  open,
59
64
  onOpenChange,
60
65
  expansionDisabled = false,
66
+ expandOnRowClick = false,
61
67
  colSpan = 999,
62
68
  ...rest
63
69
  },
@@ -65,9 +71,22 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
65
71
  ) => {
66
72
  const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);
67
73
  const id = useId();
68
-
69
74
  const isOpen = open ?? internalOpen;
70
75
 
76
+ const expansionHandler = (e) => {
77
+ onOpenChange?.(!isOpen);
78
+ if (open === undefined) {
79
+ setInternalOpen((open) => !open);
80
+ }
81
+ e.stopPropagation();
82
+ };
83
+
84
+ const onRowClick = (e) => {
85
+ if (e.target.nodeName === "TD" || e.target.nodeName === "TH") {
86
+ expansionHandler(e);
87
+ }
88
+ };
89
+
71
90
  return (
72
91
  <>
73
92
  <Row
@@ -75,7 +94,13 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
75
94
  ref={ref}
76
95
  className={cl("navds-table__expandable-row", className, {
77
96
  "navds-table__expandable-row--open": isOpen,
97
+ "navds-table__expandable-row--expansion-disabled":
98
+ expansionDisabled,
78
99
  })}
100
+ onClick={(e) => {
101
+ !expansionDisabled && expandOnRowClick && onRowClick(e);
102
+ rest?.onClick?.(e);
103
+ }}
79
104
  >
80
105
  {togglePlacement === "right" && children}
81
106
  <DataCell
@@ -89,12 +114,7 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
89
114
  className="navds-table__toggle-expand-button"
90
115
  aria-controls={id}
91
116
  aria-expanded={isOpen}
92
- onClick={() => {
93
- onOpenChange?.(!isOpen);
94
- if (open === undefined) {
95
- setInternalOpen((open) => !open);
96
- }
97
- }}
117
+ onClick={expansionHandler}
98
118
  >
99
119
  <ChevronDownIcon
100
120
  className="navds-table__expandable-icon"
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from "react";
2
2
  import { Table } from "..";
3
- import { Link } from "../..";
3
+ import { Button, Checkbox, Link } from "../..";
4
4
 
5
5
  export default {
6
6
  title: "ds-react/Table",
@@ -37,6 +37,7 @@ export const Expandable = () => {
37
37
  };
38
38
 
39
39
  export const ExpandableSmall = () => {
40
+ // eslint-disable-next-line react-hooks/rules-of-hooks
40
41
  const [open, setOpen] = useState(false);
41
42
  return (
42
43
  <Table size="small">
@@ -227,3 +228,63 @@ export const ExpandableOpen = () => {
227
228
  </Table>
228
229
  );
229
230
  };
231
+
232
+ export const ClickableRow = {
233
+ render: () => {
234
+ // eslint-disable-next-line react-hooks/rules-of-hooks
235
+ const [isRowOpen1, setIsRowOpen1] = useState(false);
236
+ // eslint-disable-next-line react-hooks/rules-of-hooks
237
+ const [isRowOpen2, setIsRowOpen2] = useState(false);
238
+
239
+ return (
240
+ <>
241
+ <Table zebraStripes>
242
+ <Table.Header>
243
+ <Table.Row>
244
+ <Table.HeaderCell>Navn</Table.HeaderCell>
245
+ <Table.HeaderCell>Info</Table.HeaderCell>
246
+ <Table.HeaderCell aria-hidden />
247
+ </Table.Row>
248
+ </Table.Header>
249
+ <Table.Body>
250
+ <Table.ExpandableRow
251
+ content={<div>placeholder row 1</div>}
252
+ togglePlacement="right"
253
+ onOpenChange={setIsRowOpen1}
254
+ data-testid="row1"
255
+ open={isRowOpen1}
256
+ expandOnRowClick
257
+ >
258
+ <Table.DataCell>Ola</Table.DataCell>
259
+ <Table.DataCell>
260
+ <Button
261
+ size="xsmall"
262
+ onClick={(e) => {
263
+ alert("Mer info");
264
+ }}
265
+ >
266
+ Mer info
267
+ </Button>
268
+ </Table.DataCell>
269
+ </Table.ExpandableRow>
270
+ <Table.ExpandableRow
271
+ content={<div>placeholder row 2</div>}
272
+ togglePlacement="right"
273
+ onOpenChange={setIsRowOpen2}
274
+ data-testid="row2"
275
+ open={isRowOpen2}
276
+ expandOnRowClick
277
+ >
278
+ <Table.DataCell>Hans</Table.DataCell>
279
+ <Table.DataCell>
280
+ <Checkbox hideLabel size="small">
281
+ Sett
282
+ </Checkbox>
283
+ </Table.DataCell>
284
+ </Table.ExpandableRow>
285
+ </Table.Body>
286
+ </Table>
287
+ </>
288
+ );
289
+ },
290
+ };