Skip to content

Commit

Permalink
feat: AnchorButton の disabledDetail 対応 (#5133)
Browse files Browse the repository at this point in the history
  • Loading branch information
uknmr authored Nov 25, 2024
1 parent 63f91bb commit 01c76fd
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 93 deletions.
10 changes: 9 additions & 1 deletion packages/smarthr-ui/src/components/Button/AnchorButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ElementRef, ElementRefProps } from '../../types'

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

type ElementProps<T extends ElementType> = Omit<
Expand All @@ -39,6 +40,7 @@ const AnchorButton = forwardRef(
suffix,
wide = false,
variant = 'secondary',
disabledDetail,
target,
rel,
elementAs,
Expand All @@ -54,7 +56,7 @@ const AnchorButton = forwardRef(
[rel, target],
)

return (
const button = (
<ButtonWrapper
{...props}
size={size}
Expand All @@ -73,6 +75,12 @@ const AnchorButton = forwardRef(
</ButtonInner>
</ButtonWrapper>
)

if (!props.href && disabledDetail) {
return <DisabledDetail button={button} disabledDetail={disabledDetail} />
}

return button
},
)

Expand Down
35 changes: 3 additions & 32 deletions packages/smarthr-ui/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import { tv } from 'tailwind-variants'

import { usePortal } from '../../hooks/usePortal'
import { DecoratorsType } from '../../types'
import { FaCircleInfoIcon } from '../Icon'
import { Loader } from '../Loader'
import { Tooltip } from '../Tooltip'
import { VisuallyHiddenText } from '../VisuallyHiddenText'

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

type ElementProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps>
Expand All @@ -21,19 +20,6 @@ const buttonStyle = tv({
'shr-align-bottom',
'[&_.smarthr-ui-Loader-spinner]:shr-h-em [&_.smarthr-ui-Loader-spinner]:shr-w-em',
],
disabledWrapper: [
'smarthr-ui-Button-disabledWrapper',
'shr-inline-flex shr-items-center shr-gap-0.25',
],
disabledTooltip: [
'shr-overflow-y-visible',
/* Tooltip との距離を変えずに反応範囲を広げるために negative space を使う */
'[&_.smarthr-ui-Icon]:-shr-m-0.25',
/* global styleなどでborder-boxが適用されている場合表示崩れを起こす為、content-boxを指定する */
'[&_.smarthr-ui-Icon]:shr-box-content',
'[&_.smarthr-ui-Icon]:shr-p-0.25',
'[&_.smarthr-ui-Icon]:shr-text-grey',
],
},
variants: {
isSecondary: {
Expand Down Expand Up @@ -76,7 +62,7 @@ export const Button = forwardRef<HTMLButtonElement, BaseProps & ElementProps & P
},
ref,
) => {
const { wrapper, loader: loaderSlot, disabledWrapper, disabledTooltip } = buttonStyle()
const { wrapper, loader: loaderSlot } = buttonStyle()
const wrapperStyle = useMemo(() => wrapper({ className }), [className, wrapper])
const loaderStyle = useMemo(
() => loaderSlot({ isSecondary: variant === 'secondary' }),
Expand Down Expand Up @@ -119,22 +105,7 @@ export const Button = forwardRef<HTMLButtonElement, BaseProps & ElementProps & P
)

if (disabled && disabledDetail) {
const DisabledDetailIcon = disabledDetail.icon || FaCircleInfoIcon

return (
<div className={disabledWrapper()}>
{button}
<Tooltip
message={disabledDetail.message}
triggerType="icon"
horizontal="auto"
vertical="auto"
className={disabledTooltip()}
>
<DisabledDetailIcon />
</Tooltip>
</div>
)
return <DisabledDetail button={button} disabledDetail={disabledDetail} />
}

return button
Expand Down
51 changes: 51 additions & 0 deletions packages/smarthr-ui/src/components/Button/DisabledDetail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { type FC } from 'react'
import { tv } from 'tailwind-variants'

import { FaCircleInfoIcon } from '../Icon'
import { Tooltip } from '../Tooltip'

type DisabledDetailProps = {
button: React.JSX.Element
disabledDetail: {
icon?: React.FunctionComponent
message: React.ReactNode
}
}

const disabledDetailStyle = tv({
slots: {
disabledWrapper: [
'smarthr-ui-Button-disabledWrapper',
'shr-inline-flex shr-items-center shr-gap-0.25',
],
disabledTooltip: [
'shr-overflow-y-visible',
/* Tooltip との距離を変えずに反応範囲を広げるために negative space を使う */
'[&_.smarthr-ui-Icon]:-shr-m-0.25',
/* global style��どでborder-boxが適用されている場合表示崩れを起こす為、content-boxを指定する */
'[&_.smarthr-ui-Icon]:shr-box-content',
'[&_.smarthr-ui-Icon]:shr-p-0.25',
'[&_.smarthr-ui-Icon]:shr-text-grey',
],
},
})

export const DisabledDetail: FC<DisabledDetailProps> = ({ button, disabledDetail }) => {
const { disabledWrapper, disabledTooltip } = disabledDetailStyle()
const DisabledDetailIcon = disabledDetail.icon ?? FaCircleInfoIcon

return (
<div className={disabledWrapper()}>
{button}
<Tooltip
message={disabledDetail.message}
triggerType="icon"
horizontal="auto"
vertical="auto"
className={disabledTooltip()}
>
<DisabledDetailIcon />
</Tooltip>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ export const Disabled: StoryObj<typeof AnchorButton> = {
},
}

export const DisabledDetail: StoryObj<typeof AnchorButton> = {
name: 'disabledDetail',
args: {
href: undefined,
disabledDetail: { message: 'ボタンが無効な理由' },
},
}

export const Square: StoryObj<typeof AnchorButton> = {
name: 'square',
args: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,82 +11,148 @@ import type { StoryFn, StoryObj } from '@storybook/react'
type Variant = ComponentProps<typeof Button>['variant']

/**
* $ pict anchor-button.pixt.txt
* size disabled prefix suffix square wide
* s true なし なし false false
* default false あり なし false true
* default true なし あり false true
* s false なし なし true false
* default true なし なし true false
* s false なし あり false false
* s true あり なし false true
* s true あり なし false false
* $ pict anchor-button.txt
* size disabled disabledDetail prefix suffix square wide
* default true あり なし なし false false
* s false なし なし あり false true
* s true なし なし あり false false
* default false なし なし なし true false
* default true なし なし あり false true
* s true なし あり なし false true
* s true あり なし あり false false
* default true あり あり なし false false
* s true あり なし なし true false
* s false なし あり なし false false
*/
const Template: StoryFn = (args) => (
<Stack {...args}>
{(['secondary', 'primary', 'danger', 'text', 'skeleton'] as Variant[]).map((variant) => (
<BaseColumn bgColor={variant === 'skeleton' ? 'GREY_20' : 'WHITE'} key={variant}>
<Cluster align="center">
<AnchorButton variant={variant} size="s">
ボタン
</AnchorButton>
<AnchorButton variant={variant} prefix={<FaCirclePlusIcon />} wide href="#">
ボタン
</AnchorButton>
<AnchorButton variant={variant} suffix={<FaCaretDownIcon />} wide>
ボタン
</AnchorButton>
<AnchorButton variant={variant} size="s" square href="#">
<FaCirclePlusIcon alt="ボタン" />
</AnchorButton>
<AnchorButton variant={variant} square>
<FaCirclePlusIcon alt="ボタン" />
</AnchorButton>
<AnchorButton variant={variant} size="s" suffix={<FaCaretDownIcon />} href="#">
ボタン
</AnchorButton>
<AnchorButton variant={variant} size="s" prefix={<FaCirclePlusIcon />} wide>
ボタン
</AnchorButton>
<AnchorButton variant={variant} size="s" prefix={<FaCirclePlusIcon />}>
ボタン
</AnchorButton>
</Cluster>
</BaseColumn>
const _cases: Array<ComponentProps<typeof AnchorButton>> = [
{
size: 'default',
href: undefined,
disabledDetail: { message: 'ボタンが無効な理由' },
prefix: undefined,
suffix: undefined,
square: false,
wide: false,
},
{
size: 's',
href: '#',
disabledDetail: undefined,
prefix: undefined,
suffix: <FaCaretDownIcon />,
square: false,
wide: true,
},
{
size: 's',
href: undefined,
disabledDetail: undefined,
prefix: undefined,
suffix: <FaCaretDownIcon />,
square: false,
wide: false,
},
{
size: 'default',
href: '#',
disabledDetail: undefined,
prefix: undefined,
suffix: undefined,
square: true,
wide: false,
},
{
size: 'default',
href: undefined,
disabledDetail: undefined,
prefix: undefined,
suffix: <FaCaretDownIcon />,
square: false,
wide: true,
},
{
size: 's',
href: undefined,
disabledDetail: undefined,
prefix: <FaCirclePlusIcon />,
suffix: undefined,
square: false,
wide: true,
},
{
size: 's',
href: undefined,
disabledDetail: { message: 'ボタンが無効な理由' },
prefix: undefined,
suffix: <FaCaretDownIcon />,
square: false,
wide: false,
},
{
size: 'default',
href: undefined,
disabledDetail: { message: 'ボタンが無効な理由' },
prefix: <FaCirclePlusIcon />,
suffix: undefined,
square: false,
wide: false,
},
{
size: 's',
href: undefined,
disabledDetail: { message: 'ボタンが無効な理由' },
prefix: undefined,
suffix: undefined,
square: true,
wide: false,
},
{
size: 's',
href: '#',
disabledDetail: undefined,
prefix: <FaCirclePlusIcon />,
suffix: undefined,
square: false,
wide: false,
},
]

const Template: StoryFn<typeof AnchorButton> = (args) => (
<Stack>
{[undefined, 'hover', 'focus-visible'].map((id) => (
<Stack id={id} key={id}>
{(['secondary', 'primary', 'danger', 'text', 'skeleton'] as Variant[]).map((variant) => (
<BaseColumn bgColor={variant === 'skeleton' ? 'GREY_20' : 'WHITE'} key={variant}>
<Cluster align="center">
{_cases.map((props, index) => (
<AnchorButton {...args} {...props} variant={variant} key={index} />
))}
</Cluster>
</BaseColumn>
))}
</Stack>
))}
</Stack>
)

export default {
title: 'Buttons(ボタン)/Button/AnchorButton/VRT',
render: Template,
parameters: {
chromatic: { disableSnapshot: false },
args: {
children: 'ボタン',
},
tags: ['!autodocs'],
}

export const VRT = {}

export const VRTHover = {
render: () => (
<>
<Template id="hover" />
<Template id="focus" />
<Template id="focus-visible" />
<Template id="active" />
</>
),
parameters: {
pseudo: {
hover: ['#hover .smarthr-ui-AnchorButton'],
focus: ['#focus .smarthr-ui-AnchorButton'],
focusVisible: ['#focus-visible .smarthr-ui-AnchorButton'],
active: ['#active .smarthr-ui-AnchorButton'],
},
chromatic: { disableSnapshot: false },
},
tags: ['!autodocs'],
}

export const VRT = {}

export const VRTForcedColors: StoryObj = {
...VRT,
parameters: {
Expand Down
Loading

0 comments on commit 01c76fd

Please sign in to comment.