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

feat: Refactor Hyperlink and ExternalHyperlink #3050

Closed
wants to merge 3 commits into from
Closed
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
4 changes: 2 additions & 2 deletions modules/docs/lib/MDXElements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const Button = (props: any) => {
<SymbolDialog
value={{
kind: 'symbol',
name: props.children[0],
name: props.children?.[0] || '',
displayName: props['data-symbol'],
value: props.children[0],
value: props.children?.[0] || '',
}}
/>
</code>
Expand Down
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
Loading