-
Notifications
You must be signed in to change notification settings - Fork 4
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(Link)!: introduce v2.0 component #1890
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
@import '../../design-tokens/mixins.css'; | ||
|
||
/*------------------------------------*\ | ||
# LINK | ||
\*------------------------------------*/ | ||
|
||
.link { | ||
color: var(--eds-theme-color-text-utility-neutral-primary); | ||
display: inline; | ||
|
||
/* TODO-AH: verify the way to sync type tokens and one-off treatments */ | ||
font-weight: 500; | ||
text-decoration-line: underline; | ||
} | ||
|
||
/** | ||
* Sub-components | ||
*/ | ||
.link__icon { | ||
padding-left: 0.25rem; | ||
|
||
/* Sub-component spacing */ | ||
&.link--size-xl, | ||
&.link--size-lg { padding-left: 0.5rem; } | ||
|
||
&.link--size-md, | ||
&.link--size-sm, | ||
&.link--size-xs { padding-left: 0.25rem; } | ||
} | ||
|
||
/** | ||
* Contexts | ||
*/ | ||
.link--context-standalone { | ||
display: block; | ||
|
||
/** | ||
* Sizes - using the presets for type ramp matching body-* | ||
*/ | ||
&.link--size-xl { | ||
font: var(--eds-theme-typography-body-xl); | ||
} | ||
|
||
&.link--size-lg { | ||
font: var(--eds-theme-typography-body-lg); | ||
} | ||
|
||
&.link--size-md { | ||
font: var(--eds-theme-typography-body-md); | ||
} | ||
|
||
&.link--size-sm { | ||
font: var(--eds-theme-typography-body-sm); | ||
} | ||
|
||
&.link--size-xs { | ||
font: var(--eds-theme-typography-body-xs); | ||
} | ||
} | ||
|
||
/** | ||
* Emphasis | ||
*/ | ||
.link--emphasis-high { | ||
color: var(--eds-theme-color-text-utility-interactive-secondary); | ||
} | ||
|
||
.link--emphasis-low { | ||
color: var(--eds-theme-color-text-utility-neutral-primary); | ||
text-decoration: none; | ||
} | ||
|
||
|
||
/** | ||
* States | ||
*/ | ||
|
||
.link:hover { | ||
color: var(--eds-theme-color-text-utility-interactive-secondary-hover); | ||
} | ||
|
||
.link:active { | ||
color: var(--eds-theme-color-text-utility-interactive-secondary-active); | ||
} | ||
|
||
.link:visited { | ||
color: var(--eds-theme-color-text-utility-interactive-visited); | ||
} | ||
|
||
.link:focus-visible { | ||
outline: 0.125rem solid var(--eds-theme-color-border-utility-focus); | ||
outline-offset: 0.125rem; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import type { StoryObj, Meta } from '@storybook/react'; | ||
import React from 'react'; | ||
import { Link, type LinkProps } from './Link-v2'; | ||
|
||
export default { | ||
title: 'Components/Link (v2)', | ||
component: Link, | ||
parameters: { | ||
badges: ['intro-1.0', 'current-2.0'], | ||
}, | ||
args: { | ||
children: 'Link', | ||
size: 'lg', | ||
href: 'https://go.czi.team/eds', | ||
// stop link from navigating to another page so we can click the link for testing | ||
onClick: (event: any) => event.preventDefault(), | ||
}, | ||
} as Meta<Args>; | ||
|
||
type Args = React.ComponentProps<typeof Link>; | ||
|
||
export const Default: StoryObj<Args> = {}; | ||
|
||
export const LinkWithChevron: StoryObj<Args> = { | ||
args: { | ||
children: 'Default', | ||
context: 'standalone', | ||
icon: 'chevron-right', | ||
}, | ||
}; | ||
|
||
export const LinkWithOpenIcon: StoryObj<Args> = { | ||
args: { | ||
children: 'Default', | ||
context: 'standalone', | ||
icon: 'open-in-new', | ||
}, | ||
}; | ||
|
||
export const Emphasis: StoryObj<Args> = { | ||
args: { | ||
size: 'md', | ||
context: 'standalone', | ||
}, | ||
render: (args) => { | ||
return ( | ||
<div> | ||
<Link {...args} emphasis="default"> | ||
Default Emphasis | ||
</Link> | ||
<Link {...args} emphasis="high"> | ||
High Emphasis | ||
</Link> | ||
<Link {...args} emphasis="low"> | ||
Low Emphasis | ||
</Link> | ||
</div> | ||
); | ||
}, | ||
}; | ||
|
||
export const LinkInParagraphContext: StoryObj<Args> = { | ||
render: ( | ||
args: React.JSX.IntrinsicAttributes & | ||
(LinkProps & React.RefAttributes<HTMLAnchorElement>), | ||
) => ( | ||
<div> | ||
Lorem ipsum dolor sit amet,{' '} | ||
<Link {...args} href="https://go.czi.team/eds"> | ||
consectetur adipiscing elit | ||
</Link> | ||
. Morbi porta at ante quis molestie. Nam scelerisque id diam at iaculis. | ||
Nullam sit amet iaculis erat. Nulla id tellus ante.{' '} | ||
<Link {...args} href="https://go.czi.team/eds"> | ||
Aliquam pellentesque ipsum sagittis, commodo neque at, ornare est. | ||
Maecenas a malesuada sem, vitae euismod erat. Nullam molestie nunc non | ||
dui dignissim fermentum. | ||
</Link>{' '} | ||
Aliquam id volutpat nulla, sed auctor orci. Fusce cursus leo nisi. Fusce | ||
vehicula vitae nisl nec ultricies. Cras ut enim nec magna semper egestas. | ||
Sed sed quam id nisl bibendum convallis. Proin suscipit, odio{' '} | ||
<Link {...args} href="https://go.czi.team/eds"> | ||
vel pulvinar | ||
</Link>{' '} | ||
euismod, risus eros ullamcorper lectus, non blandit nulla dui eget massa. | ||
</div> | ||
), | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import clsx from 'clsx'; | ||
import React, { forwardRef } from 'react'; | ||
import type { Size } from '../../util/variant-types'; | ||
import type { IconName } from '../Icon'; | ||
import Icon from '../Icon'; | ||
|
||
import styles from './Link-v2.module.css'; | ||
|
||
type LinkHTMLElementProps = Omit< | ||
React.AnchorHTMLAttributes<HTMLAnchorElement>, | ||
'disabled' | ||
>; | ||
|
||
export type LinkProps = LinkHTMLElementProps & { | ||
// Component API | ||
/** | ||
* Component used to render the element. Meant to support interaction with framework navigation libraries. | ||
* | ||
* **Default is `"a"`**. | ||
*/ | ||
as: string | React.ElementType; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding this to support links link in Remix. Will likely need some work to support fully, but will be able to test once i get thru the first wave of component implementations There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Challenge I see: Remix links currently use So to enable passing in a Remix link to here, we'd probably need to figure out how to support passing the EDS Link's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to replicate what we do using I didn't want to copy how I'm guessing this was part of why you needed to export the types earlier? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree on that goal, and not wanting to do ClickableStyle. My point is that to achieve: <EdsLink
as={RemixLink}
href={...} // or maybe to={...}?
/> we'll need to figure out how to handle href vs to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One way to do that would be for the Eds Link to take Kind of annoying, but that's the simplest thing I can see (at least until Remix updates its Link to use the standard There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yah, I think you misunderstood. The way I want to avoid polluting EDS with anything specific to Remix or some other framework, as well. I like the TypeParam approach (e.g., combine |
||
/** | ||
* The link contents or label. | ||
*/ | ||
children: string; | ||
// Design API | ||
/** | ||
* Where `Link` sits alongside other text and content: | ||
* | ||
* * **inline** - Inline link inherits the text size established within the `<p>` paragraph they are embedded in. | ||
* * **standalone** - Users can choose from the available sizes. | ||
*/ | ||
context?: 'inline' | 'standalone'; | ||
/** | ||
* (trailing) icon to use with the link | ||
*/ | ||
icon?: Extract<IconName, 'chevron-right' | 'open-in-new'>; | ||
/** | ||
* Extra or lowered colors added to a link | ||
*/ | ||
emphasis?: 'default' | 'high' | 'low'; | ||
|
||
/** | ||
* Link size inherits from the surrounding text. | ||
*/ | ||
size?: Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>; | ||
}; | ||
|
||
/** | ||
* `import {Link} from "@chanzuckerberg/eds";` | ||
* | ||
* Component for making styled anchor tags. Links allow users to navigate within or between a web page(s) or app(s). | ||
* | ||
*/ | ||
export const Link = forwardRef<HTMLAnchorElement, LinkProps>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't wait until React removes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ohh, what will happen instead? I never read the upcoming docs for things besides HTML/CSS/JS :P |
||
( | ||
{ | ||
as: Component = 'a', | ||
children, | ||
className, | ||
context, | ||
emphasis = 'default', | ||
icon, | ||
size = 'md', | ||
...other | ||
}, | ||
ref, | ||
) => { | ||
const componentClassName = clsx( | ||
className, | ||
styles['link'], | ||
context && styles[`link--context-${context}`], | ||
emphasis && styles[`link--emphasis-${emphasis}`], | ||
icon && styles['link--has-right-icon'], | ||
size && styles[`link--size-${size}`], | ||
); | ||
|
||
const iconSize = size && (['xl', 'lg'].includes(size) ? '1.5rem' : '1rem'); | ||
|
||
return ( | ||
<Component className={componentClassName} ref={ref} {...other}> | ||
{children} | ||
{icon && context === 'standalone' && ( | ||
<Icon | ||
className={styles['link__icon']} | ||
name={icon} | ||
purpose="decorative" | ||
size={iconSize} | ||
/> | ||
)} | ||
</Component> | ||
); | ||
}, | ||
); | ||
|
||
Link.displayName = 'Link'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's omitting
disabled
all about? I thoughtdisabled
already wasn't an attribute on anchor elements?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, this is probably a carry-over from the
ClickableStyles
stuff that wasn't removed a while back.