Skip to content

Commit

Permalink
fix: Refactor TextField & TextArea to use common Field (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeltaranto authored Mar 24, 2019
1 parent 364db99 commit 4fff1d6
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 150 deletions.
21 changes: 1 addition & 20 deletions lib/components/TextArea/TextArea.css.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
export default {
'.root': {
position: 'relative',
},
'.textarea': {
'.verticalResizeOnly': {
resize: 'vertical',
'&:focus': {
outline: 'none',
},
},
'.focusOverlay': {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
opacity: 0,
pointerEvents: 'none',
transition: 'opacity 0.2s',
'.textarea:focus ~ &': {
opacity: 1,
},
},
};
4 changes: 1 addition & 3 deletions lib/components/TextArea/TextArea.css.js.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// This file is automatically generated.
// Please do not change this file!
export const focusOverlay: string;
export const root: string;
export const textarea: string;
export const verticalResizeOnly: string;
88 changes: 29 additions & 59 deletions lib/components/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,12 @@
import React, { ReactNode, AllHTMLAttributes, Fragment } from 'react';
import React, { ReactNode } from 'react';
import classnames from 'classnames';
import { Box } from '../Box/Box';
import { FieldLabel } from '../FieldLabel/FieldLabel';
import { FieldMessage } from '../FieldMessage/FieldMessage';
import { Text } from '../Text/Text';
import styles from './TextArea.css.js';
import { useTheme } from '../private/ThemeContext';
import { Field, FieldProps } from '../private/Field/Field';

type NativeTextAreaProps = AllHTMLAttributes<HTMLTextAreaElement>;
interface TextAreaProps {
id: NonNullable<NativeTextAreaProps['id']>;
value: string;
onChange: NonNullable<NativeTextAreaProps['onChange']>;
onBlur?: NativeTextAreaProps['onBlur'];
onFocus?: NativeTextAreaProps['onFocus'];
label?: string;
secondaryLabel?: ReactNode;
tertiaryLabel?: ReactNode;
placeholder?: string;
message?: ReactNode | false;
tone?: 'neutral' | 'critical' | 'positive';
description?: 'string';
interface TextAreaProps extends FieldProps {
limit?: number;
}

Expand All @@ -45,6 +31,7 @@ const renderCount = ({
export const TextArea = ({
id,
label,
name,
secondaryLabel,
tertiaryLabel,
placeholder,
Expand All @@ -57,22 +44,31 @@ export const TextArea = ({
description,
limit,
}: TextAreaProps) => {
const { atoms, tokens } = useTheme();
const messageId = `${id}-message`;
const { tokens } = useTheme();

return (
<Fragment>
<FieldLabel
id={id}
label={label}
secondaryLabel={secondaryLabel}
tertiaryLabel={tertiaryLabel}
description={description}
/>
<Box className={styles.root}>
<Field
id={id}
name={name}
label={label}
description={description}
secondaryLabel={secondaryLabel}
tertiaryLabel={tertiaryLabel}
tone={tone}
message={message}
secondaryMessage={renderCount({
limit,
value,
})}
placeholder={placeholder}
value={value}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
>
{({ className, ...fieldProps }) => (
<Box
component="textarea"
id={id}
backgroundColor="input"
boxShadow={tone === 'critical' ? 'borderCritical' : 'borderStandard'}
display="block"
Expand All @@ -82,40 +78,14 @@ export const TextArea = ({
paddingTop="standardTouchableText"
paddingBottom="standardTouchableText"
borderRadius="standard"
value={value}
placeholder={placeholder}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
aria-describedby={messageId}
rows={3}
className={classnames(
styles.textarea,
atoms.fontFamily.text,
atoms.fontSize.standard,
atoms.color.neutral,
)}
style={{
minHeight: tokens.rowHeight * 15,
}}
className={classnames(styles.verticalResizeOnly, className)}
{...fieldProps}
/>
<Box
className={styles.focusOverlay}
boxShadow="outlineFocus"
borderRadius="standard"
paddingTop="standardTouchableText"
paddingBottom="standardTouchableText"
/>
</Box>
<FieldMessage
id={messageId}
tone={tone}
message={message}
secondaryMessage={renderCount({
limit,
value,
})}
/>
</Fragment>
)}
</Field>
);
};
102 changes: 34 additions & 68 deletions lib/components/TextField/TextField.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import React, { ReactNode, AllHTMLAttributes, Fragment } from 'react';
import classnames from 'classnames';
import React from 'react';
import { Box } from '../Box/Box';
import { FieldLabel } from '../FieldLabel/FieldLabel';
import { FieldMessage } from '../FieldMessage/FieldMessage';
import styles from './TextField.css.js';
import { useTheme } from '../private/ThemeContext';
import { Field, FieldProps } from '../private/Field/Field';

const validTypes = {
text: 'text',
Expand All @@ -16,24 +12,13 @@ const validTypes = {
url: 'url',
};

type NativeInputProps = AllHTMLAttributes<HTMLInputElement>;
interface TextFieldProps {
id: NonNullable<NativeInputProps['id']>;
value: NonNullable<NativeInputProps['value']>;
onChange: NonNullable<NativeInputProps['onChange']>;
onBlur?: NativeInputProps['onBlur'];
onFocus?: NativeInputProps['onFocus'];
label?: string;
secondaryLabel?: ReactNode;
tertiaryLabel?: ReactNode;
placeholder?: string;
message?: ReactNode | false;
tone?: 'neutral' | 'critical' | 'positive';
interface TextFieldProps extends FieldProps {
type?: keyof typeof validTypes;
}

export const TextField = ({
id,
name,
label,
secondaryLabel,
tertiaryLabel,
Expand All @@ -45,53 +30,34 @@ export const TextField = ({
onChange,
onBlur,
onFocus,
}: TextFieldProps) => {
const theme = useTheme();
const messageId = `${id}-message`;

return (
<Fragment>
<FieldLabel
id={id}
label={label}
secondaryLabel={secondaryLabel}
tertiaryLabel={tertiaryLabel}
}: TextFieldProps) => (
<Field
id={id}
name={name}
label={label}
secondaryLabel={secondaryLabel}
tertiaryLabel={tertiaryLabel}
tone={tone}
message={message}
placeholder={placeholder}
value={value}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
>
{fieldProps => (
<Box
component="input"
type={validTypes[type]}
boxShadow={tone === 'critical' ? 'borderCritical' : 'borderStandard'}
width="full"
paddingLeft="small"
paddingRight="small"
paddingTop="standardTouchableText"
paddingBottom="standardTouchableText"
borderRadius="standard"
{...fieldProps}
/>
<Box className={styles.root}>
<Box
component="input"
type={validTypes[type]}
id={id}
backgroundColor="input"
boxShadow={tone === 'critical' ? 'borderCritical' : 'borderStandard'}
width="full"
paddingLeft="small"
paddingRight="small"
paddingTop="standardTouchableText"
paddingBottom="standardTouchableText"
borderRadius="standard"
value={value}
placeholder={placeholder}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
aria-describedby={messageId}
className={classnames(
styles.input,
theme.atoms.fontFamily.text,
theme.atoms.fontSize.standard,
theme.atoms.color.neutral,
)}
/>
<Box
className={styles.focusOverlay}
boxShadow="outlineFocus"
borderRadius="standard"
paddingTop="standardTouchableText"
paddingBottom="standardTouchableText"
/>
</Box>
<FieldMessage id={messageId} tone={tone} message={message} />
</Fragment>
);
};
)}
</Field>
);
14 changes: 14 additions & 0 deletions lib/components/private/Field/Field.css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default {
'.fieldContainer': {
position: 'relative',
},
'.field:focus': {
outline: 'none',
},
'.field:focus ~ .focusOverlay': {
opacity: 1,
},
'.field:hover ~ .hoverOverlay': {
opacity: 1,
},
};
6 changes: 6 additions & 0 deletions lib/components/private/Field/Field.css.js.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// This file is automatically generated.
// Please do not change this file!
export const field: string;
export const fieldContainer: string;
export const focusOverlay: string;
export const hoverOverlay: string;
Loading

0 comments on commit 4fff1d6

Please sign in to comment.