Skip to content
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

SIMSBIOHUB-644: Add pagination, Update toggle group component #1436

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
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
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 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;

Check warning on line 68 in app/src/components/toolbar/CustomToggleButtonGroup.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/toolbar/CustomToggleButtonGroup.tsx#L67-L68

Added lines #L67 - L68 were not covered by tests

return (
<ToggleButtonGroup
orientation="vertical"
orientation={orientation}
value={activeView}
onChange={(_, view) => {
if (view) {
Expand All @@ -45,16 +92,15 @@
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 (

Check warning on line 98 in app/src/components/toolbar/CustomToggleButtonGroup.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/toolbar/CustomToggleButtonGroup.tsx#L98

Added line #L98 was not covered by tests
<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 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 @@
</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)}

Check warning on line 82 in app/src/features/admin/alert/AlertContainer.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/features/admin/alert/AlertContainer.tsx#L82

Added line #L82 was not covered by tests
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 [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 = [

Check warning on line 38 in app/src/features/admin/users/access-requests/AccessRequestContainer.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/features/admin/users/access-requests/AccessRequestContainer.tsx#L38

Added line #L38 was not covered by tests
{ 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 @@
</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
Loading