Skip to content

Commit

Permalink
Merge pull request #114 from ensdomains/feat/custom-recorditem-props
Browse files Browse the repository at this point in the history
feat: custom RecordItem props
  • Loading branch information
LeonmanRolls authored Apr 5, 2023
2 parents aadec37 + 18c0137 commit 5230185
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 17 deletions.
65 changes: 63 additions & 2 deletions components/src/components/atoms/RecordItem/RecordItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,30 @@ describe('<RecordItem />', () => {
expect(mockCopied).toHaveBeenCalledWith('Real value')
})

it('should render anchor if link is provided', () => {
it('should render anchor if as is a', () => {
render(
<ThemeProvider theme={lightTheme}>
<RecordItem
as="a"
data-testid="record-item"
icon={<FlameSVG />}
keyLabel="Title"
keySublabel="Subtitle"
value="Real value"
>
Display value
</RecordItem>
,
</ThemeProvider>,
)
expect(screen.getByTestId('record-item').nodeName).toBe('A')
})

it('should have link as href if as is a', () => {
render(
<ThemeProvider theme={lightTheme}>
<RecordItem
as="a"
data-testid="record-item"
icon={<FlameSVG />}
keyLabel="Title"
Expand All @@ -78,6 +98,47 @@ describe('<RecordItem />', () => {
'href',
'https://ens.domains',
)
expect(screen.getByTestId('record-item').nodeName).toBe('A')
})

it('should passthrough custom target prop if as is a', () => {
render(
<ThemeProvider theme={lightTheme}>
<RecordItem
as="a"
data-testid="record-item"
icon={<FlameSVG />}
keyLabel="Title"
keySublabel="Subtitle"
target="_parent"
value="Real value"
>
Display value
</RecordItem>
,
</ThemeProvider>,
)
expect(screen.getByTestId('record-item')).toHaveAttribute(
'target',
'_parent',
)
})

it('should render button if as is button', () => {
render(
<ThemeProvider theme={lightTheme}>
<RecordItem
as="button"
data-testid="record-item"
icon={<FlameSVG />}
keyLabel="Title"
keySublabel="Subtitle"
value="Real value"
>
Display value
</RecordItem>
,
</ThemeProvider>,
)
expect(screen.getByTestId('record-item').nodeName).toBe('BUTTON')
})
})
62 changes: 47 additions & 15 deletions components/src/components/atoms/RecordItem/RecordItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,53 @@ import { ReactNode } from 'react'

import { CheckSVG, CopySVG, UpArrowSVG } from '@/src'

import { Neverable } from '@/src/types'

import { Typography } from '../Typography/Typography'
import { useCopied } from '../../../hooks/useCopied'

type Size = 'small' | 'large'

type BaseProps = {
value: string
link?: string
size?: Size
inline?: boolean
icon?: ReactNode
keyLabel?: string | ReactNode
keySublabel?: string | ReactNode
children: string
onClick?: () => void
as?: 'button' | 'a'
}

export type Props = BaseProps
type NativeElementProps = Omit<
React.HTMLAttributes<HTMLElement>,
keyof BaseProps
>
type NativeButtonProps = Omit<
React.ButtonHTMLAttributes<HTMLButtonElement>,
keyof NativeElementProps | keyof BaseProps
>
type NativeAnchorProps = Omit<
React.AnchorHTMLAttributes<HTMLAnchorElement>,
keyof NativeElementProps | keyof BaseProps
>

type AsAnchorProps = {
as: 'a'
link?: string
} & Neverable<NativeButtonProps, NativeAnchorProps> &
NativeAnchorProps

type AsButtonProps = {
as?: 'button'
link?: never
} & Neverable<NativeAnchorProps, NativeButtonProps> &
NativeButtonProps

export type Props = BaseProps &
NativeElementProps &
(AsAnchorProps | AsButtonProps)

const Container = styled.button<{
$inline: boolean
Expand Down Expand Up @@ -133,6 +162,7 @@ const TrailingIcon = styled.svg<{ $rotate?: boolean }>(
)

export const RecordItem = ({
as: asProp = 'button',
link,
size = 'small',
inline = false,
Expand All @@ -145,7 +175,20 @@ export const RecordItem = ({
}: Props) => {
const { copy, copied } = useCopied()

const asProp = link ? 'a' : undefined
const generatedProps =
asProp === 'a'
? ({
href: link,
rel: 'nofollow noreferrer',
target: '_blank',
...props,
} as NativeElementProps & NativeAnchorProps)
: ({
onClick: () => {
copy(value)
},
...props,
} as NativeElementProps & NativeButtonProps)

const hasPrefix = !!icon || !!keyLabel
const hasLabels = !!keyLabel || !!keySublabel
Expand Down Expand Up @@ -184,18 +227,7 @@ export const RecordItem = ({
: { as: CopySVG }

return (
<Container
$inline={inline}
as={asProp}
href={link}
rel="nofollow noreferrer"
target="_blank"
type="button"
onClick={() => {
if (!link) copy(value)
}}
{...props}
>
<Container $inline={inline} as={asProp} {...generatedProps}>
{hasPrefix && (
<PrefixContainer $inline={inline} $size={size}>
{icon && <PrefixIcon>{icon}</PrefixIcon>}
Expand Down
4 changes: 4 additions & 0 deletions components/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ export type WithIcon = {
/** An svg to be used by the component */
icon?: React.ReactNode
}

export type Neverable<TNever, TOmit> = {
[P in keyof Omit<TNever, keyof TOmit>]?: never
}

0 comments on commit 5230185

Please sign in to comment.