Skip to content

Commit

Permalink
Merge branch 'main' into POLIO-1706-Campaigns-Users-cannot-filter-by-…
Browse files Browse the repository at this point in the history
…campaign-type
  • Loading branch information
quang-le committed Oct 24, 2024
2 parents 76ac4a4 + c8142a9 commit ff42903
Show file tree
Hide file tree
Showing 106 changed files with 1,956 additions and 1,769 deletions.
5 changes: 2 additions & 3 deletions docs/pages/dev/reference/guidelines/front-end/front-end.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ Don't be afraid to split your code into smaller parts, using understandable nami

## Legacy

Class component, redux, provider are still old way to create features in IASO.
Class component, proptypes are still old way to create features in IASO.
Please use `hooks`, `typescript` and `arrow component`.
Redux can still be used with state that needs to be available everywhere in the application (current user, UI constants and states, ...).
We already have a lot of typing done in each domain of the application (forms, submissions, org units, ... )

## Bluesquare-components
Expand All @@ -37,7 +36,7 @@ To make it available too everybody you have to build new files with `npm run cle
## Architecture

Main index file is located here: `hat/assets/js/apps/Iaso/index`
This is the entrypoint of the app, setting up providers, theme, react-query query client, custom plugins, redux,...
This is the entrypoint of the app, setting up providers, theme, react-query query client, custom plugins,...

**`components`**
Used to store generic components that can be used everywhere, like `inputComponent`, `buttons`, ...
Expand Down
16 changes: 11 additions & 5 deletions hat/assets/js/apps/Iaso/components/forms/Checkboxes.spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import { Box } from '@mui/material';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { Box } from '@mui/material';
import React from 'react';
import { renderWithIntl } from '../../../../test/utils/intl';
import { renderWithMuiTheme } from '../../../../test/utils/muiTheme';
import { Checkboxes } from './Checkboxes';
import { renderWithStore } from '../../../../test/utils/redux';
import InputComponent from './InputComponent';

let component;
Expand Down Expand Up @@ -40,8 +41,13 @@ const checkboxesProp = () => {
};
const renderComponent = props => {
component = mount(
renderWithStore(
<Checkboxes inline={props.inline} checkboxes={props.checkboxes} />,
renderWithMuiTheme(
renderWithIntl(
<Checkboxes
inline={props.inline}
checkboxes={props.checkboxes}
/>,
),
),
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from 'react';
import { renderWithIntl } from '../../../../test/utils/intl';
import { renderWithMuiTheme } from '../../../../test/utils/muiTheme';
import { EditableTextFields } from './EditableTextFields';
import { renderWithIntl } from '../../../../test/utils/intl';
import InputComponent from './InputComponent.tsx';
import { renderWithStore } from '../../../../test/utils/redux';

const onChange1 = sinon.spy();
const onChange2 = sinon.spy();
Expand Down Expand Up @@ -34,9 +33,7 @@ let inputs;
const renderComponent = props => {
return mount(
renderWithIntl(
renderWithMuiTheme(
renderWithStore(<EditableTextFields fields={props} />),
),
renderWithMuiTheme(<EditableTextFields fields={props} />),
),
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ const CircleMarkerComponent = props => {
draggable={draggable}
center={[item.latitude, item.longitude, item.altitude]}
eventHandlers={{
click: () => onClick(item),
click: e => {
e.originalEvent.stopPropagation();
onClick(item);
},
dragend: e => onDragend(e.target),
dblclick: e => onDblclick(e, item),
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ const MarkerComponent = props => {
icon={marker || customMarker}
position={[item.latitude, item.longitude, item.altitude]}
eventHandlers={{
click: () => onClick(item),
click: e => {
e.originalEvent.stopPropagation();
onClick(item);
},
dragend: e => onDragend(e.target),
dblclick: e => onDblclick(e, item),
}}
Expand Down
147 changes: 147 additions & 0 deletions hat/assets/js/apps/Iaso/components/nav/AccountSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import {
ClickAwayListener,
Grow,
MenuItem,
MenuList,
Paper,
Popper,
Typography,
} from '@mui/material';
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';

import { useSwitchAccount } from '../../hooks/useSwitchAccount';
import { useCurrentUser } from '../../utils/usersUtils';

type Props = {
color?: 'inherit' | 'primary' | 'secondary';
};

export const AccountSwitch: FunctionComponent<Props> = ({
color = 'inherit',
}) => {
const currentUser = useCurrentUser();

const [open, setOpen] = useState(false);
const anchorRef = useRef<HTMLDivElement>(null);

const { mutateAsync: switchAccount } = useSwitchAccount(() => {
setOpen(false);
window.location.href = '/';
});

const handleToggle = () => {
setOpen(prevOpen => !prevOpen);
};

const handleClose = (event: Event | React.SyntheticEvent) => {
if (
anchorRef.current &&
anchorRef.current.contains(event.target as HTMLElement)
) {
return;
}
setOpen(false);
};

function handleListKeyDown(event: React.KeyboardEvent) {
if (event.key === 'Tab') {
event.preventDefault();
setOpen(false);
} else if (event.key === 'Escape') {
setOpen(false);
}
}

// Return focus to the button when we transitioned from !open -> open
const prevOpen = useRef(open);
useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current?.focus();
}
prevOpen.current = open;
}, [open]);
const menuListKeyDownHandler = React.useCallback(handleListKeyDown, []);

if (currentUser.other_accounts.length === 0) {
return (
<Typography
variant="body2"
color={color}
sx={{
padding: theme => theme.spacing(0),
fontSize: 16,
}}
>
{currentUser.account.name}
</Typography>
);
}

return (
<>
<Typography
ref={anchorRef}
variant="body2"
color={color}
onClick={handleToggle}
sx={{
padding: theme => theme.spacing(0),
cursor: 'pointer',
fontSize: 16,
'&:hover': {
color: theme => theme.palette.secondary.main,
},
}}
aria-controls={open ? 'account-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
>
{currentUser.account.name}
</Typography>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
placement="bottom-end"
transition
disablePortal
>
{({ TransitionProps }) => (
<Grow
// eslint-disable-next-line react/jsx-props-no-spreading
{...TransitionProps}
style={{
transformOrigin: 'right bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList
autoFocusItem={open}
id="account-menu"
aria-labelledby="account-button"
onKeyDown={menuListKeyDownHandler}
>
{currentUser.other_accounts.map(account => (
<MenuItem
key={account.id}
selected={
account.id ===
currentUser.account.id
}
onClick={() =>
switchAccount(account.id)
}
>
{account.name}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
);
};
36 changes: 14 additions & 22 deletions hat/assets/js/apps/Iaso/components/nav/CurrentUser/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { FunctionComponent, useState } from 'react';

import { Popover, Typography } from '@mui/material';
import { Box, Popover, Typography } from '@mui/material';
import { makeStyles } from '@mui/styles';
import classnames from 'classnames';
import { useSafeIntl } from 'bluesquare-components';
import MESSAGES from '../../../domains/app/components/messages';
import { getDefaultSourceVersion } from '../../../domains/dataSources/utils';
import { User } from '../../../utils/usersUtils';
import MESSAGES from '../../../domains/app/components/messages';
import { AccountSwitch } from '../AccountSwitch';

type Props = {
currentUser: User;
Expand All @@ -23,9 +23,11 @@ const useStyles = makeStyles(theme => ({
currentUserInfos: {
display: 'block',
textAlign: 'right',
},
account: {
fontSize: 9,
cursor: 'pointer',
fontSize: 16,
'&:hover': {
color: theme.palette.secondary.main,
},
},
popOverInfos: {
display: 'block',
Expand Down Expand Up @@ -58,27 +60,17 @@ export const CurrentUserInfos: FunctionComponent<Props> = ({

return (
<>
<Typography
<Box
className={classes.currentUserInfos}
aria-owns={open ? 'mouse-over-popover' : undefined}
aria-haspopup="true"
onMouseEnter={handlePopoverOpen}
onMouseLeave={handlePopoverClose}
variant="body2"
>
<span className={classes.currentUserInfos}>
{currentUser?.user_name}
</span>

<span
className={classnames(
classes.currentUserInfos,
classes.account,
)}
>
{currentUser?.account?.name}
</span>
</Typography>

{currentUser?.user_name}
</Box>
<Box sx={{ mx: 1 }}>-</Box>
<AccountSwitch />
<Popover
id="mouse-over-popover"
className={classes.popover}
Expand Down
8 changes: 5 additions & 3 deletions hat/assets/js/apps/Iaso/components/nav/TopBarComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { useCurrentUser } from '../../utils/usersUtils.ts';

import { useSidebar } from '../../domains/app/contexts/SideBarContext.tsx';
import { CurrentUserInfos } from './CurrentUser/index.tsx';
import { LogoutButton } from './LogoutButton.tsx';
import { HomePageButton } from './HomePageButton.tsx';
import { LogoutButton } from './LogoutButton.tsx';

const styles = theme => ({
menuButton: {
Expand Down Expand Up @@ -77,7 +77,7 @@ function TopBar(props) {
container
item
direction="row"
xs={9}
xs={7}
alignItems="center"
>
{!displayBackButton && displayMenuButton && (
Expand Down Expand Up @@ -111,7 +111,7 @@ function TopBar(props) {
</Typography>
</Grid>
{currentUser && !isMobileLayout && (
<Grid container item xs={3} justifyContent="flex-end">
<Grid container item xs={5} justifyContent="flex-end">
<Box
display="flex"
alignItems="center"
Expand All @@ -122,9 +122,11 @@ function TopBar(props) {
version={window.IASO_VERSION}
/>
</Box>

<Box display="flex" justifyContent="center" pl={2}>
<HomePageButton />
</Box>

<Box display="flex" justifyContent="center" pl={1}>
<LogoutButton />
</Box>
Expand Down
11 changes: 5 additions & 6 deletions hat/assets/js/apps/Iaso/domains/app/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React from 'react';

import { renderWithIntl } from '../../../../test/utils/intl';
import { renderWithMuiTheme } from '../../../../test/utils/muiTheme';
import App from './index.tsx';
import { renderWithStore } from '../../../../test/utils/redux';

describe('App', () => {
it('render properly', () => {
const wrapper = shallow(
renderWithStore(<App plugins={[]} history={{}} />, {
subscribe: () => null,
dispatch: () => null,
getState: () => null,
}),
renderWithMuiTheme(
renderWithIntl(<App plugins={[]} history={{}} />),
),
);
expect(wrapper.exists()).to.be.true;
});
Expand Down
1 change: 1 addition & 0 deletions hat/assets/js/apps/Iaso/domains/app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,7 @@
"iaso.userRoles.dialogInfoTitle": "Warning on the new created user role",
"iaso.userRoles.edit": "Edit user role",
"iaso.userRoles.infoButton": "Got it!",
"iaso.userRoles.orgUnitWriteTypesInfos": "Select the org unit types the user role can edit",
"iaso.userRoles.title": "User roles",
"iaso.userRoles.userRolePermissions": "User role permissions",
"iaso.users.addLocations": "Add location(s)",
Expand Down
Loading

0 comments on commit ff42903

Please sign in to comment.