Skip to content

Commit

Permalink
Merge pull request #53 from kodiak-packages/52-formfield
Browse files Browse the repository at this point in the history
FormField component
  • Loading branch information
RobinWijnant authored Jun 1, 2020
2 parents 6f56a14 + effdfe7 commit c65fdd1
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 0 deletions.
67 changes: 67 additions & 0 deletions src/components/FormField/FormField.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
name: FormField
menu: Components
route: /form-field
---

import { Playground, Props } from 'docz';
import { FormField, Input } from '../../index.ts';
import styles from './docAssets/docs.module.css';

# FormField

This component is used to wrap around one or multiple form elements. The label describes the set of form elements. An error message can be set if the user did not interact correctly these elements.

## Examples

### Basic usage

<Playground>
<FormField label='First name'>
<Input />
</FormField>
</Playground>

### FormField with error message

<Playground>
<FormField label='Last name' errorMessage='This field contains the following illegal characters: ;'>
<Input value='Doe;' />
</FormField>
</Playground>

### FormField with hint

<Playground>
<FormField label='MAC address' hint='The mac address is located at the back side of the device'>
<Input placeholder='00:00:00:00:00:00' />
</FormField>
</Playground>

### FormField with preview

<Playground>
{() => {
const [hexColor, setHexColor] = React.useState('#03176b');
const [label, setLabel] = React.useState('Devops');
return (
<FormField
label='Custom label'
hint={
<span style={{backgroundColor: hexColor}} className={styles.label}>
{label}
</span>
}
>
<div className={styles.flexRow}>
<Input value={hexColor} onChange={event => setHexColor(event.target.value)} />
<Input value={label} onChange={event => setLabel(event.target.value)} />
</div>
</FormField>
);
}}
</Playground>

## API

<Props of={FormField} />
18 changes: 18 additions & 0 deletions src/components/FormField/FormField.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.label {
display: block;
margin-bottom: 10px;
font-weight: 500;
font-size: 15px;
}

.errorMessage {
display: block;
margin-top: 5px;
color: var(--color-error);
}

.hint {
display: block;
margin-top: 5px;
color: var(--color-neutral-6);
}
36 changes: 36 additions & 0 deletions src/components/FormField/FormField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { render } from '@testing-library/react';

import FormField from './FormField';

describe('FormField', () => {
test('snapshot', () => {
const { asFragment } = render(<FormField>Form elements here</FormField>);
expect(asFragment()).toMatchSnapshot();
});

test('errorMessage should be rendered', () => {
const { queryByText } = render(
<FormField errorMessage="Something went wrong">Form elements here</FormField>,
);
expect(queryByText(/Something went wrong/)).not.toBe(null);
});

test('hint should be rendered', () => {
const { queryByText } = render(
<FormField hint={<span>You should look here</span>}>Form elements here</FormField>,
);
expect(queryByText(/You should look here/)).not.toBe(null);
});

test('className prop', () => {
const className = 'center';
const { getByText } = render(<FormField className={className}>Form elements here</FormField>);
const formFieldElement = getByText(/Form elements here/)!;

const renderedClassNames = formFieldElement.className.split(' ');
expect(renderedClassNames).toContain(className);
// className in prop should be the last in the row
expect(renderedClassNames.indexOf(className)).toBe(renderedClassNames.length - 1);
});
});
27 changes: 27 additions & 0 deletions src/components/FormField/FormField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { ReactNode } from 'react';
import classNames from 'classnames';

import cssReset from '../../css-reset.module.css';
import styles from './FormField.module.css';

type Props = {
label?: string;
hint?: ReactNode;
errorMessage?: string;
className?: string;
children: ReactNode;
};

const FormField: React.FC<Props> = ({ label, className, children, hint, errorMessage }: Props) => {
const mergedClassNames = classNames(cssReset.ventura, className);
return (
<div className={mergedClassNames}>
{Boolean(label) && <span className={styles.label}>{label}</span>}
{children}
{Boolean(errorMessage) && <span className={styles.errorMessage}>{errorMessage}</span>}
{Boolean(hint) && <div className={styles.hint}>{hint}</div>}
</div>
);
};

export default FormField;
11 changes: 11 additions & 0 deletions src/components/FormField/__snapshots__/FormField.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`FormField snapshot 1`] = `
<DocumentFragment>
<div
class="ventura"
>
Form elements here
</div>
</DocumentFragment>
`;
16 changes: 16 additions & 0 deletions src/components/FormField/docAssets/docs.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.flexRow {
display: flex;
}

.flexRow > *:first-of-type {
width: 100px;
margin-right: 10px;
}

.label {
display: inline-block;
padding: 0 8px;
border-radius: 3px;
margin-top: 10px;
color: white;
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export { default as Button } from './components/Button/Button';
export { default as Input } from './components/Input/Input';
export { default as Radio } from './components/Radio/Radio';
export { default as Modal } from './components/Modal/Modal';
export { default as FormField } from './components/FormField/FormField';
export * from 'react-feather';

0 comments on commit c65fdd1

Please sign in to comment.