Skip to content

Commit

Permalink
Implement notification icon with badge for warnings and its test
Browse files Browse the repository at this point in the history
  • Loading branch information
Tusharjamdade committed Jan 8, 2025
1 parent 9bd09fc commit 470b988
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 17 deletions.
11 changes: 10 additions & 1 deletion cypress/component/Navbar.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ import Navbar from '../../src/components/Navbar';
const props = {
isLoggedIn: true,
onLogin: () => {},
warnings: ['Warning 1', 'Warning 2', 'Warning n'],
};

describe('Navbar', () => {
it('Displays a MUI Toolbar with logo, title, subtitle, documentation link, and GitHub link', () => {
cy.mount(<Navbar isLoggedIn={props.isLoggedIn} onLogin={props.onLogin} />);
cy.mount(
<Navbar
isLoggedIn={props.isLoggedIn}
onLogin={props.onLogin}
notifications={props.warnings}
/>
);
cy.get("[data-cy='navbar']").should('be.visible');
cy.get("[data-cy='navbar'] img").should('exist');
cy.get("[data-cy='navbar'] h5").should('contain', 'Neurobagel Query');
Expand All @@ -24,5 +31,7 @@ describe('Navbar', () => {
cy.get("[data-cy='navbar'] a")
.eq(1)
.should('have.attr', 'href', 'https://github.com/neurobagel/query-tool/');
// Test for Notification Icon and Badge
cy.get("[data-cy='navbar'] button").eq(1).find('svg').should('be.visible'); // Check if the notification icon exists
});
});
32 changes: 17 additions & 15 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function App() {
const [openAuthDialog, setOpenAuthDialog] = useState(false);
const [IDToken, setIDToken] = useState<string | undefined>('');
const { isAuthenticated, isLoading, getIdTokenClaims } = useAuth0();
const [warnings, setWarnings] = useState<string[]>([]);

// Extract the raw OIDC ID token from the Auth0 SDK
useEffect(() => {
Expand Down Expand Up @@ -116,10 +117,10 @@ function App() {
} else {
// If any errors occurred, report them
response.data.errors.forEach((error) => {
enqueueSnackbar(`Failed to retrieve ${NBResource} options from ${error.node_name}`, {
variant: 'warning',
action,
});
setWarnings((prev) => [
...(prev ?? []),
`Failed to retrieve ${NBResource} options from ${error.node_name}`,
]);
});
// If the results are empty, report that
if (Object.keys(response.data.responses[dataElementURI]).length === 0) {
Expand All @@ -132,10 +133,10 @@ function App() {
response.data.responses[dataElementURI].some((item) => item.Label === null) &&
NBResource !== 'pipelines'
) {
enqueueSnackbar(`Warning: Missing labels were removed for ${NBResource} `, {
variant: 'warning',
action,
});
setWarnings((prev) => [
...(prev ?? []),
`Warning: Missing labels were removed for ${NBResource}`,
]);
response.data.responses[dataElementURI] = response.data.responses[
dataElementURI
].filter((item) => item.Label !== null);
Expand Down Expand Up @@ -201,13 +202,10 @@ function App() {
} else {
// If any errors occurred, report them
response.data.errors.forEach((error) => {
enqueueSnackbar(
setWarnings((prev) => [
...(prev ?? []),
`Failed to retrieve ${pipelineURI.label} versions from ${error.node_name}`,
{
variant: 'warning',
action,
}
);
]);
});
// If the results are empty, report that
if (Object.keys(response.data.responses[pipelineURI.id]).length === 0) {
Expand Down Expand Up @@ -439,7 +437,11 @@ function App() {
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
maxSnack={7}
/>
<Navbar isLoggedIn={isAuthenticated} onLogin={() => setOpenAuthDialog(true)} />
<Navbar
isLoggedIn={isAuthenticated}
onLogin={() => setOpenAuthDialog(true)}
notifications={warnings}
/>
{showAlert() && (
<>
<Grow in={!alertDismissed}>
Expand Down
101 changes: 100 additions & 1 deletion src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,55 @@ import {
MenuItem,
ListItemIcon,
Tooltip,
Popover,
List,
ListItem,
ListItemText,
ListItemSecondaryAction,
} from '@mui/material';
import GitHub from '@mui/icons-material/GitHub';
import Article from '@mui/icons-material/Article';
import Logout from '@mui/icons-material/Logout';
import Login from '@mui/icons-material/Login';
import Avatar from '@mui/material/Avatar';
import { useAuth0 } from '@auth0/auth0-react';
import NotificationsIcon from '@mui/icons-material/Notifications';
import { enableAuth } from '../utils/constants';
import packageJson from '../../package.json';
import logo from '../assets/logo.png';

function Navbar({ isLoggedIn, onLogin }: { isLoggedIn: boolean; onLogin: () => void }) {
function Navbar({
isLoggedIn,
onLogin,
notifications,
}: {
isLoggedIn: boolean;
onLogin: () => void;
notifications: string[];
}) {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const openAccountMenu = Boolean(anchorEl);

const { user, logout } = useAuth0();
const [anchorMailEl, setAnchorMailEl] = useState<null | HTMLElement>(null);
const openMailMenu = Boolean(anchorMailEl);

const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};

const handleClose = () => {
setAnchorEl(null);
};

const handleMailClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorMailEl(event.currentTarget);
};

const handleMailClose = () => {
setAnchorMailEl(null);
};

return (
<Toolbar className="my-4" data-cy="navbar">
<div className="flex w-full items-center justify-between">
Expand All @@ -56,9 +81,83 @@ function Navbar({ isLoggedIn, onLogin }: { isLoggedIn: boolean; onLogin: () => v
<Article />
</IconButton>
</Tooltip>
<Tooltip title="Notifications">
<IconButton onClick={handleMailClick}>
<Badge badgeContent={notifications.length} color="primary">
<NotificationsIcon color="action" />
</Badge>
</IconButton>
</Tooltip>
<Popover
open={openMailMenu}
anchorEl={anchorMailEl}
onClose={handleMailClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
PaperProps={{
sx: {
width: '300px',
maxHeight: '400px',
overflowY: 'auto',
boxShadow: '0px 4px 20px rgba(0, 0, 0, 0.1)',
borderRadius: '8px',
padding: '10px',
},
}}
>
<List className="w-full max-w-[360px] overflow-y-auto bg-white">
{notifications.length > 0 ? (
notifications.map((notification) => (
<ListItem
key={notification}
alignItems="flex-start"
className="mb-2 rounded-md border-l-4 border-[#FF9800] bg-[#FFF3E0] transition-colors duration-200 hover:bg-gray-200"
>
<ListItemText
primary={
<Typography variant="subtitle2" className="font-bold text-[#E65100]">
{/* Add the appropriate title for the notification if any */}
</Typography>
}
secondary={
<Typography className="inline text-[#424242]" variant="body2">
{notification}
</Typography>
}
/>
<ListItemSecondaryAction>
<IconButton
edge="end"
aria-label="delete"
className="text-[#FF5722] hover:bg-[#FF572220]"
/>
</ListItemSecondaryAction>
</ListItem>
))
) : (
<ListItem>
<ListItemText
primary={
<Typography variant="body1" className="text-center italic text-[#757575]">
No notifications
</Typography>
}
/>
</ListItem>
)}
</List>
</Popover>

<IconButton href="https://github.com/neurobagel/query-tool/" target="_blank">
<GitHub />
</IconButton>

{enableAuth && (
<>
<IconButton onClick={handleClick}>
Expand Down

0 comments on commit 470b988

Please sign in to comment.