Let's perform the following steps to create a generic Field component:
- Create a new file called Field.tsx with the following import statements:
import { FC } from 'react';
/** @jsx jsx */
import { css, jsx } from '@emotion/core';
import {
fontFamily,
fontSize,
gray5,
gray2,
gray6,
} from './Styles';
- Let's define the Field component Props interface:
interface Props {
name: string;
label?: string;
type?: 'Text' | 'TextArea' | 'Password';
}
So, we have props for the name field, its label, and its type.
Notice the TypeScript type for the type prop. This is a union type of string literals and means that the type prop can only be Text, TextArea, or Password.
- The different field types are going to have lots of CSS properties in common, so we are going to put these in a baseCSS variable that we'll reference when we render the field:
const baseCSS = css`
box-sizing: border-box;
font-family: ${fontFamily};
font-size: ${fontSize};
margin-bottom: 5px;
padding: 8px 10px;
border: 1px solid ${gray5};
border-radius: 3px;
color: ${gray2};
background-color: white;
width: 100%;
:focus {
outline-color: ${gray5};
}
:disabled {
background-color: ${gray6};
}
`;
- Let's begin to define the Field component now with the props destructured:
export const Field: FC<Props> = ({
name,
label,
type = 'Text',
}) => null;
Notice that we have defaulted the type prop to Text.
- Let's render the Field container with the label inside:
export const Field: FC<Props> = ({
name,
label,
type = 'Text',
}) => (
<div
css={css`
display: flex;
flex-direction: column;
margin-bottom: 15px;
`}
>
{label && (
<label
css={css`
font-weight: bold;
`}
htmlFor={name}
>
{label}
</label>
)}
</div>
);
Notice how we use the short-circuit syntax to only render the label if the label prop is defined.
- Let's move on to rendering the input field:
<div ... >
{label && ( ... )}
{(type === 'Text' || type === 'Password') && (
<input type={type.toLowerCase()} id={name} css={baseCSS} />
)}
</div>
Notice how we have tied label to input using the htmlFor attribute, which will help our field to be accessible.
- Let's also render the textarea field:
<div ... >
{label && ( ... )}
{(type === 'Text' || type === 'Password') && (
<input type={type.toLowerCase()} id={name} css={baseCSS} />
)}
{type === 'TextArea' && (
<textarea
id={name}
css={css`
${baseCSS};
height: 100px;
`}
/>
)}
</div>
Notice how the textarea field composes the base CSS with the height of 100px with string literal interpolation.
That completes the Field component for the time being.