diff --git a/packages/smarthr-ui/src/components/FormControl/FormControl.tsx b/packages/smarthr-ui/src/components/FormControl/FormControl.tsx index 28431082eb..144db5ac8d 100644 --- a/packages/smarthr-ui/src/components/FormControl/FormControl.tsx +++ b/packages/smarthr-ui/src/components/FormControl/FormControl.tsx @@ -118,6 +118,8 @@ const childrenWrapper = tv({ ], }) +const SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input="true"]' + export const ActualFormControl: React.FC = ({ title, titleType = 'blockTitle', @@ -126,7 +128,7 @@ export const ActualFormControl: React.FC = ({ htmlFor, labelId, innerMargin, - statusLabelProps = [], + statusLabelProps, helpMessage, exampleMessage, errorMessages, @@ -143,7 +145,6 @@ export const ActualFormControl: React.FC = ({ const managedLabelId = labelId || defaultLabelId const inputWrapperRef = useRef(null) const isRoleGroup = as === 'fieldset' - const statusLabelList = Array.isArray(statusLabelProps) ? statusLabelProps : [statusLabelProps] const describedbyIds = useMemo(() => { const temp = [] @@ -163,13 +164,19 @@ export const ActualFormControl: React.FC = ({ return temp.join(' ') }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor]) - const actualErrorMessages = useMemo(() => { - if (!errorMessages) { - return [] - } - - return Array.isArray(errorMessages) ? errorMessages : [errorMessages] - }, [errorMessages]) + const statusLabelList = useMemo( + () => + statusLabelProps + ? Array.isArray(statusLabelProps) + ? statusLabelProps + : [statusLabelProps] + : [], + [statusLabelProps], + ) + const actualErrorMessages = useMemo( + () => (errorMessages ? (Array.isArray(errorMessages) ? errorMessages : [errorMessages]) : []), + [errorMessages], + ) const { wrapperStyle, labelStyle, errorListStyle, errorIconStyle, childrenWrapperStyle } = useMemo(() => { @@ -184,68 +191,63 @@ export const ActualFormControl: React.FC = ({ }, [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) + } + + if (input instanceof HTMLInputElement && input.type === 'file') { + const attrName = 'aria-labelledby' + const inputLabelledByIds = input.getAttribute(attrName) + + if (inputLabelledByIds) { + // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める + input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`) + } + } + } } } } }, [managedHtmlFor, isRoleGroup, managedLabelId]) useEffect(() => { - const inputWrapper = inputWrapperRef?.current - - if (inputWrapper) { - // HINT: 対象idを持つ要素が既に存在する場合、何もしない - if (!describedbyIds || inputWrapper.querySelector(`[aria-describedby="${describedbyIds}"]`)) { - return - } + if (describedbyIds) { + const attrName = 'aria-describedby' + const inputWrapper = inputWrapperRef?.current - const input = inputWrapper.querySelector('[data-smarthr-ui-input="true"]') + if (inputWrapper && !inputWrapper.querySelector(`[${attrName}="${describedbyIds}"]`)) { + const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR) - if (input && !input.getAttribute('aria-describedby')) { - input.setAttribute('aria-describedby', describedbyIds) + if (input && !input.getAttribute(attrName)) { + input.setAttribute(attrName, describedbyIds) + } } } - }, [describedbyIds, isRoleGroup]) + }, [describedbyIds]) useEffect(() => { - if (!autoBindErrorInput) { - return - } + if (autoBindErrorInput) { + const inputWrapper = inputWrapperRef?.current - const inputWrapper = inputWrapperRef?.current + if (inputWrapper) { + const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR) - if (inputWrapper) { - const input = inputWrapper.querySelector('[data-smarthr-ui-input="true"]') + if (input) { + const attrName = 'aria-invalid' - if (!input) { - return - } - - if (actualErrorMessages.length > 0) { - input.setAttribute('aria-invalid', 'true') - } else { - input.removeAttribute('aria-invalid') + if (actualErrorMessages.length > 0) { + input.setAttribute(attrName, 'true') + } else { + input.removeAttribute(attrName) + } + } } } }, [actualErrorMessages.length, autoBindErrorInput]) @@ -324,21 +326,26 @@ const TitleCluster = React.memo< ) const clusterAttrs = isRoleGroup ? { 'aria-hidden': 'true' } - : { htmlFor: managedHtmlFor, as: 'label' } + : { + htmlFor: managedHtmlFor, + id: managedLabelId, + as: 'label' as React.ComponentProps['as'], + } return ( <> - {isRoleGroup && {body}} - + {isRoleGroup && ( + + )} +