@navikt/ds-react 1.0.0-rc.2 → 1.0.0-rc.3

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 (80) hide show
  1. package/cjs/accordion/AccordionContent.js +5 -8
  2. package/cjs/accordion/AccordionHeader.js +6 -16
  3. package/cjs/accordion/AccordionItem.js +1 -5
  4. package/cjs/button/Button.js +6 -3
  5. package/cjs/form/ConfirmationPanel.js +2 -2
  6. package/cjs/form/checkbox/Checkbox.js +1 -0
  7. package/cjs/form/search/SearchButton.js +1 -3
  8. package/cjs/guide-panel/Illustration.js +34 -16
  9. package/cjs/guide-panel/index.js +3 -1
  10. package/cjs/modal/Modal.js +1 -2
  11. package/cjs/pagination/Pagination.js +6 -6
  12. package/cjs/read-more/ReadMore.js +6 -10
  13. package/cjs/table/ExpandableRow.js +6 -9
  14. package/cjs/tabs/Tab.js +1 -1
  15. package/cjs/util/AnimateHeight.js +204 -0
  16. package/esm/accordion/AccordionContent.d.ts +0 -2
  17. package/esm/accordion/AccordionContent.js +5 -8
  18. package/esm/accordion/AccordionContent.js.map +1 -1
  19. package/esm/accordion/AccordionHeader.js +6 -16
  20. package/esm/accordion/AccordionHeader.js.map +1 -1
  21. package/esm/accordion/AccordionItem.d.ts +0 -8
  22. package/esm/accordion/AccordionItem.js +1 -5
  23. package/esm/accordion/AccordionItem.js.map +1 -1
  24. package/esm/button/Button.d.ts +10 -1
  25. package/esm/button/Button.js +7 -4
  26. package/esm/button/Button.js.map +1 -1
  27. package/esm/form/ConfirmationPanel.d.ts +1 -1
  28. package/esm/form/ConfirmationPanel.js +2 -2
  29. package/esm/form/ConfirmationPanel.js.map +1 -1
  30. package/esm/form/checkbox/Checkbox.d.ts +5 -1
  31. package/esm/form/checkbox/Checkbox.js +1 -0
  32. package/esm/form/checkbox/Checkbox.js.map +1 -1
  33. package/esm/form/search/SearchButton.d.ts +1 -1
  34. package/esm/form/search/SearchButton.js +1 -3
  35. package/esm/form/search/SearchButton.js.map +1 -1
  36. package/esm/guide-panel/Illustration.d.ts +7 -2
  37. package/esm/guide-panel/Illustration.js +34 -16
  38. package/esm/guide-panel/Illustration.js.map +1 -1
  39. package/esm/guide-panel/index.d.ts +1 -0
  40. package/esm/guide-panel/index.js +1 -0
  41. package/esm/guide-panel/index.js.map +1 -1
  42. package/esm/modal/Modal.js +1 -2
  43. package/esm/modal/Modal.js.map +1 -1
  44. package/esm/pagination/Pagination.js +6 -6
  45. package/esm/pagination/Pagination.js.map +1 -1
  46. package/esm/pagination/PaginationItem.d.ts +2 -2
  47. package/esm/pagination/PaginationItem.js.map +1 -1
  48. package/esm/read-more/ReadMore.d.ts +0 -5
  49. package/esm/read-more/ReadMore.js +6 -10
  50. package/esm/read-more/ReadMore.js.map +1 -1
  51. package/esm/table/ExpandableRow.js +6 -9
  52. package/esm/table/ExpandableRow.js.map +1 -1
  53. package/esm/tabs/Tab.js +1 -1
  54. package/esm/tabs/Tab.js.map +1 -1
  55. package/esm/util/AnimateHeight.d.ts +10 -0
  56. package/esm/util/AnimateHeight.js +180 -0
  57. package/esm/util/AnimateHeight.js.map +1 -0
  58. package/package.json +3 -4
  59. package/src/accordion/AccordionContent.tsx +13 -15
  60. package/src/accordion/AccordionHeader.tsx +13 -30
  61. package/src/accordion/AccordionItem.tsx +1 -22
  62. package/src/accordion/accordion.stories.tsx +30 -31
  63. package/src/button/Button.tsx +35 -10
  64. package/src/button/button.stories.tsx +55 -28
  65. package/src/form/ConfirmationPanel.test.tsx +14 -0
  66. package/src/form/ConfirmationPanel.tsx +8 -3
  67. package/src/form/checkbox/Checkbox.test.tsx +4 -0
  68. package/src/form/checkbox/Checkbox.tsx +6 -1
  69. package/src/form/checkbox/checkbox.stories.tsx +2 -1
  70. package/src/form/search/SearchButton.tsx +7 -3
  71. package/src/guide-panel/Illustration.tsx +118 -98
  72. package/src/guide-panel/index.ts +1 -0
  73. package/src/modal/Modal.tsx +2 -3
  74. package/src/pagination/Pagination.tsx +17 -8
  75. package/src/pagination/PaginationItem.tsx +2 -3
  76. package/src/read-more/ReadMore.tsx +18 -29
  77. package/src/read-more/readmore.stories.tsx +1 -1
  78. package/src/table/ExpandableRow.tsx +11 -12
  79. package/src/tabs/Tab.tsx +1 -9
  80. package/src/util/AnimateHeight.tsx +240 -0
@@ -1,9 +1,8 @@
1
1
  import React, { forwardRef } from "react";
2
2
  import cl from "clsx";
3
- import { Button, OverridableComponent } from "..";
3
+ import { Button, ButtonProps, OverridableComponent } from "..";
4
4
 
5
- export interface PaginationItemProps
6
- extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
+ export interface PaginationItemProps extends ButtonProps {
7
6
  children: React.ReactNode;
8
7
  /**
9
8
  * Sets selected styling if true
@@ -1,9 +1,9 @@
1
1
  import React, { forwardRef, useState } from "react";
2
2
  import cl from "clsx";
3
- import { Collapse, UnmountClosed } from "react-collapse";
4
3
  import { Expand } from "@navikt/ds-icons";
5
4
  import { BodyLong } from "../typography";
6
5
  import { ExpandFilled } from "@navikt/ds-icons";
6
+ import AnimateHeight from "../util/AnimateHeight";
7
7
 
8
8
  export interface ReadMoreProps
9
9
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
@@ -25,11 +25,6 @@ export interface ReadMoreProps
25
25
  * @default false
26
26
  */
27
27
  defaultOpen?: boolean;
28
- /**
29
- * Removes content-element from dom when closed
30
- * @default false
31
- */
32
- renderContentWhenClosed?: boolean;
33
28
  /**
34
29
  * Changes fontsize for content
35
30
  * @default false
@@ -42,7 +37,6 @@ export const ReadMore = forwardRef<HTMLButtonElement, ReadMoreProps>(
42
37
  {
43
38
  className,
44
39
  header,
45
- renderContentWhenClosed = false,
46
40
  children,
47
41
  open,
48
42
  defaultOpen = false,
@@ -53,27 +47,25 @@ export const ReadMore = forwardRef<HTMLButtonElement, ReadMoreProps>(
53
47
  ref
54
48
  ) => {
55
49
  const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);
56
- const CollapseComponent = renderContentWhenClosed
57
- ? Collapse
58
- : UnmountClosed;
59
50
 
60
51
  const isOpened = open ?? internalOpen;
61
52
 
62
53
  return (
63
- <div>
54
+ <div
55
+ className={cl(
56
+ "navds-read-more",
57
+ `navds-read-more--${size}`,
58
+ className,
59
+ { "navds-read-more--open": isOpened }
60
+ )}
61
+ >
64
62
  <button
65
- type="button"
66
63
  {...rest}
67
- className={cl(
68
- "navds-read-more",
69
- "navds-body-short",
70
- `navds-read-more--${size}`,
71
- className,
72
- {
73
- "navds-read-more--open": isOpened,
74
- "navds-body-short--small": size === "small",
75
- }
76
- )}
64
+ ref={ref}
65
+ type="button"
66
+ className={cl("navds-read-more__button", "navds-body-short", {
67
+ "navds-body-short--small": size === "small",
68
+ })}
77
69
  onClick={(e) => {
78
70
  if (open === undefined) {
79
71
  setInternalOpen((isOpen) => !isOpen);
@@ -81,22 +73,19 @@ export const ReadMore = forwardRef<HTMLButtonElement, ReadMoreProps>(
81
73
  onClick?.(e);
82
74
  }}
83
75
  aria-expanded={isOpened}
84
- ref={ref}
85
76
  >
86
- <Expand className={"navds-read-more__expand-icon"} aria-hidden />
77
+ <Expand className="navds-read-more__expand-icon" aria-hidden />
87
78
  <ExpandFilled
88
- className={
89
- "navds-read-more__expand-icon navds-read-more__expand-icon--filled"
90
- }
79
+ className="navds-read-more__expand-icon navds-read-more__expand-icon--filled"
91
80
  aria-hidden
92
81
  />
93
82
  <span>{header}</span>
94
83
  </button>
95
- <CollapseComponent isOpened={isOpened}>
84
+ <AnimateHeight height={isOpened ? "auto" : 0} duration={250}>
96
85
  <BodyLong as="div" className="navds-read-more__content" size={size}>
97
86
  {children}
98
87
  </BodyLong>
99
- </CollapseComponent>
88
+ </AnimateHeight>
100
89
  </div>
101
90
  );
102
91
  }
@@ -22,7 +22,7 @@ export const Default = (props) => {
22
22
  open={props.controlled ? state : undefined}
23
23
  onClick={() => setState((x) => !x)}
24
24
  header="Grunnen til at vi spør om dette og i tillegg ber om vedlegg"
25
- {...props}
25
+ size={props.size}
26
26
  >
27
27
  Command station, this is ST 321. Code Clearance Blue. We're starting our
28
28
  approach. Deactivate the security shield.
@@ -1,10 +1,10 @@
1
- import React, { forwardRef, useState } from "react";
2
- import cl from "clsx";
3
- import Row, { RowProps } from "./Row";
4
- import DataCell from "./DataCell";
5
- import { UnmountClosed } from "react-collapse";
6
1
  import { Expand, ExpandFilled } from "@navikt/ds-icons";
2
+ import cl from "clsx";
3
+ import React, { forwardRef, useState } from "react";
7
4
  import { useId } from "..";
5
+ import AnimateHeight from "../util/AnimateHeight";
6
+ import DataCell from "./DataCell";
7
+ import Row, { RowProps } from "./Row";
8
8
 
9
9
  export interface ExpandableRowProps extends RowProps {
10
10
  /**
@@ -104,15 +104,14 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
104
104
  </Row>
105
105
  <tr className="navds-table__expanded-row" aria-hidden={!isOpen} id={id}>
106
106
  <td colSpan={999} className="navds-table__expanded-row-cell">
107
- <UnmountClosed
108
- isOpened={isOpen}
109
- theme={{
110
- collapse: "navds-table__expanded-row-collapse",
111
- content: "navds-table__expanded-row-content",
112
- }}
107
+ <AnimateHeight
108
+ className="navds-table__expanded-row-collapse"
109
+ innerClassName="navds-table__expanded-row-content"
110
+ height={isOpen ? "auto" : 0}
111
+ duration={250}
113
112
  >
114
113
  {content}
115
- </UnmountClosed>
114
+ </AnimateHeight>
116
115
  </td>
117
116
  </tr>
118
117
  </>
package/src/tabs/Tab.tsx CHANGED
@@ -24,15 +24,7 @@ export type TabType = OverridableComponent<TabProps, HTMLButtonElement>;
24
24
 
25
25
  export const Tab: TabType = forwardRef(
26
26
  (
27
- {
28
- className,
29
- as: Component = "button",
30
- label,
31
- icon,
32
- iconPosition,
33
- value,
34
- ...rest
35
- },
27
+ { className, as: Component = "button", label, icon, value, ...rest },
36
28
  ref
37
29
  ) => {
38
30
  const context = useContext(TabsContext);
@@ -0,0 +1,240 @@
1
+ /* https://github.com/Stanko/react-animate-height/blob/v3/src/index.tsx */
2
+ import React, { CSSProperties, useEffect, useRef, useState } from "react";
3
+
4
+ // ------------------ Types
5
+
6
+ export type Height = "auto" | number | `${number}%`;
7
+ type Timeout = ReturnType<typeof setTimeout>;
8
+ type Overflow = "auto" | "visible" | "hidden" | undefined;
9
+
10
+ // ------------------ Helpers
11
+
12
+ function isNumber(n: string) {
13
+ const number = parseFloat(n);
14
+ return !isNaN(number) && isFinite(number);
15
+ }
16
+
17
+ function isPercentage(height: Height) {
18
+ // Percentage height
19
+ return (
20
+ typeof height === "string" &&
21
+ height[height.length - 1] === "%" &&
22
+ isNumber(height.substring(0, height.length - 1))
23
+ );
24
+ }
25
+
26
+ function hideContent(element: HTMLDivElement | null, height: Height) {
27
+ // Check for element?.style is added cause this would fail in tests (react-test-renderer)
28
+ // Read more here: https://github.com/Stanko/react-animate-height/issues/17
29
+ if (height === 0 && element?.style) {
30
+ element.style.display = "none";
31
+ }
32
+ }
33
+
34
+ function showContent(element: HTMLDivElement | null, height: Height) {
35
+ // Check for element?.style is added cause this would fail in tests (react-test-renderer)
36
+ // Read more here: https://github.com/Stanko/react-animate-height/issues/17
37
+ if (height === 0 && element?.style) {
38
+ element.style.display = "";
39
+ }
40
+ }
41
+
42
+ // ------------------ Component
43
+
44
+ interface AnimateHeightProps extends React.HTMLAttributes<HTMLDivElement> {
45
+ duration?: number;
46
+ easing?: string;
47
+ height: Height;
48
+ innerClassName?: string;
49
+ }
50
+
51
+ const AnimateHeight: React.FC<AnimateHeightProps> = ({
52
+ children,
53
+ className,
54
+ innerClassName,
55
+ duration: userDuration = 250,
56
+ easing = "ease",
57
+ height,
58
+ ...props
59
+ }) => {
60
+ // ------------------ Initialization
61
+ const prevHeight = useRef<Height>(height);
62
+ const contentElement = useRef<HTMLDivElement>(null);
63
+
64
+ const animationClassesTimeoutID = useRef<Timeout>();
65
+ const timeoutID = useRef<Timeout>();
66
+
67
+ const isBrowser = typeof window !== "undefined";
68
+
69
+ const prefersReducedMotion = useRef<boolean>(
70
+ isBrowser && window.matchMedia
71
+ ? window.matchMedia("(prefers-reduced-motion)").matches
72
+ : false
73
+ );
74
+
75
+ const duration = prefersReducedMotion.current ? 0 : userDuration;
76
+
77
+ let initHeight: Height = height;
78
+ let initOverflow: Overflow = "visible";
79
+
80
+ if (typeof initHeight === "number") {
81
+ // Reset negative height to 0
82
+ initHeight = height < 0 ? 0 : height;
83
+ initOverflow = "hidden";
84
+ } else if (isPercentage(initHeight)) {
85
+ // If value is string "0%" make sure we convert it to number 0
86
+ initHeight = height === "0%" ? 0 : height;
87
+ initOverflow = "hidden";
88
+ }
89
+
90
+ const [currentHeight, setCurrentHeight] = useState<Height>(initHeight);
91
+ const [overflow, setOverflow] = useState<Overflow>(initOverflow);
92
+ const [useTransitions, setUseTransitions] = useState<boolean>(false);
93
+
94
+ // ------------------ Did mount
95
+ useEffect(() => {
96
+ // Hide content if height is 0 (to prevent tabbing into it)
97
+ hideContent(contentElement.current, currentHeight);
98
+
99
+ // This should be explicitly run only on mount
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ }, []);
102
+
103
+ // ------------------ Height update
104
+ useEffect(() => {
105
+ if (height !== prevHeight.current && contentElement.current) {
106
+ showContent(contentElement.current, prevHeight.current);
107
+
108
+ // Cache content height
109
+ contentElement.current.style.overflow = "hidden";
110
+ const contentHeight = contentElement.current.offsetHeight;
111
+ contentElement.current.style.overflow = "";
112
+
113
+ // set total animation time
114
+ const totalDuration = duration;
115
+
116
+ let newHeight: Height;
117
+ let timeoutHeight: Height;
118
+ let timeoutOverflow: Overflow = "hidden";
119
+ let timeoutUseTransitions: boolean;
120
+
121
+ const isCurrentHeightAuto = prevHeight.current === "auto";
122
+
123
+ if (typeof height === "number") {
124
+ // Reset negative height to 0
125
+ newHeight = height < 0 ? 0 : height;
126
+ timeoutHeight = newHeight;
127
+ } else if (isPercentage(height)) {
128
+ // If value is string "0%" make sure we convert it to number 0
129
+ newHeight = height === "0%" ? 0 : height;
130
+ timeoutHeight = newHeight;
131
+ } else {
132
+ // If not, animate to content height
133
+ // and then reset to auto
134
+ newHeight = contentHeight; // TODO solve contentHeight = 0
135
+ timeoutHeight = "auto";
136
+ timeoutOverflow = undefined;
137
+ }
138
+
139
+ if (isCurrentHeightAuto) {
140
+ // This is the height to be animated to
141
+ timeoutHeight = newHeight;
142
+
143
+ // If previous height was 'auto'
144
+ // set starting height explicitly to be able to use transition
145
+ newHeight = contentHeight;
146
+ }
147
+
148
+ // Set starting height and animating classes
149
+ // When animating from 'auto' we first need to set fixed height
150
+ // that change should be animated
151
+ setCurrentHeight(newHeight);
152
+ setOverflow("hidden");
153
+ setUseTransitions(!isCurrentHeightAuto);
154
+
155
+ // Clear timeouts
156
+ clearTimeout(timeoutID.current as Timeout);
157
+ clearTimeout(animationClassesTimeoutID.current as Timeout);
158
+
159
+ if (isCurrentHeightAuto) {
160
+ // When animating from 'auto' we use a short timeout to start animation
161
+ // after setting fixed height above
162
+ timeoutUseTransitions = true;
163
+
164
+ // Short timeout to allow rendering of the initial animation state first
165
+ timeoutID.current = setTimeout(() => {
166
+ setCurrentHeight(timeoutHeight);
167
+ setOverflow(timeoutOverflow);
168
+ setUseTransitions(timeoutUseTransitions);
169
+ }, 50);
170
+
171
+ // Set static classes and remove transitions when animation ends
172
+ animationClassesTimeoutID.current = setTimeout(() => {
173
+ setUseTransitions(false);
174
+
175
+ // ANIMATION ENDS
176
+ // Hide content if height is 0 (to prevent tabbing into it)
177
+ hideContent(contentElement.current, timeoutHeight);
178
+ }, totalDuration);
179
+ } else {
180
+ // Set end height, classes and remove transitions when animation is complete
181
+ timeoutID.current = setTimeout(() => {
182
+ setCurrentHeight(timeoutHeight);
183
+ setOverflow(timeoutOverflow);
184
+ setUseTransitions(false);
185
+
186
+ // ANIMATION ENDS
187
+ // If height is auto, don't hide the content
188
+ // (case when element is empty, therefore height is 0)
189
+ if (height !== "auto") {
190
+ // Hide content if height is 0 (to prevent tabbing into it)
191
+ hideContent(contentElement.current, newHeight); // TODO solve newHeight = 0
192
+ }
193
+ }, totalDuration);
194
+ }
195
+ }
196
+
197
+ prevHeight.current = height;
198
+
199
+ return () => {
200
+ clearTimeout(timeoutID.current as Timeout);
201
+ clearTimeout(animationClassesTimeoutID.current as Timeout);
202
+ };
203
+
204
+ // This should be explicitly run only on height change
205
+ // eslint-disable-next-line react-hooks/exhaustive-deps
206
+ }, [height]);
207
+
208
+ // ------------------ Render
209
+
210
+ const componentStyle: CSSProperties = {
211
+ height: currentHeight,
212
+ overflow: overflow,
213
+ };
214
+
215
+ if (useTransitions) {
216
+ componentStyle.transition = `height ${duration}ms ${easing} 0ms`;
217
+
218
+ // Add webkit vendor prefix still used by opera, blackberry...
219
+ componentStyle.WebkitTransition = componentStyle.transition;
220
+ }
221
+
222
+ // Check if user passed aria-hidden prop
223
+ const hasAriaHiddenProp = typeof props["aria-hidden"] !== "undefined";
224
+ const ariaHidden = hasAriaHiddenProp ? props["aria-hidden"] : height === 0;
225
+
226
+ return (
227
+ <div
228
+ {...props}
229
+ aria-hidden={ariaHidden}
230
+ className={className}
231
+ style={componentStyle}
232
+ >
233
+ <div className={innerClassName} ref={contentElement}>
234
+ {children}
235
+ </div>
236
+ </div>
237
+ );
238
+ };
239
+
240
+ export default AnimateHeight;