Skip to content

Commit

Permalink
feat: FilterDropdown に responseMessage を渡せるようにする (#3348)
Browse files Browse the repository at this point in the history
* feat: FilterDropdownにresponseMessageを渡せるようにする

* fix: Propsの渡し方を修正

* fix: responseMessageTypeの定義を共通のファイルに移動

* fix: story

* fix: 型名をUpperCamelCaseに修正

* fix: nestを減らして見通しを良くした
  • Loading branch information
k-shirahama authored May 16, 2023
1 parent 6baf7e3 commit 34cac73
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 38 deletions.
13 changes: 2 additions & 11 deletions src/components/Dialog/ActionDialog/ActionDialogContentInner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { FC, ReactNode, useCallback } from 'react'
import styled, { css } from 'styled-components'

import { Theme, useTheme } from '../../../hooks/useTheme'
import { DecoratorsType } from '../../../types/props'
import { DecoratorsType, ResponseMessageType } from '../../../types/props'
import { Button } from '../../Button'
import { HeadingTagTypes } from '../../Heading'
import { FaCheckCircleIcon, FaExclamationCircleIcon } from '../../Icon'
Expand Down Expand Up @@ -57,18 +57,9 @@ export type BaseProps = {
decorators?: DecoratorsType<'closeButtonLabel'>
}

type responseMessageType =
| {
status: 'success' | 'error'
text: ReactNode
}
| {
status: 'processing'
}

export type ActionDialogContentInnerProps = BaseProps & {
onClickClose: () => void
responseMessage?: responseMessageType
responseMessage?: ResponseMessageType
titleId: string
}

Expand Down
46 changes: 44 additions & 2 deletions src/components/Dropdown/FilterDropdown/FilterDropdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { action } from '@storybook/addon-actions'
import { Meta, StoryObj } from '@storybook/react'
import React, { ReactNode, useCallback, useState } from 'react'
import React, { ComponentProps, ReactNode, useCallback, useState } from 'react'
import styled from 'styled-components'

import { Button } from '../../Button'
import { MultiComboBox, SingleComboBox } from '../../ComboBox'
import { Input } from '../../Input'
import { Stack } from '../../Layout'
import { Cluster, Stack } from '../../Layout'
import { RadioButton } from '../../RadioButton'

import { FilterDropdown } from './FilterDropdown'
Expand All @@ -30,6 +31,8 @@ const Render: React.FC = () => {
const [isFiltered2, setIsFiltered2] = React.useState(true)
const [isFiltered3, setIsFiltered3] = React.useState(true)
const [isFiltered4, setIsFiltered4] = React.useState(true)
const [responseMessage, setResponseMessage] =
useState<ComponentProps<typeof FilterDropdown>['responseMessage']>()

return (
<Wrapper>
Expand Down Expand Up @@ -138,6 +141,45 @@ const Render: React.FC = () => {
</p>
</FilterDropdown>
</dd>
<dt>Has response message</dt>
<dd>
<FilterDropdown
isFiltered={isFiltered4}
onApply={() => setIsFiltered4(true)}
onReset={() => setIsFiltered4(false)}
responseMessage={responseMessage}
>
<Stack gap={1}>
<p>
You can change border text and color of the trigger button by setting `isFiltered`.
</p>
<p>切り替えボタン:</p>
<Cluster gap={0.5}>
<Button
onClick={() => {
setResponseMessage({ status: 'success', text: '適用しました。' })
}}
>
保存
</Button>
<Button
onClick={() => {
setResponseMessage({ status: 'error', text: '何らかのエラーが発生しました。' })
}}
>
エラー
</Button>
<Button
onClick={() => {
setResponseMessage({ status: 'processing' })
}}
>
保存中
</Button>
</Cluster>
</Stack>
</FilterDropdown>
</dd>
</List>
</Wrapper>
)
Expand Down
84 changes: 59 additions & 25 deletions src/components/Dropdown/FilterDropdown/FilterDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import innerText from 'react-innertext'
import styled, { css } from 'styled-components'

import { Theme, useTheme } from '../../../hooks/useTheme'
import { DecoratorType, DecoratorsType } from '../../../types/props'
import { DecoratorType, DecoratorsType, ResponseMessageType } from '../../../types/props'
import { Button } from '../../Button'
import { FaCheckCircleIcon, FaFilterIcon, FaUndoAltIcon } from '../../Icon'
import { Cluster } from '../../Layout'
import { FaCheckCircleIcon, FaExclamationCircleIcon, FaFilterIcon, FaUndoAltIcon } from '../../Icon'
import { Cluster, Stack } from '../../Layout'
import { Dropdown } from '../Dropdown'
import { DropdownCloser } from '../DropdownCloser'
import { DropdownContent } from '../DropdownContent'
Expand All @@ -23,6 +23,7 @@ type Props = {
decorators?: DecoratorsType<
'status' | 'triggerButton' | 'applyButton' | 'cancelButton' | 'resetButton'
>
responseMessage?: ResponseMessageType
}
type ElementProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof Props>

Expand All @@ -43,6 +44,7 @@ export const FilterDropdown: FC<Props & ElementProps> = ({
children,
hasStatusText,
decorators,
responseMessage,
...props
}: Props) => {
const themes = useTheme()
Expand Down Expand Up @@ -70,6 +72,8 @@ export const FilterDropdown: FC<Props & ElementProps> = ({
() => (hasStatusText ? undefined : innerText(status)),
[status, hasStatusText],
)
const isRequestProcessing =
responseMessage !== undefined && responseMessage.status === 'processing'

return (
<Dropdown>
Expand All @@ -93,25 +97,54 @@ export const FilterDropdown: FC<Props & ElementProps> = ({
<DropdownScrollArea>
<ContentLayout themes={themes}>{children}</ContentLayout>
</DropdownScrollArea>
<BottomLayout themes={themes}>
{onReset && (
<ResetButtonLayout themes={themes}>
<Button variant="text" size="s" prefix={<FaUndoAltIcon />} onClick={onReset}>
{resetButton}
</Button>
</ResetButtonLayout>
<ActionArea themes={themes}>
<Cluster gap={1} align="center" justify="space-between">
{onReset && (
<ResetButtonLayout themes={themes}>
<Button
variant="text"
size="s"
prefix={<FaUndoAltIcon />}
onClick={onReset}
disabled={isRequestProcessing}
>
{resetButton}
</Button>
</ResetButtonLayout>
)}

<RightButtonLayout>
<DropdownCloser>
<Button onClick={onCancel} disabled={isRequestProcessing}>
{cancelButton}
</Button>
</DropdownCloser>
<DropdownCloser>
<Button variant="primary" onClick={onApply} loading={isRequestProcessing}>
{applyButton}
</Button>
</DropdownCloser>
</RightButtonLayout>
</Cluster>
{responseMessage?.status === 'success' && (
<Message>
<FaCheckCircleIcon
color={themes.color.MAIN}
text={responseMessage.text}
role="alert"
/>
</Message>
)}
{responseMessage?.status === 'error' && (
<Message>
<FaExclamationCircleIcon
color={themes.color.DANGER}
text={responseMessage.text}
role="alert"
/>
</Message>
)}
<RightButtonLayout>
<DropdownCloser>
<Button onClick={onCancel}>{cancelButton}</Button>
</DropdownCloser>
<DropdownCloser>
<Button variant="primary" onClick={onApply}>
{applyButton}
</Button>
</DropdownCloser>
</RightButtonLayout>
</BottomLayout>
</ActionArea>
</DropdownContent>
</Dropdown>
)
Expand Down Expand Up @@ -139,10 +172,8 @@ const ContentLayout = styled.div<{ themes: Theme }>`
padding: ${space(1.5)};
`}
`
const BottomLayout = styled(Cluster).attrs({ gap: 1, align: 'center', justify: 'space-between' })<{
themes: Theme
}>`
${({ themes: { border, space } }) => css`
const ActionArea = styled(Stack).attrs({ gap: 0.5 })<{ themes: Theme }>`
${({ themes: { space, border } }) => css`
border-block-start: ${border.shorthand};
padding: ${space(1)} ${space(1.5)};
`}
Expand All @@ -158,3 +189,6 @@ const RightButtonLayout = styled(Cluster).attrs({
})`
margin-inline-start: auto;
`
const Message = styled.div`
text-align: right;
`
9 changes: 9 additions & 0 deletions src/types/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,12 @@ export type DecoratorsType<T extends string> = {
}

export type DecoratorType = (text: string) => ReactNode

export type ResponseMessageType =
| {
status: 'success' | 'error'
text: ReactNode
}
| {
status: 'processing'
}

0 comments on commit 34cac73

Please sign in to comment.