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

ui/wizard: convert wizard components to typescript #4285

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react'
import p from 'prop-types'
import StepIcon from '@mui/material/StepIcon'
import FormControl from '@mui/material/FormControl'
import FormControlLabel from '@mui/material/FormControlLabel'
Expand All @@ -11,12 +10,12 @@ import TextField from '@mui/material/TextField'
import Typography from '@mui/material/Typography'
import { FormContainer, FormField } from '../forms'
import WizardScheduleForm from './WizardScheduleForm'
import { value as valuePropType } from './propTypes'
import makeStyles from '@mui/styles/makeStyles'
import * as _ from 'lodash'
import { useIsWidthDown } from '../util/useWidth'
import MaterialSelect from '../selection/MaterialSelect'
import { useFeatures } from '../util/RequireConfig'
import { RotationType } from '../../schema'

const useStyles = makeStyles({
fieldItem: {
Expand All @@ -28,28 +27,65 @@ const useStyles = makeStyles({
},
})

export default function WizardForm(props) {
export interface WizardFormRotation {
startDate?: string
favorite?: boolean
type?: RotationType | 'never'
enable?: string
users?: string[]
timeZone?: string | null
}

export interface WizardFormSchedule {
timeZone: string | null
enable?: string
users: string[]
rotation: WizardFormRotation
followTheSunRotation: WizardFormRotation
}

export interface WizardFormValue {
teamName: string
primarySchedule: WizardFormSchedule
secondarySchedule: WizardFormSchedule
delayMinutes: number
repeat: string
key: {
label: string
value: string
} | null
}

interface WizardFormProps {
onChange: (value: WizardFormValue) => void
value: WizardFormValue
errors: Error[]
}

export default function WizardForm(props: WizardFormProps): React.JSX.Element {
const { onChange, value } = props
const fullScreen = useIsWidthDown('md')
const classes = useStyles()

const keyTypes = useFeatures().integrationKeyTypes

const handleSecondaryScheduleToggle = (e) => {
const handleSecondaryScheduleToggle = (
e: React.ChangeEvent<HTMLInputElement>,
): void => {
const newVal = _.cloneDeep(value)
newVal.secondarySchedule.enable = e.target.value
onChange(newVal)
}

const sectionHeading = (text) => {
const sectionHeading = (text: string): React.JSX.Element => {
return (
<Typography component='h2' variant='h6'>
{text}
</Typography>
)
}

const getDelayLabel = () => {
const getDelayLabel = (): string => {
if (value.secondarySchedule.enable === 'yes') {
return 'How long would you like to wait until escalating to your secondary schedule (in minutes)?'
}
Expand Down Expand Up @@ -180,13 +216,3 @@ export default function WizardForm(props) {
</FormContainer>
)
}

WizardForm.propTypes = {
onChange: p.func.isRequired,
value: valuePropType,
errors: p.arrayOf(
p.shape({
message: p.string.isRequired,
}),
),
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import DialogActions from '@mui/material/DialogActions'
import makeStyles from '@mui/styles/makeStyles'
import { DateTime } from 'luxon'
import { fieldErrors, nonFieldErrors } from '../util/errutil'
import WizardForm from './WizardForm'
import WizardForm, { WizardFormValue } from './WizardForm'
import LoadingButton from '../loading/components/LoadingButton'
import { gql, useMutation } from 'urql'
import { gql, useMutation, UseMutationExecute } from 'urql'
import { Form } from '../forms'
import {
getService,
Expand All @@ -24,6 +24,7 @@ import DialogTitleWrapper from '../dialogs/components/DialogTitleWrapper'
import DialogContentError from '../dialogs/components/DialogContentError'
import { useIsWidthDown } from '../util/useWidth'
import { Redirect } from 'wouter'
import { CreateEscalationPolicyStepInput } from '../../schema'

const mutation = gql`
mutation ($input: CreateServiceInput!) {
Expand All @@ -42,19 +43,19 @@ const useStyles = makeStyles(() => ({
},
}))

export default function WizardRouter() {
export default function WizardRouter(): React.JSX.Element {
const classes = useStyles()
const fullScreen = useIsWidthDown('md')
const [errorMessage, setErrorMessage] = useState(null)
const [errorMessage, setErrorMessage] = useState<string | null>(null)
const [complete, setComplete] = useState(false)
const [redirect, setRedirect] = useState(false)
const [value, setValue] = useState({
const [value, setValue] = useState<WizardFormValue>({
teamName: '',
delayMinutes: '',
delayMinutes: 0,
repeat: '',
key: null,
primarySchedule: {
timeZone: null,
timeZone: '',
users: [],
rotation: {
startDate: DateTime.local().startOf('day').toISO(),
Expand Down Expand Up @@ -91,11 +92,11 @@ export default function WizardRouter() {
* Handles not returning a second step if the secondary
* schedule is not enabled in the form.
*/
const getSteps = () => {
const getSteps = (): CreateEscalationPolicyStepInput[] => {
const secondary = value.secondarySchedule.enable === 'yes'
const steps = []

const step = (key) => ({
const step = (key: string): CreateEscalationPolicyStepInput => ({
delayMinutes: value.delayMinutes,
newSchedule: {
...getSchedule(key, value, secondary),
Expand All @@ -119,7 +120,11 @@ export default function WizardRouter() {
*
* e.g. createService: { newEscalationPolicy: {...} }
*/
const submit = (e, isValid, commit) => {
const submit = (
e: { preventDefault: () => void },
isValid: boolean,
commit: UseMutationExecute,
): void => {
e.preventDefault() // prevents reloading
if (!isValid) return

Expand All @@ -135,27 +140,37 @@ export default function WizardRouter() {
},
}
} catch (err) {
setErrorMessage(err.message)
setErrorMessage((err as Error).message)
}

if (variables) {
commit(variables).then((result) => {
if (result.error) {
const generalErrors = nonFieldErrors(result.error)
const graphqlErrors = fieldErrors(result.error).map((error) => {
const name = error.field
.split('.')
.pop() // get last occurrence
.replace(/([A-Z])/g, ' $1') // insert a space before all caps
.replace(/^./, (str) => str.toUpperCase()) // uppercase the first character

return `${name}: ${error.message}`
const fieldError = error.field.split('.').pop()
if (fieldError) {
const name = fieldError
.replace(/([A-Z])/g, ' $1') // insert a space before all caps
.replace(/^./, (str) => str.toUpperCase()) // uppercase the first character

return `${name}: ${error.message}`
}
})

const errors = generalErrors.concat(graphqlErrors)
const errors = [...generalErrors, ...graphqlErrors]

if (errors.length) {
setErrorMessage(errors.map((e) => e.message || e).join('\n'))
setErrorMessage(
errors
.map((e) => {
if (e instanceof Error) {
return e.message
}
return e
})
.join('\n'),
)
}
} else {
setComplete(true)
Expand All @@ -164,7 +179,7 @@ export default function WizardRouter() {
}
}

const onDialogClose = (data) => {
const onDialogClose = (data: { createService: boolean }): void => {
if (data && data.createService) {
return setRedirect(true)
}
Expand All @@ -174,7 +189,7 @@ export default function WizardRouter() {
window.scrollTo(0, 0)
}

function renderSubmittedContent() {
function renderSubmittedContent(): React.JSX.Element | undefined {
if (complete) {
return (
<DialogContent>
Expand Down Expand Up @@ -204,7 +219,6 @@ export default function WizardRouter() {
>
<CardContent>
<WizardForm
disabled={fetching}
errors={fieldErrors(error)}
value={value}
onChange={(value) => setValue(value)}
Expand Down
Loading
Loading