Skip to content

Commit 955202b

Browse files
authored
Add support for React 18 (#641)
1 parent 90c848e commit 955202b

File tree

23 files changed

+2221
-2303
lines changed

23 files changed

+2221
-2303
lines changed

.gitattributes

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package-lock.json linguist-generated=true
2+
# exclude package-lock from git diff
3+
package-lock.json -diff

package-lock.json

+1,975-2,099
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@nordcloud/gnui",
33
"description": "Nordcloud Design System - a collection of reusable React components used in Nordcloud's SaaS products",
4-
"version": "8.16.1",
4+
"version": "8.17.0",
55
"license": "MIT",
66
"repository": {
77
"type": "git",
@@ -57,7 +57,7 @@
5757
"peerDependencies": {
5858
"react": ">=16.13.0",
5959
"react-dom": ">=16.13.0",
60-
"styled-components": ">=5.3.0"
60+
"styled-components": "^5.3.0"
6161
},
6262
"dependencies": {
6363
"@types/styled-system": "^5.1.16",
@@ -81,7 +81,7 @@
8181
"@storybook/addons": "^6.5.16",
8282
"@storybook/react": "^6.5.16",
8383
"@storybook/theming": "^6.5.16",
84-
"@testing-library/jest-dom": "^5.17.0",
84+
"@testing-library/jest-dom": "^6.1.2",
8585
"@testing-library/react": "^12.1.5",
8686
"@testing-library/user-event": "^13.5.0",
8787
"@types/jest": "^29.5.3",

src/components/barchart/BarChart.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ export function BarChart({ values, ...visualProps }: BarProps) {
5353
return (
5454
<StyledBarChart {...visualProps}>
5555
<g>
56-
{percentageValues.map(({ size, x, color }, idx) => (
57-
<rect x={`${x}%`} width={`${size}%`} fill={color} key={idx} />
56+
{percentageValues.map(({ size, x, color }, index) => (
57+
<rect key={index} x={`${x}%`} width={`${size}%`} fill={color} />
5858
))}
5959
</g>
6060
</StyledBarChart>

src/components/button/Button.tsx

+101-94
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,86 @@ import { SingleColors } from "../../theme/config";
77
import { setColor } from "../../utils/setcolor";
88
import { Spinner } from "../spinner";
99
import { SVGIcon, SVGIconProps } from "../svgicon";
10+
import { PolymorphicComponentPropWithRef, PolymorphicRef } from "./typeUtils";
1011

11-
export type ButtonProps<T extends React.ElementType = "button"> =
12-
React.ComponentProps<T> & {
13-
children?: React.ReactNode | string;
14-
severity?: "high" | "low" | "medium";
15-
status?:
16-
| "accent"
17-
| "danger"
18-
| "discovery"
19-
| "notification"
20-
| "success"
21-
| "warning";
22-
size?: "md" | "sm";
23-
icon?: SVGIconProps["name"];
24-
iconRight?: boolean;
25-
initialState?: string;
26-
/**
27-
* @deprecated The property should not be used
28-
*/
29-
color?: SingleColors;
30-
form?: string;
31-
select?: boolean;
32-
className?: string;
33-
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
34-
as?: T;
35-
linkTo?: string;
36-
display?: "flex" | "inline-flex";
37-
outline?: boolean;
38-
secondary?: boolean;
39-
};
12+
type ButtonPropsInner<C extends React.ElementType> =
13+
PolymorphicComponentPropWithRef<
14+
C,
15+
{
16+
severity?: "high" | "low" | "medium";
17+
status?:
18+
| "accent"
19+
| "danger"
20+
| "discovery"
21+
| "notification"
22+
| "success"
23+
| "warning";
24+
size?: "md" | "sm";
25+
icon?: SVGIconProps["name"];
26+
iconRight?: boolean;
27+
initialState?: string;
28+
/**
29+
* @deprecated The property should not be used
30+
*/
31+
color?: SingleColors;
32+
form?: string;
33+
select?: boolean;
34+
className?: string;
35+
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
36+
linkTo?: string;
37+
display?: "flex" | "inline-flex";
38+
outline?: boolean;
39+
secondary?: boolean;
40+
}
41+
> &
42+
SpaceProps;
43+
44+
export type ButtonProps = ButtonPropsInner<"a"> | ButtonPropsInner<"button">;
45+
46+
type ButtonComponent = <C extends React.ElementType = "button">(
47+
props: ButtonPropsInner<C>
48+
) => React.ReactElement | null;
49+
50+
export const Button: ButtonComponent = React.forwardRef(
51+
<C extends React.ElementType = "button">(
52+
{ children, icon, initialState, linkTo, as, ...props }: ButtonPropsInner<C>,
53+
ref?: PolymorphicRef<C>
54+
) => {
55+
const component = as || linkTo ? "a" : "button";
56+
57+
return (
58+
<StyledButton
59+
ref={ref}
60+
as={component}
61+
href={linkTo}
62+
linkTo={linkTo}
63+
{...props}
64+
>
65+
<ButtonIcon {...{ initialState, icon }} />
66+
{children && <span>{children}</span>}
67+
</StyledButton>
68+
);
69+
}
70+
);
71+
72+
type ButtonIconProps = Pick<ButtonProps, "icon" | "initialState">;
73+
74+
function ButtonIcon({ initialState, icon }: ButtonIconProps) {
75+
switch (initialState) {
76+
case "success":
77+
return <SVGIcon name="success" />;
78+
case "error":
79+
return <SVGIcon name="danger" />;
80+
case "loading":
81+
return (
82+
<div className="spinner">
83+
<Spinner size="sm" />
84+
</div>
85+
);
86+
default:
87+
return icon ? <SVGIcon name={icon} /> : null;
88+
}
89+
}
4090

4191
const changeSize = (size: string) => {
4292
switch (size) {
@@ -161,7 +211,8 @@ const changeStatus = (status?: ButtonProps["status"]) => {
161211
}
162212
};
163213

164-
const StyledButton = styled.button<ButtonProps<React.ElementType>>`
214+
/* stylelint-disable no-descending-specificity */
215+
const StyledButton = styled.button<ButtonProps>`
165216
background: ${theme.color.interactive.primary};
166217
white-space: nowrap;
167218
font-family: ${theme.fonts.body};
@@ -175,8 +226,7 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
175226
text-decoration: none;
176227
display: ${({ display, linkTo }) =>
177228
!display && linkTo ? "inline-flex" : display || "flex"};
178-
flex-direction: ${(props: ButtonProps) =>
179-
props.iconRight ? "row-reverse" : "row"};
229+
flex-direction: ${(props) => (props.iconRight ? "row-reverse" : "row")};
180230
align-items: center;
181231
text-transform: ${theme.typography.titleCase};
182232
@@ -263,15 +313,15 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
263313
${changeSize(size)}
264314
`};
265315
266-
${({ color }) =>
316+
${({ color, severity }) =>
267317
color &&
268318
css`
269-
background-color: ${(props: ButtonProps) =>
270-
props.severity === "medium" ? "transparent" : setColor(color)};
271-
color: ${(props: ButtonProps) =>
272-
props.severity === "medium"
273-
? setColor(color)
274-
: theme.color.text.text04};
319+
background-color: ${severity === "medium"
320+
? "transparent"
321+
: setColor(color)};
322+
color: ${severity === "medium"
323+
? setColor(color)
324+
: theme.color.text.text04};
275325
border: 1px solid ${setColor(color)};
276326
&:hover,
277327
&:active,
@@ -281,68 +331,25 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
281331
}
282332
.spinner {
283333
div {
284-
border-color: ${(props: ButtonProps) =>
285-
props.severity === "medium"
286-
? setColor(color)
287-
: theme.color.text.text04}
334+
border-color: ${severity === "medium"
335+
? setColor(color)
336+
: theme.color.text.text04}
288337
transparent transparent transparent;
289338
}
290339
}
291340
&:hover {
292-
color: ${(props: ButtonProps) =>
293-
props.severity === "medium"
294-
? setColor(color)
295-
: theme.color.text.text04};
296-
background-color: ${(props: ButtonProps) =>
297-
props.severity === "medium"
298-
? lighten(0.35, setColor(color))
299-
: darken(0.1, setColor(color))};
341+
color: ${severity === "medium"
342+
? setColor(color)
343+
: theme.color.text.text04};
344+
background-color: ${severity === "medium"
345+
? lighten(0.35, setColor(color))
346+
: darken(0.1, setColor(color))};
300347
}
301348
&:active {
302-
background: ${(props: ButtonProps) =>
303-
props.severity === "medium"
304-
? lighten(0.25, setColor(color))
305-
: darken(0.2, setColor(color))};
349+
background: ${severity === "medium"
350+
? lighten(0.25, setColor(color))
351+
: darken(0.2, setColor(color))};
306352
}
307353
`}
308354
${space}
309355
`;
310-
311-
export function Button<T extends React.ElementType = "button">({
312-
children,
313-
icon,
314-
initialState,
315-
linkTo,
316-
...props
317-
}: ButtonProps<T> & SpaceProps) {
318-
return (
319-
<StyledButton
320-
as={linkTo ? "a" : "button"}
321-
href={linkTo}
322-
linkTo={linkTo}
323-
{...props}
324-
>
325-
<ButtonIcon {...{ initialState, icon }} />
326-
{children && <span>{children}</span>}
327-
</StyledButton>
328-
);
329-
}
330-
331-
type ButtonIconProps = Pick<ButtonProps, "icon" | "initialState">;
332-
333-
function ButtonIcon({ initialState, icon }: ButtonIconProps) {
334-
switch (initialState) {
335-
case "success":
336-
return <SVGIcon name="success" />;
337-
case "error":
338-
return <SVGIcon name="danger" />;
339-
case "loading":
340-
return (
341-
<div className="spinner">
342-
<Spinner size="sm" />
343-
</div>
344-
);
345-
default:
346-
return icon ? <SVGIcon name={icon} /> : null;
347-
}
348-
}

src/components/button/typeUtils.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export type PolymorphicRef<C extends React.ElementType> =
2+
React.ComponentPropsWithRef<C>["ref"];
3+
4+
type AsProp<C extends React.ElementType> = {
5+
as?: C;
6+
};
7+
8+
type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P);
9+
10+
type PolymorphicComponentProp<
11+
C extends React.ElementType,
12+
Props = object
13+
> = Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>> &
14+
React.PropsWithChildren<AsProp<C> & Props>;
15+
16+
export type PolymorphicComponentPropWithRef<
17+
C extends React.ElementType,
18+
Props = object
19+
> = PolymorphicComponentProp<C, Props> & { ref?: PolymorphicRef<C> };

src/components/checkbox/Checkbox.tsx

+32-30
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,37 @@ import styled, { css } from "styled-components";
33
import theme from "../../theme";
44
import { GnuiContainer, Flex } from "../container";
55

6-
const SingleCheckWrapper = styled(Flex)`
6+
export type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> & {
7+
labelText?: string;
8+
withoutLabel?: boolean;
9+
isIndeterminate?: boolean;
10+
ref?: React.Ref<HTMLInputElement>;
11+
};
12+
13+
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
14+
({ id, labelText, withoutLabel, isIndeterminate, ...props }, ref) => (
15+
<SingleCheckWrapper withoutLabel={withoutLabel}>
16+
<CheckboxContainer>
17+
<CheckboxInput ref={ref} type="checkbox" id={id} {...props} />
18+
<Fill />
19+
{isIndeterminate && <FillInter />}
20+
</CheckboxContainer>
21+
<CheckboxLabel withoutLabel={withoutLabel} htmlFor={id}>
22+
{labelText}
23+
</CheckboxLabel>
24+
</SingleCheckWrapper>
25+
)
26+
);
27+
28+
export type CheckboxGroupProps = Pick<CheckboxProps, "children" | "name">;
29+
30+
export function CheckboxGroup({ name, children }: CheckboxGroupProps) {
31+
return <CheckboxWrapper name={name}>{children}</CheckboxWrapper>;
32+
}
33+
34+
const SingleCheckWrapper = styled(Flex)<{
35+
withoutLabel?: boolean;
36+
}>`
737
margin-bottom: ${theme.spacing.spacing04};
838
position: ${({ withoutLabel }) => (withoutLabel ? "relative" : "static")};
939
@@ -81,7 +111,7 @@ const Fill = styled.div`
81111

82112
const FillInter = styled(Fill)``;
83113

84-
const CheckboxWrapper = styled(GnuiContainer)`
114+
const CheckboxWrapper = styled(GnuiContainer)<{ name?: string }>`
85115
position: relative;
86116
87117
${SingleCheckWrapper} {
@@ -124,31 +154,3 @@ const CheckboxInput = styled.input`
124154
}
125155
}
126156
`;
127-
128-
export type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> & {
129-
labelText?: string;
130-
withoutLabel?: boolean;
131-
isIndeterminate?: boolean;
132-
ref?: React.Ref<HTMLInputElement>;
133-
};
134-
135-
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
136-
({ id, labelText, withoutLabel, isIndeterminate, ...props }, ref) => (
137-
<SingleCheckWrapper withoutLabel={withoutLabel}>
138-
<CheckboxContainer>
139-
<CheckboxInput type="checkbox" id={id} ref={ref} {...props} />
140-
<Fill />
141-
{isIndeterminate && <FillInter />}
142-
</CheckboxContainer>
143-
<CheckboxLabel withoutLabel={withoutLabel} htmlFor={id}>
144-
{labelText}
145-
</CheckboxLabel>
146-
</SingleCheckWrapper>
147-
)
148-
);
149-
150-
export type CheckboxGroupProps = Pick<CheckboxProps, "name" | "children">;
151-
152-
export function CheckboxGroup({ name, children }: CheckboxGroupProps) {
153-
return <CheckboxWrapper name={name}>{children}</CheckboxWrapper>;
154-
}

src/components/checkmarkCheckbox/CheckmarkCheckbox.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ export const CheckmarkCheckbox = React.forwardRef<
120120
<FlexContainer>
121121
<CheckboxContainer>
122122
<CheckboxInput
123+
ref={ref}
123124
type="checkbox"
124125
id={id}
125-
ref={ref}
126126
{...props}
127127
onChange={handleChange}
128128
/>

0 commit comments

Comments
 (0)