Skip to content

Commit

Permalink
fix: refactor back button logic (#1391)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbacherycz authored Nov 25, 2024
1 parent bfa2f4c commit b42ad05
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 28 deletions.
2 changes: 1 addition & 1 deletion packages/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ function App() {
<Route path={AppRoutes.sent} element={<Inbox key="sent" viewType={'sent'} />} />
<Route path={AppRoutes.archive} element={<Inbox key="archive" viewType={'archive'} />} />
<Route path={AppRoutes.bin} element={<Inbox key="bin" viewType={'bin'} />} />
<Route path={AppRoutes.savedSearches} element={<SavedSearchesPage />} />
<Route path={AppRoutes.inboxItem} element={<InboxItemPage />} />
<Route path={AppRoutes.savedSearches} element={<SavedSearchesPage />} />
<Route path="*" element={<Navigate to="/" />} />
</Route>
<Route path="/loggedout" element={<Logout />} />
Expand Down
37 changes: 27 additions & 10 deletions packages/frontend/src/api/useParties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,24 @@ const fetchParties = async (): Promise<PartiesResult> => {
};
};

const setAllPartiesParam = (searchParamString: string) => {
const createParamsForKey = (searchParamString: string, key: string, value: string): URLSearchParams => {
const params = new URLSearchParams(searchParamString);
params.set('allParties', 'true');
return params.toString();
params.set(key, value);
return params;
};

export const useParties = (): UsePartiesOutput => {
const queryClient = useQueryClient();
const location = useLocation();
const [searchParams, setSearchParams] = useSearchParams();

const handleChangSearchParams = (searchParams: URLSearchParams) => {
/* Avoid setting search params if they are the same as the current ones */
if (searchParams.toString() !== new URLSearchParams(location.search).toString()) {
setSearchParams(searchParams, { replace: true });
}
};

const selectedPartiesQuery = useQuery<PartyFieldsFragment[]>({
queryKey: [QUERY_KEYS.SELECTED_PARTIES],
enabled: false,
Expand Down Expand Up @@ -87,15 +95,24 @@ export const useParties = (): UsePartiesOutput => {

const setSelectedPartyIds = (partyIds: string[], allOrganizationsSelected: boolean) => {
setAllOrganizationsSelected(allOrganizationsSelected);
const isPerson = partyIds[0].includes('person');
const isCurrentEndUser = partyIds[0].includes('person');
const searchParamsString = searchParams.toString();
if (allOrganizationsSelected) {
setSearchParams(setAllPartiesParam(searchParams.toString()), { replace: true });
} else if (isPerson) {
setSearchParams(stripQueryParamsForParty(searchParams.toString()), { replace: true });
const allPartiesParams = createParamsForKey(searchParamsString, 'allParties', 'true');
handleChangSearchParams(allPartiesParams);
} else if (isCurrentEndUser) {
/* endUser (type=person) is the only party selected and is default, this is the only case
* where we want to remove the party query param from the URL
*/
const currentEndUserParams = new URLSearchParams(stripQueryParamsForParty(searchParamsString));
handleChangSearchParams(currentEndUserParams);
} else {
const newSearchParams = new URLSearchParams(stripQueryParamsForParty(searchParams.toString()));
newSearchParams.append('party', encodeURIComponent(partyIds[0]));
setSearchParams(newSearchParams, { replace: true });
const params = createParamsForKey(
stripQueryParamsForParty(searchParamsString),
'party',
encodeURIComponent(partyIds[0]),
);
handleChangSearchParams(params);
}
setSelectedParties(data?.parties.filter((party) => partyIds.includes(party.party)) ?? []);
};
Expand Down
8 changes: 2 additions & 6 deletions packages/frontend/src/components/BackButton/BackButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ import { Link } from 'react-router-dom';
import { Button } from '@digdir/designsystemet-react';
import styles from './backButton.module.css';

interface BackButtonProps {
pathTo: string;
}

export function BackButton({ pathTo }: BackButtonProps) {
export function BackButton({ path }: { path: string }) {
const { t } = useTranslation();
return (
<Link to={pathTo} rel="noreferrer" className={styles.backLink}>
<Link to={path} rel="noreferrer" className={styles.backLink}>
<Button color="neutral" variant="tertiary" className={styles.backButton}>
<ArrowLeftIcon className={styles.backIcon} />
{t('word.back')}
Expand Down
9 changes: 4 additions & 5 deletions packages/frontend/src/components/InboxItem/InboxItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import type { Participant } from '../../api/useDialogById.tsx';
import type { InboxViewType } from '../../api/useDialogs.tsx';
import { FeatureFlagKeys } from '../../featureFlags';
import { useFeatureFlag } from '../../featureFlags';
import { FeatureFlagKeys, useFeatureFlag } from '../../featureFlags';
import type { InboxItemMetaField } from '../MetaDataFields';
import { MetaDataFields } from '../MetaDataFields';
import { useSelectedDialogs } from '../PageLayout';
Expand All @@ -20,11 +19,11 @@ interface InboxItemProps {
summary: string;
sender: Participant;
receiver: Participant;
linkTo: string;
isChecked?: boolean;
onCheckedChange?: (value: boolean) => void;
metaFields?: InboxItemMetaField[];
isUnread?: boolean;
linkTo?: string;
isMinimalistic?: boolean;
onClose?: () => void;
isSeenByEndUser?: boolean;
Expand All @@ -40,7 +39,7 @@ export const OptionalLinkContent = ({
}) => {
if (linkTo) {
return (
<Link to={linkTo} className={styles.link}>
<Link to={linkTo} state={{ fromView: location.pathname }} className={styles.link}>
{children}
</Link>
);
Expand Down Expand Up @@ -89,11 +88,11 @@ export const InboxItem = ({
metaFields = [],
onCheckedChange,
checkboxValue,
linkTo,
onClose,
isUnread = false,
isMinimalistic = false,
isChecked = false,
linkTo,
viewType,
}: InboxItemProps): JSX.Element => {
const { inSelectionMode } = useSelectedDialogs();
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/pages/Inbox/Inbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@ export const Inbox = ({ viewType }: InboxProps) => {
isChecked={selectedItems[item.id]}
onCheckedChange={(checked) => handleCheckedChange(item.id, checked)}
metaFields={item.metaFields}
linkTo={`/inbox/${item.id}/${location.search}`}
viewType={viewType}
linkTo={`/inbox/${item.id}/${location.search}`}
/>
))}
</InboxItems>
Expand Down
7 changes: 5 additions & 2 deletions packages/frontend/src/pages/InboxItemPage/InboxItemPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useQueryClient } from '@tanstack/react-query';
import { SystemLabel } from 'bff-types-generated';
import i18n from 'i18next';
import { useState } from 'react';
import { useParams } from 'react-router-dom';
import { useLocation, useParams } from 'react-router-dom';
import { updateSystemLabel } from '../../api/queries.ts';
import { useDialogById } from '../../api/useDialogById.tsx';
import { useDialogByIdSubscription } from '../../api/useDialogByIdSubscription.ts';
Expand All @@ -17,6 +17,7 @@ import styles from './inboxItemPage.module.css';
export const InboxItemPage = () => {
const { id } = useParams();
const { parties } = useParties();
const location = useLocation();
const { dialog, isLoading } = useDialogById(parties, id);
const [archiveLoading, setArchiveLoading] = useState<boolean>(false);
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
Expand Down Expand Up @@ -104,11 +105,13 @@ export const InboxItemPage = () => {
return <InboxItemPageSkeleton />;
}

const previousPath = (location?.state?.fromView ?? '/') + location.search;

return (
<main className={styles.itemInboxPage}>
<section className={styles.itemInboxPageContent}>
<nav className={styles.itemInboxNav}>
<BackButton pathTo="/inbox/" />
<BackButton path={previousPath} />
</nav>
<InboxItemDetail dialog={dialog} />
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { SavedSearchesFieldsFragment } from 'bff-types-generated';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { HorizontalLine } from '../../../components';
import { PlusIcon } from '../../../components/Icons/PlusIcon/PlusIcon.tsx';
import SearchFilterTag from '../SearchFilterTag/SearchFilterTag.tsx';
Expand Down Expand Up @@ -30,9 +31,9 @@ export const SavedSearchesItem = ({ savedSearch, actionPanel, isLast }: SavedSea
<>
<div className={styles.savedSearchItem} key={savedSearch.id}>
<div className={styles.searchDetails}>
<a href={`${fromView}?${queryParams.toString()}`} className={styles.goToSavedSearchLink}>
<Link to={`${fromView}?${queryParams.toString()}`} className={styles.goToSavedSearchLink}>
{savedSearch.name}
</a>
</Link>
</div>
<div>{actionPanel}</div>
</div>
Expand Down
50 changes: 50 additions & 0 deletions packages/frontend/tests/stories/messageNavigation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect, test } from '@playwright/test';
import { appURL } from '../';

test.describe('Message navigation', () => {
const pageWithMockOrganizations = `${appURL}&playwrightId=login-party-context`;

test('Back button navigates correctly and saves party', async ({ page }) => {
await page.goto(pageWithMockOrganizations);

await expect(page.getByRole('button', { name: 'Test Testesen' })).toBeVisible();
await expect(page.getByTestId('pageLayout-background')).not.toHaveClass(/.*isCompany.*/);
await expect(page.getByRole('link', { name: 'Skatten din for 2022' })).toBeVisible();
await page.getByRole('link', { name: 'Skatten din for 2022' }).click();
await page.getByRole('button', { name: 'Tilbake' }).click();
await expect(page.getByRole('button', { name: 'Test Testesen' })).toBeVisible();

await page.getByRole('button', { name: 'Test Testesen' }).click();
await page.locator('li').filter({ hasText: 'Firma AS' }).click();

await expect(page.getByRole('button', { name: 'Firma AS' })).toBeVisible();
await expect(page.getByRole('link', { name: 'This is a message 1 for Firma AS' })).toBeVisible();
await page.getByRole('link', { name: 'This is a message 1 for Firma AS' }).click();
await page.getByRole('button', { name: 'Tilbake' }).click();
await expect(page.getByRole('button', { name: 'Firma AS' })).toBeVisible();
await expect(page.getByTestId('pageLayout-background')).toHaveClass(/.*isCompany.*/);
expect(new URL(page.url()).searchParams.has('party')).toBe(true);

await page.getByRole('button', { name: 'Firma AS' }).click();
await page.locator('li').filter({ hasText: 'Alle virksomheter' }).click();
await expect(page.getByRole('button', { name: 'Alle virksomheter' })).toBeVisible();
await expect(page.getByRole('link', { name: 'This is a message 1 for Firma AS' })).toBeVisible();
await page.getByRole('link', { name: 'This is a message 1 for Firma AS' }).click();
await page.getByRole('button', { name: 'Tilbake' }).click();
await expect(page.getByRole('button', { name: 'Alle virksomheter' })).toBeVisible();
await expect(page.getByTestId('pageLayout-background')).toHaveClass(/.*isCompany.*/);
});

test('Back button navigates to previous page the message has been opened from', async ({ page }) => {
await page.goto(pageWithMockOrganizations);

await expect(page.locator('h2').filter({ hasText: /^Skatten din for 2022$/ })).toBeVisible();
await page.getByRole('link', { name: 'Skatten din for 2022' }).click();
await page.getByRole('button', { name: 'Flytt til papirkurv' }).click();

await page.getByRole('link', { name: 'Papirkurv' }).click();
await page.getByRole('link', { name: 'Skatten din for 2022' }).click();
await page.getByRole('button', { name: 'Tilbake' }).click();
await expect(page.getByRole('heading', { name: 'i papirkurv' })).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ export default {

export const Default: StoryObj<typeof BackButton> = {
args: {
pathTo: '/',
path: '/',
},
};

0 comments on commit b42ad05

Please sign in to comment.