Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#542 - Show form validation to the user #574

Merged
merged 2 commits into from
Sep 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 25 additions & 18 deletions src/components/Form/Fields.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import * as React from 'react'
import { TextAreaStyled, Input, TextAreaDisabled } from './elements'
import {
TextAreaStyled,
Input,
TextAreaDisabled,
ErrorMessage,
} from './elements'
import { FieldRenderProps } from 'react-final-form'
import Calendar, { CalendarProps } from 'react-calendar'

Expand All @@ -17,9 +22,10 @@ type YearPickerProps = IFieldProps & CalendarProps

export const InputField = ({ input, meta, ...rest }: IFieldProps) => (
<>
<Input {...input} {...rest} />
{/* CC - removed clunky error reporting until better solution implemented */}
{/* {meta.error && meta.touched && <span>{meta.error}</span>} */}
<Input invalid={meta.error && meta.touched} {...input} {...rest} />
{meta.error && meta.touched ? (
<ErrorMessage>{meta.error}</ErrorMessage>
) : null}
</>
)

Expand All @@ -28,18 +34,20 @@ export const TextAreaField = ({
meta,
disabled,
...rest
}: IFieldProps) => (
<>
{disabled ? (
// want disabled textarea to just render as styled div to remove scrollbars
<TextAreaDisabled>{input.value}</TextAreaDisabled>
) : (
<TextAreaStyled {...input} {...rest} />
)}

{/* {meta.error && meta.touched && <span>{meta.error}</span>} */}
</>
)
}: IFieldProps) =>
disabled ? (
// want disabled textarea to just render as styled div to remove scrollbars
<TextAreaDisabled>{input.value}</TextAreaDisabled>
) : (
<>
<TextAreaStyled
invalid={meta.error && meta.touched}
{...input}
{...rest}
/>
{meta.error && meta.touched && <ErrorMessage>{meta.error}</ErrorMessage>}
</>
)

export const YearPicker = ({ input, meta, ...rest }: YearPickerProps) => (
<>
Expand All @@ -53,7 +61,6 @@ export const YearPicker = ({ input, meta, ...rest }: YearPickerProps) => (
input.onChange(v)
}}
/>

{/* {meta.error && meta.touched && <span>{meta.error}</span>} */}
{/* meta.error && meta.touched && <ErrorMessage>{meta.error}</ErrorMessage> */}
</>
)
27 changes: 15 additions & 12 deletions src/components/Form/ImageInput.field.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React from 'react'
import { ImageInput } from '../ImageInput/ImageInput'
import { IFieldProps } from './Fields'
import { FieldContainer } from './elements'
import { FieldContainer, ErrorMessage } from './elements'

export const ImageInputField = ({ input, meta, ...rest }: IFieldProps) => (
<FieldContainer>
<ImageInput
{...rest}
// as validation happens on blur also want to artificially trigger when values change
// (no native blur event)
onFilesChange={files => {
input.onChange(files)
input.onBlur()
}}
/>
</FieldContainer>
<>
<FieldContainer invalid={meta.touched && meta.error}>
<ImageInput
{...rest}
// as validation happens on blur also want to artificially trigger when values change
// (no native blur event)
onFilesChange={files => {
input.onChange(files)
input.onBlur()
}}
/>
</FieldContainer>
{meta.error && meta.touched && <ErrorMessage>{meta.error}</ErrorMessage>}
</>
)
1 change: 1 addition & 0 deletions src/components/Form/LocationSearch.field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ export const LocationSearchField = ({
}
input.onBlur()
}}
placeholder="Search for a location *"
/>
)
41 changes: 22 additions & 19 deletions src/components/Form/Select.field.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as React from 'react'
import ReactFlagsSelect from 'react-flags-select'
import Select from 'react-select'
import { IFieldProps } from './Fields'
import { Styles } from 'react-select/lib/styles'
import { Props as SelectProps } from 'react-select/lib/Select'
import { FieldContainer } from './elements'
import { Styles } from 'react-select/lib/styles'
import theme from 'src/themes/styled.theme'
import ReactFlagsSelect from 'react-flags-select'
import { getCountryName } from 'src/utils/helpers'
import { FlexContainer } from '../Layout/FlexContainer'
import { ErrorMessage, FieldContainer } from './elements'
import { IFieldProps } from './Fields'

interface ISelectOption {
value: string
Expand Down Expand Up @@ -63,19 +64,22 @@ const defaultProps: Partial<ISelectFieldProps> = {
}
export const SelectField = ({ input, meta, ...rest }: ISelectFieldProps) => (
// note, we first use a div container so that default styles can be applied
<FieldContainer style={rest.style}>
<Select
styles={SelectStyles}
onChange={v => {
input.onChange(getValueFromSelect(v))
}}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={getValueForSelect(rest.options, input.value)}
{...defaultProps}
{...rest}
/>
</FieldContainer>
<FlexContainer p={0} flexWrap="wrap">
<FieldContainer invalid={meta.error && meta.touched} style={rest.style}>
<Select
styles={SelectStyles}
onChange={v => {
input.onChange(getValueFromSelect(v))
}}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={getValueForSelect(rest.options, input.value)}
{...defaultProps}
{...rest}
/>
</FieldContainer>
{meta.error && meta.touched && <ErrorMessage>{meta.error}</ErrorMessage>}
</FlexContainer>
)

export const FlagSelector = ({ input, meta, ...rest }: ISelectFieldProps) => (
Expand All @@ -89,7 +93,6 @@ export const FlagSelector = ({ input, meta, ...rest }: ISelectFieldProps) => (
{...defaultProps}
{...rest}
/>

{/* {meta.error && meta.touched && <span>{meta.error}</span>} */}
{/* meta.error && meta.touched && <ErrorMessage>{meta.error}</ErrorMessage> */}
</>
)
21 changes: 16 additions & 5 deletions src/components/Form/elements.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import styled, { css } from 'styled-components'
import theme from 'src/themes/styled.theme'

export const inputStyles = css`
border: 1px solid ${theme.colors.black};
interface IFormElement {
invalid?: boolean
}

export const inputStyles = ({ invalid }: IFormElement) => css`
border: 1px solid ${invalid ? theme.colors.error : theme.colors.black};
border-radius: 4px;
font-size: ${theme.fontSizes[2] + 'px'};
background: white;
Expand All @@ -16,12 +20,12 @@ export const inputStyles = css`
}
`

export const Input = styled.input`
export const Input = styled.input<IFormElement>`
${inputStyles};
height: 40px;
`

export const TextAreaStyled = styled.textarea`
export const TextAreaStyled = styled.textarea<IFormElement>`
${inputStyles};
height: 150px;
font-family: inherit;
Expand All @@ -32,8 +36,15 @@ export const TextAreaDisabled = styled.div`
`

// generic container used for some custom component fields
export const FieldContainer = styled.div`
export const FieldContainer = styled.div<IFormElement>`
${inputStyles};
border: 'none';
padding: 0;
`
export const ErrorMessage = styled.span`
position: relative;
bottom: ${theme.space[2]}px;
color: ${theme.colors.error};
font-size: ${theme.fontSizes[0]}px;
height: ${theme.space[0]};
`
4 changes: 2 additions & 2 deletions src/pages/Events/Content/EventsCreate/EventsCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class EventsCreate extends React.Component<IProps, IState> {
validate={value => this.validateTitle(value)}
validateFields={[]}
component={InputField}
placeholder="Title of your event"
placeholder="Title of your event *"
/>
<Field
name="tags"
Expand All @@ -117,7 +117,7 @@ export class EventsCreate extends React.Component<IProps, IState> {
validateFields={[]}
validate={value => this.validateUrl(value)}
component={InputField}
placeholder="URL to offsite link (Facebook, Meetup, etc)"
placeholder="URL to offsite link (Facebook, Meetup, etc) *"
/>
</BoxContainer>
</form>
Expand Down
46 changes: 26 additions & 20 deletions src/pages/Howto/Content/CreateHowto/Step/Step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,30 +65,36 @@ class Step extends Component<IProps, IState> {
<Field
name={`${step}.title`}
component={InputField}
placeholder={`Title of Step ${index + 1}`}
placeholder={`Title of Step ${index + 1} *`}
validate={required}
validateFields={[]}
/>
{/* Left */}
<FlexContainer p={0} pr={2} flexDirection="column" flex={1}>
<Field
name={`${step}.text`}
placeholder="Describe this step"
component={TextAreaField}
style={{ resize: 'vertical', height: '100%' }}
validate={required}
validateFields={[]}
/>
<FlexContainer p={0} flexWrap="wrap">
{/* Left */}
<FlexContainer p={0} pr={2} flexDirection="column" flex={1}>
<Field
name={`${step}.text`}
placeholder="Describe this step *"
component={TextAreaField}
style={{ resize: 'vertical', height: '100%' }}
validate={required}
validateFields={[]}
/>
</FlexContainer>
{/* right */}
<BoxContainer p={0} width={[1, '305px', null]}>
<Field
name={`${step}.images`}
component={ImageInputField}
multi
/>
<Field
name={`${step}.caption`}
component={InputField}
placeholder="Insert Caption"
/>
</BoxContainer>
</FlexContainer>
{/* right */}
<BoxContainer p={0} width={[1, '305px', null]}>
<Field name={`${step}.images`} component={ImageInputField} multi />
<Field
name={`${step}.caption`}
component={InputField}
placeholder="Insert Caption"
/>
</BoxContainer>
</FlexContainer>
</BoxContainer>
)
Expand Down