Skip to content

Commit

Permalink
feat(fields): can display custom items for MultiRadio component
Browse files Browse the repository at this point in the history
  • Loading branch information
claire2212 committed Feb 8, 2024
1 parent 99cdf9d commit 7b1a984
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 40 deletions.
34 changes: 22 additions & 12 deletions src/fields/MultiRadio.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import classnames from 'classnames'
import { equals } from 'ramda'
import { useCallback, useMemo, type CSSProperties } from 'react'
import { useCallback, useMemo, type CSSProperties, type ReactNode } from 'react'
import { Radio } from 'rsuite'
import styled, { css } from 'styled-components'

import { FieldError } from '../elements/FieldError'
import { Fieldset } from '../elements/Fieldset'
import { useFieldUndefineEffect } from '../hooks/useFieldUndefineEffect'
import { useKey } from '../hooks/useKey'
import { getRsuiteDataFromOptions } from '../utils/getRsuiteDataFromOptions'
import { getRsuiteValueFromOptionValue } from '../utils/getRsuiteValueFromOptionValue'
import { normalizeString } from '../utils/normalizeString'

import type { Option, OptionValueType } from '../types/definitions'
Expand All @@ -26,7 +28,9 @@ export type MultiRadioProps<OptionValue extends OptionValueType = string> = {
label: string
name: string
onChange?: ((nextValue: OptionValue | undefined) => Promisable<void>) | undefined
optionValueKey?: keyof OptionValue | undefined
options: Option<OptionValue>[]
renderMenuItem?: (label: string, value: OptionValue) => ReactNode
style?: CSSProperties | undefined
value?: OptionValue | undefined
}
Expand All @@ -44,6 +48,8 @@ export function MultiRadio<OptionValue extends OptionValueType = string>({
name,
onChange,
options,
optionValueKey,
renderMenuItem,
style,
value
}: MultiRadioProps<OptionValue>) {
Expand All @@ -52,15 +58,18 @@ export function MultiRadio<OptionValue extends OptionValueType = string>({
const hasError = useMemo(() => Boolean(controlledError), [controlledError])
const key = useKey([value, disabled, name])

const rsuiteData = useMemo(() => getRsuiteDataFromOptions(options, optionValueKey), [options, optionValueKey])
const selectedRsuiteValue = useMemo(
() => getRsuiteValueFromOptionValue(value, optionValueKey) ?? '',
[value, optionValueKey]
)

const handleChange = useCallback(
(nextOptionValue: OptionValue, isChecked: boolean) => {
(nextValue: OptionValue) => {
if (!onChange) {
return
}

const nextCheckedOptionValue = isChecked ? nextOptionValue : undefined

onChange(nextCheckedOptionValue)
onChange(nextValue)
},
[onChange]
)
Expand All @@ -78,16 +87,17 @@ export function MultiRadio<OptionValue extends OptionValueType = string>({
style={style}
>
<Box key={key} $hasError={hasError} $isInline={isInline} $isReadOnly={isReadOnly}>
{options.map(option => (
{rsuiteData.map(rsuiteDataItem => (
<Radio
key={JSON.stringify(option.value)}
checked={equals(option.value, value)}
disabled={!!option.isDisabled || disabled}
key={JSON.stringify(rsuiteDataItem.value)}
checked={equals(rsuiteDataItem.value, selectedRsuiteValue)}
disabled={!!rsuiteDataItem.isDisabled || disabled}
name={name}
onChange={(_: any, isChecked: boolean) => handleChange(option.value, isChecked)}
onChange={() => handleChange(rsuiteDataItem.optionValue)}
readOnly={isReadOnly}
value={selectedRsuiteValue}
>
{option.renderMenuItem ?? option.label}
{renderMenuItem ? renderMenuItem(rsuiteDataItem.label, rsuiteDataItem.optionValue) : rsuiteDataItem.label}
</Radio>
))}
</Box>
Expand Down
59 changes: 32 additions & 27 deletions stories/fields/MultiRadio.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState } from 'react'
import { useState, type FunctionComponent } from 'react'

import { Output } from '../../.storybook/components/Output'
import { generateStoryDecorator } from '../../.storybook/components/StoryDecorator'
import { Icon, MultiRadio, useFieldControl } from '../../src'
import { Icon as MonitorUiIcon, MultiRadio, useFieldControl } from '../../src'

import type { MultiRadioProps } from '../../src'
import type { IconProps, MultiRadioProps, Option } from '../../src'
import type { Meta } from '@storybook/react'

const args: MultiRadioProps = {
Expand All @@ -25,36 +25,32 @@ const args: MultiRadioProps = {
value: undefined
}

const OPTIONS_WITH_ICONS = [
type InterestPointOptionValueType = {
Icon: FunctionComponent<IconProps>
value: string
}

const OPTIONS_WITH_ICONS: Array<Option<InterestPointOptionValueType>> = [
{
label: 'Moyen de contrôle',
value: 'CONTROL_ENTITY',
renderMenuItem: (
<>
<Icon.ControlUnit size={14} />
Moyen de contrôle
</>
)
value: {
value: 'CONTROL_ENTITY',
Icon: MonitorUiIcon.ControlUnit
}
},
{
label: 'Navire de pêche',
value: 'FISHING_VESSEL',
renderMenuItem: (
<>
<Icon.FleetSegment size={14} />
Navire de pêche
</>
)
value: {
value: 'FISHING_VESSEL',
Icon: MonitorUiIcon.FleetSegment
}
},
{
label: 'Autre point',
value: 'OTHER',
renderMenuItem: (
<>
<Icon.Info size={15} />
Autre point
</>
)
value: {
value: 'OTHER',
Icon: MonitorUiIcon.Info
}
}
]
const meta: Meta<MultiRadioProps> = {
Expand Down Expand Up @@ -83,7 +79,9 @@ export function _MultiRadio(props: MultiRadioProps) {

const { controlledOnChange, controlledValue } = useFieldControl(props.value, setOutputValue)

const [outputValueWithIcon, setOutputValueWithIcons] = useState<string | undefined>('OTHER')
const [outputValueWithIcon, setOutputValueWithIcons] = useState<InterestPointOptionValueType | undefined>(
OPTIONS_WITH_ICONS[2]?.value
)

return (
<>
Expand All @@ -103,8 +101,15 @@ export function _MultiRadio(props: MultiRadioProps) {
<MultiRadio
{...props}
label="Multiradio with icons"
onChange={setOutputValueWithIcons}
onChange={nextOptionValue => setOutputValueWithIcons(nextOptionValue)}
options={OPTIONS_WITH_ICONS}
optionValueKey="value"
renderMenuItem={(label, value) => (
<>
<value.Icon />
{label}
</>
)}
value={outputValueWithIcon}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion stories/fields/Toggle.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const meta: Meta<ToggleProps> = {
export default meta

export function _Toggle(props: ToggleProps) {
const [, setOutputValue1] = useState<boolean | '∅'>(false)
const [, setOutputValue1] = useState<boolean>(false)
const { controlledOnChange, controlledValue: controlledChecked } = useFieldControl(
props.isChecked,
setOutputValue1 as any
Expand Down

0 comments on commit 7b1a984

Please sign in to comment.