Skip to content

Commit

Permalink
chore: FormControlのロジックをリファクタリング
Browse files Browse the repository at this point in the history
  • Loading branch information
AtsushiM committed Jan 1, 2025
1 parent f02c182 commit 577e831
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 75 deletions.
135 changes: 66 additions & 69 deletions packages/smarthr-ui/src/components/FormControl/FormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { StatusLabel } from '../StatusLabel'
import { Text, TextProps } from '../Text'
import { visuallyHiddenText } from '../VisuallyHiddenText/VisuallyHiddenText'

import type { Gap } from '../../types'
import type { PositiveGap } from '../../types'

type StatusLabelProps = ComponentProps<typeof StatusLabel>

Expand All @@ -38,7 +38,7 @@ type Props = PropsWithChildren<{
/** label 要素に適用する `id` 値 */
labelId?: string
/** タイトル群と子要素の間の間隔調整用(基本的には不要) */
innerMargin?: Gap
innerMargin?: PositiveGap
/** タイトルの隣に表示する `StatusLabel` の Props の配列 */
statusLabelProps?: StatusLabelProps | StatusLabelProps[]
/** タイトルの下に表示するヘルプメッセージ */
Expand Down Expand Up @@ -81,7 +81,7 @@ const formGroup = tv({
},
})

const MARGIN_MAPPER: { [key in Gap]: number } = {
const MARGIN_MAPPER: { [key in PositiveGap]: number } = {
0: 0,
0.25: 0.25,
0.5: 0.5,
Expand All @@ -105,7 +105,7 @@ const MARGIN_MAPPER: { [key in Gap]: number } = {
XXL: 3.5,
X3L: 4,
}
const MARGIN_MAPPER_KEYS = Object.keys(MARGIN_MAPPER)
const MARGIN_MAPPER_KEYS = Object.keys(MARGIN_MAPPER) as PositiveGap[]

const bodyWrapper = tv({
base: ['shr-clear-both'],
Expand All @@ -116,7 +116,7 @@ const bodyWrapper = tv({

return prev
},
{} as { [key in Gap]: string },
{} as { [key in PositiveGap]: string },
),
},
compoundVariants: [
Expand All @@ -139,7 +139,7 @@ const childrenWrapper = tv({

return prev
},
{} as { [key in Gap]: string },
{} as { [key in PositiveGap]: string },
),
isRoleGroup: {
true: '',
Expand All @@ -160,6 +160,8 @@ const childrenWrapper = tv({
],
})

const SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input="true"]'

export const ActualFormControl: React.FC<Props & ElementProps> = ({
title,
titleType = 'blockTitle',
Expand All @@ -186,8 +188,11 @@ export const ActualFormControl: React.FC<Props & ElementProps> = ({
const managedLabelId = labelId || defaultLabelId
const inputWrapperRef = useRef<HTMLDivElement>(null)
const isRoleGroup = as === 'fieldset'
const statusLabelList = Array.isArray(statusLabelProps) ? statusLabelProps : [statusLabelProps]

const statusLabelList = useMemo(
() => (Array.isArray(statusLabelProps) ? statusLabelProps : [statusLabelProps]),
[statusLabelProps],
)
const describedbyIds = useMemo(() => {
const temp = []

Expand Down Expand Up @@ -235,73 +240,70 @@ export const ActualFormControl: React.FC<Props & ElementProps> = ({
}, [className, dangerouslyTitleHidden, innerMargin, isRoleGroup])

useEffect(() => {
if (isRoleGroup) {
return
}

const inputWrapper = inputWrapperRef?.current

if (inputWrapper) {
// HINT: 対象idを持つ要素が既に存在する場合、何もしない
if (document.getElementById(managedHtmlFor)) {
return
}

const input = inputWrapper.querySelector('[data-smarthr-ui-input="true"]')

if (input) {
if (!input.getAttribute('id')) {
input.setAttribute('id', managedHtmlFor)
}

const isInputFile = input instanceof HTMLInputElement && input.type === 'file'
const inputLabelledByIds = input.getAttribute('aria-labelledby')
if (isInputFile && inputLabelledByIds) {
// InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める
input.setAttribute('aria-labelledby', `${inputLabelledByIds} ${managedLabelId}`)
if (!isRoleGroup) {
const inputWrapper = inputWrapperRef?.current

if (inputWrapper) {
// HINT: 対象idを持つ要素が既に存在する場合、何もしない
if (!document.getElementById(managedHtmlFor)) {
const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)

if (input) {
if (!input.getAttribute('id')) {
input.setAttribute('id', managedHtmlFor)
}

const isInputFile = input instanceof HTMLInputElement && input.type === 'file'

if (isInputFile) {
const attributeName = 'aria-labelledby'
const inputLabelledByIds = input.getAttribute(attributeName)

if (inputLabelledByIds) {
// InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める
input.setAttribute(attributeName, `${inputLabelledByIds} ${managedLabelId}`)
}
}
}
}
}
}
}, [managedHtmlFor, isRoleGroup, managedLabelId])
useEffect(() => {
const inputWrapper = inputWrapperRef?.current
if (describedbyIds) {
const inputWrapper = inputWrapperRef?.current

if (inputWrapper) {
// HINT: 対象idを持つ要素が既に存在する場合、何もしない
if (!describedbyIds || inputWrapper.querySelector(`[aria-describedby="${describedbyIds}"]`)) {
return
}

const input = inputWrapper.querySelector('[data-smarthr-ui-input="true"]')
if (inputWrapper && !inputWrapper.querySelector(`[aria-describedby="${describedbyIds}"]`)) {
const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)
const attributeName = 'aria-describedby'

if (input && !input.getAttribute('aria-describedby')) {
input.setAttribute('aria-describedby', describedbyIds)
if (input && !input.getAttribute(attributeName)) {
input.setAttribute(attributeName, describedbyIds)
}
}
}
}, [describedbyIds, isRoleGroup])
useEffect(() => {
if (!autoBindErrorInput) {
return
}

const inputWrapper = inputWrapperRef?.current

if (inputWrapper) {
const input = inputWrapper.querySelector('[data-smarthr-ui-input="true"]')

if (!input) {
return
}

if (actualErrorMessages.length > 0) {
input.setAttribute('aria-invalid', 'true')
} else {
input.removeAttribute('aria-invalid')
if (autoBindErrorInput) {
const inputWrapper = inputWrapperRef?.current

if (inputWrapper) {
const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)

if (input) {
const attributeName = 'aria-invalid'
if (actualErrorMessages.length > 0) {
input.setAttribute(attributeName, 'true')
} else {
input.removeAttribute(attributeName)
}
}
}
}
}, [actualErrorMessages.length, autoBindErrorInput])

const Component = as || 'div'
const Component = as

return (
<Component
Expand All @@ -324,7 +326,7 @@ export const ActualFormControl: React.FC<Props & ElementProps> = ({
<div className="shr-float-left shr-ml-0.5 shr-align-middle">{titleSuffixArea}</div>
)}
{subActionArea && <div className="shr-float-right">{subActionArea}</div>}
<Stack className={bodyWrapperStyle} gap={innerMargin ?? 0.5} inline={false}>
<Stack className={bodyWrapperStyle} gap={innerMargin ?? 0.5}>
<HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />
<ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />
<ErrorMessageList
Expand Down Expand Up @@ -369,7 +371,6 @@ const TitleCluster = React.memo<

return (
<Component
align="center"
htmlFor={!isRoleGroup ? managedHtmlFor : undefined}
id={managedLabelId}
className={labelStyle}
Expand All @@ -378,7 +379,7 @@ const TitleCluster = React.memo<
>
<Text styleType={titleType}>{title}</Text>
{statusLabelList.length > 0 && (
<Cluster gap={0.25} as="span">
<Cluster className="shr-ml-0.5" gap={0.25} inline={true} as="span">
{statusLabelList.map((prop, index) => (
<StatusLabel {...prop} key={index} />
))}
Expand Down Expand Up @@ -418,21 +419,17 @@ const ErrorMessageList = React.memo<{
managedHtmlFor: string
errorListStyle: string
errorIconStyle: string
}>(({ errorMessages, managedHtmlFor, errorListStyle, errorIconStyle }) => {
if (errorMessages.length === 0) {
return null
}

return (
}>(({ errorMessages, managedHtmlFor, errorListStyle, errorIconStyle }) =>
errorMessages.length > 0 ? (
<div id={`${managedHtmlFor}_errorMessages`} className={errorListStyle} role="alert">
{errorMessages.map((message, index) => (
<p key={index}>
<FaCircleExclamationIcon text={message} className={errorIconStyle} />
</p>
))}
</div>
)
})
) : null,
)

const SupplementaryMessageText = React.memo<
Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }
Expand Down
4 changes: 2 additions & 2 deletions packages/smarthr-ui/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ describe('index', () => {
if (innerComponents.length === 0) {
return
}
const exportedCompoenntsFromInnerDir = await getExportedDirectoryComponents(
const exportedComponentsFromInnerDir = await getExportedDirectoryComponents(
indexPath,
`./components/${dirName}`,
)
expect(exportedCompoenntsFromInnerDir.sort()).toEqual(
expect(exportedComponentsFromInnerDir.sort()).toEqual(
expect.arrayContaining(innerComponents.sort()),
)
})
Expand Down
17 changes: 15 additions & 2 deletions packages/smarthr-ui/src/themes/createSpacing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,24 @@ export type CreatedSpacingTheme = {

export type CreatedSpacingByCharTheme = (size: CharRelativeSize) => string

const positivePrimitiveTokens = [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 3.5, 4, 8] as const
export const primitiveTokens = [
0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 3.5, 4, 8, -0.25, -0.5, -0.75, -1, -1.25, -1.5, -2,
-2.5, -3, -3.5, -4, -8,
...positivePrimitiveTokens,
-0.25,
-0.5,
-0.75,
-1,
-1.25,
-1.5,
-2,
-2.5,
-3,
-3.5,
-4,
-8,
] as const

export type PositiveCharRelativeSize = (typeof positivePrimitiveTokens)[number]
export type CharRelativeSize = (typeof primitiveTokens)[number]
export type AbstractSize = keyof CreatedSpacingTheme

Expand Down
3 changes: 2 additions & 1 deletion packages/smarthr-ui/src/types/Gap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AbstractSize, CharRelativeSize } from '../themes/createSpacing'
import { AbstractSize, CharRelativeSize, PositiveCharRelativeSize } from '../themes/createSpacing'

export type PositiveGap = PositiveCharRelativeSize | AbstractSize
export type Gap = CharRelativeSize | AbstractSize
export type SeparateGap = {
row: Gap
Expand Down
2 changes: 1 addition & 1 deletion packages/smarthr-ui/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { Gap, SeparateGap } from './Gap'
export type { Gap, PositiveGap, SeparateGap } from './Gap'
export type { DecoratorsType, DecoratorType } from './Decorator'
export type { ResponseMessageType } from './ResponseMessage'
export type { LocaleMap } from './Locale'
Expand Down

0 comments on commit 577e831

Please sign in to comment.