Skip to content

Commit

Permalink
feat(project): add TextField component
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristiaanScheermeijer committed Jul 20, 2021
1 parent a1dfad7 commit 6c6efa0
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 0 deletions.
3 changes: 3 additions & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ import '@testing-library/jest-dom/extend-expect';
import { mockMatchMedia } from './src/testUtils';

mockMatchMedia();

// Mock Math.random te prevent snapshots being updated each test
global.Math.random = () => 0.123456789;
62 changes: 62 additions & 0 deletions src/components/TextField/TextField.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@use '../../styles/variables';
@use '../../styles/theme';

.textField {
width: 100%;
margin-bottom: 8px;

&.error {
.label,
.helperText {
color: theme.$text-field-error-color;
}

.input {
border-color: theme.$text-field-error-color;
}
}

&.disabled {
.input {
opacity: 0.7;
}
}

&:hover {
&:not(.disabled) {
.input {
background-color: theme.$text-field-hover-bg-color;
border-color: theme.$text-field-hover-border-color;
}
}
}
}

.label {
display: block;
margin-bottom: 4px;
}

.input {
width: 100%;
min-height: 48px;
padding: 14px 16px;
color: theme.$text-field-resting-color;
font-size: 16px;
line-height: 18px;
background-color: theme.$text-field-bg-color;
border: 1px solid theme.$text-field-resting-border-color;
border-radius: 4px;
transition: border 0.2s ease;

&:focus {
color: theme.$text-field-active-color;
border-color: theme.$text-field-active-border-color;
outline: none;
}
}

.helperText {
margin-top: 4px;
font-size: 12px;
}
33 changes: 33 additions & 0 deletions src/components/TextField/TextField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react';

import TextField from './TextField';

describe('<TextField>', () => {
test('renders and matches snapshot', () => {
const { container } = render(<TextField label="Label" placeholder="Placeholder" name="name" value="" onChange={jest.fn()} />);

expect(container).toMatchSnapshot();
});

test('renders and matches multiline snapshot', () => {
const { container } = render(<TextField label="Label" placeholder="Placeholder" name="name" value="" onChange={jest.fn()} multiline />);

expect(container).toMatchSnapshot();
});

test('triggers an onChange event when the input value changes', () => {
const onChange = jest.fn();
const { getByPlaceholderText } = render(<TextField value="" onChange={onChange} placeholder="Enter your name" />);

fireEvent.change(getByPlaceholderText('Enter your name'), { target: { value: 'John Doe' } });

expect(onChange).toBeCalled();
});

test('shows the helper text below the input', () => {
const { queryByText } = render(<TextField value="" onChange={jest.fn()} helperText="Assertive text" />);

expect(queryByText('Assertive text')).toBeDefined();
});
});
59 changes: 59 additions & 0 deletions src/components/TextField/TextField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import classNames from 'classnames';

import useOpaqueId from '../../hooks/useOpaqueId';

import styles from './TextField.module.scss';

type Props = {
className?: string;
label?: string;
placeholder?: string;
name?: string;
value: string;
type?: 'text' | 'email' | 'password' | 'search';
onChange?: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
onFocus?: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
helperText?: React.ReactNode;
error?: boolean;
disabled?: boolean;
required?: boolean;
readOnly?: boolean;
multiline?: boolean;
rows?: number;
};

const TextField: React.FC<Props> = ({ className, label, error, helperText, multiline, type = 'text', rows = 3, ...rest }: Props) => {
const id = useOpaqueId('text-field', rest.name);
const InputComponent = multiline ? 'textarea' : 'input';
const textFieldClassName = classNames(
styles.textField,
{
[styles.error]: error,
[styles.disabled]: rest.disabled,
},
className,
);

const inputProps: Partial<Props & { id: string }> = {
id,
type,
...rest,
};

if (multiline) {
inputProps.rows = rows;
}

return (
<div className={textFieldClassName}>
<label htmlFor={id} className={styles.label}>
{label}
</label>
<InputComponent className={styles.input} {...inputProps} />
{helperText ? <div className={styles.helperText}>{helperText}</div> : null}
</div>
);
};

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

exports[`<TextField> renders and matches multiline snapshot 1`] = `
<div>
<div
class="textField"
>
<label
class="label"
for="text-field_1235_name"
>
Label
</label>
<textarea
class="input"
id="text-field_1235_name"
name="name"
placeholder="Placeholder"
rows="3"
type="text"
/>
</div>
</div>
`;
exports[`<TextField> renders and matches snapshot 1`] = `
<div>
<div
class="textField"
>
<label
class="label"
for="text-field_1235_name"
>
Label
</label>
<input
class="input"
id="text-field_1235_name"
name="name"
placeholder="Placeholder"
type="text"
value=""
/>
</div>
</div>
`;
17 changes: 17 additions & 0 deletions src/hooks/useOpaqueId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect, useState } from 'react';

const generateId = (prefix?: string, suffix?: string) => {
return [prefix, Math.round(Math.random() * 10000), suffix].filter(Boolean).join('_');
};

const useOpaqueId = (prefix?: string, suffix?: string, override?: string): string => {
const [id, setId] = useState(override || generateId(prefix, suffix));

useEffect(() => {
setId(override || generateId(prefix, suffix));
}, [override, prefix, suffix]);

return id;
};

export default useOpaqueId;
11 changes: 11 additions & 0 deletions src/styles/_theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ $scroll-indicator-bg: #7f899a !default;
$sidebar-bg: #000 !default;
$sidebar-color: variables.$white !default;

// TextField
$text-field-bg-color: rgba(variables.$black, 0.54) !default;
$text-field-resting-border-color: rgba(variables.$white, 0.34) !default;
$text-field-resting-color: rgba(variables.$white, 0.7) !default;
$text-field-hover-border-color: rgba(variables.$white, 0.7) !default;
$text-field-hover-bg-color: rgba(variables.$white, 0.08) !default;
$text-field-active-color: variables.$white !default;
$text-field-active-border-color: variables.$white !default;
$text-field-placeholder-color: rgba(variables.$white, 0.7) !default;
$text-field-error-color: #FF0C3E !default;

// Toggle

$toggle-default-color: #585858 !default;
Expand Down

0 comments on commit 6c6efa0

Please sign in to comment.