Skip to content

Commit

Permalink
feat: Refactor Hyperlink and ExternalHyperlink
Browse files Browse the repository at this point in the history
  • Loading branch information
alanbsmith committed Nov 13, 2024
1 parent 8027847 commit 1e483a1
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 72 deletions.
68 changes: 43 additions & 25 deletions modules/react/button/lib/ExternalHyperlink.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import React from 'react';
import {styled, createComponent, StyledType} from '@workday/canvas-kit-react/common';

import {createComponent} from '@workday/canvas-kit-react/common';
import {SystemIcon} from '@workday/canvas-kit-react/icon';
import {calc, createStencil, createStyles, handleCsProp, px2rem} from '@workday/canvas-kit-styling';

import {extLinkIcon} from '@workday/canvas-system-icons-web';
import {SystemIcon, systemIconStyles} from '@workday/canvas-kit-react/icon';
import {Hyperlink, HyperlinkProps} from './Hyperlink';
import {system} from '@workday/canvas-tokens-web';

import {hyperlinkStencil, HyperlinkProps} from './Hyperlink';

export interface ExternalHyperlinkProps extends HyperlinkProps {
/**
Expand All @@ -13,25 +18,26 @@ export interface ExternalHyperlinkProps extends HyperlinkProps {
iconLabel?: string;
}

const iconStyles = {
...systemIconStyles({fill: 'currentColor', fillHover: 'currentColor'}),
};
const iconSize = '1em';
const minIconSize = system.space.x4;

const Anchor = styled(Hyperlink)<ExternalHyperlinkProps & StyledType>({
...iconStyles,
display: 'inline-flex',
flexDirection: 'row',
alignItems: 'center',
const externalHyperlinkStencil = createStencil({
extends: hyperlinkStencil,
base: {
display: 'inline-flex',
flexDirection: 'row',
alignItems: 'center',
},
});

const iconSize = '1em';
const minIconSize = '16px';

const StyledSystemIcon = styled(SystemIcon)<StyledType>({
...iconStyles,
width: `calc(${iconSize} - 1px)`,
minWidth: `calc(${minIconSize} - 1px)`,
marginLeft: '2px',
const externalHyperlinkIconStyles = createStyles({
width: calc.subtract(iconSize, px2rem(1)),
minWidth: calc.subtract(minIconSize, px2rem(1)),
marginInlineStart: px2rem(2),
'& svg': {
minWidth: minIconSize,
minHeight: minIconSize,
},
});

/**
Expand All @@ -41,18 +47,30 @@ const StyledSystemIcon = styled(SystemIcon)<StyledType>({
export const ExternalHyperlink = createComponent('a')({
displayName: 'ExternalHyperlink',
Component: (
{children, iconLabel = 'Opens link in new window', ...elemProps}: ExternalHyperlinkProps,
ref
{
variant,
children,
iconLabel = 'Opens link in new window',
...elemProps
}: ExternalHyperlinkProps,
ref,
Element
) => (
<Anchor ref={ref} target="_blank" rel="noreferrer" {...elemProps}>
<Element
ref={ref}
target="_blank"
rel="noreferrer"
{...handleCsProp(elemProps, externalHyperlinkStencil({variant}))}
>
<span>{children}</span>
<StyledSystemIcon
<SystemIcon
icon={extLinkIcon}
role="img"
aria-label={iconLabel}
size={iconSize}
cs={{'& svg': {minWidth: minIconSize, minHeight: minIconSize}}}
color="currentColor"
cs={externalHyperlinkIconStyles}
/>
</Anchor>
</Element>
),
});
88 changes: 43 additions & 45 deletions modules/react/button/lib/Hyperlink.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import * as React from 'react';
import {borderRadius, colors, type} from '@workday/canvas-kit-react/tokens';
import {styled, createComponent, StyledType} from '@workday/canvas-kit-react/common';
import {createComponent} from '@workday/canvas-kit-react/common';
import {createStencil, CSProps, handleCsProp, px2rem} from '@workday/canvas-kit-styling';

export interface HyperlinkProps {
import {system} from '@workday/canvas-tokens-web';

export interface HyperlinkProps extends CSProps {
/**
* sets modifier styles for Hyperlink
* - `inverse`: sets the color to frenchVanilla100 and updates hover, focus, and active pseudo-classes
Expand All @@ -12,69 +14,65 @@ export interface HyperlinkProps {
* attribute for the hyperlink URL
*/
href?: string;
/**
* The children of the `Expandable` container. This should contain `Expandable.Target` and
* `Expandable.Container`
*/
children?: React.ReactNode;
}

const variantStyles = {
inverse: {
color: colors.frenchVanilla100,
'&:hover': {
color: colors.frenchVanilla100,
background: 'rgba(255, 255, 255, 0.1)',
},
'&:focus': {
boxShadow: `0 0 0 2px ${colors.frenchVanilla100}`,
},
'&:active': {
color: colors.blueberry600,
background: colors.soap200,
},
},
};

const anchorVariants = (props: HyperlinkProps) => {
if (props.variant === 'inverse') {
return variantStyles.inverse;
}
return {};
};

const Anchor = styled('a')<HyperlinkProps & StyledType>(
{
fontFamily: type.properties.fontFamilies.default,
export const hyperlinkStencil = createStencil({
base: {
fontFamily: system.fontFamily.default, // type.properties.fontFamilies.default,
textDecoration: 'underline',
color: colors.blueberry400,
color: system.color.text.primary.default, // colors.blueberry400,
cursor: 'pointer',
borderRadius: borderRadius.s,
borderRadius: system.shape.half,
display: 'inline-block',
padding: '0 2px',
margin: '0 -2px',
padding: `0 ${px2rem(2)}`,
margin: `0 -${px2rem(2)}`,
transition: 'color 0.15s,background-color 0.15s',
'&:hover': {
color: colors.blueberry500,
background: colors.soap200,
color: system.color.text.primary.strong, // colors.blueberry500,
background: system.color.bg.alt.soft, // colors.soap200,
},
'&:focus': {
boxShadow: `0 0 0 2px ${colors.blueberry400}`,
boxShadow: `0 0 0 ${px2rem(2)} ${system.color.text.primary.default}`, // colors.blueberry400
outline: 'none',
},
'&:active': {
color: colors.blueberry600,
background: colors.soap300,
color: system.color.text.primary.stronger, // colors.blueberry600,
background: system.color.bg.alt.default, // colors.soap300,
},
},
anchorVariants
);
modifiers: {
variant: {
inverse: {
color: system.color.text.inverse, // colors.frenchVanilla100,
'&:hover': {
color: system.color.text.inverse, // colors.frenchVanilla100,
background: 'rgba(255, 255, 255, 0.1)',
},
'&:focus': {
boxShadow: `0 0 0 ${px2rem(2)} ${system.color.text.inverse}`, // colors.frenchVanilla100
},
'&:active': {
color: system.color.text.primary.stronger, // colors.blueberry600,
// It's weird to me that this is soap 200 and not 300 like the default active state.
background: system.color.bg.alt.soft, // colors.soap200,
},
},
},
},
});

/**
* `Hyperlink`s should be used when you want to navigate away from the current page or to an anchor
* on the current page.
*/
export const Hyperlink = createComponent('a')({
displayName: 'Hyperlink',
Component: ({children, ...elemProps}: HyperlinkProps, ref, Element) => (
<Anchor ref={ref} as={Element} {...elemProps}>
{children}
</Anchor>
Component: ({variant, ...elemProps}: HyperlinkProps, ref, Element) => (
<Element ref={ref} {...handleCsProp(elemProps, hyperlinkStencil({variant}))} />
),
});
4 changes: 2 additions & 2 deletions modules/react/button/stories/button/Hyperlink.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import * as HyperlinkStories from './Hyperlink.stories';

<Meta of={HyperlinkStories} />

# Canvas Kit Hyperlinks
# Canvas Kit Hyperlink

Clickable anchor elements that extend the native `<a>` element with Canvas styling.
A clickable anchor element that extends the native `<a>` element with Canvas styling.

[> Workday Design Reference](https://design.workday.com/components/buttons/buttons)

Expand Down

0 comments on commit 1e483a1

Please sign in to comment.