-
Notifications
You must be signed in to change notification settings - Fork 31
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
Callouts in DCR - PR 1/5 - Add deadline component and separate new/legacy components into two folders #6741
Changes from 17 commits
5db52ed
d6a6902
1f258a8
2860f84
1ae4aa4
bbcefff
76437df
a2b00b2
fa92c90
c4b77c6
9c7cec7
00057be
5143683
b8504e2
9e5fed8
b2db051
3ebcaae
d28e3a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,79 @@ | ||
import { css } from '@emotion/react'; | ||
import { space, text, textSans } from '@guardian/source-foundations'; | ||
import { useState } from 'react'; | ||
import { CampaignFieldFile } from '../../../types/content'; | ||
import { stringifyFileBase64 } from '../../lib/stringifyFileBase64'; | ||
import type { SerializedStyles } from '@emotion/react'; | ||
import { | ||
neutral, | ||
palette, | ||
remHeight, | ||
space, | ||
textSans, | ||
visuallyHidden, | ||
} from '@guardian/source-foundations'; | ||
import React, { useState } from 'react'; | ||
import type { CampaignFieldType } from 'src/types/content'; | ||
import { decidePalette } from 'src/web/lib/decidePalette'; | ||
import { stringifyFileBase64 } from 'src/web/lib/stringifyFileBase64'; | ||
import { FieldLabel } from './FieldLabel'; | ||
|
||
const fileUploadInputStyles = css` | ||
padding-top: 10px; | ||
padding-bottom: 10px; | ||
`; | ||
|
||
const errorMessagesStyles = css` | ||
padding-top: ${space[2]}px; | ||
color: ${text.error}; | ||
color: ${palette.error}; | ||
${textSans.small({ fontWeight: 'bold' })}; | ||
`; | ||
|
||
const uploadStyles = css` | ||
display: block; | ||
`; | ||
|
||
const textStyles = css` | ||
${textSans.small()}; | ||
color: ${neutral[46]}; | ||
display: inline-block; | ||
`; | ||
|
||
type Props = { | ||
formField: CampaignFieldFile; | ||
format: ArticleFormat; | ||
formField: CampaignFieldType; | ||
formData: { [key in string]: any }; | ||
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>; | ||
validationErrors: { [key in string]: string }; | ||
}; | ||
|
||
export const FileUpload = ({ formField, formData, setFormData }: Props) => { | ||
const customUpload = (format: ArticleFormat): SerializedStyles => css` | ||
${textSans.small()}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. presuming we don't need this as we have another text declaration below on line 59 |
||
color: ${decidePalette(format).text.richLink}; | ||
border: 1.5px solid ${decidePalette(format).text.richLink}; | ||
display: inline-flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
box-sizing: border-box; | ||
background: transparent; | ||
cursor: pointer; | ||
transition: all 0.3s ease-in-out 0s; | ||
text-decoration: none; | ||
white-space: nowrap; | ||
height: ${remHeight.ctaXsmall}rem; | ||
min-height: ${remHeight.ctaXsmall}rem; | ||
padding: ${space[3]}px; | ||
margin: ${space[3]}px ${space[3]}px 0px 0px; | ||
border-radius: ${remHeight.ctaMedium}rem; | ||
${textSans.medium({ fontWeight: 'bold' })}; | ||
width: fit-content; | ||
`; | ||
|
||
export const FileUpload = ({ | ||
formField, | ||
format, | ||
formData, | ||
setFormData, | ||
}: Props) => { | ||
const [chosenFile, setChosenFile] = useState<null | string>(); | ||
const [error, setError] = useState(''); | ||
|
||
const getFileName = (filepath?: string): string => | ||
filepath?.split(/(\\|\/)/g).pop() ?? ''; | ||
|
||
const onSelectFile = async (event: React.ChangeEvent<HTMLInputElement>) => { | ||
if (event.target.files && event.target.files[0]) { | ||
if (event.target.files?.[0]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The && operator seemed unnecessary here, hence I removed it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We even have a proposal to enforce this in the future: #6715 |
||
setError(''); | ||
try { | ||
const stringifiedFile = await stringifyFileBase64( | ||
|
@@ -35,27 +83,54 @@ export const FileUpload = ({ formField, formData, setFormData }: Props) => { | |
...formData, | ||
[formField.id]: stringifiedFile, | ||
}); | ||
setChosenFile(event.target.files[0].name); | ||
} catch (e) { | ||
setError( | ||
'Sorry there was a problem with the file you uploaded above. Check the size and type. We only accept images, pdfs and .doc or .docx files', | ||
'Sorry, there was a problem with the file you uploaded above. Check the size and type. We only accept images, pdfs and .doc or .docx files', | ||
); | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<FieldLabel formField={formField} /> | ||
<input | ||
data-testid={`form-field-${formField.id}`} | ||
css={fileUploadInputStyles} | ||
type="file" | ||
accept="image/*, .pdf" | ||
required={formField.required} | ||
onChange={onSelectFile} | ||
/> | ||
<p>We accept images and pdfs. Maximum total file size: 6MB</p> | ||
{!!error && <div css={errorMessagesStyles}>{error}</div>} | ||
<div css={uploadStyles}> | ||
<FieldLabel formField={formField} /> | ||
<div> | ||
<label css={customUpload(format)} htmlFor={formField.id}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we use the FieldLabel here rather than having 2 labels for one component? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We still do use the FieldLabel on line 97, it's just that for accessibility/CSS reasons we had to wrap the 'Choose File' button in a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the field label a html label element? If we could wrap the Choose file button in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep I get what you mean here. The problem I found was that when the whole thing was clickable you had unexpected behaviour when the 'Remove File' button appeared after uploading a file - it would mean that when you click that button you're immediately prompted to upload a file again. Just a weird UX. However, if you think there is a way around this I'd be more than happy to pair on it! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think maybe this is all a moot conversation now as we've moved the file input into source. For reference in source the label wraps the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @sophie-macmillan - I've removed this component now and have pushed the changes. |
||
Choose file | ||
<input | ||
id={formField.id} | ||
data-testid={`form-field-${formField.id}`} | ||
type="file" | ||
accept="image/*, .pdf" | ||
required={formField.required} | ||
onChange={onSelectFile} | ||
css={css` | ||
${visuallyHidden}; | ||
`} | ||
/> | ||
</label> | ||
{chosenFile ? ( | ||
<> | ||
<button | ||
type="button" | ||
css={customUpload(format)} | ||
onClick={(): void => { | ||
setChosenFile(undefined); | ||
}} | ||
> | ||
Remove File | ||
</button> | ||
<span css={textStyles}> | ||
{getFileName(chosenFile)} | ||
</span> | ||
</> | ||
) : ( | ||
<div css={textStyles}> No file chosen </div> | ||
)} | ||
{!!error && <div css={errorMessagesStyles}>{error}</div>} | ||
</div> | ||
</div> | ||
</> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,37 @@ | ||
import { CampaignFieldSelect } from '../../../types/content'; | ||
import { FieldLabel } from './FieldLabel'; | ||
import { Select as SourceSelect } from '@guardian/source-react-components'; | ||
import type { CampaignFieldSelect } from 'src/types/content'; | ||
|
||
type Props = { | ||
validationErrors?: { [key in string]: string }; | ||
formField: CampaignFieldSelect; | ||
formData: { [key in string]: any }; | ||
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>; | ||
}; | ||
|
||
export const Select = ({ formField, formData, setFormData }: Props) => ( | ||
<> | ||
<FieldLabel formField={formField} /> | ||
<select | ||
data-testid={`form-field-${formField.id}`} | ||
required={formField.required} | ||
value={ | ||
formField.id && formField.id in formData | ||
? formData[formField.id] | ||
: '' | ||
} | ||
onChange={(e) => | ||
setFormData({ | ||
...formData, | ||
[formField.id]: e.target.value, | ||
}) | ||
} | ||
> | ||
{formField.options && | ||
formField.options.map((option, index) => ( | ||
<option key={index} value={option.value}> | ||
{option.value} | ||
</option> | ||
))} | ||
</select> | ||
</> | ||
export const Select = ({ | ||
validationErrors, | ||
formField, | ||
formData, | ||
setFormData, | ||
}: Props) => ( | ||
<SourceSelect | ||
hideLabel={formField.hideLabel} | ||
data-testid={`form-field-${formField.id}`} | ||
error={validationErrors?.[formField.id]} | ||
label={formField.label} | ||
supporting={formField.description} | ||
value={formField.id in formData ? formData[formField.id] : ''} | ||
onChange={(e) => | ||
setFormData({ | ||
...formData, | ||
[formField.id]: e.target.value, | ||
}) | ||
} | ||
optional={!formField.required} | ||
children={formField.options.map((option, index) => ( | ||
<option key={index} value={option.value}> | ||
{option.value} | ||
</option> | ||
))} | ||
/> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the nice shiny new
FileUpload
component.