Skip to content
This repository has been archived by the owner on Sep 18, 2023. It is now read-only.

Commit

Permalink
refactor(views/SignIn): add FormControlInput component
Browse files Browse the repository at this point in the history
  • Loading branch information
sbolel committed Aug 8, 2023
1 parent 0e5fafa commit c7f61c9
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 287 deletions.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.1.1",
"@hookform/resolvers": "^3.2.0",
"@mui/icons-material": "^5.14.1",
"@mui/lab": "^5.0.0-alpha.137",
"@mui/material": "^5.14.1",
Expand All @@ -57,7 +57,7 @@
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-hook-form": "^7.45.2",
"react-hook-form": "^7.45.4",
"react-popper": "^2.3.0",
"react-router-dom": "^6.14.2",
"uuid": "^9.0.0",
Expand Down Expand Up @@ -97,7 +97,6 @@
"@types/testing-library__dom": "^7.5.0",
"@types/testing-library__react": "^10.2.0",
"@types/uuid": "^9.0.2",
"@types/yup": "^0.32.0",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@vitejs/plugin-react-swc": "^3.3.2",
Expand Down
85 changes: 85 additions & 0 deletions src/components/forms/InputFormControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useMemo } from 'react'
import { Control, Controller, FieldValues, Path } from 'react-hook-form'
import FormControl, { FormControlProps } from '@mui/material/FormControl'
import FormHelperText, {
FormHelperTextProps,
} from '@mui/material/FormHelperText'
import InputLabel, { InputLabelProps } from '@mui/material/InputLabel'
import OutlinedInput, { OutlinedInputProps } from '@mui/material/OutlinedInput'
import toTitleCase from '@/utils/toTitleCase'

type InputFormControlProps<TFieldValues extends FieldValues = FieldValues> = {
control: Control<TFieldValues, any>
name: Path<TFieldValues>
label?: string
FormControlProps?: FormControlProps
FormHelperTextProps?: FormHelperTextProps
InputLabelProps?: InputLabelProps
InputProps?: OutlinedInputProps
}

const InputFormControl = <TFieldValues extends FieldValues>({
control,
name,
label = toTitleCase(name),
FormControlProps,
FormHelperTextProps,
InputLabelProps,
InputProps,
}: InputFormControlProps<TFieldValues>) => {
const {
slotProps: { input: inputSlotProps = {}, root: rootSlotProps = {} } = {},
} = InputProps || {}

return (
<FormControl fullWidth {...FormControlProps}>
<Controller
control={control}
name={name}
render={({
field: { value, onBlur, onChange },
fieldState: { error, invalid },

Check failure on line 41 in src/components/forms/InputFormControl.tsx

View workflow job for this annotation

GitHub Actions / test_build_release

'invalid' is declared but its value is never read.
}) => (
<>
<InputLabel
htmlFor={`${name}-input`}
id={`${name}-label`}
{...InputLabelProps}
error={!!error}
>
{label}
</InputLabel>
<OutlinedInput
aria-describedby={error && `${name}-error`}
id={`${name}-input`}
{...InputProps}
error={!!error}
label={label}
value={value}
onBlur={onBlur}
onChange={onChange}
slotProps={{
root: rootSlotProps,
input: {
'aria-invalid': !!error,
'aria-labelledby': `${name}-label`,
pattern: name || 'text',
...inputSlotProps,
},
}}
/>
<FormHelperText
id={`${name}-error`}
{...FormHelperTextProps}
error={!!error}
>
{error?.message || ' '}
</FormHelperText>
</>
)}
/>
</FormControl>
)
}

export default InputFormControl
12 changes: 8 additions & 4 deletions src/components/forms/SubmitButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react'
import SubmitButton from './SubmitButton'
import SubmitButton from '@/components/forms/SubmitButton'

describe('SubmitButton', () => {
it('renders without crashing', () => {
Expand All @@ -14,15 +14,19 @@ describe('SubmitButton', () => {
expect(buttonElement).toHaveAttribute('type', 'submit')
expect(buttonElement).toHaveClass('MuiButton-contained')
expect(buttonElement).toHaveClass('MuiButton-containedPrimary')
expect(buttonElement.textContent).toBe('Save')
expect(buttonElement.textContent).toBe('Submit')
})

it('applies passed props correctly', () => {
render(<SubmitButton color="secondary" variant="outlined" label="Submit" />)
render(
<SubmitButton color="secondary" variant="outlined">
Save
</SubmitButton>
)
const buttonElement = screen.getByRole('button')
expect(buttonElement).toHaveClass('MuiButton-outlined')
expect(buttonElement).toHaveClass('MuiButton-outlinedSecondary')
expect(buttonElement.textContent).toBe('Submit')
expect(buttonElement.textContent).toBe('Save')
})

it('is disabled when disabled prop is passed', () => {
Expand Down
14 changes: 7 additions & 7 deletions src/components/forms/SubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
* @module sbom-harbor-ui/components/forms/SubmitButton
*/
import Button, { ButtonProps } from '@mui/material/Button'
import { PropsWithChildren } from 'react'

type InputProps = {
disabled?: boolean
label?: string
} & ButtonProps

const SubmitButton = ({ label, ...props }: InputProps) => (
<Button {...props} sx={{ mt: 3, ml: 1, ...props.sx }}>
{label}
</Button>
)
const SubmitButton = ({
children = 'Submit',
...props
}: PropsWithChildren<InputProps>) => <Button {...props}>{children}</Button>

SubmitButton.defaultProps = {
color: 'primary',
label: 'Save',
label: 'Submit',
size: 'large',
type: 'submit',
variant: 'contained',
}
Expand Down
6 changes: 5 additions & 1 deletion src/views/Dashboard/Team/TeamForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,11 @@ const TeamForm = () => {
>
Cancel
</Button>
<SubmitButton disabled={isSubmitting} />
<SubmitButton
disabled={isSubmitting}
label="Save"
sx={{ mt: 3, ml: 1 }}
/>
</Box>
</Grid>
</Grid>
Expand Down
89 changes: 88 additions & 1 deletion src/views/SignIn/SignIn.components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Styled Components for the SignIn view component.
* @module sbom-harbor-ui/views/SignIn/SignIn.components
*/
import { styled } from '@mui/material/styles'
import { styled, useTheme } from '@mui/material/styles'
import Box, { BoxProps } from '@mui/material/Box'
import MuiFormControlLabel, {
FormControlLabelProps,
Expand Down Expand Up @@ -63,3 +63,90 @@ export const FormControlLabel = styled(
fontSize: theme.typography.body2.fontSize,
},
}))

export const SignInGraphic: React.FC = () => {
const {
palette: {
primary: { main: fill },
},
} = useTheme()

return (
<svg
width={47}
fill="none"
height={26}
viewBox="0 0 268 150"
xmlns="http://www.w3.org/2000/svg"
>
<rect
rx="25.1443"
width="50.2886"
height="143.953"
fill={fill}
transform="matrix(-0.865206 0.501417 0.498585 0.866841 195.571 0)"
/>
<rect
rx="25.1443"
width="50.2886"
height="143.953"
fillOpacity="0.4"
fill="url(#paint0_linear_7821_79167)"
transform="matrix(-0.865206 0.501417 0.498585 0.866841 196.084 0)"
/>
<rect
rx="25.1443"
width="50.2886"
height="143.953"
fill={fill}
transform="matrix(0.865206 0.501417 -0.498585 0.866841 173.147 0)"
/>
<rect
rx="25.1443"
width="50.2886"
height="143.953"
fill={fill}
transform="matrix(-0.865206 0.501417 0.498585 0.866841 94.1973 0)"
/>
<rect
rx="25.1443"
width="50.2886"
height="143.953"
fillOpacity="0.4"
fill="url(#paint1_linear_7821_79167)"
transform="matrix(-0.865206 0.501417 0.498585 0.866841 94.1973 0)"
/>
<rect
rx="25.1443"
width="50.2886"
height="143.953"
fill={fill}
transform="matrix(0.865206 0.501417 -0.498585 0.866841 71.7728 0)"
/>
<defs>
<linearGradient
y1="0"
x1="25.1443"
x2="25.1443"
y2="143.953"
id="paint0_linear_7821_79167"
gradientUnits="userSpaceOnUse"
>
<stop />
<stop offset="1" stopOpacity="0" />
</linearGradient>
<linearGradient
y1="0"
x1="25.1443"
x2="25.1443"
y2="143.953"
id="paint1_linear_7821_79167"
gradientUnits="userSpaceOnUse"
>
<stop />
<stop offset="1" stopOpacity="0" />
</linearGradient>
</defs>
</svg>
)
}
67 changes: 67 additions & 0 deletions src/views/SignIn/SignIn.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// src/views/SignIn/useSignIn.test.ts
import { JSXElementConstructor, ReactElement } from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import SignIn from '@/views/SignIn/SignIn'

function setup(
jsx: ReactElement<
typeof SignIn,
string | JSXElementConstructor<typeof SignIn>
>
) {
return {
user: userEvent.setup(),
...render(jsx),
}
}

jest.mock('react-router-dom', () => ({
useNavigate: jest.fn(),
}))

describe('SignIn', () => {
beforeEach(() => {
jest.clearAllMocks()
})

it('should submit correct form data in the SignIn page', async () => {
const mockSubmit = jest.fn()

jest.mock('react-hook-form', () => ({
useForm: () => ({
control: {
email: '',
password: '',
},
handleSubmit: mockSubmit,
}),
}))

// Setup to render the SignIn page
const { user } = setup(<SignIn />)

// Type the email into the email field
await user.type(
screen.getByRole('textbox', { name: /email/i }),
'[email protected]'
)

// Type the password into the password field
await user.type(
screen.getByRole('textbox', { name: /password/i }),
'password'
)

// Save the form
await user.click(screen.getByRole('button', { name: /^login$/i }))

// Wait for form submission to complete
waitFor(() => {
expect(mockSubmit).toHaveBeenCalledWith({
email: '[email protected]',
password: 'password',
})
})
})
})
Loading

0 comments on commit c7f61c9

Please sign in to comment.