Skip to content

TCA-645 Example Complex Lint Fixes -> TCA-499_eslint #403

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

Merged
merged 4 commits into from
Nov 9, 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
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"dependencies": {
"@datadog/browser-logs": "^4.21.2",
"@heroicons/react": "^1.0.6",
"@stripe/react-stripe-js": "1.13.0",
"@stripe/stripe-js": "1.41.0",
"apexcharts": "^3.36.0",
"axios": "^1.1.2",
"browser-cookies": "^1.2.0",
Expand Down Expand Up @@ -78,8 +80,6 @@
"@babel/preset-typescript": "^7.18.6",
"@babel/runtime": "^7.19.4",
"@cypress/code-coverage": "^3.10.0",
"@stripe/react-stripe-js": "1.13.0",
"@stripe/stripe-js": "1.41.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
Expand Down Expand Up @@ -144,6 +144,7 @@
"start-server-and-test": "^1.14.0",
"style-loader": "^3.3.1",
"systemjs-webpack-interop": "^2.3.7",
"typed-scss-modules": "^7.0.1",
"webpack": "^4.41.2",
"webpack-cli": "^4.10.0",
"webpack-config-single-spa-react": "^4.0.3",
Expand Down
5 changes: 0 additions & 5 deletions src-ts/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ declare module '*.html' {

declare module '*.pdf'

declare module '*.scss' {
const scssFile: { [style: string]: any }
export = scssFile
}

declare module '*.svg' {
import * as React from 'react'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, useContext } from 'react'
import { FC, useCallback, useContext } from 'react'
import { Location, useLocation } from 'react-router-dom'

import {
Expand All @@ -14,10 +14,13 @@ const ProfileNotLoggedIn: FC<{}> = () => {
const routeData: RouteContextData = useContext(routeContext)
const location: Location = useLocation()

function signUp(): void {
const signUpHandler: () => void = useCallback(() => {
const signupUrl: string = routeData.getSignupUrl(location.pathname, routeData.toolsRoutes)
window.location.href = signupUrl
}
}, [
location.pathname,
routeData,
])

return (
<>
Expand All @@ -34,7 +37,7 @@ const ProfileNotLoggedIn: FC<{}> = () => {
label='Sign Up'
size='md'
tabIndex={-1}
onClick={signUp}
onClick={signUpHandler}
/>
</>
)
Expand Down
27 changes: 20 additions & 7 deletions src-ts/lib/breadcrumb/breadcrumb-item/BreadcrumbItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,25 @@ interface BreadcrumbItemProps {
item: BreadcrumbItemModel
}

const BreadcrumbItem: FC<BreadcrumbItemProps> = (props: BreadcrumbItemProps) => (
<li key={props.index} onClick={() => props.item.onClick?.(props.item)}>
<Link className={props.item.isElipsis && styles.elipsis} to={props.item.url}>
{props.item.name}
</Link>
</li>
)
const BreadcrumbItem: FC<BreadcrumbItemProps> = (props: BreadcrumbItemProps) => {

function onClick(): void {
props.item.onClick?.(props.item)
}

return (
<li
key={props.index}
onClick={() => onClick()}
>
<Link
className={props.item.isElipsis && styles.elipsis}
to={props.item.url}
>
{props.item.name}
</Link>
</li>
)
}

export default BreadcrumbItem
20 changes: 13 additions & 7 deletions src-ts/lib/button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const Button: FC<ButtonProps> = (props: ButtonProps) => {
className={classes}
href={props.url}
onClick={clickHandler}
rel={props.rel ?? props.target === '_blank' ? 'noreferrer' : ''}
rel={props.rel || props.target === '_blank' ? 'noreferrer' : ''}
role='button'
tabIndex={props.tabIndex}
title={props.title}
Expand Down Expand Up @@ -104,14 +104,20 @@ const Button: FC<ButtonProps> = (props: ButtonProps) => {
)
}

function getButtonClasses(props: ButtonProps): string {
function getButtonClasses(
className: string,
buttonStyle: ButtonStyle,
size: ButtonSize,
disable?: boolean,
hidden?: boolean,
): string {
const classes: string = classNames(
'button',
props.className,
props.buttonStyle || 'primary',
`button-${props.size || 'md'}`,
!!props.disable ? 'disabled' : undefined,
props.hidden ? 'hidden' : undefined,
className,
buttonStyle || 'primary',
`button-${size || 'md'}`,
{ disabled: disable },
{ hidden },
)
return classes
}
Expand Down
14 changes: 8 additions & 6 deletions src-ts/lib/form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import {
formOnChange,
formOnReset,
formOnSubmitAsync,
formValidateForm,
FormValue,
} from './form-functions'
import { validateForm } from './form-functions/form.functions'
import { FormGroups } from './form-groups'
import styles from './Form.module.scss'

Expand All @@ -41,8 +42,8 @@ interface FormProps<ValueType, RequestType> {
readonly shouldDisableButton?: (isPrimaryGroup: boolean, index: number) => boolean
}

const Form: <ValueType extends any, RequestType extends any>(props: FormProps<ValueType, RequestType>) => JSX.Element
= <ValueType extends any, RequestType extends any>(props: FormProps<ValueType, RequestType>) => {
const Form: <ValueType extends FormValue, RequestType extends FormValue>(props: FormProps<ValueType, RequestType>) => JSX.Element
= <ValueType extends FormValue, RequestType extends FormValue>(props: FormProps<ValueType, RequestType>) => {

const [formDef, setFormDef]: [FormDefinition, Dispatch<SetStateAction<FormDefinition>>]
= useState<FormDefinition>({ ...props.formDef })
Expand All @@ -65,7 +66,7 @@ const Form: <ValueType extends any, RequestType extends any>(props: FormProps<Va
return
}

validateForm(formRef.current?.elements, 'initial', inputs)
formValidateForm(formRef.current?.elements, 'initial', inputs)
checkIfFormIsValid(inputs)
}, [
formRef,
Expand All @@ -78,7 +79,7 @@ const Form: <ValueType extends any, RequestType extends any>(props: FormProps<Va
}

// so we repeat the validation when formValues changes, to support the parent component's async data loading
validateForm(formRef.current?.elements, 'change', inputs)
formValidateForm(formRef.current?.elements, 'change', inputs)
checkIfFormIsValid(inputs)
}, [
props.formValues,
Expand All @@ -90,6 +91,7 @@ const Form: <ValueType extends any, RequestType extends any>(props: FormProps<Va
if (props.resetFormOnUnmount) {
onReset()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

function checkIfFormIsValid(formInputFields: Array<FormInputModel>): void {
Expand Down Expand Up @@ -167,7 +169,7 @@ const Form: <ValueType extends any, RequestType extends any>(props: FormProps<Va
{...button}
key={button.label || `button-${index}`}
disable={disabled}
tabIndex={button.notTabble ? -1 : index + (inputs ? inputs.length : 0) + (formDef.tabIndexStart || 0)}
tabIndex={button.notTabble ? -1 : index + (inputs?.length || 0) + (formDef.tabIndexStart || 0)}
/>
)
})
Expand Down
5 changes: 5 additions & 0 deletions src-ts/lib/form/form-functions/form-value.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { InputValue } from '../form-input.model'

export interface FormValue {
[propertyName: string]: InputValue,
}
96 changes: 67 additions & 29 deletions src-ts/lib/form/form-functions/form.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import { toast } from 'react-toastify'

import { FormAction, FormDefinition } from '../form-definition.model'
import { FormGroup } from '../form-group.model'
import { FormInputModel } from '../form-input.model'
import { FormInputModel, InputValue } from '../form-input.model'

export function getInputElement(formElements: HTMLFormControlsCollection, fieldName: string): HTMLInputElement {
import { FormValue } from './form-value.model'

export type ValidationEvent = 'blur' | 'change' | 'submit' | 'initial'

export function getInputElement(formElements: HTMLFormControlsCollection, fieldName: string): HTMLInputElement | undefined {
return formElements.namedItem(fieldName) as HTMLInputElement
}

Expand All @@ -29,29 +33,43 @@ export function getInputModel(inputs: ReadonlyArray<FormInputModel>, fieldName:
return formField
}

export function initializeValues<T>(inputs: Array<FormInputModel>, formValues?: T): void {
inputs
export function initializeValues<T extends FormValue>(
inputs: Array<FormInputModel>,
formValues?: T,
): void {

const filteredInputs: ReadonlyArray<FormInputModel> = inputs
.filter(input => !input.dirty && !input.touched)
.forEach(input => {
if (input.type === 'checkbox') {
input.value = input.checked || false
} else {
input.value = !!(formValues as any)?.hasOwnProperty(input.name)
? (formValues as any)[input.name]

for (const input of filteredInputs) {
if (input.type === 'checkbox') {
input.value = input.checked || false
} else {
input.value
= !!formValues && Object.prototype.hasOwnProperty.call(formValues, input.name)
? (formValues as { [id: string]: InputValue })[input.name]
: undefined
}
})
}
}
}

export function onBlur<T>(event: FormEvent<HTMLInputElement | HTMLTextAreaElement>, inputs: ReadonlyArray<FormInputModel>, formValues?: T): void {
export function onBlur<T extends FormValue>(
event: FormEvent<HTMLInputElement | HTMLTextAreaElement>,
inputs: ReadonlyArray<FormInputModel>,
formValues?: T,
): void {
handleFieldEvent<T>(event.target as HTMLInputElement | HTMLTextAreaElement, inputs, 'blur', formValues)
}

export function onChange<T>(event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, inputs: ReadonlyArray<FormInputModel>, formValues?: T): void {
export function onChange<T extends FormValue>(
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
inputs: ReadonlyArray<FormInputModel>,
formValues?: T,
): void {
handleFieldEvent<T>(event.target as HTMLInputElement | HTMLTextAreaElement, inputs, 'change', formValues)
}

export function onReset(inputs: ReadonlyArray<FormInputModel>, formValue?: any): void {
export function onReset<T extends FormValue>(inputs: ReadonlyArray<FormInputModel>, formValue?: T): void {
inputs?.forEach(inputDef => {
const typeCastedInput: FormInputModel = inputDef as FormInputModel
typeCastedInput.dirty = false
Expand All @@ -61,7 +79,7 @@ export function onReset(inputs: ReadonlyArray<FormInputModel>, formValue?: any):
})
}

export async function onSubmitAsync<T>(
export async function onSubmitAsync<T extends FormValue>(
action: FormAction,
event: FormEvent<HTMLFormElement>,
formDef: FormDefinition,
Expand Down Expand Up @@ -93,13 +111,14 @@ export async function onSubmitAsync<T>(
}

// set the properties for the updated T value
const updatedValue: FormValue = { ...formValue }
inputs
.forEach(field => {
(formValue as any)[field.name] = field.value
updatedValue[field.name] = field.value
})

// if there are no dirty fields, don't actually perform the save
const savePromise: Promise<void> = !dirty ? Promise.resolve() : save(formValue)
const savePromise: Promise<void> = !dirty ? Promise.resolve() : save(updatedValue as T)

return savePromise
.then(() => {
Expand All @@ -109,13 +128,18 @@ export async function onSubmitAsync<T>(
toast.success(safeSuccessMessage)
onSuccess?.()
})
.catch(error => Promise.reject(error.response?.data?.result?.content || error.message || error))
.catch(error => Promise.reject(error.response?.data?.result?.content ?? error.message ?? error))
}

function handleFieldEvent<T>(input: HTMLInputElement | HTMLTextAreaElement, inputs: ReadonlyArray<FormInputModel>, event: 'blur' | 'change', formValues?: T): void {
function handleFieldEvent<T extends FormValue>(
input: HTMLInputElement | HTMLTextAreaElement,
inputs: ReadonlyArray<FormInputModel>,
event: 'blur' | 'change',
formValues?: T,
): void {

// set the dirty and touched flags on the field
const originalValue: string | undefined = (formValues as any)?.[input.name]
const originalValue: InputValue = formValues?.[input.name]

const inputDef: FormInputModel = getInputModel(inputs, input.name)

Expand Down Expand Up @@ -153,7 +177,11 @@ function handleFieldEvent<T>(input: HTMLInputElement | HTMLTextAreaElement, inpu
})
}

function validateField(formInputDef: FormInputModel, formElements: HTMLFormControlsCollection, event: 'blur' | 'change' | 'submit' | 'initial'): void {
function validateField(
formInputDef: FormInputModel,
formElements: HTMLFormControlsCollection,
event: 'blur' | 'change' | 'submit' | 'initial',
): void {

// this is the error the field had before the event took place
const previousError: string | undefined = formInputDef.error
Expand All @@ -162,7 +190,11 @@ function validateField(formInputDef: FormInputModel, formElements: HTMLFormContr
?.forEach(validatorFunction => {

// if the next error is the same as the previous error, then no need to do anything
const nextError: string | undefined = validatorFunction.validator(formInputDef.value, formElements, validatorFunction.dependentField)
const nextError: string | undefined = validatorFunction.validator(
formInputDef.value,
formElements,
validatorFunction.dependentField,
)

if (previousError === nextError) {
return
Expand All @@ -186,13 +218,19 @@ function validateField(formInputDef: FormInputModel, formElements: HTMLFormContr
})
}

export type ValidationEvent = 'blur' | 'change' | 'submit' | 'initial'
export function validateForm(
formElements: HTMLFormControlsCollection,
event: ValidationEvent,
inputs: ReadonlyArray<FormInputModel>,
): boolean {

let hasError: boolean = false

export function validateForm(formElements: HTMLFormControlsCollection, event: ValidationEvent, inputs: ReadonlyArray<FormInputModel>): boolean {
const errors: ReadonlyArray<FormInputModel> = inputs?.filter(formInputDef => {
for (const formInputDef of inputs) {
formInputDef.dirty = formInputDef.dirty || event === 'submit'
validateField(formInputDef, formElements, event)
return !!formInputDef.error
})
return !errors.length
hasError = hasError || !!formInputDef.error
}

return !hasError
}
2 changes: 2 additions & 0 deletions src-ts/lib/form/form-functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export {
onReset as formOnReset,
onSubmitAsync as formOnSubmitAsync,
getFormInputFields as formGetInputFields,
validateForm as formValidateForm,
} from './form.functions'
export * from './form-value.model'
Loading