Search code examples
react-final-form

Link inside react final form does not work


In a react final form using the custom error component, links that are after it don't work. If I use instead of the error component a simple span link works fine.

  • first input has focus
  • I click on the link below
  • the error appears, but the link is not opened
  • when I click again on the link the link is opened

What is the expected behavior?

the error should appear under, as I blurred the input the link should open on the first click

Code

based on https://final-form.org/docs/react-final-form/examples/record-level-validation

index.js

import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const onSubmit = async values => {
  await sleep(300);
  window.alert(JSON.stringify(values, 0, 2));
};

const Error = ({ name }) => (
  <Field
    name={name}
    subscription={{ touched: true, error: true }}
    render={({ meta: { touched, error } }) =>
      touched && error ? <span style={{ color: "red" }}>{error}</span> : null
    }
  />
);

const App = () => (
  <Styles>
    <h1>React Final Form Example</h1>
    <h2>Password / Confirm Validation</h2>
    <a
      href="https://final-form.org/react"
      target="_blank"
      rel="noopener noreferrer"
    >
      Read Docs
    </a>
    <Form
      onSubmit={onSubmit}
      validate={values => {
        const errors = {};
        if (!values.username) {
          errors.username = "Required";
        }

        return errors;
      }}
      render={({ handleSubmit, form, submitting, pristine, values }) => (
        <form onSubmit={handleSubmit}>
          <a
            href="https://final-form.org/react"
            target="_blank"
            rel="noopener noreferrer"
          >
            [works] Down Read Docs
          </a>
          <Field name="username">
            {({ input, meta }) => (
              <div>
                <label>Username</label>
                <input
                  {...input}
                  type="text"
                  placeholder="Username"
                  autoFocus
                />
              </div>
            )}
          </Field>

          <Error name="username" />
          <a
            href="https://final-form.org/react"
            target="_blank"
            rel="noopener noreferrer"
          >
            [does not work] Read Docs
          </a>

          <div className="buttons">
            <button type="submit" disabled={submitting}>
              Submit
            </button>
            <button
              type="button"
              onClick={form.reset}
              disabled={submitting || pristine}
            >
              Reset
            </button>
          </div>
          <pre>{JSON.stringify(values, 0, 2)}</pre>
        </form>
      )}
    />
  </Styles>
);

render(<App />, document.getElementById("root"));

Styled.js

import styled, { css } from 'styled-components'

const btn = (light, dark) => css`
  white-space: nowrap;
  display: inline-block;
  border-radius: 5px;
  padding: 5px 15px;
  font-size: 16px;
  color: white;
  &:visited {
    color: white;
  }
  background-image: linear-gradient(${light}, ${dark});
  border: 1px solid ${dark};
  &:hover {
    background-image: linear-gradient(${light}, ${dark});
    &[disabled] {
      background-image: linear-gradient(${light}, ${dark});
    }
  }
  &:visited {
    color: black;
  }
  &[disabled] {
    opacity: 0.6;
    cursor: not-allowed;
  }
`

const btnDefault = css`
  ${btn('#ffffff', '#d5d5d5')} color: #555;
`

const btnPrimary = btn('#4f93ce', '#285f8f')

export default styled.div`
  font-family: sans-serif;

  h1 {
    text-align: center;
    color: #222;
  }

  h2 {
    text-align: center;
    color: #222;
  }

  & > div {
    text-align: center;
  }

  a {
    display: block;
    text-align: center;
    color: #222;
  }

  form {
    max-width: 500px;
    margin: 10px auto;
    border: 1px solid #ccc;
    padding: 20px;
    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
    border-radius: 3px;

    & > div {
      display: flex;
      flex-flow: row nowrap;
      line-height: 2em;
      margin: 5px;
      & > label {
        color: #333;
        width: 110px;
        font-size: 1em;
        line-height: 32px;
      }
      & > input,
      & > select,
      & > textarea {
        flex: 1;
        padding: 3px 5px;
        font-size: 1em;
        margin-left: 15px;
        border: 1px solid #ccc;
        border-radius: 3px;
      }
      & > input[type='checkbox'] {
        margin-top: 7px;
      }
      & > div {
        margin-left: 16px;
        & > label {
          display: block;
          & > input {
            margin-right: 3px;
          }
        }
      }
      & > span {
        line-height: 32px;
        margin-left: 10px;
        color: #800;
        font-weight: bold;
      }
    }
    & > .buttons {
      display: flex;
      flex-flow: row nowrap;
      justify-content: center;
      margin-top: 15px;
    }
    button {
      margin: 0 10px;
      &[type='submit'] {
        ${btnPrimary};
      }
      &[type='button'] {
        ${btnDefault};
      }
    }
    pre {
      border: 1px solid #ccc;
      background: rgba(0, 0, 0, 0.1);
      box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
      padding: 20px;
    }
  }
`

Sandbox Link https://codesandbox.io/s/link-in-react-final-form-z2g2b?fontsize=14&hidenavigation=1&theme=dark


Solution

  • Wow! This is a fun one. The error is moving the link out from under your mouse cursor, so when you "mouse up" to complete the click, the mouse is no longer over the hyperlink.

    If you hold the mouse button down and then move it to follow the link before releasing, the link will "work".

    It could be fixed by making space for the error whether or not it is displayed so it doesn't shift the rest of the page down.