Skip to content

Commit

Permalink
feat: AnchorButtonをnext/linkなどのラップされたコンポーネントに対応 (#4901)
Browse files Browse the repository at this point in the history
Co-authored-by: shingo.sasaki <[email protected]>
  • Loading branch information
masa0527 and s-sasaki-0529 authored Sep 13, 2024
1 parent a054e07 commit a07d257
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 15 deletions.
49 changes: 41 additions & 8 deletions packages/smarthr-ui/src/components/Button/AnchorButton.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
import React, { AnchorHTMLAttributes, forwardRef, useMemo } from 'react'
import React, {
ComponentPropsWithoutRef,
ElementType,
FC,
PropsWithoutRef,
ReactElement,
Ref,
forwardRef,
useMemo,
} from 'react'
import { tv } from 'tailwind-variants'

import { ElementRef, ElementRefProps } from '../../types'

import { ButtonInner } from './ButtonInner'
import { ButtonWrapper } from './ButtonWrapper'
import { BaseProps } from './types'

type ElementProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>
type ElementProps<T extends ElementType> = Omit<
ComponentPropsWithoutRef<T>,
keyof Props<T> & ElementRefProps<T>
>

type Props<T extends ElementType> = BaseProps & {
/** next/linkなどのカスタムコンポーネントを指定します。指定がない場合はデフォルトで `a` タグが使用されます。 */
elementAs?: T
}

const anchorButton = tv({
base: 'smarthr-ui-AnchorButton',
})

export const AnchorButton = forwardRef<HTMLAnchorElement, BaseProps & ElementProps>(
(
const AnchorButton = forwardRef(
<T extends ElementType = 'a'>(
{
size = 'default',
square = false,
Expand All @@ -22,12 +41,13 @@ export const AnchorButton = forwardRef<HTMLAnchorElement, BaseProps & ElementPro
variant = 'secondary',
target,
rel,
elementAs,
className,
children,
...props
},
ref,
) => {
}: PropsWithoutRef<Props<T>> & ElementProps<T>,
ref: Ref<ElementRef<T>>,
): ReactElement => {
const styles = useMemo(() => anchorButton({ className }), [className])
const actualRel = useMemo(
() => (rel === undefined && target === '_blank' ? 'noopener noreferrer' : rel),
Expand All @@ -46,6 +66,7 @@ export const AnchorButton = forwardRef<HTMLAnchorElement, BaseProps & ElementPro
rel={actualRel}
isAnchor
anchorRef={ref}
elementAs={elementAs}
>
<ButtonInner prefix={prefix} suffix={suffix} size={size}>
{children}
Expand All @@ -54,5 +75,17 @@ export const AnchorButton = forwardRef<HTMLAnchorElement, BaseProps & ElementPro
)
},
)

// 型キャストなしで ForwardRefExoticComponent に合わせた型をエクスポートするための処理
type AnchorButtonType = <T extends ElementType = 'a'>(
props: Props<T> & ElementProps<T> & ElementRefProps<T>,
) => ReturnType<FC>

const ForwardedAnchorButton = AnchorButton as unknown as AnchorButtonType & {
displayName: string
}

// BottomFixedArea での判定に用いるために displayName を明示的に設定する
AnchorButton.displayName = 'AnchorButton'
ForwardedAnchorButton.displayName = 'AnchorButton'

export { ForwardedAnchorButton as AnchorButton }
7 changes: 5 additions & 2 deletions packages/smarthr-ui/src/components/Button/ButtonWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
ElementType,
ForwardedRef,
ReactNode,
useMemo,
Expand All @@ -17,6 +18,7 @@ type BaseProps = {
$loading?: boolean
className: string
children: ReactNode
elementAs?: ElementType
}

type ButtonProps = BaseProps & {
Expand Down Expand Up @@ -56,9 +58,10 @@ export function ButtonWrapper({
}, [$loading, className, size, square, variant, wide])

if (props.isAnchor) {
const { anchorRef, isAnchor: _, ...others } = props
const { anchorRef, elementAs, isAnchor: _, ...others } = props
const Component = elementAs || 'a'
// eslint-disable-next-line smarthr/a11y-anchor-has-href-attribute, jsx-a11y/anchor-has-content
return <a {...others} className={anchorStyle} ref={anchorRef} />
return <Component {...others} className={anchorStyle} ref={anchorRef} />
} else {
const { buttonRef, disabled, onClick, ...others } = props
return (
Expand Down
6 changes: 1 addition & 5 deletions packages/smarthr-ui/src/components/TextLink/TextLink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, {
ComponentPropsWithRef,
ComponentPropsWithoutRef,
ElementType,
FC,
Expand All @@ -11,12 +10,9 @@ import React, {
} from 'react'
import { tv } from 'tailwind-variants'

import { ElementRef, ElementRefProps } from '../../types'
import { FaExternalLinkAltIcon } from '../Icon'

type ElementRef<T extends ElementType> = ComponentPropsWithRef<T>['ref']

type ElementRefProps<T extends ElementType> = { ref?: ElementRef<T> }

type ElementProps<T extends ElementType> = Omit<
ComponentPropsWithoutRef<T>,
(keyof Props<T> & ElementRefProps<T>) | 'color'
Expand Down
5 changes: 5 additions & 0 deletions packages/smarthr-ui/src/types/ComponentTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ComponentPropsWithRef, ElementType } from 'react'

export type ElementRef<T extends ElementType> = ComponentPropsWithRef<T>['ref']

export type ElementRefProps<T extends ElementType> = { ref?: ElementRef<T> }
1 change: 1 addition & 0 deletions packages/smarthr-ui/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export type { Gap, SeparateGap } from './Gap'
export type { DecoratorsType, DecoratorType } from './Decorator'
export type { ResponseMessageType } from './ResponseMessage'
export type { LocaleMap } from './Locale'
export type { ElementRef, ElementRefProps } from './ComponentTypes'

0 comments on commit a07d257

Please sign in to comment.