-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DEV-5640: Migrate DReason to TypeScript
- Loading branch information
1 parent
3754131
commit 2dbab8e
Showing
11 changed files
with
419 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
spa/src/components/donationPage/pageContent/DReason/DReason.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import PropTypes, { InferProps } from 'prop-types'; | ||
import { useMemo, useState } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { usePage } from 'components/donationPage/DonationPage'; | ||
import { ReasonElement } from 'hooks/useContributionPage'; | ||
import DElement from '../DElement'; | ||
import TributeFields, { TributeFieldsProps } from './TributeFields'; | ||
import ReasonFields from './ReasonFields'; | ||
|
||
const DReasonPropTypes = { | ||
element: PropTypes.object.isRequired | ||
}; | ||
|
||
export interface DReasonProps extends InferProps<typeof DReasonPropTypes> { | ||
element: ReasonElement; | ||
} | ||
|
||
export function DReason({ element }: DReasonProps) { | ||
const { errors } = usePage(); | ||
const [selectedReason, setSelectedReason] = useState<string>(''); | ||
const [reasonText, setReasonText] = useState<string>(''); | ||
const [tributeType, setTributeType] = useState<TributeFieldsProps['tributeType']>(null); | ||
const [tributeName, setTributeName] = useState(''); | ||
const { t } = useTranslation(); | ||
const required = useMemo(() => element.requiredFields.includes('reason_for_giving'), [element.requiredFields]); | ||
|
||
return ( | ||
<DElement label={t('donationPage.dReason.reasonForGiving')} data-testid="d-reason"> | ||
<ReasonFields | ||
onChangeOption={setSelectedReason} | ||
onChangeText={setReasonText} | ||
optionError={errors.reason_for_giving} | ||
options={element.content.reasons} | ||
required={required} | ||
selectedOption={selectedReason} | ||
text={reasonText} | ||
textError={errors.reason_other} | ||
/> | ||
{(element.content.askHonoree || element.content.askInMemoryOf) && ( | ||
<TributeFields | ||
askHonoree={element.content.askHonoree} | ||
askInMemoryOf={element.content.askInMemoryOf} | ||
error={errors.reason_for_giving} | ||
onChangeTributeName={setTributeName} | ||
onChangeTributeType={setTributeType} | ||
tributeName={tributeName} | ||
tributeType={tributeType} | ||
/> | ||
)} | ||
</DElement> | ||
); | ||
} | ||
|
||
DReason.propTypes = DReasonPropTypes; | ||
DReason.type = 'DReason'; | ||
DReason.displayName = 'Reason for Giving'; | ||
DReason.description = "Collect information about the contributor's reason for giving"; | ||
DReason.required = false; | ||
DReason.unique = true; | ||
export default DReason; |
46 changes: 46 additions & 0 deletions
46
spa/src/components/donationPage/pageContent/DReason/ReasonFields.styled.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import styled from 'styled-components'; | ||
import { Select as BaseSelect, TextField as BaseTextField } from 'components/base'; | ||
|
||
export const Root = styled.div` | ||
display: grid; | ||
gap: 25px; | ||
`; | ||
|
||
export const Select = styled(BaseSelect)` | ||
&& { | ||
.NreTextFieldInputLabelRoot { | ||
font-weight: 500; | ||
margin-bottom: 0.5em; | ||
} | ||
.NreTextFieldInputLabelAsterisk { | ||
color: #ff476c; | ||
} | ||
.NreSelectMenu { | ||
background: ${({ theme }) => theme.colors.cstm_inputBackground}; | ||
border: 1px solid #080708; | ||
border-color: ${({ theme }) => theme.colors.cstm_inputBorder}; | ||
border-radius: 3px; | ||
padding: 1rem; | ||
&:focus { | ||
border-color: ${({ theme }) => theme.colors.cstm_CTAs}; | ||
} | ||
} | ||
} | ||
`; | ||
|
||
export const TextField = styled(BaseTextField)` | ||
&& { | ||
.NreTextFieldInput { | ||
background: ${({ theme }) => theme.colors.cstm_inputBackground}; | ||
border-color: ${({ theme }) => theme.colors.cstm_inputBorder}; | ||
border-width: 1px; | ||
} | ||
.NreTextFieldInputLabelAsterisk { | ||
color: #ff476c; | ||
} | ||
} | ||
`; |
94 changes: 94 additions & 0 deletions
94
spa/src/components/donationPage/pageContent/DReason/ReasonFields.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { Collapse } from '@material-ui/core'; | ||
import PropTypes, { InferProps } from 'prop-types'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { useMemo } from 'react'; | ||
import { Root, Select, TextField } from './ReasonFields.styled'; | ||
|
||
// We need to track both a selected reason and a user-entered reason so that if | ||
// the user types in a reason that exactly matches a pre-selected one, the text | ||
// field doesn't disappear. In other words, if we used a single value, then we | ||
// wouldn't be able to distinguish between a user who chose a pre-selected value | ||
// and one who typed one in that matched a pre-selected value, because we | ||
// conditionally show the text field in this component depending on the state of | ||
// the select. | ||
|
||
const ReasonFieldsPropTypes = { | ||
onChangeText: PropTypes.func.isRequired, | ||
onChangeOption: PropTypes.func.isRequired, | ||
optionError: PropTypes.string, | ||
options: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, | ||
required: PropTypes.bool, | ||
selectedOption: PropTypes.string.isRequired, | ||
text: PropTypes.string.isRequired, | ||
textError: PropTypes.string | ||
}; | ||
|
||
export interface ReasonFieldsProps extends InferProps<typeof ReasonFieldsPropTypes> { | ||
onChangeText: (value: string) => void; | ||
onChangeOption: (value: string) => void; | ||
} | ||
|
||
export function ReasonFields({ | ||
onChangeOption, | ||
onChangeText, | ||
optionError, | ||
options, | ||
required, | ||
selectedOption, | ||
text, | ||
textError | ||
}: ReasonFieldsProps) { | ||
const { t } = useTranslation(); | ||
const otherReasonLabel = t('donationPage.dReason.other'); | ||
const optionsWithOther = useMemo(() => { | ||
if (options.length > 0) { | ||
return [ | ||
...options.map((option) => ({ label: option, value: option })), | ||
{ label: otherReasonLabel, value: otherReasonLabel } | ||
]; | ||
} | ||
|
||
return []; | ||
}, [options, otherReasonLabel]); | ||
|
||
// Names on inputs below must be set exactly in order for the form to be | ||
// submitted properly. | ||
// | ||
// We also need to ensure the text field unmounts when transitioned out so | ||
// it's not present in the DOM at form submission time, so its value doesn't | ||
// appear in the form data. | ||
|
||
return ( | ||
<Root> | ||
{optionsWithOther.length > 0 && ( | ||
<Select | ||
data-testid="reason-for-giving-reason-select" | ||
error={!!optionError} | ||
fullWidth | ||
helperText={optionError} | ||
label={t('donationPage.dReason.supportWork')} | ||
name="reason_for_giving" | ||
onChange={(e) => onChangeOption(e.target.value)} | ||
options={optionsWithOther} | ||
required={!!required} | ||
value={selectedOption} | ||
></Select> | ||
)} | ||
<Collapse in={optionsWithOther.length === 0 || selectedOption === otherReasonLabel} unmountOnExit> | ||
<TextField | ||
error={!!textError} | ||
fullWidth | ||
helperText={textError} | ||
label={t('donationPage.dReason.tellUsWhy')} | ||
name="reason_other" | ||
onChange={(event) => onChangeText(event.target.value)} | ||
required={!!required} | ||
value={text} | ||
/> | ||
</Collapse> | ||
</Root> | ||
); | ||
} | ||
|
||
ReasonFields.propTypes = ReasonFieldsPropTypes; | ||
export default ReasonFields; |
46 changes: 46 additions & 0 deletions
46
spa/src/components/donationPage/pageContent/DReason/TributeFields.styled.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import styled from 'styled-components'; | ||
// Using the MUI core radio controls to match appearance in DFrequency. | ||
import { FormControlLabel as MuiFormControlLabel, Radio as MuiRadio } from '@material-ui/core'; | ||
import { Checkbox as BaseCheckbox, TextField as BaseTextField } from 'components/base'; | ||
|
||
export const Prompt = styled.p` | ||
font-size: ${({ theme }) => theme.fontSizesUpdated.md}; | ||
font-weight: 500; | ||
margin-bottom: 6px; | ||
padding-top: 25px; | ||
`; | ||
|
||
export const Checkbox = styled(BaseCheckbox)` | ||
&& { | ||
&.Mui-checked { | ||
color: ${({ theme }) => theme.colors.cstm_CTAs}; | ||
} | ||
} | ||
`; | ||
|
||
export const Radio = styled(MuiRadio)``; | ||
|
||
export const RadioFormControlLabel = styled(MuiFormControlLabel)` | ||
&& span { | ||
font-size: ${({ theme }) => theme.fontSizesUpdated.md}; | ||
} | ||
`; | ||
|
||
export const Root = styled.div` | ||
display: grid; | ||
gap: 25px; | ||
`; | ||
|
||
export const TextField = styled(BaseTextField)` | ||
&& { | ||
.NreTextFieldInput { | ||
background: ${({ theme }) => theme.colors.cstm_inputBackground}; | ||
border-color: ${({ theme }) => theme.colors.cstm_inputBorder}; | ||
border-width: 1px; | ||
} | ||
.NreTextFieldInputLabelAsterisk { | ||
color: #ff476c; | ||
} | ||
} | ||
`; |
Oops, something went wrong.