Skip to content

Commit 2c25fd6

Browse files
committed
Improve TransportMode icon testability
1 parent 4d8cdd8 commit 2c25fd6

10 files changed

Lines changed: 208 additions & 46 deletions

File tree

ui/src/components/map/markers/StopMarker.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { FC, MouseEventHandler, useEffect, useState } from 'react';
2+
import { StopRegistryTransportModeType } from '../../../generated/graphql';
23
import { MapStop } from '../types';
34

45
const testIds = {
@@ -25,6 +26,9 @@ type StopMarkerBaseProps = {
2526
readonly centerDot?: boolean;
2627
readonly inSelection?: boolean;
2728
readonly showLabel?: boolean;
29+
readonly transportModes?: ReadonlyArray<StopRegistryTransportModeType>;
30+
readonly trunkLine?: boolean;
31+
readonly speedTram?: boolean;
2832
};
2933

3034
type ExistingStopMarkerSpecialProps = {
@@ -57,6 +61,9 @@ export const StopMarker: FC<StopMarkerProps> = ({
5761
onResolveTitle,
5862
stop,
5963
showLabel = false,
64+
transportModes = [],
65+
trunkLine = false,
66+
speedTram = false,
6067
}) => {
6168
const [isMouseHovering, setIsMouseHovering] = useState(false);
6269
const [promisedTitle, setPromisedTitle] = useState<PromisedTitle | null>(
@@ -107,6 +114,9 @@ export const StopMarker: FC<StopMarkerProps> = ({
107114
onClick={onClick ? () => onClick(stop) : undefined}
108115
aria-label={stop?.label}
109116
data-testid={testId}
117+
data-transport-modes={transportModes.join(',')}
118+
data-trunk-line={trunkLine}
119+
data-speed-tram={speedTram}
110120
>
111121
<svg
112122
data-testid={testIds.icon}

ui/src/components/map/stops/Stop.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ export const Stop: FC<StopProps> = ({
239239
centerDot={selected}
240240
inSelection={inSelection}
241241
showLabel={showLabel}
242+
transportModes={activeTransportModes}
243+
trunkLine={isTrunkLineStop}
244+
speedTram={isSpeedTramStop}
242245
// eslint-disable-next-line react/jsx-props-no-spreading
243246
{...(stop
244247
? ({

ui/src/components/stop-registry/components/StopTableRow/components/LabelAndTimingPlaceTd.tsx

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import { DateTime } from 'luxon';
22
import { FC } from 'react';
33
import { useTranslation } from 'react-i18next';
44
import { Link } from 'react-router';
5-
import { twJoin } from 'tailwind-merge';
6-
import { mapStopRegistryTransportModeTypeToUiName } from '../../../../../i18n/uiNameMappings';
75
import { Row } from '../../../../../layoutComponents';
86
import { Path, routeDetails } from '../../../../../router/routeDetails';
9-
import { getTransportModeIcon } from '../../../utils/getTransportModeIcon';
7+
import { StopTransportModeIcon } from '../../StopTransportModeIcon';
108
import { StopRowTdProps } from '../types';
119

1210
const testIds = {
@@ -28,18 +26,13 @@ export const LabelAndTimingPlaceTd: FC<LabelAndTimingPlaceTdProps> = ({
2826
<td className={className}>
2927
<Row className="items-center leading-none font-bold">
3028
{stop.transportModes.map((mode) => (
31-
<i
29+
<StopTransportModeIcon
3230
key={mode}
33-
className={twJoin(
34-
getTransportModeIcon(
35-
mode,
36-
stop.activeTransportModes.includes(mode),
37-
stop.trunkLineStop,
38-
stop.speedTramStop,
39-
),
40-
'text-xl not-last-of-type:-mr-1',
41-
)}
42-
title={mapStopRegistryTransportModeTypeToUiName(t, mode)}
31+
className="text-xl not-last-of-type:-mr-1"
32+
mode={mode}
33+
active={stop.activeTransportModes.includes(mode)}
34+
trunkLine={stop.trunkLineStop}
35+
speedTram={stop.speedTramStop}
4336
/>
4437
))}
4538

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { FC } from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
import { twJoin, twMerge } from 'tailwind-merge';
4+
import { StopRegistryTransportModeType } from '../../../generated/graphql';
5+
import {
6+
getTransportModeIcon,
7+
getTransportModeIconTitle,
8+
} from '../utils/getTransportModeIcon';
9+
10+
const testIds = {
11+
unknownType: 'StopTransportModeIcon::unknown',
12+
icon: 'StopTransportModeIcon::icon',
13+
};
14+
15+
type StopTransportModeIconProps = {
16+
readonly className?: string;
17+
readonly mode: StopRegistryTransportModeType | null | undefined;
18+
readonly active: boolean;
19+
readonly trunkLine: boolean;
20+
readonly speedTram: boolean;
21+
};
22+
23+
export const StopTransportModeIcon: FC<StopTransportModeIconProps> = ({
24+
className,
25+
mode,
26+
active,
27+
trunkLine,
28+
speedTram,
29+
}) => {
30+
const { t } = useTranslation();
31+
32+
if (!mode) {
33+
return (
34+
<i
35+
className={twMerge('icon-placeholder-dot text-light-grey', className)}
36+
title={t(($) => $.accessibility.stops.transportMode.unknownModeTitle)}
37+
role="img"
38+
data-testid={testIds.unknownType}
39+
/>
40+
);
41+
}
42+
43+
return (
44+
<i
45+
className={twJoin(
46+
getTransportModeIcon(mode, active, trunkLine, speedTram),
47+
className,
48+
)}
49+
title={getTransportModeIconTitle(t, mode, active, trunkLine, speedTram)}
50+
role="img"
51+
data-testid={testIds.icon}
52+
data-transport-mode={mode}
53+
data-active={active}
54+
data-trunk-line={trunkLine && mode === StopRegistryTransportModeType.Bus}
55+
data-speed-tram={speedTram && mode === StopRegistryTransportModeType.Tram}
56+
/>
57+
);
58+
};

ui/src/components/stop-registry/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './MenuItems/OpenDetailsPage';
22
export * from './MenuItems/ShowOnMap';
33
export * from './LocatorActionButton';
44
export * from './StopTableRow';
5+
export * from './StopTransportModeIcon';

ui/src/components/stop-registry/stops/stop-details/StopDetailsPage.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import compact from 'lodash/compact';
2-
import { FC, useContext, useMemo, useState } from 'react';
1+
import { FC, useContext, useState } from 'react';
32
import { useTranslation } from 'react-i18next';
43
import { MdWarning } from 'react-icons/md';
54
import { Link } from 'react-router';
@@ -59,17 +58,12 @@ export const StopDetailsPage: FC = () => {
5958

6059
const { stopDetails, loading, error, mirroredQuays } = useGetStopDetails();
6160

62-
const mirroredTransportModes = useMemo(
63-
() => compact(mirroredQuays.map((mq) => mq.stopPlace.transportMode)),
64-
[mirroredQuays],
65-
);
66-
6761
return (
6862
<Container testId={testIds.page}>
6963
<StopTitleRow
7064
stopDetails={stopDetails}
7165
label={label}
72-
mirroredTransportModes={mirroredTransportModes}
66+
mirroredQuays={mirroredQuays}
7367
/>
7468
<StopHeaderSummaryRow className="my-2" stopDetails={stopDetails} />
7569
<StopDetailsVersion label={label} />

ui/src/components/stop-registry/stops/stop-details/title-row/StopTitleRow.tsx

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import compact from 'lodash/compact';
2+
import sortBy from 'lodash/sortBy';
23
import uniq from 'lodash/uniq';
34
import { FC } from 'react';
4-
import { twJoin } from 'tailwind-merge';
55
import { StopRegistryTransportModeType } from '../../../../../generated/graphql';
66
import { StopWithDetails } from '../../../../../types';
77
import { StopPlaceState } from '../../../../../types/stop-registry';
88
import { PageTitle } from '../../../../common';
9-
import { getTransportModeIcon } from '../../../utils/getTransportModeIcon';
9+
import { StopTransportModeIcon } from '../../../components';
10+
import { MirroredQuayDetails } from '../useGetStopDetails';
1011
import { ExtraActions } from './ExtraActions';
1112
import { OpenOnMapButton } from './OpenOnMapButton';
1213
import { StopTimetablesButton } from './StopTimetablesButton';
@@ -16,37 +17,65 @@ const testIds = {
1617
names: 'StopTitleRow::names',
1718
};
1819

20+
type ModeStatus = {
21+
readonly mode: StopRegistryTransportModeType | null;
22+
readonly active: boolean;
23+
readonly trunkLine: boolean;
24+
readonly speedTram: boolean;
25+
};
26+
27+
function getModeStatus(
28+
stop: MirroredQuayDetails | StopWithDetails,
29+
): ModeStatus {
30+
const stopPlace = 'stopPlace' in stop ? stop.stopPlace : stop.stop_place;
31+
32+
return {
33+
mode: stopPlace?.transportMode ?? null,
34+
active: stop.quay?.stopState === StopPlaceState.InOperation,
35+
trunkLine: !!stop.quay?.stopType.trunkLineStop,
36+
speedTram: !!stop.quay?.stopType.speedTramStop,
37+
};
38+
}
39+
40+
function resolveModes(
41+
stopDetails: StopWithDetails | null,
42+
mirroredQuays: ReadonlyArray<MirroredQuayDetails>,
43+
): Array<ModeStatus> {
44+
if (!stopDetails) {
45+
return [];
46+
}
47+
48+
return sortBy(
49+
[getModeStatus(stopDetails), ...mirroredQuays.map(getModeStatus)],
50+
(status) => status.mode,
51+
);
52+
}
53+
1954
type StopTitleRowProps = {
2055
readonly stopDetails: StopWithDetails | null;
2156
readonly label: string;
22-
readonly mirroredTransportModes?: ReadonlyArray<StopRegistryTransportModeType>;
57+
readonly mirroredQuays: ReadonlyArray<MirroredQuayDetails>;
2358
};
2459

2560
export const StopTitleRow: FC<StopTitleRowProps> = ({
2661
stopDetails,
2762
label,
28-
mirroredTransportModes = [],
63+
mirroredQuays,
2964
}) => {
30-
const ownMode = stopDetails?.stop_place?.transportMode;
31-
const allModes = uniq(compact([ownMode, ...mirroredTransportModes]));
32-
3365
return (
3466
<div className="flex items-center gap-2">
35-
{allModes.map((mode) => (
36-
<i
37-
key={mode}
38-
className={twJoin(
39-
getTransportModeIcon(
40-
mode,
41-
(stopDetails?.quay?.stopState ?? StopPlaceState.InOperation) ===
42-
StopPlaceState.InOperation,
43-
stopDetails?.quay?.stopType.trunkLineStop,
44-
stopDetails?.quay?.stopType.speedTramStop,
45-
),
46-
'text-3xl',
47-
)}
48-
/>
49-
))}
67+
{resolveModes(stopDetails, mirroredQuays).map(
68+
({ mode, active, trunkLine, speedTram }) => (
69+
<StopTransportModeIcon
70+
key={mode}
71+
className="text-3xl"
72+
mode={mode}
73+
active={active}
74+
trunkLine={trunkLine}
75+
speedTram={speedTram}
76+
/>
77+
),
78+
)}
5079
<PageTitle.H1
5180
className="mr-2"
5281
testId={testIds.label}

ui/src/components/stop-registry/utils/getTransportModeIcon.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TFunction } from 'i18next';
12
import { twJoin } from 'tailwind-merge';
23
import { StopRegistryTransportModeType } from '../../../generated/graphql';
34

@@ -58,3 +59,50 @@ export function getTransportModeIcon(
5859
return twJoin('icon-bus-alt', getBusColor(active, trunkLine));
5960
}
6061
}
62+
63+
function getTransportModeTypeText(
64+
t: TFunction,
65+
mode: StopRegistryTransportModeType | null | undefined,
66+
trunkLine: boolean,
67+
speedTram: boolean,
68+
) {
69+
switch (mode) {
70+
case StopRegistryTransportModeType.Tram:
71+
return speedTram
72+
? t(($) => $.accessibility.stops.transportMode.type_speedTram)
73+
: t(($) => $.accessibility.stops.transportMode.type_tram);
74+
75+
case StopRegistryTransportModeType.Metro:
76+
return t(($) => $.accessibility.stops.transportMode.type_metro);
77+
78+
case StopRegistryTransportModeType.Water:
79+
return t(($) => $.accessibility.stops.transportMode.type_water);
80+
81+
case StopRegistryTransportModeType.Rail:
82+
return t(($) => $.accessibility.stops.transportMode.type_rail);
83+
84+
case StopRegistryTransportModeType.Bus:
85+
default:
86+
return trunkLine
87+
? t(($) => $.accessibility.stops.transportMode.type_trunkLine)
88+
: t(($) => $.accessibility.stops.transportMode.type_bus);
89+
}
90+
}
91+
92+
export function getTransportModeIconTitle(
93+
t: TFunction,
94+
mode: StopRegistryTransportModeType | null | undefined,
95+
active: boolean = true,
96+
trunkLine: boolean = false,
97+
speedTram: boolean = false,
98+
) {
99+
const stopType = getTransportModeTypeText(t, mode, trunkLine, speedTram);
100+
const stopTypeState = active
101+
? t(($) => $.accessibility.stops.transportMode.status_inUse)
102+
: t(($) => $.accessibility.stops.transportMode.status_outOfUse);
103+
104+
return t(($) => $.accessibility.stops.transportMode.stopIconTitle, {
105+
stopType,
106+
stopTypeState,
107+
});
108+
}

ui/src/locales/en-US/common.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1695,7 +1695,20 @@
16951695
"addExternalLink": "Add external link",
16961696
"openExternalLink": "Open external link {{ linkTitle }}",
16971697
"editStopValidity": "Edit version for stop {{ stopLabel }}",
1698-
"stopTimetablesButton": "Show timetables for stop"
1698+
"stopTimetablesButton": "Show timetables for stop",
1699+
"transportMode": {
1700+
"unknownModeTitle": "Transport mode has not been defined",
1701+
"stopIconTitle": "Stop type: {{stopType}}. Stop status: {{stopTypeState}}",
1702+
"type_bus": "bus",
1703+
"type_trunkLine": "trunk line",
1704+
"type_tram": "tram",
1705+
"type_speedTram": "speed tram",
1706+
"type_metro": "metro",
1707+
"type_rail": "train",
1708+
"type_water": "ferry",
1709+
"status_inUse": "in use",
1710+
"status_outOfUse": "out of use"
1711+
}
16991712
},
17001713
"stopAreas": {
17011714
"showStopAreaDetails": "Show details for stop area {{ areaLabel }}"

ui/src/locales/fi-FI/common.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1695,7 +1695,20 @@
16951695
"addExternalLink": "Lisää ulkoinen linkki",
16961696
"openExternalLink": "Avaa ulkoinen linkki {{ linkTitle }}",
16971697
"editStopValidity": "Muokkaa pysäkin {{ stopLabel }} versiota",
1698-
"stopTimetablesButton": "Näytä aikataulut pysäkille"
1698+
"stopTimetablesButton": "Näytä aikataulut pysäkille",
1699+
"transportMode": {
1700+
"unknownModeTitle": "Liikennemuotoa ei ole määritelty",
1701+
"stopIconTitle": "Pysäkin tyyppi: {{stopType}}. Pysäkin tila: {{stopTypeState}}",
1702+
"type_bus": "bussi",
1703+
"type_trunkLine": "runkolinja",
1704+
"type_tram": "raitiotie",
1705+
"type_speedTram": "pikaraitiotie",
1706+
"type_metro": "metro",
1707+
"type_rail": "juna",
1708+
"type_water": "lautta",
1709+
"status_inUse": "käytössä",
1710+
"status_outOfUse": "ei käytössä"
1711+
}
16991712
},
17001713
"stopAreas": {
17011714
"showStopAreaDetails": "Näytä pysäkkialueen {{ areaLabel }} tiedot"

0 commit comments

Comments
 (0)