Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Link to add more cheap unit tests #4553

Merged
merged 8 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 67 additions & 33 deletions packages/base/Link/src/Link/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,30 @@ export type Props = BaseProps &
const defaultColor = 'blue'
const defaultVariant = 'anchor'

export const Link: OverridableComponent<Props> = forwardRef<
HTMLAnchorElement,
Props
>(function Link(props, ref) {
type ViewModel = {
className: string
href?: string
target?: string
color?: 'inherit'
rel?: string
onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void
weight: 'inherit' | 'semibold'
style?: React.CSSProperties
tabIndex?: number
as: Props['as']
ariaDisabled?: boolean
nativeHTMLAttributes: Omit<Props, keyof BaseProps>
}

/* eslint-disable complexity */
OleksandrNechai marked this conversation as resolved.
Show resolved Hide resolved
export const calculateViewModel = (props: Props): ViewModel => {
OleksandrNechai marked this conversation as resolved.
Show resolved Hide resolved
const {
href,
onClick,
children,
className,
as = 'a',
color: inputColor = 'blue',
style,
as = 'a',
variant: inputVariant = 'anchor',
tabIndex,
target,
Expand All @@ -103,9 +115,9 @@ export const Link: OverridableComponent<Props> = forwardRef<
visited = false,
noUnderline,
'aria-disabled': ariaDisabled,
...rest
...nativeHTMLAttributes
} = props
const nativeHTMLAttributes = rest

const sanitizedRel = sanitizeRel(rel, target)

// When Link is used as={Link}, TypeScript can't ensure the input to the Link is compatible with its Props type.
Expand All @@ -114,36 +126,58 @@ export const Link: OverridableComponent<Props> = forwardRef<
? inputVariant
: defaultVariant

return {
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
? color === 'blue'
? 'visited text-purple-500'
: 'visited text-gray-500'
: '',
onClick || href ? 'cursor-pointer' : '',
className
),
href: disabled ? undefined : href,
target: disabled ? undefined : target,
rel: sanitizedRel,
onClick: disabled ? undefined : onClick,
weight: variant === 'action' ? 'semibold' : 'inherit',
color: 'inherit',
style,
tabIndex,
as,
ariaDisabled: disabled || ariaDisabled,
nativeHTMLAttributes,
}
}

export const Link: OverridableComponent<Props> = forwardRef<
HTMLAnchorElement,
Props
>(function Link(props, ref) {
const viewModel = calculateViewModel(props)

return (
<Typography
{...nativeHTMLAttributes}
{...viewModel.nativeHTMLAttributes}
ref={ref}
as={as}
as={viewModel.as}
// @ts-expect-error Typography is incompatible with href prop
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
? color === 'blue'
? 'visited text-purple-500'
: 'visited text-gray-500'
: '',
onClick || href ? 'cursor-pointer' : '',
className
)}
style={style}
tabIndex={tabIndex}
aria-disabled={disabled || ariaDisabled}
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}
>
{children}
{props.children}
</Typography>
)
})
Expand Down
38 changes: 19 additions & 19 deletions packages/base/Link/src/Link/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,7 @@ exports[`Link renders 1`] = `
</div>
`;

exports[`Link renders a Link from react-router 1`] = `
<div>
<div
class="Picasso-root"
>
<div>
<a
class="m-0 font-inherit font-inherit focus:outline-none hover:underline leading-[inherit] text-blue visited:text-purple no-underline"
href="/"
>
Please verify your email
</a>
</div>
</div>
</div>
`;

exports[`Link renders disabled link 1`] = `
exports[`Link when disabled renders disabled link 1`] = `
<div>
<div
class="Picasso-root"
Expand All @@ -48,7 +31,7 @@ exports[`Link renders disabled link 1`] = `
</div>
`;

exports[`Link renders native attributes 1`] = `
exports[`Link when native attributes are provided renders native attributes 1`] = `
<div>
<div
class="Picasso-root"
Expand All @@ -65,3 +48,20 @@ exports[`Link renders native attributes 1`] = `
</div>
</div>
`;

exports[`Link when using react-router Link renders a Link from react-router 1`] = `
<div>
<div
class="Picasso-root"
>
<div>
<a
class="m-0 font-inherit font-inherit focus:outline-none hover:underline leading-[inherit] text-blue visited:text-purple no-underline"
href="/"
>
Please verify your email
</a>
</div>
</div>
</div>
`;
Loading
Loading