Skip to content

Commit 48be8b4

Browse files
s-sasaki-0529uknmr
andauthoredMay 28, 2024··
feat: SearchInput コンポーネントに onClickClear オプションを追加 (#4635)
* chore: Storybook を追加 * feat: onClickClear を実装する * fix: 強制カラーモード時にフォーカスリングが表示されるようにする * fix: 冗長なメモ化を削除 * fix: for ESLint * fix: 入力欄が空の場合は☓ボタンを表示しないようにする * fix: アイコンの代替テキストが正しく設定されていない問題を修正 * Update packages/smarthr-ui/src/components/Input/SearchInput/SearchInput.tsx Co-authored-by: KANAMORI Yu <y.kinmori@gmail.com> * fix: HTML valid になるように改装を調整する * fix: 冗長な代替メッセージを削除 * fix: div を span に変更する --------- Co-authored-by: KANAMORI Yu <y.kinmori@gmail.com>
1 parent 72d7a7d commit 48be8b4

File tree

3 files changed

+110
-34
lines changed

3 files changed

+110
-34
lines changed
 

‎packages/smarthr-ui/src/components/Input/SearchInput/SearchInput.stories.tsx

+40-19
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,46 @@ export const Default: Story = {
2222
args: {
2323
tooltipMessage: '',
2424
},
25-
render: () => (
26-
<StyledStack>
27-
<div>
28-
<p>主に入力欄に対する説明をレイアウト上配置できない場合の利用を想定しています。</p>
29-
<SearchInput
30-
name="default"
31-
tooltipMessage="氏名、ヨミガナ、社員番号で検索できます。スペース区切りでAND検索ができます。"
32-
/>
33-
</div>
34-
<div>
35-
<p>アイコンの代替テキストを設定</p>
36-
<SearchInput
37-
name="default"
38-
tooltipMessage="氏名、ヨミガナ、社員番号で検索できます。スペース区切りでAND検索ができます。"
39-
decorators={{ iconAlt: (txt) => `search.(${txt})` }}
40-
/>
41-
</div>
42-
</StyledStack>
43-
),
25+
render: () => {
26+
const [value, setValue] = React.useState('value')
27+
return (
28+
<StyledStack>
29+
<div>
30+
<p>主に入力欄に対する説明をレイアウト上配置できない場合の利用を想定しています。</p>
31+
<SearchInput
32+
name="default"
33+
onChange={(e) => setValue(e.target.value)}
34+
value={value}
35+
tooltipMessage="氏名、ヨミガナ、社員番号で検索できます。スペース区切りでAND検索ができます。"
36+
/>
37+
</div>
38+
<div>
39+
<p>検索解除ボタンを表示</p>
40+
<SearchInput
41+
name="default"
42+
value={value}
43+
onChange={(e) => setValue(e.target.value)}
44+
tooltipMessage="氏名、ヨミガナ、社員番号で検索できます。スペース区切りでAND検索ができます。"
45+
onClickClear={() => setValue('')}
46+
/>
47+
</div>
48+
<div>
49+
<p>アイコンの代替テキストを設定</p>
50+
<SearchInput
51+
name="default"
52+
value={value}
53+
onChange={(e) => setValue(e.target.value)}
54+
tooltipMessage="氏名、ヨミガナ、社員番号で検索できます。スペース区切りでAND検索ができます。"
55+
decorators={{
56+
iconAlt: (txt) => `search.(${txt})`,
57+
clearButtonAlt: (txt) => `clear.(${txt})`,
58+
}}
59+
onClickClear={() => setValue('')}
60+
/>
61+
</div>
62+
</StyledStack>
63+
)
64+
},
4465
}
4566

4667
const StyledStack = styled(Stack)`
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,82 @@
1-
import React, { ComponentProps, forwardRef, useMemo } from 'react'
1+
import React, { ComponentProps, forwardRef, useId, useMemo } from 'react'
2+
import { tv } from 'tailwind-variants'
23

3-
import { FaSearchIcon } from '../../Icon'
4+
import { UnstyledButton } from '../../Button'
5+
import { FaCircleXmarkIcon, FaMagnifyingGlassIcon } from '../../Icon'
6+
import { VisuallyHiddenText } from '../../VisuallyHiddenText'
47
import { InputWithTooltip } from '../InputWithTooltip'
58

69
import type { DecoratorsType } from '../../../types'
710

811
type Props = Omit<ComponentProps<typeof InputWithTooltip>, 'tooltipMessage' | 'prefix'> & {
912
/** 入力欄の説明を紐付けるツールチップに表示するメッセージ */
1013
tooltipMessage: React.ReactNode
11-
decorators?: DecoratorsType<'iconAlt'>
14+
decorators?: DecoratorsType<'iconAlt' | 'clearButtonAlt'>
15+
onClickClear?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
1216
}
1317

1418
const ICON_ALT = '検索'
19+
const CLEAR_BUTTON_ALT = '削除'
1520

16-
export const SearchInput = forwardRef<HTMLInputElement, Props>(({ decorators, ...props }, ref) => {
17-
const iconAlt = useMemo(() => decorators?.iconAlt?.(ICON_ALT) || ICON_ALT, [decorators])
18-
19-
return (
20-
<label>
21-
<InputWithTooltip
22-
{...props}
23-
ref={ref}
24-
prefix={<FaSearchIcon alt={iconAlt} color="TEXT_GREY" />}
25-
/>
26-
</label>
27-
)
21+
const searchInput = tv({
22+
slots: {
23+
clearButton: [
24+
'smarthr-ui-SearchInput-clearButton',
25+
'shr-group/clearButton',
26+
'shr-cursor-pointer',
27+
'focus-visible:shr-shadow-none',
28+
],
29+
clearButtonIcon: [
30+
'shr-block',
31+
'group-focus-visible/clearButton:shr-focus-indicator group-focus-visible/clearButton:shr-rounded-full',
32+
],
33+
},
2834
})
35+
36+
export const SearchInput = forwardRef<HTMLInputElement, Props>(
37+
({ decorators, onClickClear, ...props }, ref) => {
38+
const { clearButton, clearButtonIcon } = searchInput()
39+
const { clearButtonStyle, clearButtonIconStyle } = useMemo(
40+
() => ({
41+
clearButtonStyle: clearButton(),
42+
clearButtonIconStyle: clearButtonIcon(),
43+
}),
44+
[clearButton, clearButtonIcon],
45+
)
46+
47+
const iconAlt = useMemo(() => decorators?.iconAlt?.(ICON_ALT) || ICON_ALT, [decorators])
48+
const clearButtonAlt = useMemo(
49+
() => decorators?.clearButtonAlt?.(CLEAR_BUTTON_ALT) || CLEAR_BUTTON_ALT,
50+
[decorators],
51+
)
52+
53+
const defaultInptuId = useId()
54+
const inputId = props.id || defaultInptuId
55+
56+
return (
57+
<span>
58+
<label htmlFor={inputId}>
59+
<VisuallyHiddenText>{iconAlt}</VisuallyHiddenText>
60+
</label>
61+
<InputWithTooltip
62+
{...props}
63+
id={inputId}
64+
ref={ref}
65+
prefix={<FaMagnifyingGlassIcon color="TEXT_GREY" />}
66+
suffix={
67+
onClickClear &&
68+
props.value && (
69+
<UnstyledButton onClick={(e) => onClickClear(e)} className={clearButtonStyle}>
70+
<FaCircleXmarkIcon
71+
color="TEXT_BLACK"
72+
alt={clearButtonAlt}
73+
className={clearButtonIconStyle}
74+
/>
75+
</UnstyledButton>
76+
)
77+
}
78+
/>
79+
</span>
80+
)
81+
},
82+
)

‎packages/smarthr-ui/src/components/Input/SearchInput/VRTSearchInput.stories.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const SearchInputStory: StoryFn = () => (
2121
<SearchInput
2222
name="default"
2323
tooltipMessage="氏名、ヨミガナ、社員番号で検索できます。スペース区切りでAND検索ができます。"
24+
onClickClear={() => alert('clear')}
2425
/>
2526
</Wrapper>
2627
)

0 commit comments

Comments
 (0)
Please sign in to comment.