Skip to content

Commit

Permalink
[C-3278] Add TextLink component and docs (#6661)
Browse files Browse the repository at this point in the history
  • Loading branch information
amendelsohn authored Nov 13, 2023
1 parent 9b4a017 commit c91486c
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/harmony/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const parameters = {
'Layout',
['Box', 'Flex', 'Divider', 'Paper'],
'Text',
['Text', 'TextLink'],
'Components',
'Icons',
[
Expand Down
67 changes: 67 additions & 0 deletions packages/harmony/src/components/link/TextLink.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
Meta,
Title,
Description,
Primary,
Controls,
Canvas
} from '@storybook/blocks'

import { Heading } from 'storybook/components'

import * as TextLinkStories from './TextLink.stories'

<Meta of={TextLinkStories} />

<Title>Text Link</Title>

- [Overview](#overview)
- [Props](#props)
- [Variants](#variants)

<Heading>
## Overview
<Description />
</Heading>

<Primary />

## Props

<Controls />

## Variants

<Heading>
### Default

Apply the default link properties when you want to clearly indicate that the text is a link.

</Heading>
<Canvas of={TextLinkStories.Default} />

<Heading>
### Subdued

Apply the subdued link properties when you don’t want the link to distract from the overall design.

</Heading>
<Canvas of={TextLinkStories.Subdued} />

<Heading>
### Inverted

Apply the inverted link properties when the text link is on a colored background.

</Heading>
<Canvas of={TextLinkStories.Inverted} />

<Heading>
## Styling

The link takes on the styles of the closest enclosing [Text](?path=/docs/components-typography-text--documentation) component.
Hover, color is overwritten by the link variant.

</Heading>

<Canvas of={TextLinkStories.MatchesTextStyle} />
82 changes: 82 additions & 0 deletions packages/harmony/src/components/link/TextLink.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { Meta, StoryObj } from '@storybook/react'

import { Flex, Paper } from 'components/layout'
import { Text } from 'components/text/Text'
import darkBackgroundSpace from 'storybook/assets/darkBackgroundSpace.jpg'

import { TextLink } from './TextLink'

const meta: Meta<typeof TextLink> = {
title: 'Text/TextLink',
component: TextLink,
args: {
children: 'This is a link',
href: '../?path=/docs/text-textlink--documentation'
},
render: (props) => (
<Text variant='body' strength='strong'>
<Flex direction='row' gap='4xl'>
<TextLink {...props} />
<TextLink {...props} _isHovered />
</Flex>
</Text>
)
}

export default meta

type Story = StoryObj<typeof TextLink>

export const Default: Story = {}

export const Subdued: Story = {
args: {
variant: 'subdued'
}
}

export const Inverted: Story = {
args: {
variant: 'inverted'
},
render: (props) => (
<Text variant='body' strength='strong'>
<Paper
h={84}
direction='row'
gap='4xl'
alignItems='center'
p='2xl'
css={{ background: `url(${darkBackgroundSpace})` }}
>
<TextLink {...props} />
<TextLink {...props} _isHovered />
</Paper>
</Text>
)
}

export const External: Story = {
args: {
children: 'External links open a new tab',
href: 'https://audius.co',
isExternal: true
}
}

export const MatchesTextStyle: Story = {
render: (props) => (
<Text variant='heading' size='l'>
This is some large heading text{' '}
<TextLink {...props}>with a link</TextLink> in it.
</Text>
)
}

export const AsChild: Story = {
render: (props) => (
<TextLink asChild {...props}>
<p>asChild</p>
</TextLink>
)
}
60 changes: 60 additions & 0 deletions packages/harmony/src/components/link/TextLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useTheme } from '@emotion/react'
import { Slot } from '@radix-ui/react-slot'

import { Text } from 'components/text/Text'

import type { TextLinkProps } from './types'

/**
* Also known as hyperlinks, these are words or phrases that can be clicked to navigate to a linked webpage.
*/
export const TextLink = (props: TextLinkProps) => {
const {
_isHovered = false,
asChild = false,
children,
variant = 'default',
isExternal = false,
onClick,
...passthroughProps
} = props

const { color } = useTheme()

const colorByVariant = {
default: color.primary.p500,
subdued: color.text.default,
inverted: color.static.white
}

const hoverStyles = {
textDecoration: 'underline',
...(variant === 'subdued' && { color: color.primary.p300 })
}

return (
<Text
asChild
onClick={onClick}
tag='a'
css={{
color: colorByVariant[variant],
textDecoration: _isHovered ? 'underline' : 'none',
':hover': hoverStyles,
...(_isHovered && hoverStyles)
}}
{...passthroughProps}
>
{asChild ? (
<Slot>{children}</Slot>
) : (
<a
target={isExternal ? '_blank' : undefined}
rel={isExternal ? 'noreferrer' : undefined}
>
{children}
</a>
)}
</Text>
)
}
1 change: 1 addition & 0 deletions packages/harmony/src/components/link/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TextLink } from './TextLink'
49 changes: 49 additions & 0 deletions packages/harmony/src/components/link/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { HTMLProps, MouseEventHandler, ReactNode } from 'react'

export type TextLinkProps = Omit<
HTMLProps<HTMLAnchorElement>,
// Props from Text that don't match anchor props
'size' | 'color' | 'ref'
> & {
/**
* Change the default rendered element for the one passed as a child,
* merging their props and behavior.
*/
asChild?: boolean

/**
* Which variant to display.
* @default default
*/
variant?: 'default' | 'subdued' | 'inverted'

/**
* If true, prevent the click event from being propagated to other elements.
* @default true
*/
stopPropagation?: boolean

/**
* A custom click handler if you don't want to link to another page directly.
*/
onClick?: MouseEventHandler

/**
* Mark as true if the link destination is outside of the app. Causes the
* link to open in a new tab.
* @default false
*/
isExternal?: boolean

/**
* The link text to be displayed.
*/
children: ReactNode

// Internal props

/**
* @ignore: This prop is for internal use only
*/
_isHovered?: boolean
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/web/src/services/WebWorker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import importWorkerScript from 'workers/importWorkerScript'

const importWorkScriptCode = importWorkerScript.toString()
const basename = process.env.VITE_PUBLIC_URL;
const basename = process.env.VITE_PUBLIC_URL

export default class WebWorker {
/**
Expand Down

0 comments on commit c91486c

Please sign in to comment.