From 66b47fcf50c5a62df4a8bd9edf395a119a9488c4 Mon Sep 17 00:00:00 2001
From: Macgregor Aubertin-Young
<108430771+mauberti-bc@users.noreply.github.com>
Date: Tue, 10 Dec 2024 17:19:15 -0800
Subject: [PATCH 1/2] Remove alert banners from animal and telemetry page
(#1449)
* Remove alert banners from animal and telemetry page
---
app/src/features/surveys/animals/AnimalPage.tsx | 3 ---
app/src/features/surveys/telemetry/TelemetryPage.tsx | 3 ---
2 files changed, 6 deletions(-)
diff --git a/app/src/features/surveys/animals/AnimalPage.tsx b/app/src/features/surveys/animals/AnimalPage.tsx
index b8418906ad..07058fdb2d 100644
--- a/app/src/features/surveys/animals/AnimalPage.tsx
+++ b/app/src/features/surveys/animals/AnimalPage.tsx
@@ -1,11 +1,9 @@
import CircularProgress from '@mui/material/CircularProgress';
import Stack from '@mui/material/Stack';
import Box from '@mui/system/Box';
-import { SystemAlertBanner } from 'features/alert/banner/SystemAlertBanner';
import { useBiohubApi } from 'hooks/useBioHubApi';
import { useAnimalPageContext, useProjectContext, useSurveyContext } from 'hooks/useContext';
import useDataLoader from 'hooks/useDataLoader';
-import { SystemAlertBannerEnum } from 'interfaces/useAlertApi.interface';
import { useEffect } from 'react';
import { AnimalHeader } from './AnimalHeader';
import { AnimalListContainer } from './list/AnimalListContainer';
@@ -58,7 +56,6 @@ export const SurveyAnimalPage = () => {
survey_id={surveyContext.surveyId}
survey_name={surveyContext.surveyDataLoader.data.surveyData.survey_details.survey_name}
/>
-
{
@@ -74,7 +72,6 @@ export const TelemetryPage = () => {
survey_id={surveyContext.surveyId}
survey_name={surveyContext.surveyDataLoader.data.surveyData.survey_details.survey_name}
/>
-
{/* Telematry List */}
From 4c79a599f41c39b7c51d768f32a3d0879d5934e3 Mon Sep 17 00:00:00 2001
From: AMEIJER1 <141779443+AMEIJER1@users.noreply.github.com>
Date: Tue, 10 Dec 2024 18:49:33 -0800
Subject: [PATCH 2/2] Add Tooltips to Form Fields (#1427)
* example tooltip next to focal species form field on survey form
* permits and survey participation
* tooltipping ctd
* tooltip ctd1
* more tooltipping
* tooltip testing
* helpbutton for typography stack
* help button stack for box items
* fixing help button formatting
* tooltipping on edit survey page
* make fix
* make fix
* make fix
* wip: update text
* move tooltips into autocompletes and add helptext
* update survey form tooltips
* Fix app tests
---------
Co-authored-by: Macgregor Aubertin-Young
Co-authored-by: Macgregor Aubertin-Young <108430771+mauberti-bc@users.noreply.github.com>
Co-authored-by: Nick Phura
---
.../components/buttons/HelpButtonStack.tsx | 19 +++
.../components/buttons/HelpButtonTooltip.tsx | 35 ++----
.../components/fields/AutocompleteField.tsx | 4 +
app/src/components/fields/CustomTextField.tsx | 34 +++++-
.../fields/MultiAutocompleteField.tsx | 11 ++
.../MultiAutocompleteFieldVariableSize.tsx | 11 ++
.../components/fields/StartEndDateFields.tsx | 13 +-
.../fields/SystemUserAutocompleteField.tsx | 49 ++++++--
.../components/SpeciesAutocompleteField.tsx | 11 ++
.../components/ProjectDetailsForm.tsx | 1 +
.../components/ProjectObjectivesForm.test.tsx | 10 +-
.../components/ProjectObjectivesForm.tsx | 1 +
.../components/ProjectUserForm.test.tsx | 4 +-
.../projects/components/ProjectUserForm.tsx | 103 ++--------------
.../components/AnimalFormContainer.tsx | 3 +-
.../ecological-units/EcologicalUnitsForm.tsx | 41 ++++---
.../AnimalGeneralInformationForm.tsx | 87 ++++++++------
.../components/agreements/AgreementsForm.tsx | 14 ++-
.../agreements/ProprietaryDataForm.tsx | 9 +-
.../GeneralInformationForm.tsx | 2 +
.../locations/SurveyAreaMapControl.tsx | 11 +-
.../participants/SurveyUserForm.test.tsx | 4 +-
.../participants/SurveyUserForm.tsx | 112 +++---------------
.../SamplingStrategyForm.tsx | 13 +-
.../SurveySiteSelectionForm.tsx | 1 +
.../species/components/FocalSpeciesForm.tsx | 1 +
.../features/surveys/edit/EditSurveyForm.tsx | 17 ++-
.../methods/SamplingMethodFormContainer.tsx | 7 +-
.../site-groupings/SamplingBlockForm.tsx | 7 +-
.../site-groupings/SamplingStratumForm.tsx | 7 +-
30 files changed, 317 insertions(+), 325 deletions(-)
create mode 100644 app/src/components/buttons/HelpButtonStack.tsx
diff --git a/app/src/components/buttons/HelpButtonStack.tsx b/app/src/components/buttons/HelpButtonStack.tsx
new file mode 100644
index 0000000000..9e7bd7f895
--- /dev/null
+++ b/app/src/components/buttons/HelpButtonStack.tsx
@@ -0,0 +1,19 @@
+import Stack, { StackProps } from '@mui/material/Stack';
+import HelpButtonTooltip from 'components/buttons/HelpButtonTooltip';
+import { PropsWithChildren } from 'react';
+
+interface IHelpButtonStackProps extends StackProps {
+ helpText: string;
+}
+
+const HelpButtonStack = (props: PropsWithChildren) => {
+ const { helpText, children, ...stackProps } = props;
+ return (
+
+ {children}
+
+
+ );
+};
+
+export default HelpButtonStack;
diff --git a/app/src/components/buttons/HelpButtonTooltip.tsx b/app/src/components/buttons/HelpButtonTooltip.tsx
index a04b5d66d3..332779ed66 100644
--- a/app/src/components/buttons/HelpButtonTooltip.tsx
+++ b/app/src/components/buttons/HelpButtonTooltip.tsx
@@ -4,11 +4,10 @@ import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import Zoom from '@mui/material/Zoom';
-import { ReactNode } from 'react';
+import { useState } from 'react';
interface HelpButtonTooltipProps {
content: string;
- children?: ReactNode;
iconSx?: object;
}
@@ -19,31 +18,23 @@ interface HelpButtonTooltipProps {
* @param {HelpButtonTooltipProps}
* @return {*}
*/
-//TODO: Update positioning of the tooltip to be more dynamic (Add Animal form)
-const HelpButtonTooltip = ({ content, children, iconSx }: HelpButtonTooltipProps) => {
+const HelpButtonTooltip = ({ content, iconSx }: HelpButtonTooltipProps) => {
+ const [renderTooltip, setRenderTooltip] = useState(false);
+
return (
- {children}
+ {/* Tooltip should always be there, but only show when hovering */}
+ {/* IconButton is always displayed */}
setRenderTooltip(true)}
+ onMouseLeave={() => setRenderTooltip(false)}
sx={{
- position: 'absolute',
- top: '8px',
- right: '8px',
color: '#38598A',
...iconSx
}}>
diff --git a/app/src/components/fields/AutocompleteField.tsx b/app/src/components/fields/AutocompleteField.tsx
index 5e39736b3b..2324834ffc 100644
--- a/app/src/components/fields/AutocompleteField.tsx
+++ b/app/src/components/fields/AutocompleteField.tsx
@@ -4,6 +4,7 @@ import CircularProgress from '@mui/material/CircularProgress';
import grey from '@mui/material/colors/grey';
import TextField, { TextFieldProps } from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
+import HelpButtonTooltip from 'components/buttons/HelpButtonTooltip';
import { useFormikContext } from 'formik';
import get from 'lodash-es/get';
import { SyntheticEvent } from 'react';
@@ -27,6 +28,7 @@ export interface IAutocompleteField {
showValue?: boolean;
disableClearable?: boolean;
optionFilter?: 'value' | 'label'; // used to filter existing/ set data for the AutocompleteField, defaults to value in getExistingValue function
+ helpText?: string;
getOptionDisabled?: (option: IAutocompleteFieldOption) => boolean;
onChange?: (event: SyntheticEvent, option: IAutocompleteFieldOption | null) => void;
renderOption?: (params: React.HTMLAttributes, option: IAutocompleteFieldOption) => React.ReactNode;
@@ -64,6 +66,7 @@ const AutocompleteField = (props: IAutocompleteField<
blurOnSelect
handleHomeEndKeys
id={props.id}
+ fullWidth
data-testid={props.id}
value={getExistingValue(get(values, props.name))}
options={props.options}
@@ -137,6 +140,7 @@ const AutocompleteField = (props: IAutocompleteField<
endAdornment: (
<>
{props.loading ? : null}
+ {props.helpText && }
{params.InputProps.endAdornment}
>
)
diff --git a/app/src/components/fields/CustomTextField.tsx b/app/src/components/fields/CustomTextField.tsx
index 4f50ed4ae3..04c83c142a 100644
--- a/app/src/components/fields/CustomTextField.tsx
+++ b/app/src/components/fields/CustomTextField.tsx
@@ -1,4 +1,6 @@
+import InputAdornment from '@mui/material/InputAdornment';
import TextField from '@mui/material/TextField';
+import HelpButtonTooltip from 'components/buttons/HelpButtonTooltip';
import { useFormikContext } from 'formik';
import get from 'lodash-es/get';
export interface ICustomTextField {
@@ -30,6 +32,13 @@ export interface ICustomTextField {
* @memberof ICustomTextField
*/
maxLength?: number;
+ /**
+ * Optional help text to be displayed in a tooltip
+ *
+ * @type {string}
+ * @memberof ICustomTextField
+ */
+ helpText?: string;
/*
* TODO: Needed fix: Add correct hardcoded type
* Note: TextFieldProps causes build compile issue
@@ -41,7 +50,7 @@ export interface ICustomTextField {
const CustomTextField = (props: React.PropsWithChildren) => {
const { touched, errors, values, handleChange, handleBlur } = useFormikContext();
- const { name, label, other, placeholder } = props;
+ const { name, label, other, placeholder, helpText } = props;
return (
) => {
label={label}
id={name}
placeholder={placeholder}
- inputProps={{ 'data-testid': name, maxLength: props.maxLength || undefined }} // targets the internal input rather than the react component
+ inputProps={{
+ 'data-testid': name,
+ maxLength: props.maxLength || undefined
+ }}
+ InputProps={{
+ endAdornment: helpText && (
+
+
+
+ )
+ }}
onChange={handleChange}
onBlur={handleBlur}
variant="outlined"
@@ -57,6 +76,17 @@ const CustomTextField = (props: React.PropsWithChildren) => {
fullWidth={true}
error={get(touched, name) && Boolean(get(errors, name))}
helperText={get(touched, name) && get(errors, name)}
+ sx={{
+ '& .MuiInputAdornment-root': {
+ mr: 0,
+ height: '100%',
+ alignSelf: 'flex-start',
+ position: 'absolute',
+ top: 12,
+ right: 12
+ },
+ ...other?.sx
+ }}
{...other}
/>
);
diff --git a/app/src/components/fields/MultiAutocompleteField.tsx b/app/src/components/fields/MultiAutocompleteField.tsx
index 486d349a76..e26c903fc2 100644
--- a/app/src/components/fields/MultiAutocompleteField.tsx
+++ b/app/src/components/fields/MultiAutocompleteField.tsx
@@ -10,6 +10,7 @@ import Checkbox from '@mui/material/Checkbox';
import Chip from '@mui/material/Chip';
import ListItemText from '@mui/material/ListItemText';
import TextField from '@mui/material/TextField';
+import HelpButtonTooltip from 'components/buttons/HelpButtonTooltip';
import { useFormikContext } from 'formik';
import get from 'lodash-es/get';
import { useEffect, useState } from 'react';
@@ -27,6 +28,7 @@ export interface IMultiAutocompleteField {
selectedOptions?: IMultiAutocompleteFieldOption[];
required?: boolean;
filterLimit?: number;
+ helpText?: string;
chipVisible?: boolean;
onChange?: (
_event: React.ChangeEvent,
@@ -157,6 +159,15 @@ const MultiAutocompleteField: React.FC = (props) => {
label={props.label}
variant="outlined"
fullWidth
+ InputProps={{
+ ...params.InputProps,
+ endAdornment: (
+ <>
+ {props.helpText && }
+ {params.InputProps.endAdornment}
+ >
+ )
+ }}
placeholder="Type to start searching"
error={get(touched, props.id) && Boolean(get(errors, props.id))}
helperText={get(touched, props.id) && get(errors, props.id)}
diff --git a/app/src/components/fields/MultiAutocompleteFieldVariableSize.tsx b/app/src/components/fields/MultiAutocompleteFieldVariableSize.tsx
index 12ce881209..450d93fe6e 100644
--- a/app/src/components/fields/MultiAutocompleteFieldVariableSize.tsx
+++ b/app/src/components/fields/MultiAutocompleteFieldVariableSize.tsx
@@ -6,6 +6,7 @@ import Checkbox from '@mui/material/Checkbox';
import ListSubheader from '@mui/material/ListSubheader';
import TextField from '@mui/material/TextField';
import { FilterOptionsState } from '@mui/material/useAutocomplete';
+import HelpButtonTooltip from 'components/buttons/HelpButtonTooltip';
import { useFormikContext } from 'formik';
import { DebouncedFunc } from 'lodash-es';
import get from 'lodash-es/get';
@@ -44,6 +45,7 @@ export type IMultiAutocompleteField = {
label: string;
required?: boolean;
filterLimit?: number;
+ helpText?: string;
} & (ApiSearchTypeParam | defaultTypeParam);
function renderRow(props: ListChildComponentProps) {
@@ -290,6 +292,15 @@ const MultiAutocompleteFieldVariableSize: React.FC = (p
label={props.label}
variant="outlined"
fullWidth
+ InputProps={{
+ ...params.InputProps,
+ endAdornment: (
+ <>
+ {props.helpText && }
+ {params.InputProps.endAdornment}
+ >
+ )
+ }}
placeholder="Type to start searching"
error={get(touched, props.id) && Boolean(get(errors, props.id))}
helperText={get(touched, props.id) && get(errors, props.id)}
diff --git a/app/src/components/fields/StartEndDateFields.tsx b/app/src/components/fields/StartEndDateFields.tsx
index 6efcaad1af..aac15a6f02 100644
--- a/app/src/components/fields/StartEndDateFields.tsx
+++ b/app/src/components/fields/StartEndDateFields.tsx
@@ -51,7 +51,14 @@ const StartEndDateFields: React.FC = (props) => {
return (
-
+
= (props) => {
inputProps: {
'data-testid': 'start_date'
},
+
InputLabelProps: {
shrink: true
},
fullWidth: true
- }
+ },
+ popper: { placement: 'bottom-end' }
}}
label="Start Date"
format={DATE_FORMAT.ShortDateFormat}
diff --git a/app/src/components/fields/SystemUserAutocompleteField.tsx b/app/src/components/fields/SystemUserAutocompleteField.tsx
index be2e133998..ac02339761 100644
--- a/app/src/components/fields/SystemUserAutocompleteField.tsx
+++ b/app/src/components/fields/SystemUserAutocompleteField.tsx
@@ -5,6 +5,7 @@ import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import grey from '@mui/material/colors/grey';
import TextField from '@mui/material/TextField';
+import HelpButtonTooltip from 'components/buttons/HelpButtonTooltip';
import UserCard from 'components/user/UserCard';
import { useBiohubApi } from 'hooks/useBioHubApi';
import useIsMounted from 'hooks/useIsMounted';
@@ -27,6 +28,13 @@ interface ISystemUserAutocompleteFieldProps {
* @memberof ISystemUserAutocompleteFieldProps
*/
label: string;
+ /**
+ * Users to filter from the options because they have already been selected
+ *
+ * @type {number[]}
+ * @memberof ISystemUserAutocompleteFieldProps
+ */
+ selectedUsers?: number[];
/**
* Callback fired on option selection.
*
@@ -61,6 +69,13 @@ interface ISystemUserAutocompleteFieldProps {
* @memberof ISystemUserAutocompleteFieldProps
*/
clearOnSelect?: boolean;
+ /**
+ * Optional help text to be displayed in a tooltip
+ *
+ * @type {string}
+ * @memberof ISystemUserAutocompleteFieldProps
+ */
+ helpText?: string;
/**
* Whether to show start adornment magnifying glass or not
* Defaults to false
@@ -85,7 +100,18 @@ interface ISystemUserAutocompleteFieldProps {
* @return {*}
*/
export const SystemUserAutocompleteField = (props: ISystemUserAutocompleteFieldProps) => {
- const { formikFieldName, disabled, label, showStartAdornment, placeholder, onSelect, onClear, clearOnSelect } = props;
+ const {
+ formikFieldName,
+ disabled,
+ label,
+ showStartAdornment,
+ placeholder,
+ onSelect,
+ onClear,
+ clearOnSelect,
+ helpText,
+ selectedUsers
+ } = props;
const biohubApi = useBiohubApi();
const isMounted = useIsMounted();
@@ -115,15 +141,18 @@ export const SystemUserAutocompleteField = (props: ISystemUserAutocompleteFieldP
disabled={disabled}
data-testid={formikFieldName}
filterSelectedOptions
- noOptionsText={inputValue.length > 2 ? 'No matching options' : 'Enter at least 3 letters'}
+ noOptionsText={inputValue && inputValue.length > 2 ? 'No matching options' : 'Enter at least 3 letters'}
options={options}
- getOptionLabel={(option) => option.display_name}
- isOptionEqualToValue={(option, value) => {
- return option.system_user_id === value.system_user_id;
+ getOptionLabel={(option) => option.display_name || ''}
+ value={null} // Always set value to null to prevent a selected value from showing
+ isOptionEqualToValue={(option, value) => option.system_user_id === value.system_user_id}
+ filterOptions={(options) => {
+ if (selectedUsers) {
+ return options.filter((item) => !selectedUsers.includes(item.system_user_id));
+ }
+ return options;
}}
- filterOptions={(item) => item}
- inputValue={inputValue}
- // Text field value changed
+ inputValue={inputValue || ''} // Control the text field value separately
onInputChange={(_, value, reason) => {
if (clearOnSelect && reason === 'reset') {
setInputValue('');
@@ -154,11 +183,10 @@ export const SystemUserAutocompleteField = (props: ISystemUserAutocompleteFieldP
if (!isMounted()) {
return;
}
- setOptions(() => newOptions);
+ setOptions(newOptions);
setIsLoading(false);
});
}}
- // Option selected from dropdown
onChange={(_, option) => {
if (!option) {
onClear?.();
@@ -211,6 +239,7 @@ export const SystemUserAutocompleteField = (props: ISystemUserAutocompleteFieldP
endAdornment: (
<>
{isLoading ? : null}
+ {helpText && }
{params.InputProps.endAdornment}
>
)
diff --git a/app/src/components/species/components/SpeciesAutocompleteField.tsx b/app/src/components/species/components/SpeciesAutocompleteField.tsx
index e0d553bc8f..7789b7b81f 100644
--- a/app/src/components/species/components/SpeciesAutocompleteField.tsx
+++ b/app/src/components/species/components/SpeciesAutocompleteField.tsx
@@ -5,6 +5,7 @@ import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import grey from '@mui/material/colors/grey';
import TextField from '@mui/material/TextField';
+import HelpButtonTooltip from 'components/buttons/HelpButtonTooltip';
import SpeciesCard from 'components/species/components/SpeciesCard';
import { useBiohubApi } from 'hooks/useBioHubApi';
import useIsMounted from 'hooks/useIsMounted';
@@ -67,6 +68,13 @@ export interface ISpeciesAutocompleteFieldProps {
* @memberof ISpeciesAutocompleteFieldProps
*/
required?: boolean;
+ /**
+ * Optional help text to be displayed in a tooltip
+ *
+ * @type {string}
+ * @memberof ISystemUserAutocompleteFieldProps
+ */
+ helpText?: string;
/**
* If field is disabled.
*
@@ -116,6 +124,7 @@ const SpeciesAutocompleteField = (props: ISpeciesAutocompleteFieldProps) => {
error,
placeholder,
disabled,
+ helpText,
handleSpecies,
handleClear,
defaultSpecies,
@@ -191,6 +200,7 @@ const SpeciesAutocompleteField = (props: ISpeciesAutocompleteFieldProps) => {
getOptionLabel={(option) => option.scientificName}
filterOptions={(item) => item}
inputValue={inputValue}
+ fullWidth
// Text field value changed
onInputChange={(_, value, reason) => {
if (reason === 'reset') {
@@ -296,6 +306,7 @@ const SpeciesAutocompleteField = (props: ISpeciesAutocompleteFieldProps) => {
endAdornment: (
<>
{inputValue && isLoading ? : null}
+ {helpText && }
{params.InputProps.endAdornment}
>
)
diff --git a/app/src/features/projects/components/ProjectDetailsForm.tsx b/app/src/features/projects/components/ProjectDetailsForm.tsx
index 1bc437e569..efcefba18f 100644
--- a/app/src/features/projects/components/ProjectDetailsForm.tsx
+++ b/app/src/features/projects/components/ProjectDetailsForm.tsx
@@ -39,6 +39,7 @@ const ProjectDetailsForm = () => {
{
it('renders correctly with default empty values', () => {
- const { getByLabelText } = render(
+ const { getAllByLabelText } = render(
{
);
- expect(getByLabelText('Objectives', { exact: false })).toBeVisible();
+ // Target the 'Objectives' label, rather than the help icon text which includes the word 'objectives'
+ expect(getAllByLabelText('Objectives', { exact: false })[0]).toBeVisible();
});
it('renders correctly with existing objective/caveat values', () => {
@@ -29,7 +30,7 @@ describe('ProjectObjectivesForm', () => {
}
};
- const { getByLabelText, getByText } = render(
+ const { getAllByLabelText, getByText } = render(
{
);
- expect(getByLabelText('Objectives', { exact: false })).toBeVisible();
+ // Target the 'Objectives' label, rather than the help icon text which includes the word 'objectives'
+ expect(getAllByLabelText('Objectives', { exact: false })[0]).toBeVisible();
expect(getByText('a project objective')).toBeVisible();
});
});
diff --git a/app/src/features/projects/components/ProjectObjectivesForm.tsx b/app/src/features/projects/components/ProjectObjectivesForm.tsx
index f9765cfd75..c292fd8d37 100644
--- a/app/src/features/projects/components/ProjectObjectivesForm.tsx
+++ b/app/src/features/projects/components/ProjectObjectivesForm.tsx
@@ -38,6 +38,7 @@ const ProjectObjectivesForm = () => {
diff --git a/app/src/features/projects/components/ProjectUserForm.test.tsx b/app/src/features/projects/components/ProjectUserForm.test.tsx
index 7fd481a38b..6c2d80d026 100644
--- a/app/src/features/projects/components/ProjectUserForm.test.tsx
+++ b/app/src/features/projects/components/ProjectUserForm.test.tsx
@@ -100,7 +100,7 @@ describe('ProjectUserForm', () => {
);
await waitFor(async () => {
- expect(getByTestId('autocomplete-user-role-search')).toBeVisible();
+ expect(getByTestId('system_user_id')).toBeVisible();
expect(getByText('Test User', { exact: false })).toBeVisible();
});
});
@@ -140,7 +140,7 @@ describe('ProjectUserForm', () => {
);
await waitFor(async () => {
- expect(getByTestId('autocomplete-user-role-search')).toBeVisible();
+ expect(getByTestId('system_user_id')).toBeVisible();
expect(getByTestId('remove-user-role-button-0')).toBeVisible();
});
});
diff --git a/app/src/features/projects/components/ProjectUserForm.tsx b/app/src/features/projects/components/ProjectUserForm.tsx
index 08474c0236..10d7a1211a 100644
--- a/app/src/features/projects/components/ProjectUserForm.tsx
+++ b/app/src/features/projects/components/ProjectUserForm.tsx
@@ -1,24 +1,15 @@
-import { mdiMagnify } from '@mdi/js';
-import Icon from '@mdi/react';
-import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
import Box from '@mui/material/Box';
import Collapse from '@mui/material/Collapse';
-import grey from '@mui/material/colors/grey';
-import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import AlertBar from 'components/alert/AlertBar';
-import UserCard from 'components/user/UserCard';
+import { SystemUserAutocompleteField } from 'components/fields/SystemUserAutocompleteField';
import UserRoleSelector from 'components/user/UserRoleSelector';
import { PROJECT_ROLE } from 'constants/roles';
import { useFormikContext } from 'formik';
-import { useBiohubApi } from 'hooks/useBioHubApi';
-import useDataLoader from 'hooks/useDataLoader';
import { ICode } from 'interfaces/useCodesApi.interface';
import { ICreateProjectRequest, IGetProjectParticipant } from 'interfaces/useProjectApi.interface';
import { ISystemUser } from 'interfaces/useUserApi.interface';
-import { useEffect, useState } from 'react';
import { TransitionGroup } from 'react-transition-group';
-import { alphabetizeObjects } from 'utils/Utils';
import yup from 'utils/YupSchema';
export const ProjectUserRoleYupSchema = yup.object().shape({
@@ -48,19 +39,6 @@ export const ProjectUserRoleFormInitialValues = {
const ProjectUserForm = (props: IProjectUserFormProps) => {
const { handleSubmit, values, setFieldValue, errors, setErrors } = useFormikContext();
- const biohubApi = useBiohubApi();
-
- const searchUserDataLoader = useDataLoader((keyword: string) => biohubApi.user.searchSystemUser(keyword));
-
- const [searchText, setSearchText] = useState('');
-
- const [sortedUsers, setSortedUsers] = useState([]);
-
- useEffect(() => {
- if (searchUserDataLoader.data) {
- setSortedUsers(alphabetizeObjects(searchUserDataLoader.data, 'display_name'));
- }
- }, [searchUserDataLoader.data]);
const handleAddUser = (user: ISystemUser | IGetProjectParticipant) => {
setFieldValue(`participants[${values.participants.length}]`, {
@@ -161,76 +139,19 @@ const ProjectUserForm = (props: IProjectUserFormProps) => {
)}
- {
- const searchFilter = createFilterOptions({ ignoreCase: true });
- const unselectedOptions = options.filter(
- (item) => !values.participants.some((existing) => existing.system_user_id === item.system_user_id)
- );
- return searchFilter(unselectedOptions, state);
- }}
- getOptionLabel={(option) => option.display_name}
- inputValue={searchText}
- onInputChange={(_, value, reason) => {
- if (reason === 'reset') {
- setSearchText('');
- } else {
- setSearchText(value);
-
- if (value.length >= 3) {
- // Only search if the search text is at least 3 characters long
- searchUserDataLoader.refresh(value);
- }
- }
- }}
- onChange={(_, option) => {
- if (option) {
- handleAddUser(option);
+ participant.system_user_id)}
+ clearOnSelect
+ onSelect={(value) => {
+ if (value) {
+ handleAddUser(value);
}
}}
- renderInput={(params) => (
-
-
-
- )
- }}
- />
- )}
- renderOption={(renderProps, renderOption) => {
- return (
-
-
-
-
-
- );
- }}
+ key="project-user-filter"
/>
diff --git a/app/src/features/surveys/animals/animal-form/components/AnimalFormContainer.tsx b/app/src/features/surveys/animals/animal-form/components/AnimalFormContainer.tsx
index ef3564ae54..c8305208f1 100644
--- a/app/src/features/surveys/animals/animal-form/components/AnimalFormContainer.tsx
+++ b/app/src/features/surveys/animals/animal-form/components/AnimalFormContainer.tsx
@@ -112,9 +112,10 @@ export const AnimalFormContainer = (props: IAnimalFormProps) => {
summary="Enter information to identify the animal"
component={}
/>
+
}
/>
diff --git a/app/src/features/surveys/animals/animal-form/components/ecological-units/EcologicalUnitsForm.tsx b/app/src/features/surveys/animals/animal-form/components/ecological-units/EcologicalUnitsForm.tsx
index a732318aae..a250b403d0 100644
--- a/app/src/features/surveys/animals/animal-form/components/ecological-units/EcologicalUnitsForm.tsx
+++ b/app/src/features/surveys/animals/animal-form/components/ecological-units/EcologicalUnitsForm.tsx
@@ -3,6 +3,7 @@ import { Icon } from '@mdi/react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Collapse from '@mui/material/Collapse';
+import HelpButtonStack from 'components/buttons/HelpButtonStack';
import { EcologicalUnitDualSelect } from 'components/species/ecological-units/EcologicalUnitDualSelect';
import { FieldArray, FieldArrayRenderProps, useFormikContext } from 'formik';
import { useCritterbaseApi } from 'hooks/useCritterbaseApi';
@@ -56,25 +57,27 @@ export const EcologicalUnitsForm = () => {
))}
-
+
+
+
>
)}
/>
diff --git a/app/src/features/surveys/animals/animal-form/components/general-information/AnimalGeneralInformationForm.tsx b/app/src/features/surveys/animals/animal-form/components/general-information/AnimalGeneralInformationForm.tsx
index e7c6eabf40..f374c3ef7b 100644
--- a/app/src/features/surveys/animals/animal-form/components/general-information/AnimalGeneralInformationForm.tsx
+++ b/app/src/features/surveys/animals/animal-form/components/general-information/AnimalGeneralInformationForm.tsx
@@ -1,6 +1,7 @@
import Collapse from '@mui/material/Collapse';
import Grid from '@mui/material/Grid';
import Box from '@mui/system/Box';
+import HelpButtonStack from 'components/buttons/HelpButtonStack';
import AutocompleteField, { IAutocompleteFieldOption } from 'components/fields/AutocompleteField';
import CustomTextField from 'components/fields/CustomTextField';
import SpeciesAutocompleteField from 'components/species/components/SpeciesAutocompleteField';
@@ -44,24 +45,26 @@ export const AnimalGeneralInformationForm = (props: IAnimalGeneralInformationFor
- {
- setFieldValue('species', species);
- setFieldValue('ecological_units', []);
- if (species) {
- measurementsDataLoader.refresh(species.tsn);
- return;
- }
- measurementsDataLoader.clearData();
- }}
- clearOnSelect={true}
- error={errors.species}
- />
+
+ {
+ setFieldValue('species', species);
+ setFieldValue('ecological_units', []);
+ if (species) {
+ measurementsDataLoader.refresh(species.tsn);
+ return;
+ }
+ measurementsDataLoader.clearData();
+ }}
+ clearOnSelect={true}
+ error={errors.species}
+ />
+
{values.species && (
-
+
+
+
-
+
+
+
-
+
+
+
diff --git a/app/src/features/surveys/components/agreements/AgreementsForm.tsx b/app/src/features/surveys/components/agreements/AgreementsForm.tsx
index 0016ba55f9..5ddfab154c 100644
--- a/app/src/features/surveys/components/agreements/AgreementsForm.tsx
+++ b/app/src/features/surveys/components/agreements/AgreementsForm.tsx
@@ -1,10 +1,10 @@
-import Box from '@mui/material/Box';
import Checkbox from '@mui/material/Checkbox';
import FormControl from '@mui/material/FormControl';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormHelperText from '@mui/material/FormHelperText';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
+import HelpButtonStack from 'components/buttons/HelpButtonStack';
import { useFormikContext } from 'formik';
import { StringBoolean } from 'types/misc';
import yup from 'utils/YupSchema';
@@ -60,9 +60,11 @@ const AgreementsForm = () => {
)}
-
- {
- const searchFilter = createFilterOptions({ ignoreCase: true });
- const unselectedOptions = options.filter(
- (item) => !values.participants.some((existing) => existing.system_user_id === item.system_user_id)
- );
- return searchFilter(unselectedOptions, state);
- }}
- getOptionLabel={(option) => option.display_name}
- inputValue={searchText}
- onInputChange={(_, value, reason) => {
- if (reason === 'reset') {
- setSearchText('');
- } else {
- setSearchText(value);
-
- if (value.length >= 3) {
- // Only search if the search text is at least 3 characters long
- searchUserDataLoader.refresh(value);
- }
- }
- }}
- onChange={(_, option) => {
- if (option) {
- handleAddUser(option);
- }
- }}
- renderInput={(params) => (
-
-
-
- )
- }}
- />
- )}
- renderOption={(renderProps, renderOption) => {
- return (
-
-
-
-
-
- );
- }}
- />
-
+ participant.system_user_id)}
+ clearOnSelect
+ onSelect={(value) => {
+ if (value) {
+ handleAddUser(value);
+ }
+ }}
+ />
{
selectedRole={getSelectedRole(index)}
handleAdd={handleAddUserRole}
handleRemove={handleRemoveUser}
- key={user.system_user_id}
- label={'Select a Job'}
+ label="Select a Job"
/>
);
diff --git a/app/src/features/surveys/components/sampling-strategy/SamplingStrategyForm.tsx b/app/src/features/surveys/components/sampling-strategy/SamplingStrategyForm.tsx
index d60bab9f44..57c9296adf 100644
--- a/app/src/features/surveys/components/sampling-strategy/SamplingStrategyForm.tsx
+++ b/app/src/features/surveys/components/sampling-strategy/SamplingStrategyForm.tsx
@@ -1,6 +1,7 @@
import Box from '@mui/material/Box';
import Collapse from '@mui/material/Collapse';
import Typography from '@mui/material/Typography';
+import HelpButtonStack from 'components/buttons/HelpButtonStack';
import { useState } from 'react';
import SurveyBlockSection from './blocks/SurveyBlockForm';
import SurveyStratumForm from './stratums/SurveyStratumForm';
@@ -14,7 +15,9 @@ const SamplingStrategyForm = () => {
- Add Stratum
+
+ Add Stratum
+
{
-
- Add Blocks (optional)
-
+ Add Blocks (optional)
{
id="site_selection.strategies"
label="Site selection strategy"
options={siteStrategies}
+ helpText="Select how the locations of sampling sites were chosen."
selectedOptions={selectedSiteStrategies}
required={true}
onChange={(_, selectedOptions, reason) => {
diff --git a/app/src/features/surveys/components/species/components/FocalSpeciesForm.tsx b/app/src/features/surveys/components/species/components/FocalSpeciesForm.tsx
index 69dd036cda..035af446aa 100644
--- a/app/src/features/surveys/components/species/components/FocalSpeciesForm.tsx
+++ b/app/src/features/surveys/components/species/components/FocalSpeciesForm.tsx
@@ -33,6 +33,7 @@ export const FocalSpeciesForm = () => {
{
if (values.species.focal_species.some((focalSpecies) => focalSpecies.tsn === species.tsn)) {
diff --git a/app/src/features/surveys/edit/EditSurveyForm.tsx b/app/src/features/surveys/edit/EditSurveyForm.tsx
index c228734833..8b26b90f7f 100644
--- a/app/src/features/surveys/edit/EditSurveyForm.tsx
+++ b/app/src/features/surveys/edit/EditSurveyForm.tsx
@@ -4,6 +4,7 @@ import Divider from '@mui/material/Divider';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import FormikErrorSnackbar from 'components/alert/FormikErrorSnackbar';
+import HelpButtonStack from 'components/buttons/HelpButtonStack';
import HorizontalSplitFormComponent from 'components/fields/HorizontalSplitFormComponent';
import { CodesContext } from 'contexts/codesContext';
import { ProjectContext } from 'contexts/projectContext';
@@ -117,7 +118,9 @@ const EditSurveyForm = <
summary="Enter any permits used in this survey"
component={
- Were any permits used in this survey?
+
+ Were any permits used in this survey?
+
}
@@ -130,7 +133,9 @@ const EditSurveyForm = <
summary="Specify funding sources for this survey"
component={
- Do any funding agencies require this survey to be submitted?
+
+ Do any funding agencies require this survey to be submitted?
+
}
@@ -160,7 +165,7 @@ const EditSurveyForm = <
}
/>
@@ -184,7 +189,7 @@ const EditSurveyForm = <
}
/>
@@ -195,7 +200,9 @@ const EditSurveyForm = <
summary="Indicate whether any data is proprietary"
component={
- Is any data in this survey proprietary?
+
+ Is any data in this survey proprietary?
+
{
diff --git a/app/src/features/surveys/sampling-information/methods/SamplingMethodFormContainer.tsx b/app/src/features/surveys/sampling-information/methods/SamplingMethodFormContainer.tsx
index 429da93169..77b6e10b6d 100644
--- a/app/src/features/surveys/sampling-information/methods/SamplingMethodFormContainer.tsx
+++ b/app/src/features/surveys/sampling-information/methods/SamplingMethodFormContainer.tsx
@@ -18,6 +18,7 @@ import Menu, { MenuProps } from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
+import HelpButtonStack from 'components/buttons/HelpButtonStack';
import ColouredRectangleChip from 'components/chips/ColouredRectangleChip';
import { CodesContext } from 'contexts/codesContext';
import { ISurveySampleMethodFormData } from 'features/surveys/sampling-information/methods/components/SamplingMethodForm';
@@ -142,7 +143,11 @@ export const SamplingMethodFormContainer = () => {