aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/organisms/forms/search-form/search-form.tsx
blob: 3f16ad0c9b4f7ae76dec385f4bf4132e0c1e53a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { forwardRef, type ForwardRefRenderFunction, useId } from 'react';
import { useIntl } from 'react-intl';
import { type FormSubmitHandler, useForm } from '../../../../utils/hooks';
import {
  Button,
  Form,
  type FormProps,
  Icon,
  Input,
  Label,
} from '../../../atoms';
import { LabelledField } from '../../../molecules';
import styles from './search-form.module.scss';

export type SearchFormData = { query: string };

export type SearchFormSubmit = FormSubmitHandler<SearchFormData>;

export type SearchFormProps = Omit<FormProps, 'children' | 'onSubmit'> & {
  /**
   * Should the label be visually hidden?
   *
   * @default false
   */
  isLabelHidden?: boolean;
  /**
   * A callback function to handle search form submit.
   */
  onSubmit?: SearchFormSubmit;
};

const SearchFormWithRef: ForwardRefRenderFunction<
  HTMLInputElement,
  SearchFormProps
> = ({ className = '', isLabelHidden = false, onSubmit, ...props }, ref) => {
  const intl = useIntl();
  const { values, submit, submitStatus, update } = useForm<SearchFormData>({
    initialValues: { query: '' },
    submitHandler: onSubmit,
  });
  const id = useId();
  const formClass = [
    styles.wrapper,
    styles[isLabelHidden ? 'wrapper--no-label' : 'wrapper--has-label'],
    className,
  ].join(' ');
  const labels = {
    button: intl.formatMessage({
      defaultMessage: 'Search',
      description: 'SearchForm: button accessible name',
      id: 'WMqQrv',
    }),
    field: intl.formatMessage({
      defaultMessage: 'Search for:',
      description: 'SearchForm: field accessible label',
      id: 'X8oujO',
    }),
  };

  return (
    <Form {...props} className={formClass} onSubmit={submit}>
      <LabelledField
        className={styles.field}
        field={
          <Input
            className={styles.input}
            id={id}
            // eslint-disable-next-line react/jsx-no-literals
            name="query"
            onChange={update}
            ref={ref}
            // eslint-disable-next-line react/jsx-no-literals
            type="search"
            value={values.query}
          />
        }
        label={
          <Label htmlFor={id} isHidden={isLabelHidden}>
            {labels.field}
          </Label>
        }
      />
      <Button
        aria-label={labels.button}
        className={styles.btn}
        isLoading={submitStatus === 'PENDING'}
        // eslint-disable-next-line react/jsx-no-literals
        kind="neutral"
        // eslint-disable-next-line react/jsx-no-literals
        shape="initial"
        type="submit"
      >
        <Icon
          aria-hidden
          className={styles.icon}
          // eslint-disable-next-line react/jsx-no-literals
          shape="magnifying-glass"
          // eslint-disable-next-line react/jsx-no-literals
          size="lg"
        />
      </Button>
    </Form>
  );
};

/**
 * SearchForm component
 *
 * Render a search form.
 */
export const SearchForm = forwardRef(SearchFormWithRef);