Skip to content

Commit

Permalink
feat: TextLinkをnext/linkなどのラップされたコンポーネントに対応 (#4867)
Browse files Browse the repository at this point in the history
Co-authored-by: たふみ <[email protected]>
  • Loading branch information
masa0527 and Qs-F authored Aug 27, 2024
1 parent 9ec1e18 commit 68cf805
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 13 deletions.
52 changes: 45 additions & 7 deletions packages/smarthr-ui/src/components/TextLink/TextLink.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
import React, { AnchorHTMLAttributes, ReactNode, forwardRef, useMemo } from 'react'
import React, {
ComponentPropsWithRef,
ComponentPropsWithoutRef,
ElementType,
FC,
ReactNode,
forwardRef,
useMemo,
} from 'react'
import { tv } from 'tailwind-variants'

import { FaExternalLinkAltIcon } from '../Icon'

type ElementProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof Props | 'color'>
type Props = {
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'
>

type Props<T extends ElementType> = {
/** リンクをクリックした時に発火するコールバック関数 */
onClick?: (e: React.MouseEvent) => void
/** テキストの前に表示するアイコン */
prefix?: ReactNode
/** テキストの後ろに表示するアイコン */
suffix?: ReactNode
/** TextLinkを利用しつつnext/linkなどと併用する場合に指定する */
elementAs?: T
}

type TextLinkComponent = <T extends ElementType = 'a'>(
props: Props<T> & ElementProps<T> & ElementRefProps<T>,
) => ReturnType<FC>

const textLink = tv({
slots: {
anchor:
Expand All @@ -25,8 +47,23 @@ const { anchor, prefixWrapper, suffixWrapper } = textLink()
const prefixWrapperClassName = prefixWrapper()
const suffixWrapperClassName = suffixWrapper()

export const TextLink = forwardRef<HTMLAnchorElement, Props & ElementProps>(
({ href, target, rel, onClick, children, prefix, suffix, className, ...others }, ref) => {
export const TextLink: TextLinkComponent = forwardRef(
<T extends ElementType = 'a'>(
{
elementAs,
href,
target,
rel,
onClick,
children,
prefix,
suffix,
className,
...others
}: Props<T> & ElementProps<T>,
ref: ElementRef<T>,
) => {
const Component = elementAs || 'a'
const actualSuffix = useMemo(() => {
if (target === '_blank' && suffix === undefined) {
return <FaExternalLinkAltIcon aria-label="別タブで開く" />
Expand Down Expand Up @@ -64,7 +101,8 @@ export const TextLink = forwardRef<HTMLAnchorElement, Props & ElementProps>(
}, [href, onClick])

return (
<a
// eslint-disable-next-line smarthr/a11y-delegate-element-has-role-presentation
<Component
{...others}
ref={ref}
href={actualHref}
Expand All @@ -76,7 +114,7 @@ export const TextLink = forwardRef<HTMLAnchorElement, Props & ElementProps>(
{prefix && <span className={prefixWrapperClassName}>{prefix}</span>}
{children}
{actualSuffix && <span className={suffixWrapperClassName}>{actualSuffix}</span>}
</a>
</Component>
)
},
)
9 changes: 6 additions & 3 deletions packages/smarthr-ui/src/components/UpwardLink/UpwardLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ const upwardLink = tv({
})

type Props = Omit<ComponentProps<typeof TextLink>, 'prefix' | 'suffix'> &
VariantProps<typeof upwardLink>
VariantProps<typeof upwardLink> & {
/** `TextLink`に渡す `elementAs` をオプションで指定 */
elementAs?: ComponentProps<typeof TextLink>['elementAs']
}

export const UpwardLink: React.FC<Props> = ({ indent = true, className, ...rest }) => {
export const UpwardLink: React.FC<Props> = ({ indent = true, className, elementAs, ...rest }) => {
const style = upwardLink({ indent, className })
return (
<div className={style}>
{/* eslint-disable-next-line smarthr/a11y-anchor-has-href-attribute */}
<TextLink {...rest} prefix={<FaArrowLeftIcon />} />
<TextLink {...rest} elementAs={elementAs} prefix={<FaArrowLeftIcon />} />
</div>
)
}
17 changes: 17 additions & 0 deletions sandbox/next/src/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'
import Link from 'next/link'
import { Heading, Section, UpwardLink } from 'smarthr-ui'

export default function About() {
return (
<main>
<Section>
<Heading>Welcome to the About Page</Heading>
</Section>

<UpwardLink elementAs={Link} href="/" indent={false}>
前へ戻る
</UpwardLink>
</main>
)
}
25 changes: 22 additions & 3 deletions sandbox/next/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
'use client'

import { Button } from 'smarthr-ui'
import Link from 'next/link'
import { Button, FaAddressCardIcon, FaArrowRightIcon, TextLink } from 'smarthr-ui'

export default function Home() {
return <Button variant="primary">Hello, Next.</Button>
return (
<main>
<Button variant="primary">Hello, Next.</Button>
<ol>
<li>
<Link href="/about">next/link</Link>
</li>
<li>
<TextLink elementAs={Link} href="/about" suffix={<FaArrowRightIcon />}>
smarthr-ui with next/link
</TextLink>
</li>
<li>
<TextLink elementAs={Link} href="/about" prefix={<FaAddressCardIcon />}>
smarthr-ui with next/link
</TextLink>
</li>
</ol>
</main>
)
}

0 comments on commit 68cf805

Please sign in to comment.