From f1d1d9e229efecc2f771368b161ae9119b5e3383 Mon Sep 17 00:00:00 2001 From: Pavel Klibani Date: Thu, 7 Sep 2023 16:50:26 +0200 Subject: [PATCH] Refactor(web-react): Remove useValidationText hook - Removed useValidationText hook and implement ValidationText same way as HelperText component - Connected ValidationText with its input by aria-describedby --- .../src/components/Checkbox/Checkbox.tsx | 19 +++++----- .../src/components/Field/ValidationText.tsx | 10 ++++-- .../Field/__tests__/HelperText.test.tsx | 35 +++++++++++++++++++ .../Field/__tests__/useValidationText.test.ts | 28 --------------- .../web-react/src/components/Field/index.ts | 1 - .../components/Field/useValidationText.tsx | 27 -------------- .../src/components/FieldGroup/FieldGroup.tsx | 14 +++----- .../FileUploader/FileUploaderInput.tsx | 27 ++++++++++---- .../src/components/Select/Select.tsx | 16 ++++----- .../TextFieldBase/TextFieldBase.tsx | 19 +++++----- 10 files changed, 93 insertions(+), 103 deletions(-) create mode 100644 packages/web-react/src/components/Field/__tests__/HelperText.test.tsx delete mode 100644 packages/web-react/src/components/Field/__tests__/useValidationText.test.ts delete mode 100644 packages/web-react/src/components/Field/useValidationText.tsx diff --git a/packages/web-react/src/components/Checkbox/Checkbox.tsx b/packages/web-react/src/components/Checkbox/Checkbox.tsx index 1016aa51f9..523e92999e 100644 --- a/packages/web-react/src/components/Checkbox/Checkbox.tsx +++ b/packages/web-react/src/components/Checkbox/Checkbox.tsx @@ -2,7 +2,7 @@ import React, { forwardRef, ForwardedRef } from 'react'; import classNames from 'classnames'; import { useStyleProps } from '../../hooks'; import { SpiritCheckboxProps } from '../../types'; -import { useValidationText, HelperText } from '../Field'; +import { HelperText, ValidationText } from '../Field'; import useAriaDescribedBy from '../Field/useAriaDescribedBy'; import { useCheckboxStyleProps } from './useCheckboxStyleProps'; @@ -32,14 +32,6 @@ const _Checkbox = (props: SpiritCheckboxProps, ref: ForwardedRef )} - {renderValidationText} + {validationState && validationText && ( + + )} ); diff --git a/packages/web-react/src/components/Field/ValidationText.tsx b/packages/web-react/src/components/Field/ValidationText.tsx index 4ecf6f389b..0ef2228307 100644 --- a/packages/web-react/src/components/Field/ValidationText.tsx +++ b/packages/web-react/src/components/Field/ValidationText.tsx @@ -4,24 +4,28 @@ import { ValidationTextProp } from '../../types'; export interface ValidationTextProps extends ValidationTextProp { className?: string; elementType?: ElementType; + id?: string; } const defaultProps = { className: undefined, elementType: 'div', + id: undefined, }; export const ValidationText = (props: ValidationTextProps) => { - const { className, validationText, elementType: ElementTag = 'div' } = props; + const { className, validationText, elementType: ElementTag = 'div', id } = props; return Array.isArray(validationText) ? ( -
    +
      {validationText.map((item) => (
    • {item}
    • ))}
    ) : ( - {validationText} + + {validationText} + ); }; diff --git a/packages/web-react/src/components/Field/__tests__/HelperText.test.tsx b/packages/web-react/src/components/Field/__tests__/HelperText.test.tsx new file mode 100644 index 0000000000..5b70c0db6e --- /dev/null +++ b/packages/web-react/src/components/Field/__tests__/HelperText.test.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import HelperText from '../HelperText'; + +describe('HelperText', () => { + it('should render helper text', () => { + const dom = render(Helper Text); + + const element = dom.container.querySelector('div') as HTMLElement; + expect(element).not.toBeNull(); + expect(element.textContent).toBe('Helper Text'); + }); + + it('should render with custom element type', () => { + const { container } = render(Helper Text); + + const element = container.querySelector('span') as HTMLElement; + expect(element).not.toBeNull(); + expect(element.textContent).toBe('Helper Text'); + }); + + it('should render with className and id', () => { + const { container } = render( + + Helper Text + , + ); + + const element = container.querySelector('.test__helperText') as HTMLElement; + expect(element).not.toBeNull(); + expect(element.getAttribute('id')).toBe('test__helperTextId'); + expect(element.textContent).toBe('Helper Text'); + }); +}); diff --git a/packages/web-react/src/components/Field/__tests__/useValidationText.test.ts b/packages/web-react/src/components/Field/__tests__/useValidationText.test.ts deleted file mode 100644 index 7cf5d2b19d..0000000000 --- a/packages/web-react/src/components/Field/__tests__/useValidationText.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import { useValidationText } from '../useValidationText'; - -describe('useValidationText', () => { - it('should return undefined', () => { - const { result } = renderHook(() => useValidationText({ validationTextClassName: '', validationState: undefined })); - - expect(result.current).toBeUndefined(); - }); - - it('should return ValidationText component', () => { - const { result } = renderHook(() => - useValidationText({ - validationTextClassName: 'TextField__validationText', - validationState: 'danger', - validationText: 'required', - }), - ); - - expect(result.current).toMatchInlineSnapshot(` - - `); - }); -}); diff --git a/packages/web-react/src/components/Field/index.ts b/packages/web-react/src/components/Field/index.ts index 9dfd9d035f..66dba1ff66 100644 --- a/packages/web-react/src/components/Field/index.ts +++ b/packages/web-react/src/components/Field/index.ts @@ -1,6 +1,5 @@ export * from './ValidationText'; export * from './HelperText'; -export * from './useValidationText'; export * from './useAriaDescribedBy'; export { default as ValidationText } from './ValidationText'; export { default as HelperText } from './HelperText'; diff --git a/packages/web-react/src/components/Field/useValidationText.tsx b/packages/web-react/src/components/Field/useValidationText.tsx deleted file mode 100644 index bff99ffd03..0000000000 --- a/packages/web-react/src/components/Field/useValidationText.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { ElementType, ReactNode, useMemo } from 'react'; -import { ValidationState, ValidationTextType } from '../../types'; -import ValidationText from './ValidationText'; - -export interface UseValidationTextProps { - validationElementType?: ElementType; - validationState?: ValidationState; - validationText?: ValidationTextType; - validationTextClassName?: string; -} - -export const useValidationText = (props: UseValidationTextProps): ReactNode => { - const { validationElementType, validationState, validationText, validationTextClassName } = props; - - return useMemo( - () => - validationState && - validationText && ( - - ), - [validationElementType, validationState, validationText, validationTextClassName], - ); -}; diff --git a/packages/web-react/src/components/FieldGroup/FieldGroup.tsx b/packages/web-react/src/components/FieldGroup/FieldGroup.tsx index b9486ba278..a4d66a825c 100644 --- a/packages/web-react/src/components/FieldGroup/FieldGroup.tsx +++ b/packages/web-react/src/components/FieldGroup/FieldGroup.tsx @@ -2,9 +2,9 @@ import React from 'react'; import classNames from 'classnames'; import { SpiritFieldGroupProps } from '../../types'; import { useStyleProps } from '../../hooks'; -import { useValidationText } from '../Field/useValidationText'; import useAriaDescribedBy from '../Field/useAriaDescribedBy'; import HelperText from '../Field/HelperText'; +import ValidationText from '../Field/ValidationText'; import { useFieldGroupStyleProps } from './useFieldGroupStyleProps'; const FieldGroup = (props: SpiritFieldGroupProps) => { @@ -26,19 +26,13 @@ const FieldGroup = (props: SpiritFieldGroupProps) => { const { classProps } = useFieldGroupStyleProps({ isFluid, isRequired, validationState }); const { styleProps, props: transferProps } = useStyleProps(rest); - const { helperTextId, joinedAriaDescribedBy } = useAriaDescribedBy({ + const { validationTextId, helperTextId, joinedAriaDescribedBy } = useAriaDescribedBy({ id, helperText, validationText, ariaDescribedBy, }); - const renderValidationText = useValidationText({ - validationTextClassName: classProps.validationText, - validationState, - validationText, - }); - return (
    { {helperText} )} - {renderValidationText} + {validationState && validationText && ( + + )}
    ); }; diff --git a/packages/web-react/src/components/FileUploader/FileUploaderInput.tsx b/packages/web-react/src/components/FileUploader/FileUploaderInput.tsx index d426e81863..61f063e2d2 100644 --- a/packages/web-react/src/components/FileUploader/FileUploaderInput.tsx +++ b/packages/web-react/src/components/FileUploader/FileUploaderInput.tsx @@ -2,15 +2,17 @@ import React from 'react'; import classNames from 'classnames'; import { SpiritFileUploaderInputProps } from '../../types'; import { useStyleProps } from '../../hooks'; -import { useValidationText } from '../Field'; +import { HelperText, ValidationText } from '../Field'; import { DEFAULT_FILE_QUEUE_LIMIT, DEFAULT_FILE_SIZE_LIMIT } from './constants'; import { useFileUploaderStyleProps } from './useFileUploaderStyleProps'; import { useFileUploaderInput } from './useFileUploaderInput'; import { Icon } from '../Icon'; +import useAriaDescribedBy from '../Field/useAriaDescribedBy'; const FileUploaderInput = (props: SpiritFileUploaderInputProps) => { const { accept, + 'aria-describedby': ariaDescribedBy = '', dropZoneRef, helperText, iconName = 'upload', @@ -64,10 +66,11 @@ const FileUploaderInput = (props: SpiritFileUploaderInputProps) => { }); const { styleProps, props: transferProps } = useStyleProps(restProps); - const renderValidationText = useValidationText({ - validationTextClassName: classProps.input.validationText, - validationState, + const { validationTextId, helperTextId, joinedAriaDescribedBy } = useAriaDescribedBy({ + id, + helperText, validationText, + ariaDescribedBy, }); return ( @@ -84,6 +87,7 @@ const FileUploaderInput = (props: SpiritFileUploaderInputProps) => { {label} {   {labelText} -
    {helperText}
    + {helperText && ( + + {helperText} + + )} - {renderValidationText} + {validationState && validationText && ( + + )} ); }; diff --git a/packages/web-react/src/components/Select/Select.tsx b/packages/web-react/src/components/Select/Select.tsx index 6c41423b28..f190f5b443 100644 --- a/packages/web-react/src/components/Select/Select.tsx +++ b/packages/web-react/src/components/Select/Select.tsx @@ -2,10 +2,10 @@ import React, { forwardRef, ForwardedRef } from 'react'; import classNames from 'classnames'; import { SpiritSelectProps } from '../../types'; import { useStyleProps } from '../../hooks'; -import { useSelectStyleProps } from './useSelectStyleProps'; -import { HelperText, useValidationText } from '../Field'; +import { HelperText, ValidationText } from '../Field'; import { Icon } from '../Icon'; import useAriaDescribedBy from '../Field/useAriaDescribedBy'; +import { useSelectStyleProps } from './useSelectStyleProps'; /* We need an exception for components exported with forwardRef */ /* eslint no-underscore-dangle: ['error', { allow: ['_Select'] }] */ @@ -28,19 +28,13 @@ const _Select = (props: SpiritSelectProps, ref: ForwardedRef) const { classProps } = useSelectStyleProps({ isDisabled, isFluid, isRequired, isLabelHidden, validationState }); const { styleProps, props: transferProps } = useStyleProps(restProps); - const { helperTextId, joinedAriaDescribedBy } = useAriaDescribedBy({ + const { validationTextId, helperTextId, joinedAriaDescribedBy } = useAriaDescribedBy({ id, helperText, validationText, ariaDescribedBy, }); - const renderValidationText = useValidationText({ - validationTextClassName: classProps.validationText, - validationState, - validationText, - }); - return (
    ); }; diff --git a/packages/web-react/src/components/TextFieldBase/TextFieldBase.tsx b/packages/web-react/src/components/TextFieldBase/TextFieldBase.tsx index 6b2b6b6eb6..2fc992576b 100644 --- a/packages/web-react/src/components/TextFieldBase/TextFieldBase.tsx +++ b/packages/web-react/src/components/TextFieldBase/TextFieldBase.tsx @@ -2,7 +2,7 @@ import React, { forwardRef, ForwardedRef } from 'react'; import classNames from 'classnames'; import { useStyleProps } from '../../hooks'; import { SpiritTextFieldBaseProps, TextFieldBasePasswordToggleProps } from '../../types'; -import { HelperText, useValidationText } from '../Field'; +import { HelperText, ValidationText } from '../Field'; import useAriaDescribedBy from '../Field/useAriaDescribedBy'; import { useTextFieldBaseStyleProps } from './useTextFieldBaseStyleProps'; import TextFieldBaseInput from './TextFieldBaseInput'; @@ -27,19 +27,13 @@ const _TextFieldBase = (props: SpiritTextFieldBaseProps, ref: ForwardedRef