@toptal/picasso-link 3.0.5-alpha-fx-6095-number-input-misses-type-button-353a18c00.1 → 3.0.5-alpha-FX-NULL-do-not-pass-min-and-max-3ab759916.14

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.
@@ -32,6 +32,21 @@ export declare type Props = BaseProps & AnchorHTMLAttributes<HTMLAnchorElement>
32
32
  noUnderline?: boolean;
33
33
  'aria-disabled'?: boolean;
34
34
  };
35
+ declare type ViewModel = {
36
+ className: string;
37
+ href?: string;
38
+ target?: string;
39
+ color?: 'inherit';
40
+ rel?: string;
41
+ onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void;
42
+ weight: 'inherit' | 'semibold';
43
+ style?: React.CSSProperties;
44
+ tabIndex?: number;
45
+ as: Props['as'];
46
+ ariaDisabled?: boolean;
47
+ nativeHTMLAttributes: Omit<Props, keyof BaseProps>;
48
+ };
49
+ export declare const calculateViewModel: (props: Props) => ViewModel;
35
50
  export declare const Link: OverridableComponent<Props>;
36
51
  export default Link;
37
52
  //# sourceMappingURL=Link.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../../src/Link/Link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACzE,OAAO,KAAqB,MAAM,OAAO,CAAA;AAEzC,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAG7E,QAAA,MAAM,eAAe,4BAA6B,CAAA;AAClD,QAAA,MAAM,iBAAiB,+BAAgC,CAAA;AAEvD,aAAK,WAAW,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,aAAK,SAAS,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AA0CjD,oBAAY,KAAK,GAAG,SAAS,GAC3B,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IACxC,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IAC9D;;;OAGG;IACH,EAAE,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAA;IACnD,qDAAqD;IACrD,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,iCAAiC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAKH,eAAO,MAAM,IAAI,EAAE,oBAAoB,CAAC,KAAK,CA+D3C,CAAA;AAWF,eAAe,IAAI,CAAA"}
1
+ {"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../../src/Link/Link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACzE,OAAO,KAAqB,MAAM,OAAO,CAAA;AAEzC,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAG7E,QAAA,MAAM,eAAe,4BAA6B,CAAA;AAClD,QAAA,MAAM,iBAAiB,+BAAgC,CAAA;AAEvD,aAAK,WAAW,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,aAAK,SAAS,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AA0CjD,oBAAY,KAAK,GAAG,SAAS,GAC3B,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IACxC,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IAC9D;;;OAGG;IACH,EAAE,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAA;IACnD,qDAAqD;IACrD,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,iCAAiC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAKH,aAAK,SAAS,GAAG;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IAC9D,MAAM,EAAE,SAAS,GAAG,UAAU,CAAA;IAC9B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IACf,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,oBAAoB,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,SAAS,CAAC,CAAA;CACnD,CAAA;AAGD,eAAO,MAAM,kBAAkB,UAAW,KAAK,KAAG,SAqDjD,CAAA;AAED,eAAO,MAAM,IAAI,EAAE,oBAAoB,CAAC,KAAK,CA0B3C,CAAA;AAWF,eAAe,IAAI,CAAA"}
@@ -48,22 +48,39 @@ const COLOR_DISABLED_MAP = {
48
48
  };
49
49
  const defaultColor = 'blue';
50
50
  const defaultVariant = 'anchor';
51
- export const Link = forwardRef(function Link(props, ref) {
52
- const { href, onClick, children, className, color: inputColor = 'blue', style, as = 'a', variant: inputVariant = 'anchor', tabIndex, target, rel, disabled, visited = false, noUnderline, 'aria-disabled': ariaDisabled } = props, rest = __rest(props, ["href", "onClick", "children", "className", "color", "style", "as", "variant", "tabIndex", "target", "rel", "disabled", "visited", "noUnderline", 'aria-disabled']);
53
- const nativeHTMLAttributes = rest;
51
+ /* eslint-disable complexity */
52
+ export const calculateViewModel = (props) => {
53
+ const { href, onClick, className, as = 'a', color: inputColor = 'blue', style, variant: inputVariant = 'anchor', tabIndex, target, rel, disabled, visited = false, noUnderline, 'aria-disabled': ariaDisabled } = props, nativeHTMLAttributes = __rest(props, ["href", "onClick", "className", "as", "color", "style", "variant", "tabIndex", "target", "rel", "disabled", "visited", "noUnderline", 'aria-disabled']);
54
54
  const sanitizedRel = sanitizeRel(rel, target);
55
55
  // When Link is used as={Link}, TypeScript can't ensure the input to the Link is compatible with its Props type.
56
56
  const color = supportedColors.includes(inputColor) ? inputColor : defaultColor;
57
57
  const variant = supportedVariants.includes(inputVariant)
58
58
  ? inputVariant
59
59
  : defaultVariant;
60
- return (React.createElement(Typography, Object.assign({}, nativeHTMLAttributes, { ref: ref, as: as,
61
- // @ts-expect-error Typography is incompatible with href prop
62
- href: disabled ? undefined : href, target: disabled ? undefined : target, rel: sanitizedRel, onClick: disabled ? undefined : onClick, color: 'inherit', weight: variant === 'action' ? 'semibold' : 'inherit', className: twMerge('focus:outline-none hover:underline leading-[inherit]', COLOR_DISABLED_MAP[color][variant][disabled ? 'disabled' : 'normal'], disabled ? 'cursor-not-allowed' : '', noUnderline ? '!no-underline' : '', visited
60
+ return {
61
+ className: twMerge('focus:outline-none hover:underline leading-[inherit]', COLOR_DISABLED_MAP[color][variant][disabled ? 'disabled' : 'normal'], disabled ? 'cursor-not-allowed' : '', noUnderline ? '!no-underline' : '', visited
63
62
  ? color === 'blue'
64
63
  ? 'visited text-purple-500'
65
64
  : 'visited text-gray-500'
66
- : '', onClick || href ? 'cursor-pointer' : '', className), style: style, tabIndex: tabIndex, "aria-disabled": disabled || ariaDisabled }), children));
65
+ : '', onClick || href ? 'cursor-pointer' : '', className),
66
+ href: disabled ? undefined : href,
67
+ target: disabled ? undefined : target,
68
+ rel: sanitizedRel,
69
+ onClick: disabled ? undefined : onClick,
70
+ weight: variant === 'action' ? 'semibold' : 'inherit',
71
+ color: 'inherit',
72
+ style,
73
+ tabIndex,
74
+ as,
75
+ ariaDisabled: disabled || ariaDisabled,
76
+ nativeHTMLAttributes,
77
+ };
78
+ };
79
+ export const Link = forwardRef(function Link(props, ref) {
80
+ const viewModel = calculateViewModel(props);
81
+ return (React.createElement(Typography, Object.assign({}, viewModel.nativeHTMLAttributes, { ref: ref, as: viewModel.as,
82
+ // @ts-expect-error Typography is incompatible with href prop
83
+ href: viewModel.href, target: viewModel.target, rel: viewModel.rel, onClick: viewModel.onClick, color: viewModel.color, weight: viewModel.weight, className: viewModel.className, style: viewModel.style, tabIndex: viewModel.tabIndex, "aria-disabled": viewModel.ariaDisabled }), props.children));
67
84
  });
68
85
  Link.defaultProps = {
69
86
  as: 'a',
@@ -1 +1 @@
1
- {"version":3,"file":"Link.js","sourceRoot":"","sources":["../../../src/Link/Link.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Link.js","sourceRoot":"","sources":["../../../src/Link/Link.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toptal/picasso-link",
3
- "version": "3.0.5-alpha-fx-6095-number-input-misses-type-button-353a18c00.1+353a18c00",
3
+ "version": "3.0.5-alpha-FX-NULL-do-not-pass-min-and-max-3ab759916.14+3ab759916",
4
4
  "description": "Toptal UI components library - Link",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "homepage": "https://github.com/toptal/picasso/tree/master/packages/picasso#readme",
24
24
  "dependencies": {
25
- "@toptal/picasso-typography": "4.0.3-alpha-fx-6095-number-input-misses-type-button-353a18c00.1+353a18c00",
26
- "@toptal/picasso-utils": "3.0.1-alpha-fx-6095-number-input-misses-type-button-353a18c00.1+353a18c00"
25
+ "@toptal/picasso-typography": "4.0.3-alpha-FX-NULL-do-not-pass-min-and-max-3ab759916.14+3ab759916",
26
+ "@toptal/picasso-utils": "3.0.1-alpha-FX-NULL-do-not-pass-min-and-max-3ab759916.14+3ab759916"
27
27
  },
28
28
  "sideEffects": [
29
29
  "**/styles.ts",
@@ -39,14 +39,14 @@
39
39
  ".": "./dist-package/src/index.js"
40
40
  },
41
41
  "devDependencies": {
42
- "@toptal/picasso-provider": "5.0.1-alpha-fx-6095-number-input-misses-type-button-353a18c00.175+353a18c00",
43
- "@toptal/picasso-tailwind-merge": "2.0.3-alpha-fx-6095-number-input-misses-type-button-353a18c00.1+353a18c00",
44
- "@toptal/picasso-test-utils": "1.1.2-alpha-fx-6095-number-input-misses-type-button-353a18c00.254+353a18c00"
42
+ "@toptal/picasso-provider": "5.0.1-alpha-FX-NULL-do-not-pass-min-and-max-3ab759916.188+3ab759916",
43
+ "@toptal/picasso-tailwind-merge": "2.0.3-alpha-FX-NULL-do-not-pass-min-and-max-3ab759916.14+3ab759916",
44
+ "@toptal/picasso-test-utils": "1.1.2-alpha-FX-NULL-do-not-pass-min-and-max-3ab759916.267+3ab759916"
45
45
  },
46
46
  "files": [
47
47
  "dist-package/**",
48
48
  "!dist-package/tsconfig.tsbuildinfo",
49
49
  "src"
50
50
  ],
51
- "gitHead": "353a18c00d04fe7c52e2ba8a52a4bc900ca33bda"
51
+ "gitHead": "3ab75991614e7b60794ebca5dcc0ec8863b417b9"
52
52
  }
package/src/Link/Link.tsx CHANGED
@@ -83,18 +83,30 @@ export type Props = BaseProps &
83
83
  const defaultColor = 'blue'
84
84
  const defaultVariant = 'anchor'
85
85
 
86
- export const Link: OverridableComponent<Props> = forwardRef<
87
- HTMLAnchorElement,
88
- Props
89
- >(function Link(props, ref) {
86
+ type ViewModel = {
87
+ className: string
88
+ href?: string
89
+ target?: string
90
+ color?: 'inherit'
91
+ rel?: string
92
+ onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void
93
+ weight: 'inherit' | 'semibold'
94
+ style?: React.CSSProperties
95
+ tabIndex?: number
96
+ as: Props['as']
97
+ ariaDisabled?: boolean
98
+ nativeHTMLAttributes: Omit<Props, keyof BaseProps>
99
+ }
100
+
101
+ /* eslint-disable complexity */
102
+ export const calculateViewModel = (props: Props): ViewModel => {
90
103
  const {
91
104
  href,
92
105
  onClick,
93
- children,
94
106
  className,
107
+ as = 'a',
95
108
  color: inputColor = 'blue',
96
109
  style,
97
- as = 'a',
98
110
  variant: inputVariant = 'anchor',
99
111
  tabIndex,
100
112
  target,
@@ -103,9 +115,9 @@ export const Link: OverridableComponent<Props> = forwardRef<
103
115
  visited = false,
104
116
  noUnderline,
105
117
  'aria-disabled': ariaDisabled,
106
- ...rest
118
+ ...nativeHTMLAttributes
107
119
  } = props
108
- const nativeHTMLAttributes = rest
120
+
109
121
  const sanitizedRel = sanitizeRel(rel, target)
110
122
 
111
123
  // When Link is used as={Link}, TypeScript can't ensure the input to the Link is compatible with its Props type.
@@ -114,36 +126,58 @@ export const Link: OverridableComponent<Props> = forwardRef<
114
126
  ? inputVariant
115
127
  : defaultVariant
116
128
 
129
+ return {
130
+ className: twMerge(
131
+ 'focus:outline-none hover:underline leading-[inherit]',
132
+ COLOR_DISABLED_MAP[color][variant][disabled ? 'disabled' : 'normal'],
133
+ disabled ? 'cursor-not-allowed' : '',
134
+ noUnderline ? '!no-underline' : '',
135
+ visited
136
+ ? color === 'blue'
137
+ ? 'visited text-purple-500'
138
+ : 'visited text-gray-500'
139
+ : '',
140
+ onClick || href ? 'cursor-pointer' : '',
141
+ className
142
+ ),
143
+ href: disabled ? undefined : href,
144
+ target: disabled ? undefined : target,
145
+ rel: sanitizedRel,
146
+ onClick: disabled ? undefined : onClick,
147
+ weight: variant === 'action' ? 'semibold' : 'inherit',
148
+ color: 'inherit',
149
+ style,
150
+ tabIndex,
151
+ as,
152
+ ariaDisabled: disabled || ariaDisabled,
153
+ nativeHTMLAttributes,
154
+ }
155
+ }
156
+
157
+ export const Link: OverridableComponent<Props> = forwardRef<
158
+ HTMLAnchorElement,
159
+ Props
160
+ >(function Link(props, ref) {
161
+ const viewModel = calculateViewModel(props)
162
+
117
163
  return (
118
164
  <Typography
119
- {...nativeHTMLAttributes}
165
+ {...viewModel.nativeHTMLAttributes}
120
166
  ref={ref}
121
- as={as}
167
+ as={viewModel.as}
122
168
  // @ts-expect-error Typography is incompatible with href prop
123
- href={disabled ? undefined : href}
124
- target={disabled ? undefined : target}
125
- rel={sanitizedRel}
126
- onClick={disabled ? undefined : onClick}
127
- color='inherit'
128
- weight={variant === 'action' ? 'semibold' : 'inherit'}
129
- className={twMerge(
130
- 'focus:outline-none hover:underline leading-[inherit]',
131
- COLOR_DISABLED_MAP[color][variant][disabled ? 'disabled' : 'normal'],
132
- disabled ? 'cursor-not-allowed' : '',
133
- noUnderline ? '!no-underline' : '',
134
- visited
135
- ? color === 'blue'
136
- ? 'visited text-purple-500'
137
- : 'visited text-gray-500'
138
- : '',
139
- onClick || href ? 'cursor-pointer' : '',
140
- className
141
- )}
142
- style={style}
143
- tabIndex={tabIndex}
144
- aria-disabled={disabled || ariaDisabled}
169
+ href={viewModel.href}
170
+ target={viewModel.target}
171
+ rel={viewModel.rel}
172
+ onClick={viewModel.onClick}
173
+ color={viewModel.color}
174
+ weight={viewModel.weight}
175
+ className={viewModel.className}
176
+ style={viewModel.style}
177
+ tabIndex={viewModel.tabIndex}
178
+ aria-disabled={viewModel.ariaDisabled}
145
179
  >
146
- {children}
180
+ {props.children}
147
181
  </Typography>
148
182
  )
149
183
  })
@@ -14,24 +14,7 @@ exports[`Link renders 1`] = `
14
14
  </div>
15
15
  `;
16
16
 
17
- exports[`Link renders a Link from react-router 1`] = `
18
- <div>
19
- <div
20
- class="Picasso-root"
21
- >
22
- <div>
23
- <a
24
- class="m-0 font-inherit font-inherit focus:outline-none hover:underline leading-[inherit] text-blue visited:text-purple no-underline"
25
- href="/"
26
- >
27
- Please verify your email
28
- </a>
29
- </div>
30
- </div>
31
- </div>
32
- `;
33
-
34
- exports[`Link renders disabled link 1`] = `
17
+ exports[`Link when disabled renders disabled link 1`] = `
35
18
  <div>
36
19
  <div
37
20
  class="Picasso-root"
@@ -48,7 +31,7 @@ exports[`Link renders disabled link 1`] = `
48
31
  </div>
49
32
  `;
50
33
 
51
- exports[`Link renders native attributes 1`] = `
34
+ exports[`Link when native attributes are provided renders native attributes 1`] = `
52
35
  <div>
53
36
  <div
54
37
  class="Picasso-root"
@@ -65,3 +48,20 @@ exports[`Link renders native attributes 1`] = `
65
48
  </div>
66
49
  </div>
67
50
  `;
51
+
52
+ exports[`Link when using react-router Link renders a Link from react-router 1`] = `
53
+ <div>
54
+ <div
55
+ class="Picasso-root"
56
+ >
57
+ <div>
58
+ <a
59
+ class="m-0 font-inherit font-inherit focus:outline-none hover:underline leading-[inherit] text-blue visited:text-purple no-underline"
60
+ href="/"
61
+ >
62
+ Please verify your email
63
+ </a>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ `;
package/src/Link/test.tsx CHANGED
@@ -2,6 +2,7 @@ import React from 'react'
2
2
  import { render, fireEvent } from '@toptal/picasso-test-utils'
3
3
  import { MemoryRouter, Link as RouterLink } from 'react-router-dom'
4
4
 
5
+ import { calculateViewModel } from './Link'
5
6
  import { Link } from '../Link'
6
7
 
7
8
  describe('Link', () => {
@@ -11,91 +12,335 @@ describe('Link', () => {
11
12
  expect(container).toMatchSnapshot()
12
13
  })
13
14
 
14
- it('renders native attributes', () => {
15
- const { container } = render(
16
- <Link
17
- onBlur={() => window.alert('onBlur')}
18
- rel='noopener'
19
- target='_blank'
20
- download='filename'
21
- href='https://toptal.com/filename.txt'
22
- >
23
- Please verify your email
24
- </Link>
25
- )
15
+ describe('when native attributes are provided', () => {
16
+ it('renders native attributes', () => {
17
+ const { container } = render(
18
+ <Link
19
+ onBlur={() => window.alert('onBlur')}
20
+ rel='noopener'
21
+ target='_blank'
22
+ download='filename'
23
+ href='https://toptal.com/filename.txt'
24
+ >
25
+ Please verify your email
26
+ </Link>
27
+ )
26
28
 
27
- expect(container).toMatchSnapshot()
29
+ expect(container).toMatchSnapshot()
30
+ })
28
31
  })
29
32
 
30
- it('renders a Link from react-router', () => {
31
- const { container } = render(
32
- <MemoryRouter>
33
- <div>
34
- <Link as={RouterLink} to='/'>
35
- Please verify your email
36
- </Link>
37
- </div>
38
- </MemoryRouter>
39
- )
33
+ describe('when using react-router Link', () => {
34
+ it('renders a Link from react-router', () => {
35
+ const { container } = render(
36
+ <MemoryRouter>
37
+ <div>
38
+ <Link as={RouterLink} to='/'>
39
+ Please verify your email
40
+ </Link>
41
+ </div>
42
+ </MemoryRouter>
43
+ )
40
44
 
41
- expect(container).toMatchSnapshot()
45
+ expect(container).toMatchSnapshot()
46
+ })
42
47
  })
43
48
 
44
- it('renders disabled link', () => {
45
- const { container } = render(
46
- <Link
47
- rel='noopener'
48
- target='_blank'
49
- download='filename'
50
- href='https://toptal.com/filename.txt'
51
- disabled
52
- >
53
- Please verify your email
54
- </Link>
55
- )
49
+ describe('when disabled', () => {
50
+ it('renders disabled link', () => {
51
+ const { container } = render(
52
+ <Link
53
+ rel='noopener'
54
+ target='_blank'
55
+ download='filename'
56
+ href='https://toptal.com/filename.txt'
57
+ disabled
58
+ >
59
+ Please verify your email
60
+ </Link>
61
+ )
56
62
 
57
- expect(container).toMatchSnapshot()
63
+ expect(container).toMatchSnapshot()
64
+ })
65
+
66
+ it('does not allow onClick', () => {
67
+ const onClick = jest.fn()
68
+ const { getByTestId } = render(
69
+ <Link
70
+ data-testid='foo'
71
+ onClick={onClick}
72
+ href='https://foo.bar'
73
+ disabled
74
+ >
75
+ Test
76
+ </Link>
77
+ )
78
+
79
+ fireEvent.click(getByTestId('foo'))
80
+ expect(onClick).not.toHaveBeenCalled()
81
+ })
82
+
83
+ it('does not have href', () => {
84
+ const { getByTestId } = render(
85
+ <Link data-testid='foo' href='https://foo.bar' disabled>
86
+ Test
87
+ </Link>
88
+ )
89
+
90
+ expect(getByTestId('foo')).not.toHaveAttribute('href')
91
+ })
92
+ })
93
+
94
+ describe('when target="_blank"', () => {
95
+ it('adds rel="noopener" if rel is absent', () => {
96
+ render(
97
+ <Link href='http://example.com' target='_blank'>
98
+ External Link
99
+ </Link>
100
+ )
101
+ expect(document.querySelector('a')).toHaveAttribute('rel', 'noopener')
102
+ })
103
+
104
+ it('does not add rel="noopener" if noreferrer is present', () => {
105
+ render(
106
+ <Link href='http://example.com' target='_blank' rel='noreferrer'>
107
+ External Link
108
+ </Link>
109
+ )
110
+ expect(document.querySelector('a')).not.toHaveAttribute('rel', 'noopener')
111
+ })
112
+ })
113
+ })
114
+
115
+ describe('calculateViewModel', () => {
116
+ describe('when no props are provided', () => {
117
+ it('applies default values', () => {
118
+ const props = {}
119
+ const result = calculateViewModel(props)
120
+
121
+ expect(result.className).toContain('text-blue-500')
122
+ expect(result.href).toBeUndefined()
123
+ expect(result.target).toBeUndefined()
124
+ expect(result.rel).toBeUndefined()
125
+ expect(result.onClick).toBeUndefined()
126
+ expect(result.weight).toBe('inherit')
127
+ expect(result.ariaDisabled).toBeUndefined()
128
+ })
129
+ })
130
+
131
+ describe('when href, target and onClick are provided', () => {
132
+ it('passes them through to view model', () => {
133
+ const props = {
134
+ href: 'https://example.com',
135
+ target: '_blank',
136
+ onClick: jest.fn(),
137
+ }
138
+ const result = calculateViewModel(props)
139
+
140
+ expect(result.href).toBe('https://example.com')
141
+ expect(result.target).toBe('_blank')
142
+ expect(result.onClick).toBe(props.onClick)
143
+ })
144
+ })
145
+
146
+ describe('when target="_blank" and rel is provided', () => {
147
+ it('sanitizes rel for target="_blank"', () => {
148
+ const props = {
149
+ target: '_blank',
150
+ rel: 'nofollow',
151
+ }
152
+ const result = calculateViewModel(props)
153
+
154
+ expect(result.rel).toBe('nofollow noopener')
155
+ })
156
+ })
157
+
158
+ describe('when disabled', () => {
159
+ it('applies disabled behavior', () => {
160
+ const props = {
161
+ disabled: true,
162
+ href: 'https://example.com',
163
+ target: '_blank',
164
+ onClick: jest.fn(),
165
+ }
166
+ const result = calculateViewModel(props)
167
+
168
+ expect(result.className).toContain('cursor-pointer')
169
+ expect(result.href).toBeUndefined()
170
+ expect(result.target).toBeUndefined()
171
+ expect(result.onClick).toBeUndefined()
172
+ expect(result.ariaDisabled).toBe(true)
173
+ })
174
+
175
+ it('applies disabled color styles for the provided color', () => {
176
+ const blueProps = {
177
+ color: 'blue',
178
+ disabled: true,
179
+ }
180
+ const whiteProps = {
181
+ color: 'white',
182
+ disabled: true,
183
+ }
184
+
185
+ const blueResult = calculateViewModel(blueProps)
186
+ const whiteResult = calculateViewModel(whiteProps)
187
+
188
+ expect(blueResult.className).toContain(
189
+ 'focus:outline-none hover:underline leading-[inherit] text-gray-600 underline cursor-not-allowed'
190
+ )
191
+ expect(whiteResult.className).toContain('text-gray-600')
192
+ })
58
193
  })
59
194
 
60
- it('adds rel="noopener" to target="_blank" links in its absence', () => {
61
- render(
62
- <Link href='http://example.com' target='_blank'>
63
- External Link
64
- </Link>
65
- )
195
+ describe('when visited is true', () => {
196
+ it('applies visited class when color is blue', () => {
197
+ const props = {
198
+ visited: true,
199
+ color: 'blue',
200
+ }
201
+ const result = calculateViewModel(props)
66
202
 
67
- expect(document.querySelector('a')).toHaveAttribute('rel', 'noopener')
203
+ expect(result.className).toContain('visited text-purple-500')
204
+ })
205
+
206
+ it('applies visited class when color is white', () => {
207
+ const props = {
208
+ color: 'white',
209
+ visited: true,
210
+ }
211
+ const result = calculateViewModel(props)
212
+
213
+ expect(result.className).toContain('visited text-gray-500')
214
+ })
215
+ })
216
+
217
+ describe('when noUnderline is true', () => {
218
+ it('handles noUnderline properly', () => {
219
+ const props = {
220
+ noUnderline: true,
221
+ color: 'blue',
222
+ }
223
+ const result = calculateViewModel(props)
224
+
225
+ expect(result.className).toContain('!no-underline')
226
+ })
227
+ })
228
+
229
+ describe('when variant is provided', () => {
230
+ it('applies correct weight based on variant', () => {
231
+ const actionProps = {
232
+ variant: 'action' as const,
233
+ }
234
+ const anchorProps = {
235
+ variant: 'anchor' as const,
236
+ }
237
+
238
+ const actionResult = calculateViewModel(actionProps)
239
+ const anchorResult = calculateViewModel(anchorProps)
240
+
241
+ expect(actionResult.weight).toBe('semibold')
242
+ expect(anchorResult.weight).toBe('inherit')
243
+ })
244
+ })
245
+
246
+ describe('when unsupported color or variant is provided', () => {
247
+ it('uses default values', () => {
248
+ const props = {
249
+ color: 'unsupportedColor',
250
+ variant: 'unsupportedVariant',
251
+ }
252
+ const result = calculateViewModel(props)
253
+
254
+ expect(result.className).toContain('text-blue-500')
255
+ expect(result.weight).toBe('inherit')
256
+ })
257
+ })
258
+
259
+ describe('when className is provided', () => {
260
+ it('applies custom className', () => {
261
+ const props = {
262
+ className: 'custom-class',
263
+ }
264
+ const result = calculateViewModel(props)
265
+
266
+ expect(result.className).toContain('custom-class')
267
+ })
68
268
  })
69
269
 
70
- it('does not add rel="noopener" to target="_blank" if noreferrer is present', () => {
71
- render(
72
- <Link href='http://example.com' target='_blank' rel='noreferrer'>
73
- External Link
74
- </Link>
75
- )
270
+ describe('when tabIndex is provided', () => {
271
+ it('applies tabIndex', () => {
272
+ const props = {
273
+ tabIndex: 0,
274
+ }
275
+ const result = calculateViewModel(props)
76
276
 
77
- expect(document.querySelector('a')).not.toHaveAttribute('rel', 'noopener')
277
+ expect(result.tabIndex).toBe(0)
278
+ })
78
279
  })
79
280
 
80
- it('does not allow onClick when disabled', () => {
81
- const onClick = jest.fn()
82
- const { getByTestId } = render(
83
- <Link data-testid='foo' onClick={onClick} href='https://foo.bar' disabled>
84
- Test
85
- </Link>
86
- )
281
+ describe('when additional native HTML attributes are provided', () => {
282
+ it('includes them', () => {
283
+ const props = {
284
+ 'data-test-id': 'link-element',
285
+ }
286
+ const result = calculateViewModel(props)
87
287
 
88
- fireEvent.click(getByTestId('foo'))
89
- expect(onClick).not.toHaveBeenCalled()
288
+ expect(result.nativeHTMLAttributes['data-test-id']).toBe('link-element')
289
+ })
90
290
  })
91
291
 
92
- it('does not have href when disabled', () => {
93
- const { getByTestId } = render(
94
- <Link data-testid='foo' href='https://foo.bar' disabled>
95
- Test
96
- </Link>
97
- )
292
+ describe('when "as" prop is provided', () => {
293
+ it('applies the default "as" prop as "a" when no "as" prop is provided', () => {
294
+ const props = {}
295
+ const result = calculateViewModel(props)
296
+
297
+ expect(result.as).toBe('a')
298
+ })
299
+
300
+ it('applies the provided "as" prop when a custom element type is given', () => {
301
+ const props = {
302
+ as: 'button',
303
+ }
304
+ const result = calculateViewModel(props)
305
+
306
+ expect(result.as).toBe('button')
307
+ })
308
+
309
+ it('applies the provided "as" prop with a custom React component', () => {
310
+ const CustomComponent = () => <div />
311
+ const props = {
312
+ as: CustomComponent,
313
+ }
314
+ const result = calculateViewModel(props)
315
+
316
+ expect(result.as).toBe(CustomComponent)
317
+ })
318
+ })
319
+
320
+ describe('when color prop is provided', () => {
321
+ it('applies the default color "blue" when no color prop is provided', () => {
322
+ const props = {}
323
+ const result = calculateViewModel(props)
324
+
325
+ expect(result.className).toContain('text-blue-500')
326
+ })
327
+
328
+ it('applies the white color when color="white" is provided', () => {
329
+ const props = {
330
+ color: 'white',
331
+ }
332
+ const result = calculateViewModel(props)
333
+
334
+ expect(result.className).toContain('inherit')
335
+ })
336
+
337
+ it('fallbacks to the default color "blue" if an unsupported color is provided', () => {
338
+ const props = {
339
+ color: 'unsupportedColor',
340
+ }
341
+ const result = calculateViewModel(props)
98
342
 
99
- expect(getByTestId('foo')).not.toHaveAttribute('href')
343
+ expect(result.className).toContain('text-blue-500')
344
+ })
100
345
  })
101
346
  })