# All files / layouts/Form Form.js

28.57% Statements 4/14
100% Branches 0/0
0% Functions 0/10
28.57% Lines 4/14

    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
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141

    5x

    5x

    5x

    5x

     import PropTypes from 'prop-types';
    import styled from 'styled-components';
    import FieldGroup from './FieldGroup';
    import { Container as BinaryFieldContainer } from './BinaryField';
    import { Container as FieldWrapperContainer } from './FieldWrapper';
    import { Container as AccordionContainer } from '../../components/Accordion/Accordion';
    import FormColumn from './FormColumn';
    import FormText from './FormText';
    import FlexFields from './FlexFields';
    import Flow from '../Flow/Flow';

    // TODO: simplify field layout by wrapping all single fields in a single container element,
    // so we don't have to select on so many different types of classes here

    const Form = styled.form.withConfig({ displayName: 'Form' })`
      background: 'transparent';
      color: inherit;
      display: flex;
      flex-direction: row;
      flex-wrap: wrap;
      justify-content: space-between;
      align-items: stretch;

      /* these are field groups which help layout rows inside a form */
      & ${FieldGroup},
      & ${FormText},
      & > ${FlexFields} {
        flex: 0 0 100%;
        margin-top: ${({ theme }) => theme.form.elementVerticalSpacing / 2}px;
        margin-bottom: ${({ theme }) => theme.form.elementVerticalSpacing / 2}px;
      }

      & > ${FormColumn} {
        flex: 0 0 50%;
      }

      & > ${AccordionContainer} {
        flex: 1 0 100%;
      }

      /* these are inputs directly within a field group inside a form */
      & ${FieldGroup} > ${FieldWrapperContainer}:not(:first-child),
      & ${FieldGroup} > ${BinaryFieldContainer}:not(:first-child) {
        margin-left: ${({ theme }) => theme.form.elementHorizontalSpacing / 2}px;
      }
      & ${FieldGroup} > ${FieldWrapperContainer}:not(:last-child),
      & ${FieldGroup} > ${BinaryFieldContainer}:not(:last-child) {
        margin-right: ${({ theme }) => theme.form.elementHorizontalSpacing / 2}px;
      }

      /* these are 'loose' inputs placed directly inside a form */
      & > ${FieldWrapperContainer},
      & > ${BinaryFieldContainer},
      & ${AccordionContainer} ${FieldWrapperContainer},
      & ${AccordionContainer} ${BinaryFieldContainer} {
        margin: 0;
        margin-top: ${({ theme }) => theme.form.elementVerticalSpacing / 2}px;
        margin-bottom: ${({ theme }) => theme.form.elementVerticalSpacing / 2}px;

        /*
          loose inputs naturally take up about 1/2 width.
          we don't enforce this with margin, since that would cause extra margins on the 'outside'
          of the form. Instead, we specify how much space should be between inputs by subtracting it
          from their flex-basis, and then use the Form's justify-content and wrapping to make sure that
          space goes between the items on the inside of the form.
        */
        flex: 0 1 calc(50% - ${({ theme }) => theme.form.elementHorizontalSpacing / 2}px);
      }

      & > ${FieldGroup}:last-child,
      & > ${FormText}:last-child {
        margin-bottom: 0;
      }
      & > ${FieldGroup}:first-child,
      & > ${FormText}:first-child {
        margin-top: 0;
      }

      &:invalid button[type="submit"] {
        background: ${({ theme }) => theme.button.disabledBG};
        color: ${({ theme }) => theme.button.disabledFG};
        border: ${({ theme }) => theme.button.disabledBorder};
        cursor: default;
        box-shadow: none;
      }
      &:invalid button[type="submit"]::after {
        left: 160%;
      }
      &:invalid button[type="submit"]::before {
        left: -60%;
      }

      & > ${Flow} {
        margin: 0;
        width: 100%;
      }
    `;

    Form.propTypes = {
      /**
       * Adds an id to the element.
       */
      id: PropTypes.string,
      /**
       * Adds a class name to the element.
       */
      className: PropTypes.string,
    };

    Form.defaultProps = {
      id: null,
      className: null,
    };

    Form.usage = `
    # Form

    Form is a smart layout component. It tries its best to lay out any Input components you place within it in a reasonable way. There are two ways to put Inputs in forms:

    1. 'Loose' inputs, placed directly in the form. The form will attempt to size these inputs to half the total width of the form.

    2. FieldGroups, which take up a full row in the form, and can align / size their child inputs however they want using FlexBox.

    Regardless of whether Inputs are 'loose' or inside a FieldGroup, the Form will try to put the proper padding between them and ensure that the outside edges are not padded.

    \`\`\`
    <FormBox>
      <Form>
        <TextInput />
        <TextInput />
        <FieldGroup>
          <ButtonInput />
          <LinkInput />
        </FieldGroup>
      </Form>
    </FormBox>
    \`\`\`
    `;

    export default Form;