Skip to content

Commit d52bd95

Browse files
committed
Improve additional tab url for emergencies
1 parent 5d43975 commit d52bd95

File tree

7 files changed

+109
-25
lines changed

7 files changed

+109
-25
lines changed

src/App/routes/SmartNavigate.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,28 @@ type RouteKey = string;
99

1010
interface Props extends NavigateProps {
1111
hashToRouteMap: Record<string, RouteKey>;
12+
forwardUnmatchedHashTo?: string;
1213
}
1314

1415
function SmartNavigate(props: Props) {
1516
const {
1617
hashToRouteMap,
18+
forwardUnmatchedHashTo,
1719
...navigateProps
1820
} = props;
21+
1922
const location = useLocation();
2023
const newRoute = isTruthyString(location.hash)
21-
? hashToRouteMap[location.hash]
24+
? (hashToRouteMap[location.hash] ?? forwardUnmatchedHashTo)
2225
: undefined;
2326

2427
if (isDefined(newRoute)) {
2528
return (
2629
<Navigate
27-
to={newRoute}
30+
to={{
31+
pathname: newRoute,
32+
hash: location.hash,
33+
}}
2834
replace
2935
/>
3036
);

src/App/routes/index.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,8 @@ const emergencyIndex = customWrapRoute({
611611
'#activities': 'activities',
612612
'#surge': 'surge',
613613
},
614+
// TODO: make this typesafe
615+
forwardUnmatchedHashTo: 'additional-info',
614616
},
615617
},
616618
context: {
@@ -670,6 +672,7 @@ const emergencySurge = customWrapRoute({
670672
},
671673
});
672674

675+
// TODO: remove this route
673676
const emergencyAdditionalInfoOne = customWrapRoute({
674677
parent: emergenciesLayout,
675678
path: 'additional-info-1',
@@ -684,6 +687,7 @@ const emergencyAdditionalInfoOne = customWrapRoute({
684687
visibility: 'anything',
685688
},
686689
});
690+
// TODO: remove this route
687691
const emergencyAdditionalInfoTwo = customWrapRoute({
688692
parent: emergenciesLayout,
689693
path: 'additional-info-2',
@@ -698,6 +702,7 @@ const emergencyAdditionalInfoTwo = customWrapRoute({
698702
visibility: 'anything',
699703
},
700704
});
705+
// TODO: remove this route
701706
const emergencyAdditionalInfoThree = customWrapRoute({
702707
parent: emergenciesLayout,
703708
path: 'additional-info-3',
@@ -713,6 +718,19 @@ const emergencyAdditionalInfoThree = customWrapRoute({
713718
},
714719
});
715720

721+
const emergencyAdditionalInfo = customWrapRoute({
722+
parent: emergenciesLayout,
723+
path: 'additional-info/:tabId?',
724+
component: {
725+
render: () => import('#views/EmergencyAdditionalTab'),
726+
props: {},
727+
},
728+
context: {
729+
title: 'Emergency Additional Info Tab',
730+
visibility: 'anything',
731+
},
732+
});
733+
716734
type DefaultSurgeChild = 'overview';
717735
const surgeLayout = customWrapRoute({
718736
parent: rootLayout,
@@ -2975,6 +2993,7 @@ const wrappedRoutes = {
29752993
emergencyAdditionalInfoOne,
29762994
emergencyAdditionalInfoTwo,
29772995
emergencyAdditionalInfoThree,
2996+
emergencyAdditionalInfo,
29782997
surgeLayout,
29792998
surgeOverview,
29802999
surgeOperationalToolbox,

src/components/NavigationTab/index.tsx

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { useCallback, useContext, useMemo } from 'react';
2-
import { _cs, isDefined } from '@togglecorp/fujs';
2+
import {
3+
_cs,
4+
isFalsyString,
5+
isNotDefined,
6+
isTruthyString,
7+
} from '@togglecorp/fujs';
38
import {
49
matchPath,
510
NavLink,
@@ -19,9 +24,11 @@ interface Props {
1924
children?: React.ReactNode;
2025
stepCompleted?: boolean;
2126
title?: string;
22-
parentRoute?: boolean;
2327
disabled?: boolean;
2428

29+
parentRoute?: boolean;
30+
matchParam?: string;
31+
2532
to: keyof WrappedRoutes | undefined | null;
2633

2734
urlParams?: UrlParams;
@@ -39,8 +46,9 @@ function NavigationTab(props: Props) {
3946
className,
4047
title,
4148
stepCompleted,
42-
parentRoute = false,
4349
disabled: disabledFromProps,
50+
parentRoute = false,
51+
matchParam,
4452
} = props;
4553

4654
const {
@@ -71,13 +79,42 @@ function NavigationTab(props: Props) {
7179
}
7280
}, [disabled]);
7381

74-
const isActive = isDefined(to) && matchPath(
75-
{
76-
// eslint-disable-next-line react/destructuring-assignment
77-
path: routes[to].absolutePath,
78-
end: !parentRoute,
82+
const matchParamValue = isTruthyString(matchParam)
83+
? urlParams?.[matchParam]
84+
: undefined;
85+
86+
const isActive = useMemo(
87+
() => {
88+
if (isNotDefined(to)) {
89+
return false;
90+
}
91+
92+
const match = matchPath(
93+
{
94+
// eslint-disable-next-line react/destructuring-assignment
95+
path: routes[to].absolutePath,
96+
end: !parentRoute,
97+
},
98+
location.pathname,
99+
);
100+
101+
if (isNotDefined(match)) {
102+
return false;
103+
}
104+
105+
if (isTruthyString(matchParam)) {
106+
const paramValue = match.params[matchParam];
107+
108+
if (isFalsyString(paramValue)) {
109+
return false;
110+
}
111+
112+
return matchParamValue === paramValue;
113+
}
114+
115+
return true;
79116
},
80-
location.pathname,
117+
[to, routes, location.pathname, matchParam, parentRoute, matchParamValue],
81118
);
82119

83120
const linkClassName = useMemo(

src/utils/outletContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type Snippets = EmergencySnippetsResponse['results'];
99

1010
interface EmergencyAdditionalTabs {
1111
name: string;
12+
tabId: string;
1213
infoPageId: 1 | 2 | 3;
1314
routeName: string;
1415
snippets: Snippets;

src/views/Emergency/index.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,28 +160,41 @@ export function Component() {
160160
return [];
161161
}
162162

163-
const tabOneTitle = emergencyResponse.tab_one_title || 'Tab One';
164-
const tabTwoTitle = emergencyResponse.tab_two_title || 'Tab Two';
165-
const tabThreeTitle = emergencyResponse.tab_three_title || 'Tab Three';
163+
const tabOneTitle = emergencyResponse.tab_one_title || 'Additional Info 1';
164+
const tabTwoTitle = emergencyResponse.tab_two_title || 'Additional Info 2';
165+
const tabThreeTitle = emergencyResponse.tab_three_title || 'Additional Info 3';
166+
167+
function toKebabCase(str: string) {
168+
return str.toLocaleLowerCase().split(' ').join('-');
169+
}
166170

167171
return [
168172
{
169173
name: tabOneTitle,
174+
tabId: toKebabCase(tabOneTitle),
170175
routeName: 'emergencyAdditionalInfoOne' as const,
171176
infoPageId: 1 as const,
172-
snippets: emergencySnippetResponse.results.filter((snippet) => snippet.tab === 1),
177+
snippets: emergencySnippetResponse.results.filter(
178+
(snippet) => snippet.tab === 1,
179+
),
173180
},
174181
{
175182
name: tabTwoTitle,
183+
tabId: toKebabCase(tabTwoTitle),
176184
routeName: 'emergencyAdditionalInfoTwo' as const,
177185
infoPageId: 2 as const,
178-
snippets: emergencySnippetResponse.results.filter((snippet) => snippet.tab === 2),
186+
snippets: emergencySnippetResponse.results.filter(
187+
(snippet) => snippet.tab === 2,
188+
),
179189
},
180190
{
181191
name: tabThreeTitle,
192+
tabId: toKebabCase(tabThreeTitle),
182193
routeName: 'emergencyAdditionalInfoThree' as const,
183194
infoPageId: 3 as const,
184-
snippets: emergencySnippetResponse.results.filter((snippet) => snippet.tab === 3),
195+
snippets: emergencySnippetResponse.results.filter(
196+
(snippet) => snippet.tab === 3,
197+
),
185198
},
186199
].filter((tabInfo) => tabInfo.snippets.length > 0);
187200
}, [emergencyResponse, emergencySnippetResponse]);
@@ -328,11 +341,13 @@ export function Component() {
328341
)}
329342
{emergencyAdditionalTabs.map((tab) => (
330343
<NavigationTab
331-
key={tab.routeName}
332-
to={tab.routeName}
344+
key={tab.tabId}
345+
to="emergencyAdditionalInfo"
333346
urlParams={{
334347
emergencyId,
348+
tabId: tab.tabId,
335349
}}
350+
matchParam="tabId"
336351
>
337352
{tab.name}
338353
</NavigationTab>

src/views/EmergencyAdditionalTab/index.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
1-
import { useOutletContext } from 'react-router-dom';
1+
import {
2+
useOutletContext,
3+
useParams,
4+
} from 'react-router-dom';
5+
26
import { type EmergencyOutletContext } from '#utils/outletContext';
37
import HtmlOutput from '#components/HtmlOutput';
48

59
import styles from './styles.module.css';
610

711
interface Props {
8-
infoPageId: number;
12+
infoPageId?: number;
913
}
1014

1115
// eslint-disable-next-line import/prefer-default-export
1216
export function Component(props: Props) {
17+
const urlParams = useParams<{ tabId: string }>();
1318
const { infoPageId } = props;
1419
const { emergencyAdditionalTabs } = useOutletContext<EmergencyOutletContext>();
1520

1621
const additionalTab = emergencyAdditionalTabs?.find(
17-
(tab) => tab.infoPageId === infoPageId,
22+
(tab) => tab.infoPageId === infoPageId
23+
|| urlParams.tabId === tab.tabId,
1824
);
25+
1926
return (
20-
<div
21-
className={styles.additionalTab}
22-
>
27+
<div className={styles.additionalTab}>
2328
{additionalTab?.snippets?.map((snippet) => (
2429
<HtmlOutput
2530
key={snippet.id}

src/views/EmergencySlug/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function Component() {
3232
}
3333

3434
// FIXME: Add a wrapper around navigate
35+
// We can also use generatePath
3536
return (
3637
<Navigate to={`/emergencies/${emergencyResponse?.id}`} replace />
3738
);

0 commit comments

Comments
 (0)