@sproutsocial/seeds-react-link 1.0.9 → 1.1.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +36 -0
- package/dist/esm/index.js +40 -36
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +40 -36
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/Link.tsx +47 -36
- package/src/__tests__/features.test.tsx +84 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -8,14 +8,14 @@ $ tsup --dts
|
|
|
8
8
|
[34mCLI[39m Cleaning output folder
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[32m4.
|
|
12
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m5.
|
|
13
|
-
[32mCJS[39m ⚡️ Build success in
|
|
14
|
-
[32mESM[39m [1mdist/esm/index.js [22m[32m2.
|
|
15
|
-
[32mESM[39m [1mdist/esm/index.js.map [22m[32m5.
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m4.36 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m5.70 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 204ms
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m2.43 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m5.67 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 192ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.
|
|
21
|
-
Done in
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 28215ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.19 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.19 KB[39m
|
|
21
|
+
Done in 35.02s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-link
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 29ced53: Add React forwardRef support to Link component
|
|
8
|
+
|
|
9
|
+
This change adds ref forwarding to the Link component, enabling access to the underlying DOM element (HTMLAnchorElement or HTMLButtonElement).
|
|
10
|
+
|
|
11
|
+
**New Features:**
|
|
12
|
+
|
|
13
|
+
- Link component now accepts a `ref` prop
|
|
14
|
+
- Ref correctly types as `HTMLAnchorElement` when href is provided
|
|
15
|
+
- Ref correctly types as `HTMLButtonElement` when href is omitted
|
|
16
|
+
- Full compatibility with focus management, DOM measurements, and third-party libraries
|
|
17
|
+
|
|
18
|
+
**Non-breaking:** Existing code without refs continues to work unchanged. This is a purely additive enhancement.
|
|
19
|
+
|
|
20
|
+
**Technical Details:**
|
|
21
|
+
|
|
22
|
+
- Link Container maintained as `styled(Text)` to preserve component hierarchy
|
|
23
|
+
- Works because Text now also supports forwardRef (see related changeset)
|
|
24
|
+
- Added comprehensive test coverage (7 new test cases)
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [29ced53]
|
|
29
|
+
- @sproutsocial/seeds-react-text@1.4.0
|
|
30
|
+
|
|
31
|
+
## 1.0.10
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- Updated dependencies [7716e16]
|
|
36
|
+
- @sproutsocial/seeds-react-theme@3.4.0
|
|
37
|
+
- @sproutsocial/seeds-react-mixins@4.2.3
|
|
38
|
+
|
|
3
39
|
## 1.0.9
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/Link.tsx
|
|
2
|
-
import "react";
|
|
2
|
+
import * as React from "react";
|
|
3
3
|
|
|
4
4
|
// src/styles.ts
|
|
5
5
|
import styled, { css } from "styled-components";
|
|
@@ -54,43 +54,47 @@ var styles_default = Container;
|
|
|
54
54
|
|
|
55
55
|
// src/Link.tsx
|
|
56
56
|
import { jsx } from "react/jsx-runtime";
|
|
57
|
-
var Link = (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
var Link = React.forwardRef(
|
|
58
|
+
({
|
|
59
|
+
href,
|
|
60
|
+
external,
|
|
61
|
+
children,
|
|
62
|
+
disabled: disabled2,
|
|
63
|
+
onClick,
|
|
64
|
+
as,
|
|
65
|
+
underline = true,
|
|
66
|
+
qa = {},
|
|
67
|
+
...rest
|
|
68
|
+
}, ref) => {
|
|
69
|
+
if (!href && external) {
|
|
70
|
+
console.warn(
|
|
71
|
+
"Warning: external prop cannot be set without a href declaration"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const type = as || (href ? "a" : "button");
|
|
75
|
+
return /* @__PURE__ */ jsx(
|
|
76
|
+
styles_default,
|
|
77
|
+
{
|
|
78
|
+
ref,
|
|
79
|
+
href,
|
|
80
|
+
target: external ? "_blank" : void 0,
|
|
81
|
+
type: type === "button" ? "button" : void 0,
|
|
82
|
+
rel: external ? "noopener noreferrer" : void 0,
|
|
83
|
+
forwardedAs: type,
|
|
84
|
+
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
85
|
+
disabled: disabled2,
|
|
86
|
+
onClick,
|
|
87
|
+
underline,
|
|
88
|
+
"data-qa-link": "",
|
|
89
|
+
"data-qa-link-isdisabled": disabled2 === true,
|
|
90
|
+
...qa,
|
|
91
|
+
...rest,
|
|
92
|
+
children
|
|
93
|
+
}
|
|
71
94
|
);
|
|
72
95
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
styles_default,
|
|
76
|
-
{
|
|
77
|
-
href,
|
|
78
|
-
target: external ? "_blank" : void 0,
|
|
79
|
-
type: type === "button" ? "button" : void 0,
|
|
80
|
-
rel: external ? "noopener noreferrer" : void 0,
|
|
81
|
-
forwardedAs: type,
|
|
82
|
-
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
83
|
-
disabled: disabled2,
|
|
84
|
-
onClick,
|
|
85
|
-
underline,
|
|
86
|
-
"data-qa-link": "",
|
|
87
|
-
"data-qa-link-isdisabled": disabled2 === true,
|
|
88
|
-
...qa,
|
|
89
|
-
...rest,
|
|
90
|
-
children
|
|
91
|
-
}
|
|
92
|
-
);
|
|
93
|
-
};
|
|
96
|
+
);
|
|
97
|
+
Link.displayName = "Link";
|
|
94
98
|
var Link_default = Link;
|
|
95
99
|
|
|
96
100
|
// src/LinkTypes.ts
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Link.tsx","../../src/styles.ts","../../src/LinkTypes.ts","../../src/constants.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Container from \"./styles\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst Link = ({\n
|
|
1
|
+
{"version":3,"sources":["../../src/Link.tsx","../../src/styles.ts","../../src/LinkTypes.ts","../../src/constants.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Container from \"./styles\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst Link = React.forwardRef<\n HTMLAnchorElement | HTMLButtonElement,\n TypeLinkProps\n>(\n (\n {\n href,\n external,\n children,\n disabled,\n onClick,\n as,\n underline = true,\n qa = {},\n ...rest\n },\n ref\n ) => {\n if (!href && external) {\n // eslint-disable-next-line no-console\n console.warn(\n \"Warning: external prop cannot be set without a href declaration\"\n );\n }\n\n const type = as || (href ? \"a\" : \"button\");\n return (\n <Container\n ref={ref}\n href={href}\n target={external ? \"_blank\" : undefined}\n type={type === \"button\" ? \"button\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n forwardedAs={type}\n aria-disabled={disabled ? disabled : undefined}\n disabled={disabled}\n onClick={onClick}\n underline={underline}\n data-qa-link=\"\"\n data-qa-link-isdisabled={disabled === true}\n {...qa}\n {...rest}\n >\n {children}\n </Container>\n );\n }\n);\n\nLink.displayName = \"Link\";\n\nexport default Link;\n","import styled, { css } from \"styled-components\";\nimport {\n TYPOGRAPHY,\n COMMON,\n FLEXBOX,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport { focusRing, disabled } from \"@sproutsocial/seeds-react-mixins\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst Container = styled(Text)<TypeLinkProps>`\n border: 0;\n font-family: ${(props) => props.theme.fontFamily};\n text-decoration: ${(props) => (props.underline ? \"underline\" : \"none\")};\n appearance: none;\n cursor: pointer;\n ${(props) =>\n props.disabled &&\n css`\n opacity: 0.4;\n cursor: not-allowed;\n `}\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n color: ${(props) => props.theme.colors.link.base};\n\n &:hover {\n color: ${(props) => props.theme.colors.link.hover};\n text-decoration: underline;\n }\n\n &:active {\n color: ${(props) => props.theme.colors.link.hover};\n }\n\n &:focus {\n ${focusRing}\n }\n\n &:focus:active {\n box-shadow: none;\n }\n\n ${(props) =>\n !props.href &&\n css`\n background: none;\n `}\n\n ${(props) => props.disabled && disabled}\n\n ${COMMON}\n ${TYPOGRAPHY}\n ${FLEXBOX}\n`;\n\nexport default Container;\n","import * as React from \"react\";\nimport type { TypeStyledComponentsCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeTextProps } from \"@sproutsocial/seeds-react-text\";\nimport type { TypeSystemFlexboxProps } from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeLinkProps\n extends Omit<\n TypeTextProps,\n keyof Omit<React.ComponentPropsWithoutRef<\"span\">, \"color\">\n >,\n Omit<React.ComponentPropsWithoutRef<\"button\">, \"color\">,\n TypeSystemFlexboxProps {\n /** Optional prop to make the URL open in a new tab */\n external?: boolean;\n children: React.ReactNode;\n\n /** Setting this prop will cause the component to be rendered as a link */\n href?: string;\n\n /** Disables user action and applies a disabled style to the component */\n disabled?: boolean;\n\n /** Can be used in addition to an href but still renders as a link. Omitting href will render as button */\n onClick?: (e: React.SyntheticEvent<HTMLButtonElement>) => void;\n as?: TypeStyledComponentsCommonProps[\"as\"];\n underline?: boolean;\n qa?: { [key: string]: unknown }; // should this be string|number?\n}\n","import { COLOR_BLUE_900 } from \"@sproutsocial/seeds-color\";\n\nconst defaultLink = {\n color: \"link\",\n hoverColor: \"link.hover\",\n activeColor: COLOR_BLUE_900,\n fontWeight: \"semibold\",\n};\n\nconst linkTheme = {\n default: defaultLink,\n};\n\nexport default linkTheme;\n","import Link from \"./Link\";\n\nexport default Link;\nexport { Link };\nexport * from \"./LinkTypes\";\nexport * from \"./constants\";\n"],"mappings":";AAAA,YAAY,WAAW;;;ACAvB,OAAO,UAAU,WAAW;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,WAAW,gBAAgB;AACpC,OAAO,UAAU;AAGjB,IAAM,YAAY,OAAO,IAAI;AAAA;AAAA,iBAEZ,CAAC,UAAU,MAAM,MAAM,UAAU;AAAA,qBAC7B,CAAC,UAAW,MAAM,YAAY,cAAc,MAAO;AAAA;AAAA;AAAA,IAGpE,CAAC,UACD,MAAM,YACN;AAAA;AAAA;AAAA,KAGC;AAAA,iBACY,CAAC,UAAU,MAAM,MAAM,YAAY,QAAQ;AAAA,WACjD,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,IAAI;AAAA;AAAA;AAAA,aAGrC,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,aAKxC,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA,MAI/C,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOX,CAAC,UACD,CAAC,MAAM,QACP;AAAA;AAAA,KAEC;AAAA;AAAA,IAED,CAAC,UAAU,MAAM,YAAY,QAAQ;AAAA;AAAA,KAEpC,MAAM;AAAA,KACN,UAAU;AAAA,KACV,OAAO;AAAA;AAGZ,IAAO,iBAAQ;;;ADxBT;AA3BN,IAAM,OAAa;AAAA,EAIjB,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,KAAK,CAAC;AAAA,IACN,GAAG;AAAA,EACL,GACA,QACG;AACH,QAAI,CAAC,QAAQ,UAAU;AAErB,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,OAAO,OAAO,MAAM;AACjC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,QAAQ,WAAW,WAAW;AAAA,QAC9B,MAAM,SAAS,WAAW,WAAW;AAAA,QACrC,KAAK,WAAW,wBAAwB;AAAA,QACxC,aAAa;AAAA,QACb,iBAAeA,YAAWA,YAAW;AAAA,QACrC,UAAUA;AAAA,QACV;AAAA,QACA;AAAA,QACA,gBAAa;AAAA,QACb,2BAAyBA,cAAa;AAAA,QACrC,GAAG;AAAA,QACH,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AACF;AAEA,KAAK,cAAc;AAEnB,IAAO,eAAQ;;;AEvDf,OAAuB;;;ACAvB,SAAS,sBAAsB;;;ACE/B,IAAO,gBAAQ;","names":["disabled"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
import { TypeSystemFlexboxProps, TypeStyledComponentsCommonProps } from '@sproutsocial/seeds-react-system-props';
|
|
4
3
|
import { TypeTextProps } from '@sproutsocial/seeds-react-text';
|
|
@@ -20,6 +19,6 @@ interface TypeLinkProps extends Omit<TypeTextProps, keyof Omit<React.ComponentPr
|
|
|
20
19
|
};
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
declare const Link:
|
|
22
|
+
declare const Link: React.ForwardRefExoticComponent<Omit<TypeLinkProps, "ref"> & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
|
|
24
23
|
|
|
25
24
|
export { Link, type TypeLinkProps, Link as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
import { TypeSystemFlexboxProps, TypeStyledComponentsCommonProps } from '@sproutsocial/seeds-react-system-props';
|
|
4
3
|
import { TypeTextProps } from '@sproutsocial/seeds-react-text';
|
|
@@ -20,6 +19,6 @@ interface TypeLinkProps extends Omit<TypeTextProps, keyof Omit<React.ComponentPr
|
|
|
20
19
|
};
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
declare const Link:
|
|
22
|
+
declare const Link: React.ForwardRefExoticComponent<Omit<TypeLinkProps, "ref"> & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
|
|
24
23
|
|
|
25
24
|
export { Link, type TypeLinkProps, Link as default };
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
38
|
// src/Link.tsx
|
|
39
|
-
var React = require("react");
|
|
39
|
+
var React = __toESM(require("react"));
|
|
40
40
|
|
|
41
41
|
// src/styles.ts
|
|
42
42
|
var import_styled_components = __toESM(require("styled-components"));
|
|
@@ -87,43 +87,47 @@ var styles_default = Container;
|
|
|
87
87
|
|
|
88
88
|
// src/Link.tsx
|
|
89
89
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
90
|
-
var Link = (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
var Link = React.forwardRef(
|
|
91
|
+
({
|
|
92
|
+
href,
|
|
93
|
+
external,
|
|
94
|
+
children,
|
|
95
|
+
disabled: disabled2,
|
|
96
|
+
onClick,
|
|
97
|
+
as,
|
|
98
|
+
underline = true,
|
|
99
|
+
qa = {},
|
|
100
|
+
...rest
|
|
101
|
+
}, ref) => {
|
|
102
|
+
if (!href && external) {
|
|
103
|
+
console.warn(
|
|
104
|
+
"Warning: external prop cannot be set without a href declaration"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const type = as || (href ? "a" : "button");
|
|
108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
109
|
+
styles_default,
|
|
110
|
+
{
|
|
111
|
+
ref,
|
|
112
|
+
href,
|
|
113
|
+
target: external ? "_blank" : void 0,
|
|
114
|
+
type: type === "button" ? "button" : void 0,
|
|
115
|
+
rel: external ? "noopener noreferrer" : void 0,
|
|
116
|
+
forwardedAs: type,
|
|
117
|
+
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
118
|
+
disabled: disabled2,
|
|
119
|
+
onClick,
|
|
120
|
+
underline,
|
|
121
|
+
"data-qa-link": "",
|
|
122
|
+
"data-qa-link-isdisabled": disabled2 === true,
|
|
123
|
+
...qa,
|
|
124
|
+
...rest,
|
|
125
|
+
children
|
|
126
|
+
}
|
|
104
127
|
);
|
|
105
128
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
styles_default,
|
|
109
|
-
{
|
|
110
|
-
href,
|
|
111
|
-
target: external ? "_blank" : void 0,
|
|
112
|
-
type: type === "button" ? "button" : void 0,
|
|
113
|
-
rel: external ? "noopener noreferrer" : void 0,
|
|
114
|
-
forwardedAs: type,
|
|
115
|
-
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
116
|
-
disabled: disabled2,
|
|
117
|
-
onClick,
|
|
118
|
-
underline,
|
|
119
|
-
"data-qa-link": "",
|
|
120
|
-
"data-qa-link-isdisabled": disabled2 === true,
|
|
121
|
-
...qa,
|
|
122
|
-
...rest,
|
|
123
|
-
children
|
|
124
|
-
}
|
|
125
|
-
);
|
|
126
|
-
};
|
|
129
|
+
);
|
|
130
|
+
Link.displayName = "Link";
|
|
127
131
|
var Link_default = Link;
|
|
128
132
|
|
|
129
133
|
// src/LinkTypes.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Link.tsx","../src/styles.ts","../src/LinkTypes.ts","../src/constants.ts"],"sourcesContent":["import Link from \"./Link\";\n\nexport default Link;\nexport { Link };\nexport * from \"./LinkTypes\";\nexport * from \"./constants\";\n","import * as React from \"react\";\nimport Container from \"./styles\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst Link = ({\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Link.tsx","../src/styles.ts","../src/LinkTypes.ts","../src/constants.ts"],"sourcesContent":["import Link from \"./Link\";\n\nexport default Link;\nexport { Link };\nexport * from \"./LinkTypes\";\nexport * from \"./constants\";\n","import * as React from \"react\";\nimport Container from \"./styles\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst Link = React.forwardRef<\n HTMLAnchorElement | HTMLButtonElement,\n TypeLinkProps\n>(\n (\n {\n href,\n external,\n children,\n disabled,\n onClick,\n as,\n underline = true,\n qa = {},\n ...rest\n },\n ref\n ) => {\n if (!href && external) {\n // eslint-disable-next-line no-console\n console.warn(\n \"Warning: external prop cannot be set without a href declaration\"\n );\n }\n\n const type = as || (href ? \"a\" : \"button\");\n return (\n <Container\n ref={ref}\n href={href}\n target={external ? \"_blank\" : undefined}\n type={type === \"button\" ? \"button\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n forwardedAs={type}\n aria-disabled={disabled ? disabled : undefined}\n disabled={disabled}\n onClick={onClick}\n underline={underline}\n data-qa-link=\"\"\n data-qa-link-isdisabled={disabled === true}\n {...qa}\n {...rest}\n >\n {children}\n </Container>\n );\n }\n);\n\nLink.displayName = \"Link\";\n\nexport default Link;\n","import styled, { css } from \"styled-components\";\nimport {\n TYPOGRAPHY,\n COMMON,\n FLEXBOX,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport { focusRing, disabled } from \"@sproutsocial/seeds-react-mixins\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst Container = styled(Text)<TypeLinkProps>`\n border: 0;\n font-family: ${(props) => props.theme.fontFamily};\n text-decoration: ${(props) => (props.underline ? \"underline\" : \"none\")};\n appearance: none;\n cursor: pointer;\n ${(props) =>\n props.disabled &&\n css`\n opacity: 0.4;\n cursor: not-allowed;\n `}\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n color: ${(props) => props.theme.colors.link.base};\n\n &:hover {\n color: ${(props) => props.theme.colors.link.hover};\n text-decoration: underline;\n }\n\n &:active {\n color: ${(props) => props.theme.colors.link.hover};\n }\n\n &:focus {\n ${focusRing}\n }\n\n &:focus:active {\n box-shadow: none;\n }\n\n ${(props) =>\n !props.href &&\n css`\n background: none;\n `}\n\n ${(props) => props.disabled && disabled}\n\n ${COMMON}\n ${TYPOGRAPHY}\n ${FLEXBOX}\n`;\n\nexport default Container;\n","import * as React from \"react\";\nimport type { TypeStyledComponentsCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeTextProps } from \"@sproutsocial/seeds-react-text\";\nimport type { TypeSystemFlexboxProps } from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeLinkProps\n extends Omit<\n TypeTextProps,\n keyof Omit<React.ComponentPropsWithoutRef<\"span\">, \"color\">\n >,\n Omit<React.ComponentPropsWithoutRef<\"button\">, \"color\">,\n TypeSystemFlexboxProps {\n /** Optional prop to make the URL open in a new tab */\n external?: boolean;\n children: React.ReactNode;\n\n /** Setting this prop will cause the component to be rendered as a link */\n href?: string;\n\n /** Disables user action and applies a disabled style to the component */\n disabled?: boolean;\n\n /** Can be used in addition to an href but still renders as a link. Omitting href will render as button */\n onClick?: (e: React.SyntheticEvent<HTMLButtonElement>) => void;\n as?: TypeStyledComponentsCommonProps[\"as\"];\n underline?: boolean;\n qa?: { [key: string]: unknown }; // should this be string|number?\n}\n","import { COLOR_BLUE_900 } from \"@sproutsocial/seeds-color\";\n\nconst defaultLink = {\n color: \"link\",\n hoverColor: \"link.hover\",\n activeColor: COLOR_BLUE_900,\n fontWeight: \"semibold\",\n};\n\nconst linkTheme = {\n default: defaultLink,\n};\n\nexport default linkTheme;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;;;ACAvB,+BAA4B;AAC5B,sCAIO;AACP,gCAAoC;AACpC,8BAAiB;AAGjB,IAAM,gBAAY,yBAAAA,SAAO,wBAAAC,OAAI;AAAA;AAAA,iBAEZ,CAAC,UAAU,MAAM,MAAM,UAAU;AAAA,qBAC7B,CAAC,UAAW,MAAM,YAAY,cAAc,MAAO;AAAA;AAAA;AAAA,IAGpE,CAAC,UACD,MAAM,YACN;AAAA;AAAA;AAAA,KAGC;AAAA,iBACY,CAAC,UAAU,MAAM,MAAM,YAAY,QAAQ;AAAA,WACjD,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,IAAI;AAAA;AAAA;AAAA,aAGrC,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,aAKxC,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA,MAI/C,mCAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOX,CAAC,UACD,CAAC,MAAM,QACP;AAAA;AAAA,KAEC;AAAA;AAAA,IAED,CAAC,UAAU,MAAM,YAAY,kCAAQ;AAAA;AAAA,KAEpC,sCAAM;AAAA,KACN,0CAAU;AAAA,KACV,uCAAO;AAAA;AAGZ,IAAO,iBAAQ;;;ADxBT;AA3BN,IAAM,OAAa;AAAA,EAIjB,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAAC;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,KAAK,CAAC;AAAA,IACN,GAAG;AAAA,EACL,GACA,QACG;AACH,QAAI,CAAC,QAAQ,UAAU;AAErB,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,OAAO,OAAO,MAAM;AACjC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,QAAQ,WAAW,WAAW;AAAA,QAC9B,MAAM,SAAS,WAAW,WAAW;AAAA,QACrC,KAAK,WAAW,wBAAwB;AAAA,QACxC,aAAa;AAAA,QACb,iBAAeA,YAAWA,YAAW;AAAA,QACrC,UAAUA;AAAA,QACV;AAAA,QACA;AAAA,QACA,gBAAa;AAAA,QACb,2BAAyBA,cAAa;AAAA,QACrC,GAAG;AAAA,QACH,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AACF;AAEA,KAAK,cAAc;AAEnB,IAAO,eAAQ;;;AEvDf,IAAAC,SAAuB;;;ACAvB,yBAA+B;;;AJE/B,IAAO,gBAAQ;","names":["styled","Text","disabled","React"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-link",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Seeds React Link",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"test:watch": "jest --watch --coverage=false"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@sproutsocial/seeds-react-theme": "^3.
|
|
21
|
+
"@sproutsocial/seeds-react-theme": "^3.4.0",
|
|
22
22
|
"@sproutsocial/seeds-react-system-props": "^3.0.1",
|
|
23
|
-
"@sproutsocial/seeds-react-mixins": "^4.2.
|
|
24
|
-
"@sproutsocial/seeds-react-text": "^1.
|
|
23
|
+
"@sproutsocial/seeds-react-mixins": "^4.2.3",
|
|
24
|
+
"@sproutsocial/seeds-react-text": "^1.4.0",
|
|
25
25
|
"@sproutsocial/seeds-color": "^2.3.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@sproutsocial/seeds-tsconfig": "*",
|
|
36
36
|
"@sproutsocial/seeds-testing": "*",
|
|
37
37
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
38
|
-
"@sproutsocial/seeds-react-icon": "^2.1.
|
|
39
|
-
"@sproutsocial/seeds-react-box": "^1.1.
|
|
38
|
+
"@sproutsocial/seeds-react-icon": "^2.1.2",
|
|
39
|
+
"@sproutsocial/seeds-react-box": "^1.1.11"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"styled-components": "^5.2.3"
|
package/src/Link.tsx
CHANGED
|
@@ -2,44 +2,55 @@ import * as React from "react";
|
|
|
2
2
|
import Container from "./styles";
|
|
3
3
|
import type { TypeLinkProps } from "./LinkTypes";
|
|
4
4
|
|
|
5
|
-
const Link =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
const Link = React.forwardRef<
|
|
6
|
+
HTMLAnchorElement | HTMLButtonElement,
|
|
7
|
+
TypeLinkProps
|
|
8
|
+
>(
|
|
9
|
+
(
|
|
10
|
+
{
|
|
11
|
+
href,
|
|
12
|
+
external,
|
|
13
|
+
children,
|
|
14
|
+
disabled,
|
|
15
|
+
onClick,
|
|
16
|
+
as,
|
|
17
|
+
underline = true,
|
|
18
|
+
qa = {},
|
|
19
|
+
...rest
|
|
20
|
+
},
|
|
21
|
+
ref
|
|
22
|
+
) => {
|
|
23
|
+
if (!href && external) {
|
|
24
|
+
// eslint-disable-next-line no-console
|
|
25
|
+
console.warn(
|
|
26
|
+
"Warning: external prop cannot be set without a href declaration"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const type = as || (href ? "a" : "button");
|
|
31
|
+
return (
|
|
32
|
+
<Container
|
|
33
|
+
ref={ref}
|
|
34
|
+
href={href}
|
|
35
|
+
target={external ? "_blank" : undefined}
|
|
36
|
+
type={type === "button" ? "button" : undefined}
|
|
37
|
+
rel={external ? "noopener noreferrer" : undefined}
|
|
38
|
+
forwardedAs={type}
|
|
39
|
+
aria-disabled={disabled ? disabled : undefined}
|
|
40
|
+
disabled={disabled}
|
|
41
|
+
onClick={onClick}
|
|
42
|
+
underline={underline}
|
|
43
|
+
data-qa-link=""
|
|
44
|
+
data-qa-link-isdisabled={disabled === true}
|
|
45
|
+
{...qa}
|
|
46
|
+
{...rest}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
</Container>
|
|
20
50
|
);
|
|
21
51
|
}
|
|
52
|
+
);
|
|
22
53
|
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<Container
|
|
26
|
-
href={href}
|
|
27
|
-
target={external ? "_blank" : undefined}
|
|
28
|
-
type={type === "button" ? "button" : undefined}
|
|
29
|
-
rel={external ? "noopener noreferrer" : undefined}
|
|
30
|
-
forwardedAs={type}
|
|
31
|
-
aria-disabled={disabled ? disabled : undefined}
|
|
32
|
-
disabled={disabled}
|
|
33
|
-
onClick={onClick}
|
|
34
|
-
underline={underline}
|
|
35
|
-
data-qa-link=""
|
|
36
|
-
data-qa-link-isdisabled={disabled === true}
|
|
37
|
-
{...qa}
|
|
38
|
-
{...rest}
|
|
39
|
-
>
|
|
40
|
-
{children}
|
|
41
|
-
</Container>
|
|
42
|
-
);
|
|
43
|
-
};
|
|
54
|
+
Link.displayName = "Link";
|
|
44
55
|
|
|
45
56
|
export default Link;
|
|
@@ -109,3 +109,87 @@ describe("Racine Link", () => {
|
|
|
109
109
|
);
|
|
110
110
|
});
|
|
111
111
|
});
|
|
112
|
+
|
|
113
|
+
describe("ref forwarding", () => {
|
|
114
|
+
it("should forward ref to anchor element when href is provided", () => {
|
|
115
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
|
116
|
+
render(
|
|
117
|
+
<Link href="/test" ref={ref}>
|
|
118
|
+
Test Link
|
|
119
|
+
</Link>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
|
123
|
+
expect(ref.current?.tagName).toBe("A");
|
|
124
|
+
expect(ref.current?.getAttribute("href")).toBe("/test");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should forward ref to button element when no href is provided", () => {
|
|
128
|
+
const ref = React.createRef<HTMLButtonElement>();
|
|
129
|
+
render(
|
|
130
|
+
<Link onClick={() => {}} ref={ref}>
|
|
131
|
+
Test Button
|
|
132
|
+
</Link>
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
136
|
+
expect(ref.current?.tagName).toBe("BUTTON");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should allow ref access to DOM methods", () => {
|
|
140
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
|
141
|
+
render(
|
|
142
|
+
<Link href="/test" ref={ref}>
|
|
143
|
+
Test Link
|
|
144
|
+
</Link>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Verify we can call DOM methods
|
|
148
|
+
expect(typeof ref.current?.focus).toBe("function");
|
|
149
|
+
expect(typeof ref.current?.blur).toBe("function");
|
|
150
|
+
expect(typeof ref.current?.getBoundingClientRect).toBe("function");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should work without ref (backward compatibility)", () => {
|
|
154
|
+
// This should not throw or break
|
|
155
|
+
expect(() => {
|
|
156
|
+
render(<Link href="/test">Test Link</Link>);
|
|
157
|
+
}).not.toThrow();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should handle ref with external links", () => {
|
|
161
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
|
162
|
+
render(
|
|
163
|
+
<Link href="https://example.com" external ref={ref}>
|
|
164
|
+
External
|
|
165
|
+
</Link>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
|
169
|
+
expect(ref.current?.getAttribute("target")).toBe("_blank");
|
|
170
|
+
expect(ref.current?.getAttribute("rel")).toBe("noopener noreferrer");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should handle ref with disabled state", () => {
|
|
174
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
|
175
|
+
render(
|
|
176
|
+
<Link href="/test" disabled ref={ref}>
|
|
177
|
+
Disabled
|
|
178
|
+
</Link>
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
|
182
|
+
expect(ref.current?.getAttribute("aria-disabled")).toBe("true");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("should handle ref with custom 'as' prop", () => {
|
|
186
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
|
187
|
+
render(
|
|
188
|
+
<Link href="/test" as="a" ref={ref}>
|
|
189
|
+
Custom As
|
|
190
|
+
</Link>
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
|
194
|
+
});
|
|
195
|
+
});
|