Skip to content

Commit

Permalink
SIMSBIOHUB-644: Add pagination, Update toggle group component (#1436)
Browse files Browse the repository at this point in the history
* Add pagination to sample periods table on the manage sampling information page.
Some minor tweaks to existing related components (tweaks the jsdoc, sorting the props, etc).

* Replace most instances of `ToggleButtonGroup` with `CustomToggleButtonGroup`.
Tweak CSS for StyledDataGrid.
Remove padding around uses of StyledDataGrid.

* Disable row selection in a few tables that missed it

* ignore-skip

---------

Co-authored-by: Macgregor Aubertin-Young <[email protected]>
  • Loading branch information
NickPhura and mauberti-bc authored Dec 6, 2024
1 parent a7bcd49 commit 1c83bb7
Show file tree
Hide file tree
Showing 36 changed files with 407 additions and 784 deletions.
1 change: 1 addition & 0 deletions app/src/components/attachments/list/AttachmentsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ const AttachmentsList = <T extends IGetProjectAttachment | IGetSurveyAttachment>
columns={attachmentsListColumnDefs}
rows={attachments}
pageSizeOptions={pageSizeOptions}
disableRowSelectionOnClick
initialState={{
pagination: {
paginationModel: {
Expand Down
30 changes: 23 additions & 7 deletions app/src/components/data-grid/StyledDataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,31 @@ export const StyledDataGrid = <R extends GridValidRowModel = any>(props: StyledD
borderBottom: 'none'
}
},
'& .MuiDataGrid-columnHeader:first-of-type, .MuiDataGrid-cell:first-of-type': {
pl: 2
// Define custom header padding for the first column vs every other column
'& .MuiDataGrid-columnHeader:first-of-type:not(.MuiDataGrid-columnHeaderCheckbox)': {
pl: 3 // Add extra padding to the first header, unless it is a checkbox header
},
'& .MuiDataGrid-columnHeader:last-of-type, .MuiDataGrid-cell:last-of-type': {
pr: 2
'& .MuiDataGrid-columnHeader:first-of-type.MuiDataGrid-columnHeaderCheckbox': {
pl: 2 // Add extra padding to the first header when it is a checkbox header
},
'& .MuiDataGrid-columnHeader:not(:first-of-type)': {
pl: 1 // Add extra padding to every other header
},
// Define custom cell padding for the first column vs every other column
'& .MuiDataGrid-cell:first-of-type:not(.MuiDataGrid-cellCheckbox)': {
pl: 3 // Add extra padding to the first cell, unless it is a checkbox cell
},
'& .MuiDataGrid-cell:first-of-type.MuiDataGrid-cellCheckbox': {
pl: 2 // Add extra padding to the first cell when it is a checkbox cell
},
'& .MuiDataGrid-cell:not(:first-of-type)': {
pl: 1 // Add extra padding to every other cell
},
// Ensure the draggable container is at least 50px wide
'& .MuiDataGrid-columnHeaderDraggableContainer': {
minWidth: '50px'
},
// Custom styling for cell content at different densities
'&.MuiDataGrid-root--densityCompact .MuiDataGrid-cell': {
py: '8px',
wordWrap: 'anywhere'
Expand All @@ -61,9 +80,6 @@ export const StyledDataGrid = <R extends GridValidRowModel = any>(props: StyledD
py: '22px',
wordWrap: 'anywhere'
},
'& .MuiDataGrid-columnHeaderDraggableContainer': {
minWidth: '50px'
},
...props.sx
}}
/>
Expand Down
86 changes: 66 additions & 20 deletions app/src/components/toolbar/CustomToggleButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,73 @@ import Button from '@mui/material/Button';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';

interface CustomToggleButtonGroupProps<T extends string> {
views: Array<{ value: T; label: string; icon: string }>;
activeView: T;
onViewChange: (view: T) => void;
export interface ToggleButtonView<ViewValueType> {
/**
* The value of the toggle button, which will be passed to the `onViewChange` callback.
*
* @type {ViewValueType}
* @memberof ToggleButtonView
*/
value: ViewValueType;
/**
* The label to display for the toggle button.
*
* @type {string}
* @memberof ToggleButtonView
*/
label: string;
/**
* An optional start icon.
*
* @type {string}
* @memberof ToggleButtonView
*/
icon?: string;
}

interface CustomToggleButtonGroupProps<ViewValueType extends string> {
/**
* An array of views to display in the toggle button group.
*
* @type {ToggleButtonView<ViewValueType>[]}
* @memberof CustomToggleButtonGroupProps
*/
views: ToggleButtonView<ViewValueType>[];
/**
* The currently active view.
*
* @type {ViewValueType}
* @memberof CustomToggleButtonGroupProps
*/
activeView: ViewValueType;
/**
* Callback fired when a toggle button is clicked.
*
* @memberof CustomToggleButtonGroupProps
*/
onViewChange: (view: ViewValueType) => void;
/**
* The orientation of the toggle button group.
*
* @type {('horizontal' | 'vertical')}
* @memberof CustomToggleButtonGroupProps
*/
orientation: 'horizontal' | 'vertical';
}

/**
* A custom toggle button group that allows users to select from multiple views.
*
* TODO: Update all togglebuttongroups throughout the app to use this component for consistent styling
*
* @param {CustomToggleButtonGroupProps<T>} props
* @template ViewValueType
* @param {CustomToggleButtonGroupProps<ViewValueType>} props
* @return {*}
*/
const CustomToggleButtonGroup = <T extends string>(props: CustomToggleButtonGroupProps<T>) => {
const { views, activeView, onViewChange } = props;
const CustomToggleButtonGroup = <ViewValueType extends string>(props: CustomToggleButtonGroupProps<ViewValueType>) => {
const { views, activeView, onViewChange, orientation } = props;

return (
<ToggleButtonGroup
orientation="vertical"
orientation={orientation}
value={activeView}
onChange={(_, view) => {
if (view) {
Expand All @@ -45,16 +92,15 @@ const CustomToggleButtonGroup = <T extends string>(props: CustomToggleButtonGrou
justifyContent: 'flex-start'
}
}}>
{views.map((view) => (
<ToggleButton
key={view.value}
component={Button}
color="primary"
startIcon={<Icon path={view.icon} size={0.75} />}
value={view.value}>
{view.label}
</ToggleButton>
))}
{views.map((view) => {
const startIcon = (view.icon && <Icon path={view.icon} size={0.75} />) || undefined;

return (
<ToggleButton key={view.value} component={Button} color="primary" startIcon={startIcon} value={view.value}>
{view.label}
</ToggleButton>
);
})}
</ToggleButtonGroup>
);
};
Expand Down
41 changes: 9 additions & 32 deletions app/src/features/admin/alert/AlertContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Divider from '@mui/material/Divider';
import Paper from '@mui/material/Paper';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import CustomToggleButtonGroup from 'components/toolbar/CustomToggleButtonGroup';
import dayjs from 'dayjs';
import { useBiohubApi } from 'hooks/useBioHubApi';
import useDataLoader from 'hooks/useDataLoader';
Expand Down Expand Up @@ -76,38 +75,16 @@ const AlertListContainer = () => {
</Button>
</Toolbar>
<Divider />
<Box p={2} display="flex" justifyContent="space-between">
<ToggleButtonGroup
value={activeView}
onChange={(_, view) => view && setActiveView(view)}
exclusive
sx={{
width: '100%',
gap: 1,
'& Button': {
py: 0.5,
px: 1.5,
border: 'none !important',
fontWeight: 700,
borderRadius: '4px !important',
fontSize: '0.875rem',
letterSpacing: '0.02rem'
}
}}>
{views.map(({ value, label, icon }) => (
<ToggleButton
key={value}
value={value}
component={Button}
color="primary"
startIcon={<Icon path={icon} size={0.75} />}>
{label}
</ToggleButton>
))}
</ToggleButtonGroup>
<Box p={2}>
<CustomToggleButtonGroup
views={views}
activeView={activeView}
onViewChange={(view) => setActiveView(view)}
orientation="horizontal"
/>
</Box>
<Divider />
<Box p={2}>
<Box>
{/* Modals */}
<CreateAlert open={modalState.create} onClose={closeModal} />
{alertId && modalState.edit && <EditAlert alertId={alertId} open={modalState.edit} onClose={closeModal} />}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { mdiCancel, mdiCheck, mdiExclamationThick } from '@mdi/js';
import Icon from '@mdi/react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Divider from '@mui/material/Divider';
import Paper from '@mui/material/Paper';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup/ToggleButtonGroup';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import CustomToggleButtonGroup from 'components/toolbar/CustomToggleButtonGroup';
import { IGetAccessRequestsListResponse } from 'interfaces/useAdminApi.interface';
import { useState } from 'react';
import AccessRequestActionedList from './list/actioned/AccessRequestActionedList';
Expand All @@ -34,16 +31,16 @@ const AccessRequestContainer = (props: IAccessRequestContainerProps) => {

const [activeView, setActiveView] = useState<AccessRequestViewEnum>(AccessRequestViewEnum.PENDING);

const views = [
{ value: AccessRequestViewEnum.PENDING, label: 'Pending', icon: mdiExclamationThick },
{ value: AccessRequestViewEnum.ACTIONED, label: 'Approved', icon: mdiCheck },
{ value: AccessRequestViewEnum.REJECTED, label: 'Rejected', icon: mdiCancel }
];

const pendingRequests = accessRequests.filter((request) => request.status_name === 'Pending');
const actionedRequests = accessRequests.filter((request) => request.status_name === 'Actioned');
const rejectedRequests = accessRequests.filter((request) => request.status_name === 'Rejected');

const views = [
{ value: AccessRequestViewEnum.PENDING, label: `Pending (${pendingRequests.length})`, icon: mdiExclamationThick },
{ value: AccessRequestViewEnum.ACTIONED, label: `Approved (${actionedRequests.length})`, icon: mdiCheck },
{ value: AccessRequestViewEnum.REJECTED, label: `Rejected (${rejectedRequests.length})`, icon: mdiCancel }
];

return (
<Paper>
<Toolbar>
Expand All @@ -53,58 +50,17 @@ const AccessRequestContainer = (props: IAccessRequestContainerProps) => {
</Toolbar>
<Divider />
<Box p={2} display="flex" justifyContent="space-between">
<ToggleButtonGroup
value={activeView}
onChange={(_, view) => {
if (!view) {
// An active view must be selected at all times
return;
}

<CustomToggleButtonGroup
views={views}
activeView={activeView}
onViewChange={(view) => {
setActiveView(view);
}}
exclusive
sx={{
width: '100%',
gap: 1,
'& Button': {
py: 0.5,
px: 1.5,
border: 'none !important',
fontWeight: 700,
borderRadius: '4px !important',
fontSize: '0.875rem',
letterSpacing: '0.02rem'
}
}}>
{views.map((view) => {
const getCount = () => {
switch (view.value) {
case AccessRequestViewEnum.PENDING:
return pendingRequests.length;
case AccessRequestViewEnum.ACTIONED:
return actionedRequests.length;
case AccessRequestViewEnum.REJECTED:
return rejectedRequests.length;
default:
return 0;
}
};
return (
<ToggleButton
key={view.value}
value={view.value}
component={Button}
color="primary"
startIcon={<Icon path={view.icon} size={0.75} />}>
{view.label} ({getCount()})
</ToggleButton>
);
})}
</ToggleButtonGroup>
orientation="horizontal"
/>
</Box>
<Divider />
<Box p={2}>
<Box>
{activeView === AccessRequestViewEnum.PENDING && (
<AccessRequestPendingList accessRequests={pendingRequests} refresh={refresh} />
)}
Expand Down
6 changes: 3 additions & 3 deletions app/src/features/admin/users/active/ActiveUsersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ const ActiveUsersList = (props: IActiveUsersListProps) => {
{
field: 'system_user_id',
headerName: 'ID',
width: 70,
minWidth: 70,
width: 85,
minWidth: 85,
renderHeader: () => (
<Typography color={grey[500]} variant="body2" fontWeight={700}>
ID
Expand Down Expand Up @@ -412,7 +412,7 @@ const ActiveUsersList = (props: IActiveUsersListProps) => {
</Button>
</Toolbar>
<Divider></Divider>
<Box p={2}>
<Box>
<StyledDataGrid<ISystemUser>
noRowsMessage="No Active Users"
columns={activeUsersColumnDefs}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ const UsersDetailProjects: React.FC<IProjectDetailsProps> = (props) => {
</Typography>
</Toolbar>
<Divider></Divider>
<Box p={2}>
<Box>
<Table sx={{ tableLayout: 'fixed' }}>
<TableHead>
<TableRow>
Expand Down
9 changes: 4 additions & 5 deletions app/src/features/projects/view/ProjectAttachments.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { mdiAttachment, mdiFilePdfBox, mdiTrayArrowUp } from '@mdi/js';
import Icon from '@mdi/react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Divider from '@mui/material/Divider';
import { IReportMetaForm } from 'components/attachments/ReportMetaForm';
Expand Down Expand Up @@ -107,10 +106,10 @@ const ProjectAttachments = () => {
</ProjectRoleGuard>
)}
/>
<Divider></Divider>
<Box p={2}>
<ProjectAttachmentsList />
</Box>

<Divider />

<ProjectAttachmentsList />
</>
);
};
Expand Down
Loading

0 comments on commit 1c83bb7

Please sign in to comment.