From 18c0137e8d672208ada245a25a4ac60f8965ffd4 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 5 Apr 2023 10:13:03 +1000 Subject: [PATCH] feat: custom RecordItem props --- .../atoms/RecordItem/RecordItem.test.tsx | 65 ++++++++++++++++++- .../atoms/RecordItem/RecordItem.tsx | 62 +++++++++++++----- components/src/types/index.ts | 4 ++ 3 files changed, 114 insertions(+), 17 deletions(-) diff --git a/components/src/components/atoms/RecordItem/RecordItem.test.tsx b/components/src/components/atoms/RecordItem/RecordItem.test.tsx index 4f2bce08..1a3ef183 100644 --- a/components/src/components/atoms/RecordItem/RecordItem.test.tsx +++ b/components/src/components/atoms/RecordItem/RecordItem.test.tsx @@ -58,10 +58,30 @@ describe('', () => { expect(mockCopied).toHaveBeenCalledWith('Real value') }) - it('should render anchor if link is provided', () => { + it('should render anchor if as is a', () => { render( } + keyLabel="Title" + keySublabel="Subtitle" + value="Real value" + > + Display value + + , + , + ) + expect(screen.getByTestId('record-item').nodeName).toBe('A') + }) + + it('should have link as href if as is a', () => { + render( + + } keyLabel="Title" @@ -78,6 +98,47 @@ describe('', () => { 'href', 'https://ens.domains', ) - expect(screen.getByTestId('record-item').nodeName).toBe('A') + }) + + it('should passthrough custom target prop if as is a', () => { + render( + + } + keyLabel="Title" + keySublabel="Subtitle" + target="_parent" + value="Real value" + > + Display value + + , + , + ) + expect(screen.getByTestId('record-item')).toHaveAttribute( + 'target', + '_parent', + ) + }) + + it('should render button if as is button', () => { + render( + + } + keyLabel="Title" + keySublabel="Subtitle" + value="Real value" + > + Display value + + , + , + ) + expect(screen.getByTestId('record-item').nodeName).toBe('BUTTON') }) }) diff --git a/components/src/components/atoms/RecordItem/RecordItem.tsx b/components/src/components/atoms/RecordItem/RecordItem.tsx index 639e3b42..34c7a123 100644 --- a/components/src/components/atoms/RecordItem/RecordItem.tsx +++ b/components/src/components/atoms/RecordItem/RecordItem.tsx @@ -4,6 +4,8 @@ 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' @@ -11,7 +13,6 @@ type Size = 'small' | 'large' type BaseProps = { value: string - link?: string size?: Size inline?: boolean icon?: ReactNode @@ -19,9 +20,37 @@ type BaseProps = { keySublabel?: string | ReactNode children: string onClick?: () => void + as?: 'button' | 'a' } -export type Props = BaseProps +type NativeElementProps = Omit< + React.HTMLAttributes, + keyof BaseProps +> +type NativeButtonProps = Omit< + React.ButtonHTMLAttributes, + keyof NativeElementProps | keyof BaseProps +> +type NativeAnchorProps = Omit< + React.AnchorHTMLAttributes, + keyof NativeElementProps | keyof BaseProps +> + +type AsAnchorProps = { + as: 'a' + link?: string +} & Neverable & + NativeAnchorProps + +type AsButtonProps = { + as?: 'button' + link?: never +} & Neverable & + NativeButtonProps + +export type Props = BaseProps & + NativeElementProps & + (AsAnchorProps | AsButtonProps) const Container = styled.button<{ $inline: boolean @@ -133,6 +162,7 @@ const TrailingIcon = styled.svg<{ $rotate?: boolean }>( ) export const RecordItem = ({ + as: asProp = 'button', link, size = 'small', inline = false, @@ -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 @@ -184,18 +227,7 @@ export const RecordItem = ({ : { as: CopySVG } return ( - { - if (!link) copy(value) - }} - {...props} - > + {hasPrefix && ( {icon && {icon}} diff --git a/components/src/types/index.ts b/components/src/types/index.ts index a870ab3f..e7d97d0c 100644 --- a/components/src/types/index.ts +++ b/components/src/types/index.ts @@ -58,3 +58,7 @@ export type WithIcon = { /** An svg to be used by the component */ icon?: React.ReactNode } + +export type Neverable = { + [P in keyof Omit]?: never +}