@sproutsocial/seeds-react-link 1.1.5 → 1.1.12
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 +60 -0
- package/dist/esm/index.js +115 -11
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +116 -12
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/Link.stories.tsx +17 -0
- package/src/Link.tsx +7 -46
- package/src/LinkHybrid.tsx +103 -0
- package/src/LinkTailwind.tsx +86 -0
- package/src/__tests__/features.test.tsx +3 -4
- package/src/__tests__/tailwind.test.tsx +55 -0
- package/src/link.css +57 -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[
|
|
12
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
13
|
-
[32mCJS[39m ⚡️ Build success in
|
|
14
|
-
[32mESM[39m [1mdist/esm/index.js [22m[
|
|
15
|
-
[32mESM[39m [1mdist/esm/index.js.map [22m[
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m6.97 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m11.31 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 82ms
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m4.89 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m11.24 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 83ms
|
|
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 7.
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 6217ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.40 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.40 KB[39m
|
|
21
|
+
Done in 7.82s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-link
|
|
2
2
|
|
|
3
|
+
## 1.1.12
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- edc1819: Migrate link, text, banner, loader-button, and content-header to the Tailwind hybrid pattern (mirroring seeds-react-button). Each component gains a pure CSS-class `*Tailwind` implementation, a `*Hybrid` router that falls back to styled-components when system/styled props are present, and a component-scoped CSS file of `seeds-*` classes built on `var(--…)` tokens. Component CSS is wrapped in `@layer components` so consumer Tailwind utility overrides win the cascade, and the per-component CSS import is dropped from the `*Tailwind` files since the CSS is aggregated into `@sproutsocial/racine/css/components` at build time. Legacy `styles.ts` paths are unchanged.
|
|
8
|
+
- Updated dependencies [edc1819]
|
|
9
|
+
- @sproutsocial/seeds-react-text@1.4.3
|
|
10
|
+
|
|
11
|
+
## 1.1.11
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [5892f44]
|
|
16
|
+
- @sproutsocial/seeds-react-system-props@3.1.1
|
|
17
|
+
- @sproutsocial/seeds-react-text@1.4.2
|
|
18
|
+
|
|
19
|
+
## 1.1.10
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [5114a32]
|
|
24
|
+
- @sproutsocial/seeds-react-theme@4.1.0
|
|
25
|
+
- @sproutsocial/seeds-react-mixins@4.3.7
|
|
26
|
+
|
|
27
|
+
## 1.1.9
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Updated dependencies [132ef9d]
|
|
32
|
+
- Updated dependencies [132ef9d]
|
|
33
|
+
- @sproutsocial/seeds-react-theme@4.0.0
|
|
34
|
+
- @sproutsocial/seeds-react-system-props@3.1.0
|
|
35
|
+
- @sproutsocial/seeds-react-mixins@4.3.6
|
|
36
|
+
- @sproutsocial/seeds-react-text@1.4.1
|
|
37
|
+
|
|
38
|
+
## 1.1.8
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- Updated dependencies [47c62b3]
|
|
43
|
+
- @sproutsocial/seeds-react-theme@3.7.1
|
|
44
|
+
- @sproutsocial/seeds-react-mixins@4.3.5
|
|
45
|
+
|
|
46
|
+
## 1.1.7
|
|
47
|
+
|
|
48
|
+
### Patch Changes
|
|
49
|
+
|
|
50
|
+
- Updated dependencies [06da9c2]
|
|
51
|
+
- @sproutsocial/seeds-react-theme@3.7.0
|
|
52
|
+
- @sproutsocial/seeds-react-mixins@4.3.4
|
|
53
|
+
|
|
54
|
+
## 1.1.6
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- Updated dependencies [4ba8720]
|
|
59
|
+
- Updated dependencies [df9d751]
|
|
60
|
+
- @sproutsocial/seeds-react-theme@3.6.2
|
|
61
|
+
- @sproutsocial/seeds-react-mixins@4.3.3
|
|
62
|
+
|
|
3
63
|
## 1.1.5
|
|
4
64
|
|
|
5
65
|
### Patch Changes
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// src/Link.tsx
|
|
2
|
-
import * as
|
|
2
|
+
import * as React3 from "react";
|
|
3
|
+
|
|
4
|
+
// src/LinkHybrid.tsx
|
|
5
|
+
import * as React2 from "react";
|
|
3
6
|
|
|
4
7
|
// src/styles.ts
|
|
5
8
|
import styled, { css } from "styled-components";
|
|
@@ -52,9 +55,26 @@ var Container = styled(Text)`
|
|
|
52
55
|
`;
|
|
53
56
|
var styles_default = Container;
|
|
54
57
|
|
|
55
|
-
// src/
|
|
58
|
+
// src/LinkTailwind.tsx
|
|
59
|
+
import * as React from "react";
|
|
56
60
|
import { jsx } from "react/jsx-runtime";
|
|
57
|
-
|
|
61
|
+
function cn(...inputs) {
|
|
62
|
+
const classes = [];
|
|
63
|
+
for (const input of inputs) {
|
|
64
|
+
if (!input) continue;
|
|
65
|
+
if (typeof input === "string") {
|
|
66
|
+
classes.push(input);
|
|
67
|
+
} else if (typeof input === "object") {
|
|
68
|
+
for (const [key, value] of Object.entries(input)) {
|
|
69
|
+
if (value) {
|
|
70
|
+
classes.push(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return classes.join(" ");
|
|
76
|
+
}
|
|
77
|
+
var LinkTailwind = React.forwardRef(
|
|
58
78
|
({
|
|
59
79
|
href,
|
|
60
80
|
external,
|
|
@@ -64,27 +84,32 @@ var Link = React.forwardRef(
|
|
|
64
84
|
as,
|
|
65
85
|
underline = true,
|
|
66
86
|
qa = {},
|
|
87
|
+
className,
|
|
67
88
|
...rest
|
|
68
89
|
}, ref) => {
|
|
69
|
-
if (!href && external) {
|
|
70
|
-
console.warn(
|
|
71
|
-
"Warning: external prop cannot be set without a href declaration"
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
90
|
const type = as || (href ? "a" : "button");
|
|
91
|
+
const Component = type;
|
|
92
|
+
const classes = cn(
|
|
93
|
+
"seeds-link",
|
|
94
|
+
{
|
|
95
|
+
"seeds-link-underline": !!underline,
|
|
96
|
+
"seeds-link-disabled": !!disabled2,
|
|
97
|
+
"seeds-link-no-href": !href
|
|
98
|
+
},
|
|
99
|
+
className
|
|
100
|
+
);
|
|
75
101
|
return /* @__PURE__ */ jsx(
|
|
76
|
-
|
|
102
|
+
Component,
|
|
77
103
|
{
|
|
78
104
|
ref,
|
|
79
105
|
href,
|
|
80
106
|
target: external ? "_blank" : void 0,
|
|
81
107
|
type: type === "button" ? "button" : void 0,
|
|
82
108
|
rel: external ? "noopener noreferrer" : void 0,
|
|
83
|
-
forwardedAs: type,
|
|
84
109
|
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
85
110
|
disabled: disabled2,
|
|
86
111
|
onClick,
|
|
87
|
-
|
|
112
|
+
className: classes,
|
|
88
113
|
"data-qa-link": "",
|
|
89
114
|
"data-qa-link-isdisabled": disabled2 === true,
|
|
90
115
|
...qa,
|
|
@@ -94,6 +119,85 @@ var Link = React.forwardRef(
|
|
|
94
119
|
);
|
|
95
120
|
}
|
|
96
121
|
);
|
|
122
|
+
LinkTailwind.displayName = "LinkTailwind";
|
|
123
|
+
|
|
124
|
+
// src/LinkHybrid.tsx
|
|
125
|
+
import { hasStyledProps } from "@sproutsocial/seeds-react-system-props";
|
|
126
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
127
|
+
var TYPOGRAPHY_KEYS = [
|
|
128
|
+
"fontSize",
|
|
129
|
+
"typeScale",
|
|
130
|
+
"fontWeight",
|
|
131
|
+
"textAlign",
|
|
132
|
+
"lineHeight",
|
|
133
|
+
"isItalicized",
|
|
134
|
+
"fontFamily",
|
|
135
|
+
"fontStyle"
|
|
136
|
+
];
|
|
137
|
+
var hasLinkStyledProps = (props) => hasStyledProps(props) || TYPOGRAPHY_KEYS.some((key) => props[key] !== void 0);
|
|
138
|
+
var LinkHybrid = React2.forwardRef(
|
|
139
|
+
({
|
|
140
|
+
href,
|
|
141
|
+
external,
|
|
142
|
+
children,
|
|
143
|
+
disabled: disabled2,
|
|
144
|
+
onClick,
|
|
145
|
+
as,
|
|
146
|
+
underline = true,
|
|
147
|
+
qa = {},
|
|
148
|
+
...rest
|
|
149
|
+
}, ref) => {
|
|
150
|
+
if (!href && external) {
|
|
151
|
+
console.warn(
|
|
152
|
+
"Warning: external prop cannot be set without a href declaration"
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const type = as || (href ? "a" : "button");
|
|
156
|
+
if (hasLinkStyledProps(rest)) {
|
|
157
|
+
return /* @__PURE__ */ jsx2(
|
|
158
|
+
styles_default,
|
|
159
|
+
{
|
|
160
|
+
ref,
|
|
161
|
+
href,
|
|
162
|
+
target: external ? "_blank" : void 0,
|
|
163
|
+
type: type === "button" ? "button" : void 0,
|
|
164
|
+
rel: external ? "noopener noreferrer" : void 0,
|
|
165
|
+
forwardedAs: type,
|
|
166
|
+
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
167
|
+
disabled: disabled2,
|
|
168
|
+
onClick,
|
|
169
|
+
underline,
|
|
170
|
+
"data-qa-link": "",
|
|
171
|
+
"data-qa-link-isdisabled": disabled2 === true,
|
|
172
|
+
...qa,
|
|
173
|
+
...rest,
|
|
174
|
+
children
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return /* @__PURE__ */ jsx2(
|
|
179
|
+
LinkTailwind,
|
|
180
|
+
{
|
|
181
|
+
ref,
|
|
182
|
+
href,
|
|
183
|
+
external,
|
|
184
|
+
disabled: disabled2,
|
|
185
|
+
onClick,
|
|
186
|
+
as,
|
|
187
|
+
underline,
|
|
188
|
+
qa,
|
|
189
|
+
...rest,
|
|
190
|
+
children
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
LinkHybrid.displayName = "Link";
|
|
196
|
+
var LinkHybrid_default = LinkHybrid;
|
|
197
|
+
|
|
198
|
+
// src/Link.tsx
|
|
199
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
200
|
+
var Link = React3.forwardRef((props, ref) => /* @__PURE__ */ jsx3(LinkHybrid_default, { ...props, ref }));
|
|
97
201
|
Link.displayName = "Link";
|
|
98
202
|
var Link_default = Link;
|
|
99
203
|
|
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 = 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"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Link.tsx","../../src/LinkHybrid.tsx","../../src/styles.ts","../../src/LinkTailwind.tsx","../../src/LinkTypes.ts","../../src/constants.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport LinkHybrid from \"./LinkHybrid\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\n/**\n * Link component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system/typography props or a styled() extension).\n */\nconst Link = React.forwardRef<\n HTMLAnchorElement | HTMLButtonElement,\n TypeLinkProps\n>((props, ref) => <LinkHybrid {...props} ref={ref} />);\n\nLink.displayName = \"Link\";\n\nexport default Link;\n","/**\n * Hybrid Link Component\n * Automatically chooses between Tailwind and styled-components based on props.\n *\n * Link's underlying Container is `styled(Text)`, so it accepts typography props\n * (e.g. `fontSize`). `hasStyledProps` does not include typography props, so we\n * additionally route to the styled-components path when any typography prop is\n * present — mirroring Text — so font sizing is never silently dropped.\n */\n\nimport * as React from \"react\";\nimport Container from \"./styles\"; // Styled-components version\nimport { LinkTailwind } from \"./LinkTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst TYPOGRAPHY_KEYS = [\n \"fontSize\",\n \"typeScale\",\n \"fontWeight\",\n \"textAlign\",\n \"lineHeight\",\n \"isItalicized\",\n \"fontFamily\",\n \"fontStyle\",\n] as const;\n\nconst hasLinkStyledProps = (props: Record<string, unknown>): boolean =>\n hasStyledProps(props) ||\n TYPOGRAPHY_KEYS.some((key) => props[key] !== undefined);\n\nconst LinkHybrid = 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\n if (hasLinkStyledProps(rest)) {\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 // Use Tailwind version (preferred - better performance)\n return (\n <LinkTailwind\n ref={ref}\n href={href}\n external={external}\n disabled={disabled}\n onClick={onClick}\n as={as}\n underline={underline}\n qa={qa}\n {...rest}\n >\n {children}\n </LinkTailwind>\n );\n }\n);\n\nLinkHybrid.displayName = \"Link\";\n\nexport default LinkHybrid;\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","/**\n * Tailwind CSS implementation of Link component\n * Uses Seeds link CSS classes from link.css\n */\n\nimport * as React from \"react\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LinkTailwind = 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 className,\n ...rest\n },\n ref\n ) => {\n const type = as || (href ? \"a\" : \"button\");\n const Component = type as any;\n\n const classes = cn(\n \"seeds-link\",\n {\n \"seeds-link-underline\": !!underline,\n \"seeds-link-disabled\": !!disabled,\n \"seeds-link-no-href\": !href,\n },\n className\n );\n\n return (\n <Component\n ref={ref}\n href={href}\n target={external ? \"_blank\" : undefined}\n type={type === \"button\" ? \"button\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n aria-disabled={disabled ? disabled : undefined}\n disabled={disabled}\n onClick={onClick}\n className={classes}\n data-qa-link=\"\"\n data-qa-link-isdisabled={disabled === true}\n {...qa}\n {...rest}\n >\n {children}\n </Component>\n );\n }\n);\n\nLinkTailwind.displayName = \"LinkTailwind\";\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":"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","names":["React","React","disabled","jsx","disabled","jsx"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -19,6 +19,11 @@ interface TypeLinkProps extends Omit<TypeTextProps, keyof Omit<React.ComponentPr
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Link component. Automatically routes between the Tailwind implementation
|
|
24
|
+
* (preferred) and the styled-components implementation (for consumers using
|
|
25
|
+
* system/typography props or a styled() extension).
|
|
26
|
+
*/
|
|
22
27
|
declare const Link: React.ForwardRefExoticComponent<Omit<TypeLinkProps, "ref"> & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
|
|
23
28
|
|
|
24
29
|
export { Link, type TypeLinkProps, Link as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ interface TypeLinkProps extends Omit<TypeTextProps, keyof Omit<React.ComponentPr
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Link component. Automatically routes between the Tailwind implementation
|
|
24
|
+
* (preferred) and the styled-components implementation (for consumers using
|
|
25
|
+
* system/typography props or a styled() extension).
|
|
26
|
+
*/
|
|
22
27
|
declare const Link: React.ForwardRefExoticComponent<Omit<TypeLinkProps, "ref"> & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
|
|
23
28
|
|
|
24
29
|
export { Link, type TypeLinkProps, Link as default };
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,10 @@ __export(index_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
38
|
// src/Link.tsx
|
|
39
|
-
var
|
|
39
|
+
var React3 = __toESM(require("react"));
|
|
40
|
+
|
|
41
|
+
// src/LinkHybrid.tsx
|
|
42
|
+
var React2 = __toESM(require("react"));
|
|
40
43
|
|
|
41
44
|
// src/styles.ts
|
|
42
45
|
var import_styled_components = __toESM(require("styled-components"));
|
|
@@ -85,9 +88,26 @@ var Container = (0, import_styled_components.default)(import_seeds_react_text.de
|
|
|
85
88
|
`;
|
|
86
89
|
var styles_default = Container;
|
|
87
90
|
|
|
88
|
-
// src/
|
|
91
|
+
// src/LinkTailwind.tsx
|
|
92
|
+
var React = __toESM(require("react"));
|
|
89
93
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
90
|
-
|
|
94
|
+
function cn(...inputs) {
|
|
95
|
+
const classes = [];
|
|
96
|
+
for (const input of inputs) {
|
|
97
|
+
if (!input) continue;
|
|
98
|
+
if (typeof input === "string") {
|
|
99
|
+
classes.push(input);
|
|
100
|
+
} else if (typeof input === "object") {
|
|
101
|
+
for (const [key, value] of Object.entries(input)) {
|
|
102
|
+
if (value) {
|
|
103
|
+
classes.push(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return classes.join(" ");
|
|
109
|
+
}
|
|
110
|
+
var LinkTailwind = React.forwardRef(
|
|
91
111
|
({
|
|
92
112
|
href,
|
|
93
113
|
external,
|
|
@@ -97,27 +117,32 @@ var Link = React.forwardRef(
|
|
|
97
117
|
as,
|
|
98
118
|
underline = true,
|
|
99
119
|
qa = {},
|
|
120
|
+
className,
|
|
100
121
|
...rest
|
|
101
122
|
}, ref) => {
|
|
102
|
-
if (!href && external) {
|
|
103
|
-
console.warn(
|
|
104
|
-
"Warning: external prop cannot be set without a href declaration"
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
123
|
const type = as || (href ? "a" : "button");
|
|
124
|
+
const Component = type;
|
|
125
|
+
const classes = cn(
|
|
126
|
+
"seeds-link",
|
|
127
|
+
{
|
|
128
|
+
"seeds-link-underline": !!underline,
|
|
129
|
+
"seeds-link-disabled": !!disabled2,
|
|
130
|
+
"seeds-link-no-href": !href
|
|
131
|
+
},
|
|
132
|
+
className
|
|
133
|
+
);
|
|
108
134
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
109
|
-
|
|
135
|
+
Component,
|
|
110
136
|
{
|
|
111
137
|
ref,
|
|
112
138
|
href,
|
|
113
139
|
target: external ? "_blank" : void 0,
|
|
114
140
|
type: type === "button" ? "button" : void 0,
|
|
115
141
|
rel: external ? "noopener noreferrer" : void 0,
|
|
116
|
-
forwardedAs: type,
|
|
117
142
|
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
118
143
|
disabled: disabled2,
|
|
119
144
|
onClick,
|
|
120
|
-
|
|
145
|
+
className: classes,
|
|
121
146
|
"data-qa-link": "",
|
|
122
147
|
"data-qa-link-isdisabled": disabled2 === true,
|
|
123
148
|
...qa,
|
|
@@ -127,11 +152,90 @@ var Link = React.forwardRef(
|
|
|
127
152
|
);
|
|
128
153
|
}
|
|
129
154
|
);
|
|
155
|
+
LinkTailwind.displayName = "LinkTailwind";
|
|
156
|
+
|
|
157
|
+
// src/LinkHybrid.tsx
|
|
158
|
+
var import_seeds_react_system_props2 = require("@sproutsocial/seeds-react-system-props");
|
|
159
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
160
|
+
var TYPOGRAPHY_KEYS = [
|
|
161
|
+
"fontSize",
|
|
162
|
+
"typeScale",
|
|
163
|
+
"fontWeight",
|
|
164
|
+
"textAlign",
|
|
165
|
+
"lineHeight",
|
|
166
|
+
"isItalicized",
|
|
167
|
+
"fontFamily",
|
|
168
|
+
"fontStyle"
|
|
169
|
+
];
|
|
170
|
+
var hasLinkStyledProps = (props) => (0, import_seeds_react_system_props2.hasStyledProps)(props) || TYPOGRAPHY_KEYS.some((key) => props[key] !== void 0);
|
|
171
|
+
var LinkHybrid = React2.forwardRef(
|
|
172
|
+
({
|
|
173
|
+
href,
|
|
174
|
+
external,
|
|
175
|
+
children,
|
|
176
|
+
disabled: disabled2,
|
|
177
|
+
onClick,
|
|
178
|
+
as,
|
|
179
|
+
underline = true,
|
|
180
|
+
qa = {},
|
|
181
|
+
...rest
|
|
182
|
+
}, ref) => {
|
|
183
|
+
if (!href && external) {
|
|
184
|
+
console.warn(
|
|
185
|
+
"Warning: external prop cannot be set without a href declaration"
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const type = as || (href ? "a" : "button");
|
|
189
|
+
if (hasLinkStyledProps(rest)) {
|
|
190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
191
|
+
styles_default,
|
|
192
|
+
{
|
|
193
|
+
ref,
|
|
194
|
+
href,
|
|
195
|
+
target: external ? "_blank" : void 0,
|
|
196
|
+
type: type === "button" ? "button" : void 0,
|
|
197
|
+
rel: external ? "noopener noreferrer" : void 0,
|
|
198
|
+
forwardedAs: type,
|
|
199
|
+
"aria-disabled": disabled2 ? disabled2 : void 0,
|
|
200
|
+
disabled: disabled2,
|
|
201
|
+
onClick,
|
|
202
|
+
underline,
|
|
203
|
+
"data-qa-link": "",
|
|
204
|
+
"data-qa-link-isdisabled": disabled2 === true,
|
|
205
|
+
...qa,
|
|
206
|
+
...rest,
|
|
207
|
+
children
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
212
|
+
LinkTailwind,
|
|
213
|
+
{
|
|
214
|
+
ref,
|
|
215
|
+
href,
|
|
216
|
+
external,
|
|
217
|
+
disabled: disabled2,
|
|
218
|
+
onClick,
|
|
219
|
+
as,
|
|
220
|
+
underline,
|
|
221
|
+
qa,
|
|
222
|
+
...rest,
|
|
223
|
+
children
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
LinkHybrid.displayName = "Link";
|
|
229
|
+
var LinkHybrid_default = LinkHybrid;
|
|
230
|
+
|
|
231
|
+
// src/Link.tsx
|
|
232
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
233
|
+
var Link = React3.forwardRef((props, ref) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LinkHybrid_default, { ...props, ref }));
|
|
130
234
|
Link.displayName = "Link";
|
|
131
235
|
var Link_default = Link;
|
|
132
236
|
|
|
133
237
|
// src/LinkTypes.ts
|
|
134
|
-
var
|
|
238
|
+
var React4 = require("react");
|
|
135
239
|
|
|
136
240
|
// src/constants.ts
|
|
137
241
|
var import_seeds_color = require("@sproutsocial/seeds-color");
|
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 = 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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Link.tsx","../src/LinkHybrid.tsx","../src/styles.ts","../src/LinkTailwind.tsx","../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 LinkHybrid from \"./LinkHybrid\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\n/**\n * Link component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system/typography props or a styled() extension).\n */\nconst Link = React.forwardRef<\n HTMLAnchorElement | HTMLButtonElement,\n TypeLinkProps\n>((props, ref) => <LinkHybrid {...props} ref={ref} />);\n\nLink.displayName = \"Link\";\n\nexport default Link;\n","/**\n * Hybrid Link Component\n * Automatically chooses between Tailwind and styled-components based on props.\n *\n * Link's underlying Container is `styled(Text)`, so it accepts typography props\n * (e.g. `fontSize`). `hasStyledProps` does not include typography props, so we\n * additionally route to the styled-components path when any typography prop is\n * present — mirroring Text — so font sizing is never silently dropped.\n */\n\nimport * as React from \"react\";\nimport Container from \"./styles\"; // Styled-components version\nimport { LinkTailwind } from \"./LinkTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\nconst TYPOGRAPHY_KEYS = [\n \"fontSize\",\n \"typeScale\",\n \"fontWeight\",\n \"textAlign\",\n \"lineHeight\",\n \"isItalicized\",\n \"fontFamily\",\n \"fontStyle\",\n] as const;\n\nconst hasLinkStyledProps = (props: Record<string, unknown>): boolean =>\n hasStyledProps(props) ||\n TYPOGRAPHY_KEYS.some((key) => props[key] !== undefined);\n\nconst LinkHybrid = 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\n if (hasLinkStyledProps(rest)) {\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 // Use Tailwind version (preferred - better performance)\n return (\n <LinkTailwind\n ref={ref}\n href={href}\n external={external}\n disabled={disabled}\n onClick={onClick}\n as={as}\n underline={underline}\n qa={qa}\n {...rest}\n >\n {children}\n </LinkTailwind>\n );\n }\n);\n\nLinkHybrid.displayName = \"Link\";\n\nexport default LinkHybrid;\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","/**\n * Tailwind CSS implementation of Link component\n * Uses Seeds link CSS classes from link.css\n */\n\nimport * as React from \"react\";\nimport type { TypeLinkProps } from \"./LinkTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LinkTailwind = 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 className,\n ...rest\n },\n ref\n ) => {\n const type = as || (href ? \"a\" : \"button\");\n const Component = type as any;\n\n const classes = cn(\n \"seeds-link\",\n {\n \"seeds-link-underline\": !!underline,\n \"seeds-link-disabled\": !!disabled,\n \"seeds-link-no-href\": !href,\n },\n className\n );\n\n return (\n <Component\n ref={ref}\n href={href}\n target={external ? \"_blank\" : undefined}\n type={type === \"button\" ? \"button\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n aria-disabled={disabled ? disabled : undefined}\n disabled={disabled}\n onClick={onClick}\n className={classes}\n data-qa-link=\"\"\n data-qa-link-isdisabled={disabled === true}\n {...qa}\n {...rest}\n >\n {children}\n </Component>\n );\n }\n);\n\nLinkTailwind.displayName = \"LinkTailwind\";\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":"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","names":["React","React","styled","Text","disabled","import_seeds_react_system_props","import_jsx_runtime","disabled","import_jsx_runtime","React"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-link",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
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": "^
|
|
22
|
-
"@sproutsocial/seeds-react-system-props": "^3.
|
|
23
|
-
"@sproutsocial/seeds-react-mixins": "^4.3.
|
|
24
|
-
"@sproutsocial/seeds-react-text": "^1.4.
|
|
21
|
+
"@sproutsocial/seeds-react-theme": "^4.1.0",
|
|
22
|
+
"@sproutsocial/seeds-react-system-props": "^3.1.1",
|
|
23
|
+
"@sproutsocial/seeds-react-mixins": "^4.3.7",
|
|
24
|
+
"@sproutsocial/seeds-react-text": "^1.4.3",
|
|
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.
|
|
39
|
-
"@sproutsocial/seeds-react-box": "^1.1.
|
|
38
|
+
"@sproutsocial/seeds-react-icon": "^2.4.0",
|
|
39
|
+
"@sproutsocial/seeds-react-box": "^1.1.21"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"styled-components": "^5.2.3"
|
package/src/Link.stories.tsx
CHANGED
|
@@ -99,3 +99,20 @@ export const DecorativeBackgrounds: Story = {
|
|
|
99
99
|
/>
|
|
100
100
|
),
|
|
101
101
|
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Demonstrates that Tailwind utility classes passed via `className` override the
|
|
105
|
+
* component's own CSS. Because `link.css` lives in `@layer components`, the
|
|
106
|
+
* `text-[#FF6B00]` utility wins the cascade and recolors the link text orange,
|
|
107
|
+
* overriding the component's default blue link color. Passing only `className`
|
|
108
|
+
* (no typography/system props) routes the Link to its Tailwind path so the
|
|
109
|
+
* override applies.
|
|
110
|
+
*/
|
|
111
|
+
export const TailwindOverrides: Story = {
|
|
112
|
+
render: () => (
|
|
113
|
+
<Link href="#tailwind-override" className="text-[#FF6B00]">
|
|
114
|
+
Tailwind utilities override the component CSS
|
|
115
|
+
</Link>
|
|
116
|
+
),
|
|
117
|
+
name: "🚀 Tailwind class overrides",
|
|
118
|
+
};
|
package/src/Link.tsx
CHANGED
|
@@ -1,55 +1,16 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
2
|
+
import LinkHybrid from "./LinkHybrid";
|
|
3
3
|
import type { TypeLinkProps } from "./LinkTypes";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Link component. Automatically routes between the Tailwind implementation
|
|
7
|
+
* (preferred) and the styled-components implementation (for consumers using
|
|
8
|
+
* system/typography props or a styled() extension).
|
|
9
|
+
*/
|
|
5
10
|
const Link = React.forwardRef<
|
|
6
11
|
HTMLAnchorElement | HTMLButtonElement,
|
|
7
12
|
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>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
);
|
|
13
|
+
>((props, ref) => <LinkHybrid {...props} ref={ref} />);
|
|
53
14
|
|
|
54
15
|
Link.displayName = "Link";
|
|
55
16
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Link Component
|
|
3
|
+
* Automatically chooses between Tailwind and styled-components based on props.
|
|
4
|
+
*
|
|
5
|
+
* Link's underlying Container is `styled(Text)`, so it accepts typography props
|
|
6
|
+
* (e.g. `fontSize`). `hasStyledProps` does not include typography props, so we
|
|
7
|
+
* additionally route to the styled-components path when any typography prop is
|
|
8
|
+
* present — mirroring Text — so font sizing is never silently dropped.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
import Container from "./styles"; // Styled-components version
|
|
13
|
+
import { LinkTailwind } from "./LinkTailwind"; // Tailwind version
|
|
14
|
+
import { hasStyledProps } from "@sproutsocial/seeds-react-system-props";
|
|
15
|
+
import type { TypeLinkProps } from "./LinkTypes";
|
|
16
|
+
|
|
17
|
+
const TYPOGRAPHY_KEYS = [
|
|
18
|
+
"fontSize",
|
|
19
|
+
"typeScale",
|
|
20
|
+
"fontWeight",
|
|
21
|
+
"textAlign",
|
|
22
|
+
"lineHeight",
|
|
23
|
+
"isItalicized",
|
|
24
|
+
"fontFamily",
|
|
25
|
+
"fontStyle",
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
const hasLinkStyledProps = (props: Record<string, unknown>): boolean =>
|
|
29
|
+
hasStyledProps(props) ||
|
|
30
|
+
TYPOGRAPHY_KEYS.some((key) => props[key] !== undefined);
|
|
31
|
+
|
|
32
|
+
const LinkHybrid = React.forwardRef<
|
|
33
|
+
HTMLAnchorElement | HTMLButtonElement,
|
|
34
|
+
TypeLinkProps
|
|
35
|
+
>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
href,
|
|
39
|
+
external,
|
|
40
|
+
children,
|
|
41
|
+
disabled,
|
|
42
|
+
onClick,
|
|
43
|
+
as,
|
|
44
|
+
underline = true,
|
|
45
|
+
qa = {},
|
|
46
|
+
...rest
|
|
47
|
+
},
|
|
48
|
+
ref
|
|
49
|
+
) => {
|
|
50
|
+
if (!href && external) {
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.warn(
|
|
53
|
+
"Warning: external prop cannot be set without a href declaration"
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const type = as || (href ? "a" : "button");
|
|
58
|
+
|
|
59
|
+
if (hasLinkStyledProps(rest)) {
|
|
60
|
+
return (
|
|
61
|
+
<Container
|
|
62
|
+
ref={ref}
|
|
63
|
+
href={href}
|
|
64
|
+
target={external ? "_blank" : undefined}
|
|
65
|
+
type={type === "button" ? "button" : undefined}
|
|
66
|
+
rel={external ? "noopener noreferrer" : undefined}
|
|
67
|
+
forwardedAs={type}
|
|
68
|
+
aria-disabled={disabled ? disabled : undefined}
|
|
69
|
+
disabled={disabled}
|
|
70
|
+
onClick={onClick}
|
|
71
|
+
underline={underline}
|
|
72
|
+
data-qa-link=""
|
|
73
|
+
data-qa-link-isdisabled={disabled === true}
|
|
74
|
+
{...qa}
|
|
75
|
+
{...rest}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</Container>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Use Tailwind version (preferred - better performance)
|
|
83
|
+
return (
|
|
84
|
+
<LinkTailwind
|
|
85
|
+
ref={ref}
|
|
86
|
+
href={href}
|
|
87
|
+
external={external}
|
|
88
|
+
disabled={disabled}
|
|
89
|
+
onClick={onClick}
|
|
90
|
+
as={as}
|
|
91
|
+
underline={underline}
|
|
92
|
+
qa={qa}
|
|
93
|
+
{...rest}
|
|
94
|
+
>
|
|
95
|
+
{children}
|
|
96
|
+
</LinkTailwind>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
LinkHybrid.displayName = "Link";
|
|
102
|
+
|
|
103
|
+
export default LinkHybrid;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind CSS implementation of Link component
|
|
3
|
+
* Uses Seeds link CSS classes from link.css
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import type { TypeLinkProps } from "./LinkTypes";
|
|
8
|
+
|
|
9
|
+
// Utility for merging class names properly
|
|
10
|
+
function cn(
|
|
11
|
+
...inputs: (string | undefined | null | false | Record<string, boolean>)[]
|
|
12
|
+
): string {
|
|
13
|
+
const classes: string[] = [];
|
|
14
|
+
|
|
15
|
+
for (const input of inputs) {
|
|
16
|
+
if (!input) continue;
|
|
17
|
+
|
|
18
|
+
if (typeof input === "string") {
|
|
19
|
+
classes.push(input);
|
|
20
|
+
} else if (typeof input === "object") {
|
|
21
|
+
for (const [key, value] of Object.entries(input)) {
|
|
22
|
+
if (value) {
|
|
23
|
+
classes.push(key);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return classes.join(" ");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const LinkTailwind = React.forwardRef<
|
|
33
|
+
HTMLAnchorElement | HTMLButtonElement,
|
|
34
|
+
TypeLinkProps
|
|
35
|
+
>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
href,
|
|
39
|
+
external,
|
|
40
|
+
children,
|
|
41
|
+
disabled,
|
|
42
|
+
onClick,
|
|
43
|
+
as,
|
|
44
|
+
underline = true,
|
|
45
|
+
qa = {},
|
|
46
|
+
className,
|
|
47
|
+
...rest
|
|
48
|
+
},
|
|
49
|
+
ref
|
|
50
|
+
) => {
|
|
51
|
+
const type = as || (href ? "a" : "button");
|
|
52
|
+
const Component = type as any;
|
|
53
|
+
|
|
54
|
+
const classes = cn(
|
|
55
|
+
"seeds-link",
|
|
56
|
+
{
|
|
57
|
+
"seeds-link-underline": !!underline,
|
|
58
|
+
"seeds-link-disabled": !!disabled,
|
|
59
|
+
"seeds-link-no-href": !href,
|
|
60
|
+
},
|
|
61
|
+
className
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Component
|
|
66
|
+
ref={ref}
|
|
67
|
+
href={href}
|
|
68
|
+
target={external ? "_blank" : undefined}
|
|
69
|
+
type={type === "button" ? "button" : undefined}
|
|
70
|
+
rel={external ? "noopener noreferrer" : undefined}
|
|
71
|
+
aria-disabled={disabled ? disabled : undefined}
|
|
72
|
+
disabled={disabled}
|
|
73
|
+
onClick={onClick}
|
|
74
|
+
className={classes}
|
|
75
|
+
data-qa-link=""
|
|
76
|
+
data-qa-link-isdisabled={disabled === true}
|
|
77
|
+
{...qa}
|
|
78
|
+
{...rest}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
</Component>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
LinkTailwind.displayName = "LinkTailwind";
|
|
@@ -102,11 +102,10 @@ describe("Racine Link", () => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
it("Can render with an underline", () => {
|
|
105
|
+
// The Tailwind path renders the link with the `seeds-link-underline`
|
|
106
|
+
// class, which sets `text-decoration: underline` in link.css.
|
|
105
107
|
render(<Link underline>Link</Link>);
|
|
106
|
-
expect(screen.getByText("Link")).
|
|
107
|
-
"text-decoration",
|
|
108
|
-
"underline"
|
|
109
|
-
);
|
|
108
|
+
expect(screen.getByText("Link")).toHaveClass("seeds-link-underline");
|
|
110
109
|
});
|
|
111
110
|
});
|
|
112
111
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@sproutsocial/seeds-react-testing-library";
|
|
3
|
+
import Link from "../Link";
|
|
4
|
+
|
|
5
|
+
describe("Link Tailwind path", () => {
|
|
6
|
+
it("renders with the seeds-link class when no styled props are used", () => {
|
|
7
|
+
render(<Link href="/test">Tailwind Link</Link>);
|
|
8
|
+
const el = screen.getByText("Tailwind Link");
|
|
9
|
+
expect(el.tagName).toBe("A");
|
|
10
|
+
expect(el).toHaveClass("seeds-link");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("applies the disabled class when disabled", () => {
|
|
14
|
+
render(<Link disabled>Disabled</Link>);
|
|
15
|
+
expect(screen.getByText("Disabled")).toHaveClass("seeds-link-disabled");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("forwards a consumer className (styled() extension passthrough)", () => {
|
|
19
|
+
render(
|
|
20
|
+
<Link href="/test" className="sc-extension">
|
|
21
|
+
Class Link
|
|
22
|
+
</Link>
|
|
23
|
+
);
|
|
24
|
+
const el = screen.getByText("Class Link");
|
|
25
|
+
expect(el).toHaveClass("seeds-link");
|
|
26
|
+
expect(el).toHaveClass("sc-extension");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("routes typography props (fontSize) to the styled-components path", () => {
|
|
30
|
+
render(
|
|
31
|
+
<Link href="/test" fontSize={300}>
|
|
32
|
+
Sized Link
|
|
33
|
+
</Link>
|
|
34
|
+
);
|
|
35
|
+
expect(screen.getByText("Sized Link")).not.toHaveClass("seeds-link");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("routes fontFamily to the styled-components path", () => {
|
|
39
|
+
render(
|
|
40
|
+
<Link href="/test" fontFamily="mono">
|
|
41
|
+
Mono Link
|
|
42
|
+
</Link>
|
|
43
|
+
);
|
|
44
|
+
expect(screen.getByText("Mono Link")).not.toHaveClass("seeds-link");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("routes fontStyle to the styled-components path", () => {
|
|
48
|
+
render(
|
|
49
|
+
<Link href="/test" fontStyle="italic">
|
|
50
|
+
Italic Link
|
|
51
|
+
</Link>
|
|
52
|
+
);
|
|
53
|
+
expect(screen.getByText("Italic Link")).not.toHaveClass("seeds-link");
|
|
54
|
+
});
|
|
55
|
+
});
|
package/src/link.css
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds Link component classes
|
|
3
|
+
* Use these instead of writing out individual Tailwind utility classes.
|
|
4
|
+
*
|
|
5
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
6
|
+
* CSS variable definitions and dark mode support.
|
|
7
|
+
*
|
|
8
|
+
* Rules live in the Tailwind `components` layer so consumer utility classes
|
|
9
|
+
* (e.g. mx-200, p-300, inline-flex) win the cascade over these defaults.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
@layer components {
|
|
13
|
+
.seeds-link {
|
|
14
|
+
border: 0;
|
|
15
|
+
appearance: none;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
font-family: var(--font-family);
|
|
18
|
+
font-weight: var(--font-weight-semibold);
|
|
19
|
+
color: var(--color-link-base);
|
|
20
|
+
text-decoration: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.seeds-link-underline {
|
|
24
|
+
text-decoration: underline;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.seeds-link:hover {
|
|
28
|
+
color: var(--color-link-hover);
|
|
29
|
+
text-decoration: underline;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.seeds-link:active {
|
|
33
|
+
color: var(--color-link-hover);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.seeds-link:focus {
|
|
37
|
+
outline: none;
|
|
38
|
+
box-shadow:
|
|
39
|
+
0 0 0 1px var(--color-button-primary-bg-base),
|
|
40
|
+
0 0 0 4px color-mix(in srgb, var(--color-button-primary-bg-base) 30%, transparent);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.seeds-link:focus:active {
|
|
44
|
+
box-shadow: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.seeds-link-disabled {
|
|
48
|
+
opacity: 0.4;
|
|
49
|
+
cursor: not-allowed;
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* When rendered as a button (no href) remove the native button background */
|
|
54
|
+
.seeds-link-no-href {
|
|
55
|
+
background: none;
|
|
56
|
+
}
|
|
57
|
+
}
|