Skip to content

Commit

Permalink
feat: Breadcrumbs (#487)
Browse files Browse the repository at this point in the history
  • Loading branch information
josebui authored Aug 19, 2022
1 parent f117f5a commit 600545c
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 22 deletions.
10 changes: 9 additions & 1 deletion src/group/components/GroupView.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';

import _ from 'lodash/fp';
import { useTranslation } from 'react-i18next';
Expand All @@ -24,6 +24,7 @@ import PageContainer from 'layout/PageContainer';
import PageHeader from 'layout/PageHeader';
import PageLoader from 'layout/PageLoader';
import { useRefreshProgressContext } from 'layout/RefreshProgressProvider';
import { useBreadcrumbsParams } from 'navigation/breadcrumbsContext';
import Restricted from 'permissions/components/Restricted';

import { GroupContextProvider } from 'group/groupContext';
Expand Down Expand Up @@ -142,6 +143,13 @@ const GroupView = () => {
fetching
);

useBreadcrumbsParams(
useMemo(
() => ({ groupName: group?.name, loading: !group?.name }),
[group?.name]
)
);

useEffect(() => {
dispatch(fetchGroupView(slug));
}, [dispatch, slug]);
Expand Down
10 changes: 9 additions & 1 deletion src/group/membership/components/GroupMembers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';

import _ from 'lodash/fp';
import { usePermission } from 'permissions';
Expand All @@ -12,6 +12,7 @@ import { useDocumentTitle } from 'common/document';
import PageContainer from 'layout/PageContainer';
import PageHeader from 'layout/PageHeader';
import PageLoader from 'layout/PageLoader';
import { useBreadcrumbsParams } from 'navigation/breadcrumbsContext';

import { GroupContextProvider } from 'group/groupContext';
import { fetchGroupForMembers } from 'group/groupSlice';
Expand Down Expand Up @@ -40,6 +41,13 @@ const Header = () => {
fetching
);

useBreadcrumbsParams(
useMemo(
() => ({ groupName: group?.name, loading: !group?.name }),
[group?.name]
)
);

const { loading: loadingPermissions, allowed } = usePermission(
'group.manageMembers',
group
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import theme from 'theme';

import 'index.css';

import Breadcrumbs from 'navigation/Breadcrumbs';

const App = () => {
const contentRef = useRef();
const navigationRef = useRef();
Expand All @@ -43,6 +45,7 @@ const App = () => {
flex: 1,
}}
>
<Breadcrumbs />
<Routes />
</Box>
<Footer />
Expand Down
10 changes: 9 additions & 1 deletion src/landscape/components/LandscapeView.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';

import _ from 'lodash/fp';
import { Trans, useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -27,6 +27,7 @@ import PageContainer from 'layout/PageContainer';
import PageHeader from 'layout/PageHeader';
import PageLoader from 'layout/PageLoader';
import { useRefreshProgressContext } from 'layout/RefreshProgressProvider';
import { useBreadcrumbsParams } from 'navigation/breadcrumbsContext';
import Restricted from 'permissions/components/Restricted';

import { GroupContextProvider } from 'group/groupContext';
Expand Down Expand Up @@ -118,6 +119,13 @@ const LandscapeView = () => {
fetching
);

useBreadcrumbsParams(
useMemo(
() => ({ landscapeName: landscape?.name, loading: !landscape?.name }),
[landscape?.name]
)
);

useEffect(() => {
dispatch(fetchLandscapeView(slug));
}, [dispatch, slug]);
Expand Down
10 changes: 9 additions & 1 deletion src/landscape/membership/components/LandscapeMembers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';

import _ from 'lodash/fp';
import { usePermission } from 'permissions';
Expand All @@ -12,6 +12,7 @@ import { useDocumentTitle } from 'common/document';
import PageContainer from 'layout/PageContainer';
import PageHeader from 'layout/PageHeader';
import PageLoader from 'layout/PageLoader';
import { useBreadcrumbsParams } from 'navigation/breadcrumbsContext';

import { GroupContextProvider } from 'group/groupContext';
import GroupMembersList from 'group/membership/components/GroupMembersList';
Expand Down Expand Up @@ -42,6 +43,13 @@ const Header = ({ landscape, fetching }) => {
fetching
);

useBreadcrumbsParams(
useMemo(
() => ({ landscapeName: landscape?.name, loading: !landscape?.name }),
[landscape?.name]
)
);

if (loadingPermissions) {
return null;
}
Expand Down
10 changes: 8 additions & 2 deletions src/layout/AppWrappers.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import 'forms/yup';
// Analytics
import 'monitoring/analytics';

import { BreadcrumbsContextProvider } from 'navigation/breadcrumbsContext';

import RefreshProgressProvider from './RefreshProgressProvider';

// Wrappers
// Router, Theme, Global State, Permissions, Notifications
// Router, Theme, Global State, Permissions, Notifications, Breadcrumbs
const AppWrappers = ({ children, theme, store, permissionsRules }) => (
<React.StrictMode>
<BrowserRouter>
Expand All @@ -28,7 +30,11 @@ const AppWrappers = ({ children, theme, store, permissionsRules }) => (
<Provider store={store}>
<RefreshProgressProvider>
<PermissionsProvider rules={permissionsRules}>
<NotificationsWrapper>{children}</NotificationsWrapper>
<NotificationsWrapper>
<BreadcrumbsContextProvider>
{children}
</BreadcrumbsContextProvider>
</NotificationsWrapper>
</PermissionsProvider>
</RefreshProgressProvider>
</Provider>
Expand Down
11 changes: 8 additions & 3 deletions src/localization/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@
"members_list_pending_confirmation_message": "Do you want to deny {{userName}}’s request for membership in {{name}}?",
"members_list_pending_confirmation_button": "Deny Request",
"members_list_pending_reject": "Deny",
"home_pending_message": "Waiting for the group manager’s approval"
"home_pending_message": "Waiting for the group manager’s approval",
"breadcrumbs_view": "{{groupName}}",
"breadcrumbs_members": "Members"
},
"tool": {
"requirements": "System requirements",
Expand Down Expand Up @@ -191,7 +193,8 @@
"nav_label": "Main navigation",
"nav_label_short": "Main",
"skip_to_main_content": "Skip to main content",
"skip_to_main_navigation": "Skip to main navigation"
"skip_to_main_navigation": "Skip to main navigation",
"breadcrumbs_label": "Breadcrumbs"
},
"landscape": {
"add": "Add a Landscape",
Expand Down Expand Up @@ -292,7 +295,9 @@
"list_map_help": "<0>Can’t find your landscape on this map? <1>Add it to Terraso</1> or <3>learn more about landscapes</3>.</0>",
"list_map_help_url": "https://terraso.org/help/add-a-new-landscape-to-terraso/",
"list_map_section_label": "Landscapes map",
"list_map_popup_link": "View details about {{name}}"
"list_map_popup_link": "View details about {{name}}",
"breadcrumbs_view": "{{landscapeName}}",
"breadcrumbs_members": "Members"
},
"sharedData": {
"title": "Shared files",
Expand Down
11 changes: 8 additions & 3 deletions src/localization/locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@
"members_list_pending_confirmation_message": "¿Quieres denegar la solicitud de membresía de {{userName}} en {{name}}?",
"members_list_pending_confirmation_button": "Denegar solicitud",
"members_list_pending_reject": "Denegar",
"home_pending_message": "Esperando la aprobación del administrador del grupo"
"home_pending_message": "Esperando la aprobación del administrador del grupo",
"breadcrumbs_view": "{{groupName}}",
"breadcrumbs_members": "Miembros"
},
"tool": {
"avilability": "Disponibilidad",
Expand Down Expand Up @@ -191,7 +193,8 @@
"nav_label": "Navegación principal",
"nav_label_short": "Principal",
"skip_to_main_content": "Saltar al contenido principal",
"skip_to_main_navigation": "Saltar a la navegación principal"
"skip_to_main_navigation": "Saltar a la navegación principal",
"breadcrumbs_label": "Árbol de navegación"
},
"landscape": {
"add": "Agregar un paisaje",
Expand Down Expand Up @@ -293,7 +296,9 @@
"list_map_help": "<0>¿No puedes encontrar tu paisaje en este mapa? <1>Añádelo a Terraso</1> o <3>aprende más sobre paisajes</3>.</0>",
"list_map_help_url": "https://terraso.org/es/ayuda/anade-un-nuevo-paisaje-a-terraso/",
"list_map_section_label": "Mapa de paisajes",
"list_map_popup_link": "Ver detalles sobre {{name}}"
"list_map_popup_link": "Ver detalles sobre {{name}}",
"breadcrumbs_view": "{{landscapeName}}",
"breadcrumbs_members": "Miembros"
},
"common": {
"dialog_cancel_label": "Cancelar",
Expand Down
62 changes: 62 additions & 0 deletions src/navigation/Breadcrumbs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';

import _ from 'lodash/fp';
import { useTranslation } from 'react-i18next';
import { Link as RouterLink } from 'react-router-dom';

import {
Container,
Link,
Breadcrumbs as MuiBreadcrumbs,
Typography,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';

import { useBreadcrumbs } from './Routes';
import { useBreadcrumbsContext } from './breadcrumbsContext';

const Breadcrumbs = () => {
const { t } = useTranslation();
const breadcrumbs = useBreadcrumbs();
const { breadcrumbsParams } = useBreadcrumbsContext();
const { loading = true } = breadcrumbsParams;

if (loading || _.isEmpty(breadcrumbs)) {
return null;
}
return (
<>
<Typography sx={visuallyHidden} variant="h2">
{t('navigation.breadcrumbs_label')}
</Typography>
<Container
component={MuiBreadcrumbs}
aria-label={t('navigation.breadcrumbs_label')}
sx={{
mt: 3,
}}
>
<Link component={RouterLink} to="/">
{t('home.title')}
</Link>
{breadcrumbs.map(({ to, label, current }) => (
<Link
component={RouterLink}
to={to}
key={to}
{...(current
? {
color: 'gray.dark1',
'aria-current': 'page',
}
: {})}
>
{t(label, breadcrumbsParams)}
</Link>
))}
</Container>
</>
);
};

export default Breadcrumbs;
62 changes: 62 additions & 0 deletions src/navigation/Breadcrumbs.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { render, screen } from 'tests/utils';

import React, { useMemo } from 'react';

import { useLocation } from 'react-router-dom';

import Breadcrumbs from './Breadcrumbs';
import { useBreadcrumbsParams } from './breadcrumbsContext';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: jest.fn(),
}));

const TestComponent = () => {
useBreadcrumbsParams(
useMemo(() => ({ groupName: 'Group Name', loading: false }), [])
);
return <Breadcrumbs />;
};

const setup = async () => {
await render(<TestComponent />);
};
test('Breadcrumbs: Dont Show items', async () => {
useLocation.mockReturnValue({
pathname: '/groups',
});
await setup();
expect(
screen.queryByRole('navigation', { name: 'Breadcrumbs' })
).not.toBeInTheDocument();
});
test('Breadcrumbs: Show items', async () => {
useLocation.mockReturnValue({
pathname: '/groups/group-1/members',
});
await setup();
expect(
screen.getByRole('navigation', { name: 'Breadcrumbs' })
).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Home' })).toHaveAttribute(
'href',
'/'
);
expect(screen.getByRole('link', { name: 'Groups' })).toHaveAttribute(
'href',
'/groups'
);
expect(screen.getByRole('link', { name: 'Group Name' })).toHaveAttribute(
'href',
'/groups/group-1'
);
expect(screen.getByRole('link', { name: 'Members' })).toHaveAttribute(
'href',
'/groups/group-1/members'
);
expect(screen.getByRole('link', { name: 'Members' })).toHaveAttribute(
'aria-current',
'page'
);
});
Loading

0 comments on commit 600545c

Please sign in to comment.