diff --git a/frontend/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.styles.tsx b/frontend/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.styles.tsx index 1caa9f0fa9..4e871a8f21 100644 --- a/frontend/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.styles.tsx +++ b/frontend/src/component/admin/apiToken/ApiTokenForm/ApiTokenForm.styles.tsx @@ -1,6 +1,4 @@ import { Box, Button, styled } from '@mui/material'; -import Input from '../../../common/Input/Input.tsx'; -import GeneralSelect from '../../../common/GeneralSelect/GeneralSelect.tsx'; export const StyledContainer = styled('div')(() => ({ maxWidth: '400px', @@ -12,27 +10,6 @@ export const StyledForm = styled('form')(() => ({ height: '100%', })); -export const StyledInput = styled(Input)(({ theme }) => ({ - width: '100%', - marginBottom: theme.spacing(2), -})); - -export const StyledSelectInput = styled(GeneralSelect)(({ theme }) => ({ - marginBottom: theme.spacing(2), - minWidth: '400px', - [theme.breakpoints.down('sm')]: { - minWidth: '379px', - }, -})); - -export const StyledInputDescription = styled('p')(({ theme }) => ({ - marginBottom: theme.spacing(1), -})); - -export const StyledInputLabel = styled('label')(({ theme }) => ({ - marginBottom: theme.spacing(1), -})); - export const CancelButton = styled(Button)(({ theme }) => ({ marginLeft: theme.spacing(3), })); diff --git a/frontend/src/component/admin/apiToken/ApiTokenForm/EnvironmentSelector/EnvironmentSelector.tsx b/frontend/src/component/admin/apiToken/ApiTokenForm/EnvironmentSelector/EnvironmentSelector.tsx index 464be7eda0..161bfbafea 100644 --- a/frontend/src/component/admin/apiToken/ApiTokenForm/EnvironmentSelector/EnvironmentSelector.tsx +++ b/frontend/src/component/admin/apiToken/ApiTokenForm/EnvironmentSelector/EnvironmentSelector.tsx @@ -1,10 +1,8 @@ import { TokenType } from '../../../../../interfaces/token.ts'; import KeyboardArrowDownOutlined from '@mui/icons-material/KeyboardArrowDownOutlined'; import type React from 'react'; -import { - StyledInputDescription, - StyledSelectInput, -} from '../ApiTokenForm.styles'; +import { FormField } from 'component/common/FormField/FormField'; +import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect'; import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments'; interface IEnvironmentSelectorProps { @@ -31,21 +29,19 @@ export const EnvironmentSelector = ({ })); return ( - <> - - Which environment should the token have access to? - - + - + ); }; diff --git a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/ProjectSelector.tsx b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/ProjectSelector.tsx index 02f19b465a..7baefc924f 100644 --- a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/ProjectSelector.tsx +++ b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/ProjectSelector.tsx @@ -1,7 +1,7 @@ import { SelectProjectInput } from './SelectProjectInput/SelectProjectInput.tsx'; import { TokenType } from '../../../../../interfaces/token.ts'; import type React from 'react'; -import { StyledInputDescription } from '../ApiTokenForm.styles'; +import { FormField } from 'component/common/FormField/FormField'; import useProjects from 'hooks/api/getters/useProjects/useProjects'; import type { ApiTokenFormErrorType } from '../useApiTokenForm.ts'; import { useOptionalPathParam } from 'hooks/useOptionalPathParam'; @@ -33,10 +33,10 @@ export const ProjectSelector = ({ } return ( - <> - - Which project do you want to give access to? - + clearErrors('projects')} /> - + ); }; diff --git a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.test.tsx b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.test.tsx index 61d91c5dbe..9bb3bac3e8 100644 --- a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.test.tsx +++ b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.test.tsx @@ -54,7 +54,7 @@ describe('SelectProjectInput', () => { screen.getByLabelText(/all current and future projects/i), ).not.toBeChecked(); - expect(screen.getByLabelText('Projects')).toBeEnabled(); + expect(screen.getByRole('combobox')).toBeEnabled(); await user.click(screen.getByTestId('select-all-projects')); @@ -62,7 +62,7 @@ describe('SelectProjectInput', () => { screen.getByLabelText(/all current and future projects/i), ).toBeChecked(); - expect(screen.getByLabelText('Projects')).toBeDisabled(); + expect(screen.getByRole('combobox')).toBeDisabled(); }); it('renders with autocomplete enabled if default value is not a wildcard', () => { @@ -84,7 +84,7 @@ describe('SelectProjectInput', () => { it('selects and deselects all options', async () => { const user = userEvent.setup(); render(); - await user.click(screen.getByLabelText('Projects')); + await user.click(screen.getByRole('combobox')); let button = screen.getByRole('button', { name: /select all/i, @@ -118,7 +118,7 @@ describe('SelectProjectInput', () => { ]} />, ); - await user.click(screen.getByLabelText('Projects')); + await user.click(screen.getByRole('combobox')); const button = screen.queryByRole('button', { name: /select all/i, @@ -141,7 +141,7 @@ describe('SelectProjectInput', () => { ]} />, ); - const input = await screen.findByLabelText('Projects'); + const input = await screen.findByRole('combobox'); await user.type(input, 'alp'); await waitFor(() => { diff --git a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx index 420627190c..d938d72775 100644 --- a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx +++ b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx @@ -45,6 +45,8 @@ export interface ISelectProjectInputProps { onChange: (value: string[]) => void; onFocus?: () => void; error?: string; + id?: string; + 'aria-describedby'?: string; } export const SelectProjectInput: FC = ({ @@ -54,6 +56,8 @@ export const SelectProjectInput: FC = ({ disabled, error, onFocus, + id, + 'aria-describedby': ariaDescribedby, }) => { const [projects, setProjects] = useState( typeof defaultValue === 'string' ? [defaultValue] : defaultValue, @@ -123,10 +127,16 @@ export const SelectProjectInput: FC = ({ const renderInput = (params: AutocompleteRenderInputParams) => ( = ({ ); return ( - - + + = ({ /> { return ( - <> - - What would you like to call this token? - - + setTokenName(e.target.value)} - label='Token name' error={errors.tokenName !== undefined} - errorText={errors.tokenName} + helperText={errors.tokenName} onFocus={() => clearErrors('tokenName')} autoFocus /> - + ); }; diff --git a/frontend/src/component/admin/apiToken/ApiTokenForm/TokenTypeSelector/TokenTypeSelector.tsx b/frontend/src/component/admin/apiToken/ApiTokenForm/TokenTypeSelector/TokenTypeSelector.tsx index 0d37e8457c..2e9f954771 100644 --- a/frontend/src/component/admin/apiToken/ApiTokenForm/TokenTypeSelector/TokenTypeSelector.tsx +++ b/frontend/src/component/admin/apiToken/ApiTokenForm/TokenTypeSelector/TokenTypeSelector.tsx @@ -1,12 +1,11 @@ -import { StyledContainer, StyledInputLabel } from '../ApiTokenForm.styles'; import { Box, - FormControl, FormControlLabel, Radio, RadioGroup, Typography, } from '@mui/material'; +import { FormField } from 'component/common/FormField/FormField'; import type { TokenType } from 'interfaces/token'; export type SelectOption = { @@ -27,53 +26,51 @@ export const TokenTypeSelector = ({ apiTokenTypes, }: ITokenTypeSelectorProps) => { return ( - - - - What do you want to connect? - - setType(value as TokenType)} - > - {apiTokenTypes.map( - ({ key, label, title, enabled: hasAccess }) => ( - - } - label={ + + setType(value as TokenType)} + > + {apiTokenTypes.map( + ({ key, label, title, enabled: hasAccess }) => ( + + } + label={ + - - {label} - - {title} - - + {label} + + {title} + - } - /> - ), - )} - - - + + } + /> + ), + )} + + ); }; diff --git a/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx b/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx index 94e6729d36..d22cbee854 100644 --- a/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx +++ b/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx @@ -121,7 +121,7 @@ export const AutoCreateForm = ({ style={{ width: '400px' }} rows={2} variant='outlined' - size='small' + size='large' /> diff --git a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx index 5220b3e6d8..653b585beb 100644 --- a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx +++ b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx @@ -181,7 +181,7 @@ export const OidcAuth = () => { disabled={!data.enabled || oidcConfiguredThroughEnv} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' /> @@ -199,7 +199,7 @@ export const OidcAuth = () => { disabled={!data.enabled || oidcConfiguredThroughEnv} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' required /> @@ -220,7 +220,7 @@ export const OidcAuth = () => { disabled={!data.enabled || oidcConfiguredThroughEnv} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' required /> @@ -315,7 +315,7 @@ export const OidcAuth = () => { disabled={!data.enabled || oidcConfiguredThroughEnv} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' /> @@ -338,7 +338,7 @@ export const OidcAuth = () => { disabled={!data.enabled} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' placeholder='custom_scope1 custom_scope2' /> diff --git a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx index b72e898da6..ba125f9d9a 100644 --- a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx +++ b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx @@ -165,7 +165,7 @@ export const SamlAuth = () => { disabled={!data.enabled || samlConfiguredThroughEnv} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' required /> @@ -187,7 +187,7 @@ export const SamlAuth = () => { disabled={!data.enabled || samlConfiguredThroughEnv} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' required /> @@ -212,7 +212,7 @@ export const SamlAuth = () => { rows={14} maxRows={14} variant='outlined' - size='small' + size='large' required slotProps={{ input: { @@ -243,7 +243,7 @@ export const SamlAuth = () => { disabled={!data.enabled || samlConfiguredThroughEnv} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' /> @@ -269,7 +269,7 @@ export const SamlAuth = () => { rows={14} maxRows={14} variant='outlined' - size='small' + size='large' slotProps={{ input: { style: { diff --git a/frontend/src/component/admin/auth/SsoGroupSettings.tsx b/frontend/src/component/admin/auth/SsoGroupSettings.tsx index 5e33f44a13..d3154c784b 100644 --- a/frontend/src/component/admin/auth/SsoGroupSettings.tsx +++ b/frontend/src/component/admin/auth/SsoGroupSettings.tsx @@ -79,7 +79,7 @@ export const SsoGroupSettings = ({ disabled={!data.enableGroupSyncing || disabled} style={{ width: '400px' }} variant='outlined' - size='small' + size='large' required /> diff --git a/frontend/src/component/admin/banners/BannerModal/BannerForm.tsx b/frontend/src/component/admin/banners/BannerModal/BannerForm.tsx index 4645548e3f..bc52a6efeb 100644 --- a/frontend/src/component/admin/banners/BannerModal/BannerForm.tsx +++ b/frontend/src/component/admin/banners/BannerModal/BannerForm.tsx @@ -195,7 +195,7 @@ export const BannerForm = ({ What type of banner is it? setVariant(variant as BannerVariant) @@ -208,7 +208,7 @@ export const BannerForm = ({ What icon should be displayed on the banner? { setIconOption(iconOption as IconOption); @@ -312,7 +312,7 @@ export const BannerForm = ({ What action should be available in the banner? { setLinkOption(linkOption as LinkOption); diff --git a/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx b/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx index 2dc4d83daf..d60f64bb3f 100644 --- a/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx +++ b/frontend/src/component/admin/groups/GroupForm/GroupFormUsersSelect/GroupFormUsersSelect.tsx @@ -21,6 +21,10 @@ const StyledOption = styled('div')(({ theme }) => ({ const StyledTags = styled('div')(({ theme }) => ({ paddingLeft: theme.spacing(1), + minWidth: 0, + overflow: 'hidden', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', })); const StyledGroupFormUsersSelect = styled('div')(({ theme }) => ({ @@ -112,7 +116,7 @@ export const GroupFormUsersSelect: FC = ({ { } disabled={loading || saving} fullWidth - size='small' + size='large' type={hasCredentials && !showUrl ? 'password' : 'text'} slotProps={{ input: { diff --git a/frontend/src/component/admin/license/LicenseForm.tsx b/frontend/src/component/admin/license/LicenseForm.tsx index 5eb1e7b935..63a4287bb3 100644 --- a/frontend/src/component/admin/license/LicenseForm.tsx +++ b/frontend/src/component/admin/license/LicenseForm.tsx @@ -148,7 +148,7 @@ export const LicenseForm = () => { value={token} style={{ width: '100%' }} variant='outlined' - size='small' + size='large' multiline rows={6} required diff --git a/frontend/src/component/admin/users/AccessOverview/AccessOverviewSelect.tsx b/frontend/src/component/admin/users/AccessOverview/AccessOverviewSelect.tsx index f4ef50a2d1..27bed33cb3 100644 --- a/frontend/src/component/admin/users/AccessOverview/AccessOverviewSelect.tsx +++ b/frontend/src/component/admin/users/AccessOverview/AccessOverviewSelect.tsx @@ -22,7 +22,7 @@ export const AccessOverviewSelect = ({ renderInput={(params) => ( )} - size='small' + size='large' fullWidth {...rest} /> diff --git a/frontend/src/component/admin/users/AccessOverview/ProjectAccess/ProjectAccessSection.tsx b/frontend/src/component/admin/users/AccessOverview/ProjectAccess/ProjectAccessSection.tsx index 64f12c09c4..70691bf959 100644 --- a/frontend/src/component/admin/users/AccessOverview/ProjectAccess/ProjectAccessSection.tsx +++ b/frontend/src/component/admin/users/AccessOverview/ProjectAccess/ProjectAccessSection.tsx @@ -98,7 +98,7 @@ export const ProjectAccessSection = ({ setSelectedProjectIds(e.target.value as string[]) } renderValue={() => projectSelectorLabel} - size='small' + size='large' sx={{ minWidth: 150, maxWidth: 200, flexShrink: 0 }} displayEmpty MenuProps={{ @@ -138,7 +138,7 @@ export const ProjectAccessSection = ({ } }} renderValue={() => environmentSelectorLabel} - size='small' + size='large' sx={{ minWidth: 150, maxWidth: 200, flexShrink: 0 }} displayEmpty MenuProps={{ diff --git a/frontend/src/component/admin/users/UsersList/AccessRequestsTable/RoleSelectCell.tsx b/frontend/src/component/admin/users/UsersList/AccessRequestsTable/RoleSelectCell.tsx index bcbd70cbd9..414c34777a 100644 --- a/frontend/src/component/admin/users/UsersList/AccessRequestsTable/RoleSelectCell.tsx +++ b/frontend/src/component/admin/users/UsersList/AccessRequestsTable/RoleSelectCell.tsx @@ -21,7 +21,7 @@ export const RoleSelectCell = ({ }: IRoleSelectCellProps) => ( onChange(e.target.value as number)} variant='outlined' diff --git a/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx b/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx index 991c60332b..0efb6b9025 100644 --- a/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx +++ b/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx @@ -122,7 +122,7 @@ const ChangePassword = ({ helperText={error} onChange={updateField} variant='outlined' - size='small' + size='large' /> value.label} disabled={disabled} - size='small' + size='large' multiple /> diff --git a/frontend/src/component/common/DateTimePicker/DateTimePicker.tsx b/frontend/src/component/common/DateTimePicker/DateTimePicker.tsx index f359f8881d..71bf4c0068 100644 --- a/frontend/src/component/common/DateTimePicker/DateTimePicker.tsx +++ b/frontend/src/component/common/DateTimePicker/DateTimePicker.tsx @@ -42,7 +42,7 @@ export const DateTimePicker = ({ return ( = ({ + } diff --git a/frontend/src/component/common/FormField/FormField.test.tsx b/frontend/src/component/common/FormField/FormField.test.tsx new file mode 100644 index 0000000000..cf53bd88a5 --- /dev/null +++ b/frontend/src/component/common/FormField/FormField.test.tsx @@ -0,0 +1,52 @@ +import { screen } from '@testing-library/react'; +import { render } from 'utils/testRenderer'; +import { expect, test } from 'vitest'; +import { FormField } from './FormField.tsx'; + +test('associates the static label with the control', () => { + render( + + + , + ); + + // getByLabelText resolves the