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

Migration to react 18 #727

Merged
merged 44 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b931ad1
Update to 18 rmf auth
Angatupyry Jul 5, 2023
0e3e1a4
Update to react router v6
Angatupyry Jul 5, 2023
c3bacfe
Update to 18 rmf component
Angatupyry Jul 5, 2023
c5f9067
Add prop to render children
Angatupyry Jul 5, 2023
eb8e952
Update to 18 dashboard
Angatupyry Jul 5, 2023
df315cf
Update lock file
Angatupyry Jul 5, 2023
2762146
Porting to react 18
Angatupyry Jul 5, 2023
13718f3
Merge branch 'main' into cr/react-18
Angatupyry Jul 6, 2023
d1d2dd2
Add pathname harcode to render tab name correctly
Angatupyry Jul 6, 2023
00ca34e
Adding * to render nested routes
Angatupyry Jul 6, 2023
e06b769
Remove close tag
Angatupyry Jul 6, 2023
fffdb42
Remove testing console
Angatupyry Jul 6, 2023
e039d14
Adding nested routes
Angatupyry Jul 6, 2023
c5c9312
Remove useMatch and pass only username to render child route properly
Angatupyry Jul 6, 2023
5966bba
Drawing routes according to react router v6
Angatupyry Jul 6, 2023
c7d2883
Removing unused import
Angatupyry Jul 6, 2023
76446bd
Render user profile by username
Angatupyry Jul 6, 2023
b5fd1ab
Update react testing library
Angatupyry Jul 6, 2023
278ad94
Updating react testing library package and use fireevent instead of u…
Angatupyry Jul 11, 2023
a03e359
Use fireevent instead of userEvent
Angatupyry Jul 11, 2023
4a24a9e
Use fireevent instead of userEvent
Angatupyry Jul 11, 2023
bbe7c08
Update test according to react 18
Angatupyry Jul 11, 2023
c3a6551
Remove userEvent and use fireevent instad
Angatupyry Jul 11, 2023
06385b8
Add describe to wrap testing and use fire event instead of userevent
Angatupyry Jul 11, 2023
7bbd0ad
FireEvent instead of userEvent
Angatupyry Jul 11, 2023
c1dfce3
Wrap test into describe
Angatupyry Jul 11, 2023
81098c3
Wrap test into describe
Angatupyry Jul 11, 2023
be8cfa6
Add fireevent instead of userevent.
Angatupyry Jul 11, 2023
362c5d7
FireEvent added and waitFor expect
Angatupyry Jul 11, 2023
acc67d6
Remove unnecessary function to render component
Angatupyry Jul 11, 2023
6b462a6
Remove test comment
Angatupyry Jul 11, 2023
6b2b6b0
Using testing library renderHook
Angatupyry Jul 11, 2023
56fead3
Update react testing library
Angatupyry Jul 12, 2023
f5b28a3
Adding function to avoid warning act(...)
Angatupyry Jul 12, 2023
b8ce97d
Fix test according to the new version of testing library
Angatupyry Jul 12, 2023
20b1344
Adding beforeAll code test to silence warning
Angatupyry Jul 12, 2023
e38a7f2
Comment test failed
Angatupyry Jul 12, 2023
c650b3b
Create a use stack navigator test acording to new version of testing …
Angatupyry Jul 12, 2023
084900c
Add comment to explain change
Angatupyry Jul 13, 2023
174abff
main admin path Navigate to users
Angatupyry Jul 13, 2023
b776e54
Improve function to be more easy to read
Angatupyry Jul 13, 2023
0615881
Fix test in log table report and useasync function
Angatupyry Jul 13, 2023
178f6d1
Remove unused import
Angatupyry Jul 13, 2023
8e62a77
Aaron/react 18 (#729)
aaronchongth Jul 27, 2023
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
21 changes: 10 additions & 11 deletions packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@
"@mui/x-date-pickers": "^5.0.20",
"@types/debug": "^4.1.5",
"@types/leaflet": "^1.5.17",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@types/react-grid-layout": "^1.3.2",
"@types/react-leaflet": "^2.5.2",
"@types/react-router": "^5.1.7",
"@types/react-router-dom": "^5.1.7",
"@types/react-router": "^5.1.20",
"@types/react-router-dom": "^5.3.3",
"ajv": "^8.10.0",
"api-client": "workspace:*",
"axios": "^0.21.1",
Expand All @@ -65,14 +65,14 @@
"keycloak-js": "^11.0.2",
"leaflet": "^1.7.1",
"node-vibrant": "^3.1.6",
"react": "^17.0.2",
"react": "^18.2.0",
"react-components": "workspace:*",
"react-customizable-progressbar": "^1.0.3",
"react-dom": "^17.0.2",
"react-dom": "^18.2.0",
"react-grid-layout": "^1.3.4",
"react-leaflet": "^2.7.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-router": "^6.14.1",
"react-router-dom": "^6.14.1",
"rmf-auth": "workspace:*",
"rmf-models": "workspace:*",
"rxjs": "^7.5.5"
Expand All @@ -85,10 +85,9 @@
"@storybook/node-logger": "^6.5.9",
"@storybook/preset-create-react-app": "^3.2.0",
"@storybook/react": "^6.5.8",
"@testing-library/dom": "^8.20.0",
"@testing-library/dom": "^9.3.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^11.2.7",
"@testing-library/react-hooks": "^5.1.3",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^12.8.3",
"@types/jest": "^26.0.13",
"api-server": "file:../api-server",
Expand Down
26 changes: 14 additions & 12 deletions packages/dashboard/src/components/admin/drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import { SvgIconComponent } from '@mui/icons-material';
import AccountIcon from '@mui/icons-material/AccountCircle';
import SecurityIcon from '@mui/icons-material/Security';
import React from 'react';
import { matchPath, RouteProps, useHistory, useLocation, useRouteMatch } from 'react-router';
import { RouteProps, useNavigate, useLocation } from 'react-router';

export type AdminDrawerValues = 'Users' | 'Roles';

const drawerValuesRoutesMap: Record<AdminDrawerValues, RouteProps> = {
Users: { path: '/users', exact: true },
Roles: { path: '/roles', exact: true },
Users: { path: '/users' },
Roles: { path: '/roles' },
};

const prefix = 'drawer';
Expand Down Expand Up @@ -48,22 +48,24 @@ const StyledDrawer = styled((props: DrawerProps) => <Drawer {...props} />)(({ th

export function AdminDrawer(): JSX.Element {
const location = useLocation();
const history = useHistory();
const match = useRouteMatch();
const navigate = useNavigate();
const activeItem = React.useMemo<AdminDrawerValues>(() => {
const matched = Object.entries(drawerValuesRoutesMap).find(([_k, v]) =>
matchPath(location.pathname, `${match.path}${v.path}`),
const matched = Object.entries(drawerValuesRoutesMap).find(
([_k, v]) => location.pathname === `/admin${v.path}`,
);

return matched ? (matched[0] as AdminDrawerValues) : 'Users';
}, [location.pathname, match.path]);
}, [location.pathname]);

const DrawerItem = React.useCallback(
({ Icon, text, route }: { Icon: SvgIconComponent; text: AdminDrawerValues; route: string }) => {
return (
<ListItem
button
className={activeItem === text ? classes.activeItem : undefined}
onClick={() => history.push(route)}
onClick={() => {
navigate(route);
}}
>
<ListItemIcon>
<Icon className={classes.itemIcon} />
Expand All @@ -72,16 +74,16 @@ export function AdminDrawer(): JSX.Element {
</ListItem>
);
},
[activeItem, history],
[activeItem, navigate],
);

return (
<StyledDrawer variant="permanent" classes={{ paper: classes.drawerPaper }}>
<Toolbar />
<div className={classes.drawerContainer}>
<List>
<DrawerItem text="Users" route={`${match.path}/users`} Icon={AccountIcon} />
<DrawerItem text="Roles" route={`${match.path}/roles`} Icon={SecurityIcon} />
<DrawerItem text="Users" route={'users'} Icon={AccountIcon} />
<DrawerItem text="Roles" route={'roles'} Icon={SecurityIcon} />
</List>
</div>
</StyledDrawer>
Expand Down
27 changes: 9 additions & 18 deletions packages/dashboard/src/components/admin/router.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
import React from 'react';
import { Redirect, Route, Switch, useRouteMatch } from 'react-router-dom';
import { Route, Routes, Navigate, Outlet } from 'react-router-dom';
import { AdminDrawer } from './drawer';
import { RoleListPage } from './role-list-page';
import { UserListPage } from './user-list-page';
import { UserProfilePage } from './user-profile-page';

export function AdminRouter(): JSX.Element {
const match = useRouteMatch();

return (
<>
<Switch>
<Route path={`${match.path}/users/:user`}>
<UserProfilePage />
</Route>
<Route exact path={`${match.path}/users`}>
<UserListPage />
</Route>
<Route exact path={`${match.path}/roles`}>
<RoleListPage />
</Route>
<Route>
<Redirect to={`${match.path}/users`} />
</Route>
</Switch>
<AdminDrawer />
<Routes>
<Route path={'/*'} element={<Navigate to={'users'} />} />
<Route path={'/users/:username'} element={<UserProfilePage />} />
<Route path={'users'} element={<UserListPage />} />
<Route path={'roles'} element={<RoleListPage />} />
<Route element={<Navigate to={'users'} />} />
</Routes>
<Outlet />
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import { render, waitFor } from '@testing-library/react';
import { render, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { getActionText, RmfAction } from '../../permissions';
import { PermissionsCard } from '../permissions-card';

// TODO(AA): To remove after
// https://github.com/testing-library/react-testing-library/issues/1216
// has been resolved.
// Workaround for "Warning: An update to ComponentName inside a test was not
// wrapped in act(...)."
const originalError = console.error;
beforeAll(() => {
console.error = (...args) => {
if (/Warning.*not wrapped in act/.test(args[0])) {
return;
}
originalError.call(console, ...args);
};
});

afterAll(() => {
console.error = originalError;
});

describe('PermissionsCard', () => {
it('renders permissions', async () => {
const root = render(
Expand All @@ -25,15 +44,14 @@ describe('PermissionsCard', () => {

it('calls removePermission when button is clicked', async () => {
const removePermission = jest.fn();
const root = render(
const { getByText } = render(
<PermissionsCard
getPermissions={() => [{ action: RmfAction.TaskRead, authz_grp: 'test_group' }]}
removePermission={removePermission}
/>,
);
await waitFor(() => root.getByText('Remove'));
userEvent.click(root.getByText('Remove'));
await waitFor(() => root.getByLabelText('loading'));
await waitFor(() => getByText('Remove'));
fireEvent.click(getByText('Remove'));
expect(removePermission).toHaveBeenCalled();
expect(removePermission.mock.calls[0][0].action).toBe(RmfAction.TaskRead);
expect(removePermission.mock.calls[0][0].authz_grp).toBe('test_group');
Expand Down
11 changes: 3 additions & 8 deletions packages/dashboard/src/components/admin/user-list-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
import SearchIcon from '@mui/icons-material/Search';
import React from 'react';
import { ConfirmationDialog, Loading, useAsync } from 'react-components';
import { useHistory, useRouteMatch } from 'react-router';
import { useNavigate } from 'react-router';
import { AppControllerContext } from '../app-contexts';
import { CreateUserDialog, CreateUserDialogProps } from './create-user-dialog';

Expand Down Expand Up @@ -56,8 +56,7 @@ export function UserListCard({
createUser,
}: UserListCardProps): JSX.Element {
const safeAsync = useAsync();
const history = useHistory();
const match = useRouteMatch();
const navigate = useNavigate();
const [users, setUsers] = React.useState<string[]>([]);
const [selectedUser, setSelectedUser] = React.useState<string | null>(null);
const [search, setSearch] = React.useState('');
Expand Down Expand Up @@ -135,11 +134,7 @@ export function UserListCard({
</TableHead>
<TableBody>
{users.map((u) => (
<TableRow
key={u}
className={classes.tableRow}
onClick={() => history.push(`${match.path}/${u}`)}
>
<TableRow key={u} className={classes.tableRow} onClick={() => navigate(`${u}`)}>
<TableCell>{u}</TableCell>
<TableCell>
<Button
Expand Down
11 changes: 5 additions & 6 deletions packages/dashboard/src/components/admin/user-profile-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,33 @@ import { User } from 'api-client';
import { AxiosError } from 'axios';
import React from 'react';
import { useAsync } from 'react-components';
import { useRouteMatch } from 'react-router';
import { useParams } from 'react-router';
import { RmfAppContext } from '../rmf-app';
import { getApiErrorMessage } from '../utils';
import { ManageRolesCard } from './manage-roles-dialog';
import { adminPageClasses, AdminPageContainer } from './page-css';
import { UserProfileCard } from './user-profile';

export function UserProfilePage(): JSX.Element | null {
const match = useRouteMatch<{ user: string }>();
const userId: string | undefined = match.params.user;
const { username } = useParams();
const safeAsync = useAsync();
const { adminApi } = React.useContext(RmfAppContext) || {};
const [user, setUser] = React.useState<User | undefined>(undefined);
const [notFound, setNotFound] = React.useState(false);

const refresh = React.useCallback(() => {
if (!adminApi || !userId) return;
if (!adminApi || !username) return;
(async () => {
try {
setUser((await safeAsync(adminApi.getUserAdminUsersUsernameGet(userId))).data);
setUser((await safeAsync(adminApi.getUserAdminUsersUsernameGet(username))).data);
} catch (e) {
if ((e as AxiosError).response?.status !== 404) {
throw new Error(getApiErrorMessage(e));
}
setNotFound(true);
}
})();
}, [adminApi, safeAsync, userId]);
}, [adminApi, safeAsync, username]);

React.useEffect(() => {
refresh();
Expand Down
Loading
Loading