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/alerts: convert CreateAlertDialog, CreateAlertForm, and useCreateAlerts to ts #2423

Merged
merged 5 commits into from
Jun 17, 2022
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
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import React, { useState, useEffect } from 'react'
import p from 'prop-types'
import {
Button,
Grid,
Stepper,
Step,
StepLabel,
Typography,
Theme,
} from '@mui/material'
import makeStyles from '@mui/styles/makeStyles'
import { makeStyles } from '@mui/styles'
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
import _ from 'lodash'

import { useCreateAlerts } from './useCreateAlerts'
import { fieldErrors, allErrors } from '../../util/errutil'
import { fieldErrors } from '../../util/errutil'
import FormDialog from '../../dialogs/FormDialog'
import { CreateAlertForm } from './StepContent/CreateAlertForm'
import { CreateAlertReview } from './StepContent/CreateAlertReview'
import AppLink from '../../util/AppLink'

const pluralize = (num) => (num !== 1 ? 's' : '')
export interface Value {
summary: string
details: string
serviceIDs: string[]
}

const pluralize = (num: number): string => (num !== 1 ? 's' : '')

const useStyles = makeStyles((theme) => ({
const useStyles = makeStyles((theme: Theme) => ({
dialog: {
[theme.breakpoints.up('md')]: {
height: '65vh',
Expand All @@ -32,10 +38,13 @@ const useStyles = makeStyles((theme) => ({
},
}))

export default function CreateAlertDialog(props) {
export default function CreateAlertDialog(props: {
onClose: () => void
serviceID: string
}): JSX.Element {
const classes = useStyles()
const [step, setStep] = useState(0)
const [value, setValue] = useState({
const [value, setValue] = useState<Value>({
summary: '',
details: '',
serviceIDs: props.serviceID ? [props.serviceID] : [],
Expand All @@ -60,7 +69,7 @@ export default function CreateAlertDialog(props) {
? ['Alert Info', 'Confirm']
: ['Alert Info', 'Service Selection', 'Confirm']

const onNext = () => {
const onNext = (): void => {
if (currentStep === 0 && props.serviceID) {
setStep(currentStep + 2)
} else if (currentStep < 2) {
Expand All @@ -76,8 +85,8 @@ export default function CreateAlertDialog(props) {
.map((a) => a.id)
.value()

const failedServices = allErrors(error).map((e) => ({
id: getSvcID(e.path),
const failedServices = fieldErrors(error).map((e) => ({
id: getSvcID(e.path ? e.path[1] : ''),
message: e.message,
}))

Expand Down Expand Up @@ -143,7 +152,7 @@ export default function CreateAlertDialog(props) {
<CreateAlertForm
activeStep={currentStep}
value={value}
onChange={(newValue) => setValue(newValue)}
onChange={(newValue: Value) => setValue(newValue)}
disabled={loading}
errors={fieldErrors(error)}
/>
Expand All @@ -156,8 +165,3 @@ export default function CreateAlertDialog(props) {
/>
)
}

CreateAlertDialog.propTypes = {
onClose: p.func.isRequired,
serviceID: p.string,
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react'
import { FormContainer, FormField } from '../../../forms'
import { CreateAlertInfo } from './CreateAlertInfo'
import { CreateAlertServiceSelect } from './CreateAlertServiceSelect'
import { CreateAlertConfirm } from './CreateAlertConfirm'
import { Value } from '../CreateAlertDialog'

// TODO: extend FormContainer once that file has been converted to typescript
interface CreateAlertFormProps {
activeStep: number
value: Value

errors: Error[]

onChange?: (newValue: Value) => void
disabled?: boolean

mapValue?: () => void
mapOnChangeValue?: () => void

// Enables functionality to remove an incoming value at it's index from
// an array field if the new value is falsey.
removeFalseyIdxs?: boolean
}

// TODO: remove this interface once FormContainer.js has been converted to TS
interface Error {
message: string
field: string
helpLink?: string
}

export function CreateAlertForm({
activeStep,
...otherProps
}: CreateAlertFormProps): JSX.Element {
return (
<FormContainer optionalLabels {...otherProps}>
{activeStep === 0 && <CreateAlertInfo />}
{activeStep === 1 && (
<FormField
required
render={(props) => <CreateAlertServiceSelect {...props} />}
name='serviceIDs'
/>
)}
{activeStep === 2 && <CreateAlertConfirm />}
</FormContainer>
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { gql, useMutation } from '@apollo/client'

import {
DocumentNode,
gql,
MutationFunction,
MutationResult,
useMutation,
} from '@apollo/client'
import { fieldAlias, mergeFields, mapInputVars } from '../../util/graphql'
import { GraphQLClientWithErrors } from '../../apollo'
import { Value } from './CreateAlertDialog'

interface Variable {
summary: string
details: string
serviceID: string
}

const baseMutation = gql`
mutation CreateAlertMutation($input: CreateAlertInput!) {
Expand All @@ -11,23 +23,28 @@ const baseMutation = gql`
}
`

const getAliasedMutation = (mutation, index) =>
const getAliasedMutation = (
mutation: DocumentNode,
index: string | number,
): DocumentNode =>
mapInputVars(fieldAlias(mutation, 'alias' + index), {
input: 'input' + index,
})

// useCreateAlerts will return mutation, status and a function for mapping
// field/paths from the response to the respective service ID.
export const useCreateAlerts = (value) => {
export const useCreateAlerts = (
value: Value,
): [MutationFunction, MutationResult, (alias: string | number) => string] => {
// 1. build mutation
let m = getAliasedMutation(baseMutation, 0)
for (let i = 1; i < value.serviceIDs.length; i++) {
m = mergeFields(m, getAliasedMutation(baseMutation, i))
}

// 2. build variables, alias -> service ID map
const variables = {}
const aliasIDMap = {}
const variables: { [key: string]: Variable } = {}
const aliasIDMap: { [key: string]: string } = {}
value.serviceIDs.forEach((svcID, i) => {
aliasIDMap['alias' + i] = svcID
variables[`input${i}`] = {
Expand All @@ -43,5 +60,5 @@ export const useCreateAlerts = (value) => {
client: GraphQLClientWithErrors,
})

return [mutate, status, (alias) => aliasIDMap[alias]]
return [mutate, status, (alias: string | number) => aliasIDMap[alias]]
}
3 changes: 3 additions & 0 deletions web/src/app/dialogs/FormDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ FormDialog.propTypes = {

// make dialog fill vertical space
fullHeight: p.bool,

// gets spread to material-ui
PaperProps: p.object,
}

FormDialog.defaultProps = {
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/util/errutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function fieldErrors(err?: ApolloError): FieldError[] {
}

// allErrors will return a flat list of all errors in the graphQL error.
export function allErrors(err?: ApolloError): Error[] {
export function allErrors(err?: ApolloError): (FieldError | Error)[] {
if (!err) return []
return nonFieldErrors(err).concat(fieldErrors(err))
}
Expand Down