@conveyorhq/arrow-ds 1.170.0 → 1.171.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.
- package/package.json +1 -1
- package/public/components/Button/Button.js +4 -2
- package/public/components/Link/Link.js +4 -2
- package/public/components/Reference/Reference.js +8 -4
- package/public/utilities/strings.d.ts +1 -0
- package/public/utilities/strings.js +12 -1
- package/src/components/Button/Button.tsx +4 -0
- package/src/components/Link/Link.tsx +5 -0
- package/src/components/Reference/Reference.tsx +40 -34
- package/src/utilities/strings.ts +11 -0
package/package.json
CHANGED
|
@@ -32,6 +32,7 @@ const react_1 = __importStar(require("react"));
|
|
|
32
32
|
const Icon_1 = require("../Icon");
|
|
33
33
|
const Link_1 = require("../Link");
|
|
34
34
|
const bem_1 = require("../../utilities/bem");
|
|
35
|
+
const strings_1 = require("../../utilities/strings");
|
|
35
36
|
const ButtonGroup_1 = require("./ButtonGroup");
|
|
36
37
|
const contexts_1 = require("../../contexts");
|
|
37
38
|
const cn = "Button";
|
|
@@ -70,7 +71,7 @@ function getButtonSizeClasses(size, icon, children) {
|
|
|
70
71
|
? buttonIconOnlySizeClasses[size]
|
|
71
72
|
: buttonSizeClasses[size];
|
|
72
73
|
}
|
|
73
|
-
const Button = (0, react_1.forwardRef)(({ as: Component = "button", children, className, block = false, size = BUTTON_SIZE.MEDIUM, variant = BUTTON_VARIANT.PRIMARY, depressed = false, isLoading = false, theme: themeProp, icon, iconColor, iconSpin = false, iconPosition = BUTTON_ICON_POSITION.LEFT, iconPrefix, disabled, type: typeProp, ...rest }, ref) => {
|
|
74
|
+
const Button = (0, react_1.forwardRef)(({ as: Component = "button", children, className, block = false, size = BUTTON_SIZE.MEDIUM, variant = BUTTON_VARIANT.PRIMARY, depressed = false, isLoading = false, theme: themeProp, icon, iconColor, iconSpin = false, iconPosition = BUTTON_ICON_POSITION.LEFT, iconPrefix, disabled, type: typeProp, href, ...rest }, ref) => {
|
|
74
75
|
const isDisabled = disabled || isLoading;
|
|
75
76
|
const themeContext = (0, react_1.useContext)(contexts_1.ThemeContext);
|
|
76
77
|
const theme = themeProp || themeContext;
|
|
@@ -90,10 +91,11 @@ const Button = (0, react_1.forwardRef)(({ as: Component = "button", children, cl
|
|
|
90
91
|
type = "button";
|
|
91
92
|
}
|
|
92
93
|
const propsIfLink = Component === Link_1.Link ? { noStyles: true } : {};
|
|
94
|
+
const safeHref = (0, strings_1.isSafeUrl)(href) ? href : "#";
|
|
93
95
|
if (variant === BUTTON_VARIANT.PLAIN) {
|
|
94
96
|
return (react_1.default.createElement(Component, { type: type, className: className, disabled: disabled, ref: ref, ...rest }, children));
|
|
95
97
|
}
|
|
96
|
-
return (react_1.default.createElement(Component, { type: type, className: buttonClassNames, disabled: isDisabled, ref: ref, ...propsIfLink, ...rest },
|
|
98
|
+
return (react_1.default.createElement(Component, { type: type, className: buttonClassNames, disabled: isDisabled, ref: ref, href: safeHref, ...propsIfLink, ...rest },
|
|
97
99
|
icon && iconProps && iconPosition === BUTTON_ICON_POSITION.LEFT && (react_1.default.createElement(Icon_1.Icon, { ...iconProps })),
|
|
98
100
|
children && (react_1.default.createElement("span", { className: (0, classnames_1.default)((0, bem_1.bem)(cn, { e: "content" }), isLoading && (0, bem_1.bem)(cn, { e: "content", m: "invisible" })) }, children)),
|
|
99
101
|
icon && iconProps && iconPosition === BUTTON_ICON_POSITION.RIGHT && (react_1.default.createElement(Icon_1.Icon, { ...iconProps })),
|
|
@@ -30,12 +30,14 @@ exports.Link = void 0;
|
|
|
30
30
|
const react_1 = __importStar(require("react"));
|
|
31
31
|
const classnames_1 = __importDefault(require("classnames"));
|
|
32
32
|
const bem_1 = require("../../utilities/bem");
|
|
33
|
+
const strings_1 = require("../../utilities/strings");
|
|
33
34
|
const status_1 = require("../../contexts/status");
|
|
34
35
|
const cn = "Link";
|
|
35
36
|
exports.Link = (0, react_1.forwardRef)((props, ref) => {
|
|
36
|
-
const { as: Component = "a", className, disabled, noStyles, statusVariant: statusVariantProp, target, ...rest } = props;
|
|
37
|
+
const { as: Component = "a", className, disabled, noStyles, statusVariant: statusVariantProp, target, href, ...rest } = props;
|
|
37
38
|
const context = (0, react_1.useContext)(status_1.StatusContext);
|
|
38
39
|
const calculatedVariant = statusVariantProp || context;
|
|
39
40
|
const classes = (0, classnames_1.default)(!noStyles && (0, bem_1.bem)(cn), calculatedVariant && (0, bem_1.bem)(cn, { m: calculatedVariant }), disabled && "isDisabled", className);
|
|
40
|
-
|
|
41
|
+
const safeHref = (0, strings_1.isSafeUrl)(href) ? href : "#";
|
|
42
|
+
return (react_1.default.createElement(Component, { className: classes, disabled: Component === "button" && disabled, target: target, rel: `${target === "_blank" ? "noopener noreferrer" : ""}`, ref: ref, href: safeHref, ...rest }));
|
|
41
43
|
});
|
|
@@ -31,14 +31,18 @@ const react_1 = __importStar(require("react"));
|
|
|
31
31
|
const classnames_1 = __importDefault(require("classnames"));
|
|
32
32
|
const Box_1 = require("../Box");
|
|
33
33
|
const bem_1 = require("../../utilities/bem");
|
|
34
|
+
const strings_1 = require("../../utilities/strings");
|
|
34
35
|
const Icon_1 = require("../Icon");
|
|
35
36
|
const Badge_1 = require("../Badge");
|
|
36
37
|
const cn = "Reference";
|
|
37
38
|
const ReferenceGroup = ({ className, ...rest }) => (react_1.default.createElement(Box_1.Box, { className: (0, classnames_1.default)((0, bem_1.bem)(`${cn}Group`), className), ...rest }));
|
|
38
|
-
const Reference = (0, react_1.forwardRef)(({ as: Component = "a", disabled, className, icon, iconPrefix, badgeVariant, badgeLabel, children, target, noLink, reverse = false, ...rest }, ref) =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const Reference = (0, react_1.forwardRef)(({ as: Component = "a", disabled, className, icon, iconPrefix, badgeVariant, badgeLabel, children, target, noLink, reverse = false, href, ...rest }, ref) => {
|
|
40
|
+
const safeHref = (0, strings_1.isSafeUrl)(href) ? href : "#";
|
|
41
|
+
return (react_1.default.createElement(Component, { className: (0, classnames_1.default)((0, bem_1.bem)(cn), !noLink && (0, bem_1.bem)(cn, { m: "isLink" }), disabled && (0, bem_1.bem)(cn, { m: "disabled" }), reverse && "flex-row-reverse", className), disabled: Component === "button" && disabled, target: target, rel: `${target === "_blank" ? "noopener noreferrer" : ""}`, ref: ref, href: safeHref, ...rest },
|
|
42
|
+
icon && (react_1.default.createElement(Icon_1.Icon, { className: (0, classnames_1.default)((0, bem_1.bem)(cn, { e: "icon" })), icon: icon, prefix: iconPrefix })),
|
|
43
|
+
children,
|
|
44
|
+
badgeLabel && (react_1.default.createElement(Badge_1.Badge, { className: (0, classnames_1.default)((0, bem_1.bem)(cn, { e: "badge" })), variant: badgeVariant, size: Badge_1.BADGE_SIZE.MICRO }, badgeLabel))));
|
|
45
|
+
});
|
|
42
46
|
const CompoundReference = Object.assign(Reference, {
|
|
43
47
|
Group: ReferenceGroup,
|
|
44
48
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.camelToTitleCase = exports.capitalize = void 0;
|
|
3
|
+
exports.isSafeUrl = exports.camelToTitleCase = exports.capitalize = void 0;
|
|
4
4
|
const capitalize = (s) => {
|
|
5
5
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
6
6
|
};
|
|
@@ -10,3 +10,14 @@ const camelToTitleCase = (s) => {
|
|
|
10
10
|
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
11
11
|
};
|
|
12
12
|
exports.camelToTitleCase = camelToTitleCase;
|
|
13
|
+
const isSafeUrl = (url) => {
|
|
14
|
+
const rejectedProtocols = ["javascript:"];
|
|
15
|
+
try {
|
|
16
|
+
const parsedUrl = new URL(url, window.location.origin);
|
|
17
|
+
return !rejectedProtocols.includes(parsedUrl.protocol);
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.isSafeUrl = isSafeUrl;
|
|
@@ -8,6 +8,7 @@ import React, {
|
|
|
8
8
|
import { Icon, IconType, ICON_TYPE, ICON_STYLE_PREFIX } from "../Icon";
|
|
9
9
|
import { Link } from "../Link";
|
|
10
10
|
import { bem } from "../../utilities/bem";
|
|
11
|
+
import { isSafeUrl } from "../../utilities/strings";
|
|
11
12
|
import { ButtonGroup } from "./ButtonGroup";
|
|
12
13
|
import { THEME } from "../../types";
|
|
13
14
|
import { ThemeContext } from "../../contexts";
|
|
@@ -131,6 +132,7 @@ const Button = forwardRef<HTMLElement, ButtonProps>(
|
|
|
131
132
|
disabled,
|
|
132
133
|
type: typeProp,
|
|
133
134
|
// Other
|
|
135
|
+
href,
|
|
134
136
|
...rest
|
|
135
137
|
}: ButtonProps,
|
|
136
138
|
ref,
|
|
@@ -173,6 +175,7 @@ const Button = forwardRef<HTMLElement, ButtonProps>(
|
|
|
173
175
|
type = "button";
|
|
174
176
|
}
|
|
175
177
|
const propsIfLink = Component === Link ? { noStyles: true } : {};
|
|
178
|
+
const safeHref = isSafeUrl(href) ? href : "#";
|
|
176
179
|
|
|
177
180
|
if (variant === BUTTON_VARIANT.PLAIN) {
|
|
178
181
|
return (
|
|
@@ -194,6 +197,7 @@ const Button = forwardRef<HTMLElement, ButtonProps>(
|
|
|
194
197
|
className={buttonClassNames}
|
|
195
198
|
disabled={isDisabled}
|
|
196
199
|
ref={ref}
|
|
200
|
+
href={safeHref}
|
|
197
201
|
{...propsIfLink}
|
|
198
202
|
{...rest}
|
|
199
203
|
>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef, useContext } from "react";
|
|
2
2
|
import classNames from "classnames";
|
|
3
3
|
import { bem } from "../../utilities/bem";
|
|
4
|
+
import { isSafeUrl } from "../../utilities/strings";
|
|
4
5
|
import { STATUS_VARIANT } from "../../types";
|
|
5
6
|
import { StatusContext } from "../../contexts/status";
|
|
6
7
|
|
|
@@ -24,6 +25,7 @@ export const Link = forwardRef<
|
|
|
24
25
|
noStyles,
|
|
25
26
|
statusVariant: statusVariantProp,
|
|
26
27
|
target,
|
|
28
|
+
href,
|
|
27
29
|
...rest
|
|
28
30
|
} = props;
|
|
29
31
|
const context = useContext(StatusContext);
|
|
@@ -36,6 +38,8 @@ export const Link = forwardRef<
|
|
|
36
38
|
className,
|
|
37
39
|
);
|
|
38
40
|
|
|
41
|
+
const safeHref = isSafeUrl(href) ? href : "#";
|
|
42
|
+
|
|
39
43
|
return (
|
|
40
44
|
<Component
|
|
41
45
|
className={classes}
|
|
@@ -43,6 +47,7 @@ export const Link = forwardRef<
|
|
|
43
47
|
target={target}
|
|
44
48
|
rel={`${target === "_blank" ? "noopener noreferrer" : ""}`}
|
|
45
49
|
ref={ref}
|
|
50
|
+
href={safeHref}
|
|
46
51
|
{...rest}
|
|
47
52
|
/>
|
|
48
53
|
);
|
|
@@ -2,6 +2,7 @@ import React, { forwardRef } from "react";
|
|
|
2
2
|
import classNames from "classnames";
|
|
3
3
|
import { Box, BoxProps } from "../Box";
|
|
4
4
|
import { bem } from "../../utilities/bem";
|
|
5
|
+
import { isSafeUrl } from "../../utilities/strings";
|
|
5
6
|
import { ICON_STYLE_PREFIX, Icon, IconType } from "../Icon";
|
|
6
7
|
import { STATUS_VARIANT } from "../../types";
|
|
7
8
|
import { Badge, BADGE_SIZE } from "../Badge";
|
|
@@ -42,43 +43,48 @@ const Reference = forwardRef<HTMLElement, ReferenceProps>(
|
|
|
42
43
|
target,
|
|
43
44
|
noLink,
|
|
44
45
|
reverse = false,
|
|
46
|
+
href,
|
|
45
47
|
...rest
|
|
46
48
|
},
|
|
47
49
|
ref,
|
|
48
|
-
) =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
50
|
+
) => {
|
|
51
|
+
const safeHref = isSafeUrl(href) ? href : "#";
|
|
52
|
+
return (
|
|
53
|
+
<Component
|
|
54
|
+
className={classNames(
|
|
55
|
+
bem(cn),
|
|
56
|
+
!noLink && bem(cn, { m: "isLink" }),
|
|
57
|
+
disabled && bem(cn, { m: "disabled" }),
|
|
58
|
+
reverse && "flex-row-reverse",
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
disabled={Component === "button" && disabled}
|
|
62
|
+
target={target}
|
|
63
|
+
rel={`${target === "_blank" ? "noopener noreferrer" : ""}`}
|
|
64
|
+
ref={ref}
|
|
65
|
+
href={safeHref}
|
|
66
|
+
{...rest}
|
|
67
|
+
>
|
|
68
|
+
{icon && (
|
|
69
|
+
<Icon
|
|
70
|
+
className={classNames(bem(cn, { e: "icon" }))}
|
|
71
|
+
icon={icon}
|
|
72
|
+
prefix={iconPrefix}
|
|
73
|
+
/>
|
|
74
|
+
)}
|
|
75
|
+
{children}
|
|
76
|
+
{badgeLabel && (
|
|
77
|
+
<Badge
|
|
78
|
+
className={classNames(bem(cn, { e: "badge" }))}
|
|
79
|
+
variant={badgeVariant}
|
|
80
|
+
size={BADGE_SIZE.MICRO}
|
|
81
|
+
>
|
|
82
|
+
{badgeLabel}
|
|
83
|
+
</Badge>
|
|
84
|
+
)}
|
|
85
|
+
</Component>
|
|
86
|
+
);
|
|
87
|
+
},
|
|
82
88
|
);
|
|
83
89
|
|
|
84
90
|
const CompoundReference = Object.assign(Reference, {
|
package/src/utilities/strings.ts
CHANGED
|
@@ -6,3 +6,14 @@ export const camelToTitleCase = (s: string) => {
|
|
|
6
6
|
const result = s.replace(/([A-Z])/g, " $1");
|
|
7
7
|
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
8
8
|
};
|
|
9
|
+
|
|
10
|
+
export const isSafeUrl = (url: string) => {
|
|
11
|
+
// eslint-disable-next-line no-script-url
|
|
12
|
+
const rejectedProtocols = ["javascript:"];
|
|
13
|
+
try {
|
|
14
|
+
const parsedUrl = new URL(url, window.location.origin);
|
|
15
|
+
return !rejectedProtocols.includes(parsedUrl.protocol);
|
|
16
|
+
} catch (e) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|