@opentripplanner/map-popup 6.1.0-alpha.2 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import styled from "styled-components";
4
4
  import { Station, Stop } from "@opentripplanner/types";
5
5
  import { IntlProvider } from "react-intl";
6
6
  import { Meta } from "@storybook/react";
7
- import MapPopupContents from "./index";
7
+ import MapPopupContents, { Feed } from "./index";
8
8
 
9
9
  // HOC to wrap components with IntlProvider
10
10
  const withIntl = (Story: () => JSX.Element) => (
@@ -18,7 +18,7 @@ export default {
18
18
  decorators: [withIntl]
19
19
  } as Meta;
20
20
 
21
- const STOP = {
21
+ const STOP_NO_CODE = {
22
22
  flex: false,
23
23
  gtfsId: "9526",
24
24
  id: "9526",
@@ -27,6 +27,47 @@ const STOP = {
27
27
  name: "W Burnside & SW 2nd"
28
28
  };
29
29
 
30
+ const STOP_WITH_CODE = {
31
+ flex: false,
32
+ code: "9526",
33
+ gtfsId: "9526",
34
+ id: "9526",
35
+ lat: 45.523009,
36
+ lon: -122.672529,
37
+ name: "W Burnside & SW 2nd"
38
+ };
39
+
40
+ const STOP_WITH_FEED_ID = {
41
+ flex: false,
42
+ code: "9526",
43
+ gtfsId: "trimet:9526",
44
+ id: "trimet:9526",
45
+ lat: 45.523009,
46
+ lon: -122.672529,
47
+ name: "W Burnside & SW 2nd"
48
+ };
49
+
50
+ const SAMPLE_FEEDS: Feed[] = [
51
+ {
52
+ feedId: "trimet",
53
+ publisher: {
54
+ name: "TriMet"
55
+ }
56
+ },
57
+ {
58
+ feedId: "c-tran",
59
+ publisher: {
60
+ name: "C-TRAN"
61
+ }
62
+ },
63
+ {
64
+ feedId: "portland-streetcar",
65
+ publisher: {
66
+ name: "Portland Streetcar"
67
+ }
68
+ }
69
+ ];
70
+
30
71
  const STATION = {
31
72
  "stroke-width": 2,
32
73
  allowDropoff: true,
@@ -91,14 +132,25 @@ const getEntityPrefixExample = (entity: Stop | Station) => {
91
132
 
92
133
  export const StopEntity = (): JSX.Element => (
93
134
  <MapPopupContents
94
- entity={STOP}
135
+ entity={STOP_WITH_CODE}
136
+ feeds={SAMPLE_FEEDS}
137
+ setLocation={action("setLocation")}
138
+ setViewedStop={action("setViewedStop")}
139
+ />
140
+ );
141
+
142
+ export const StopEntityWithFeedName = (): JSX.Element => (
143
+ <MapPopupContents
144
+ entity={STOP_WITH_FEED_ID}
145
+ feeds={SAMPLE_FEEDS}
95
146
  setLocation={action("setLocation")}
96
147
  setViewedStop={action("setViewedStop")}
97
148
  />
98
149
  );
99
- export const StopEntitywithEntityPrefix = (): JSX.Element => (
150
+
151
+ export const StopEntityWithEntityPrefix = (): JSX.Element => (
100
152
  <MapPopupContents
101
- entity={STOP}
153
+ entity={STOP_WITH_CODE}
102
154
  getEntityPrefix={getEntityPrefixExample}
103
155
  setLocation={action("setLocation")}
104
156
  setViewedStop={action("setViewedStop")}
@@ -106,7 +158,15 @@ export const StopEntitywithEntityPrefix = (): JSX.Element => (
106
158
  );
107
159
 
108
160
  export const StopEntityNoHandlers = (): JSX.Element => (
109
- <MapPopupContents entity={STOP} />
161
+ <MapPopupContents entity={STOP_WITH_CODE} />
162
+ );
163
+
164
+ export const StopEntityNoStopCode = (): JSX.Element => (
165
+ <MapPopupContents
166
+ entity={STOP_NO_CODE}
167
+ setLocation={action("setLocation")}
168
+ setViewedStop={action("setViewedStop")}
169
+ />
110
170
  );
111
171
 
112
172
  export const StationEntity = (): JSX.Element => (
@@ -191,7 +191,7 @@ exports[`Map Popup StopEntity smoke-test 1`] = `
191
191
  <strong>
192
192
  Stop ID: 9526
193
193
  </strong>
194
- <button class="styled__ViewStopButton-sc-12v7ov3-0 fRdwNC">
194
+ <button class="styled__ViewStopButton-sc-12v7ov3-0 hXaHvR">
195
195
  Stop Viewer
196
196
  </button>
197
197
  </p>
@@ -256,7 +256,69 @@ exports[`Map Popup StopEntityNoHandlers smoke-test 1`] = `
256
256
  </div>
257
257
  `;
258
258
 
259
- exports[`Map Popup StopEntitywithEntityPrefix smoke-test 1`] = `
259
+ exports[`Map Popup StopEntityNoStopCode smoke-test 1`] = `
260
+ <div class="styled__MapOverlayPopup-sc-12kjso7-1 cPqJxe">
261
+ <div id="focus-9526-popup-focus-trap"
262
+ role="presentation"
263
+ >
264
+ <header class="styled__PopupTitle-sc-12kjso7-3 jRNaQh">
265
+ W Burnside &amp; SW 2nd
266
+ </header>
267
+ <p class="styled__PopupRow-sc-12kjso7-2 ckOOWr">
268
+ <button class="styled__ViewStopButton-sc-12v7ov3-0 bbtcwi">
269
+ Stop Viewer
270
+ </button>
271
+ </p>
272
+ <p class="styled__PopupRow-sc-12kjso7-2 ckOOWr">
273
+ <strong>
274
+ Plan a trip:
275
+ </strong>
276
+ <span class="styled__FromToPickerSpan-sc-vb4790-1 giBPod">
277
+ <span class="styled__LocationPickerSpan-sc-vb4790-0 gsVfXo">
278
+ <svg viewbox="0 0 512 512"
279
+ height="0.9em"
280
+ width="0.9em"
281
+ aria-hidden="true"
282
+ focusable="false"
283
+ fill="currentColor"
284
+ xmlns="http://www.w3.org/2000/svg"
285
+ class="StyledIconBase-sc-ea9ulj-0 ebjPRL styled__FromIcon-sc-n5xcvc-0 dDqEuj"
286
+ >
287
+ <path fill="currentColor"
288
+ d="M160 256c0-53.9 42.1-96 96-96 53 0 96 42.1 96 96 0 53-43 96-96 96-53.9 0-96-43-96-96zm352 0c0 141.4-114.6 256-256 256S0 397.4 0 256 114.6 0 256 0s256 114.6 256 256zM256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48z"
289
+ >
290
+ </path>
291
+ </svg>
292
+ <button class="styled__Button-sc-vb4790-2 ekklXB">
293
+ From here
294
+ </button>
295
+ </span>
296
+ <span class="styled__LocationPickerSpan-sc-vb4790-0 gsVfXo">
297
+ <svg viewbox="0 0 384 512"
298
+ height="0.9em"
299
+ width="0.9em"
300
+ aria-hidden="true"
301
+ focusable="false"
302
+ fill="currentColor"
303
+ xmlns="http://www.w3.org/2000/svg"
304
+ class="StyledIconBase-sc-ea9ulj-0 ebjPRL styled__ToIcon-sc-n5xcvc-2 gZxRwk"
305
+ >
306
+ <path fill="currentColor"
307
+ d="M215.7 499.2C267 435 384 279.4 384 192 384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2 12.3 15.3 35.1 15.3 47.4 0zM192 256c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z"
308
+ >
309
+ </path>
310
+ </svg>
311
+ <button class="styled__Button-sc-vb4790-2 ekklXB">
312
+ To here
313
+ </button>
314
+ </span>
315
+ </span>
316
+ </p>
317
+ </div>
318
+ </div>
319
+ `;
320
+
321
+ exports[`Map Popup StopEntityWithEntityPrefix smoke-test 1`] = `
260
322
  <div class="styled__MapOverlayPopup-sc-12kjso7-1 cPqJxe">
261
323
  <div id="focus-9526-popup-focus-trap"
262
324
  role="presentation"
@@ -271,7 +333,72 @@ exports[`Map Popup StopEntitywithEntityPrefix smoke-test 1`] = `
271
333
  <strong>
272
334
  Stop ID: 9526
273
335
  </strong>
274
- <button class="styled__ViewStopButton-sc-12v7ov3-0 fRdwNC">
336
+ <button class="styled__ViewStopButton-sc-12v7ov3-0 hXaHvR">
337
+ Stop Viewer
338
+ </button>
339
+ </p>
340
+ <p class="styled__PopupRow-sc-12kjso7-2 ckOOWr">
341
+ <strong>
342
+ Plan a trip:
343
+ </strong>
344
+ <span class="styled__FromToPickerSpan-sc-vb4790-1 giBPod">
345
+ <span class="styled__LocationPickerSpan-sc-vb4790-0 gsVfXo">
346
+ <svg viewbox="0 0 512 512"
347
+ height="0.9em"
348
+ width="0.9em"
349
+ aria-hidden="true"
350
+ focusable="false"
351
+ fill="currentColor"
352
+ xmlns="http://www.w3.org/2000/svg"
353
+ class="StyledIconBase-sc-ea9ulj-0 ebjPRL styled__FromIcon-sc-n5xcvc-0 dDqEuj"
354
+ >
355
+ <path fill="currentColor"
356
+ d="M160 256c0-53.9 42.1-96 96-96 53 0 96 42.1 96 96 0 53-43 96-96 96-53.9 0-96-43-96-96zm352 0c0 141.4-114.6 256-256 256S0 397.4 0 256 114.6 0 256 0s256 114.6 256 256zM256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48z"
357
+ >
358
+ </path>
359
+ </svg>
360
+ <button class="styled__Button-sc-vb4790-2 ekklXB">
361
+ From here
362
+ </button>
363
+ </span>
364
+ <span class="styled__LocationPickerSpan-sc-vb4790-0 gsVfXo">
365
+ <svg viewbox="0 0 384 512"
366
+ height="0.9em"
367
+ width="0.9em"
368
+ aria-hidden="true"
369
+ focusable="false"
370
+ fill="currentColor"
371
+ xmlns="http://www.w3.org/2000/svg"
372
+ class="StyledIconBase-sc-ea9ulj-0 ebjPRL styled__ToIcon-sc-n5xcvc-2 gZxRwk"
373
+ >
374
+ <path fill="currentColor"
375
+ d="M215.7 499.2C267 435 384 279.4 384 192 384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2 12.3 15.3 35.1 15.3 47.4 0zM192 256c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z"
376
+ >
377
+ </path>
378
+ </svg>
379
+ <button class="styled__Button-sc-vb4790-2 ekklXB">
380
+ To here
381
+ </button>
382
+ </span>
383
+ </span>
384
+ </p>
385
+ </div>
386
+ </div>
387
+ `;
388
+
389
+ exports[`Map Popup StopEntityWithFeedName smoke-test 1`] = `
390
+ <div class="styled__MapOverlayPopup-sc-12kjso7-1 cPqJxe">
391
+ <div id="focus-trimet3A9526-popup-focus-trap"
392
+ role="presentation"
393
+ >
394
+ <header class="styled__PopupTitle-sc-12kjso7-3 jRNaQh">
395
+ W Burnside &amp; SW 2nd (TriMet 9526)
396
+ </header>
397
+ <p class="styled__PopupRow-sc-12kjso7-2 ckOOWr">
398
+ <strong>
399
+ Stop ID: 9526
400
+ </strong>
401
+ <button class="styled__ViewStopButton-sc-12v7ov3-0 hXaHvR">
275
402
  Stop Viewer
276
403
  </button>
277
404
  </p>
package/src/index.tsx CHANGED
@@ -14,7 +14,7 @@ import { ViewStopButton } from "./styled";
14
14
 
15
15
  // Load the default messages.
16
16
  import defaultEnglishMessages from "../i18n/en-US.yml";
17
- import { makeDefaultGetEntityName } from "./util";
17
+ import { makeDefaultGetEntityName, type StopIdAgencyMap } from "./util";
18
18
 
19
19
  // HACK: We should flatten the messages loaded above because
20
20
  // the YAML loaders behave differently between webpack and our version of jest:
@@ -22,6 +22,14 @@ import { makeDefaultGetEntityName } from "./util";
22
22
  // - the yaml loader for jest returns messages with flattened ids.
23
23
  export const defaultMessages: { [key: string]: string } = flatten(defaultEnglishMessages);
24
24
 
25
+ export type Feed = {
26
+ feedId: string;
27
+ publisher: {
28
+ name: string;
29
+ };
30
+ };
31
+
32
+
25
33
  const generateLocation = (entity: Entity, name: string) => {
26
34
  // @ts-expect-error some of these values may be null, but that's ok
27
35
  const { lon: entityLon, lat: entityLat, x, y } = entity
@@ -63,17 +71,18 @@ const StationHubDetails = ({ station }: { station: Station }) => {
63
71
  const StopDetails = ({ id, setViewedStop }: { id: string, setViewedStop: () => void; }) => {
64
72
  return (
65
73
  <Styled.PopupRow>
66
- <strong>
67
- <FormattedMessage
68
- defaultMessage={defaultMessages["otpUi.MapPopup.stopId"]}
69
- description="Displays the stop id"
70
- id="otpUi.MapPopup.stopId"
71
- values={{
72
- stopId: id
73
- }}
74
- />
75
- </strong>
76
- <ViewStopButton onClick={setViewedStop}>
74
+ {id &&
75
+ <strong>
76
+ <FormattedMessage
77
+ defaultMessage={defaultMessages["otpUi.MapPopup.stopId"]}
78
+ description="Displays the stop id"
79
+ id="otpUi.MapPopup.stopId"
80
+ values={{
81
+ stopId: id
82
+ }}
83
+ />
84
+ </strong>}
85
+ <ViewStopButton onClick={setViewedStop} stopId={id}>
77
86
  <FormattedMessage
78
87
  defaultMessage={defaultMessages["otpUi.MapPopup.stopViewer"]}
79
88
  description="Text for link that opens the stop viewer"
@@ -89,8 +98,9 @@ type Props = {
89
98
  closePopup?: (arg?: any) => void
90
99
  configCompanies?: ConfiguredCompany[];
91
100
  entity: Entity
92
- getEntityName?: (entity: Entity, configCompanies: Company[],) => string;
101
+ getEntityName?: (entity: Entity, configCompanies: Company[], feedName?: string) => string;
93
102
  getEntityPrefix?: (entity: Entity) => JSX.Element
103
+ feeds?: Feed[]
94
104
  setLocation?: ({ location, locationType }: { location: Location, locationType: string }) => void;
95
105
  setViewedStop?: StopEventHandler;
96
106
  };
@@ -102,21 +112,37 @@ function entityIsStation(entity: Entity): entity is Station {
102
112
  /**
103
113
  * Renders a map popup for a stop, scooter, or shared bike
104
114
  */
105
- export function MapPopup({ closePopup = () => {}, configCompanies, entity, getEntityName, getEntityPrefix, setLocation, setViewedStop }: Props): JSX.Element {
115
+ export function MapPopup({
116
+ closePopup = () => {},
117
+ configCompanies,
118
+ entity,
119
+ getEntityName,
120
+ getEntityPrefix,
121
+ setLocation,
122
+ setViewedStop,
123
+ feeds,
124
+ }: Props): JSX.Element {
106
125
 
107
126
  const intl = useIntl()
108
127
  if (!entity) return <></>
109
128
 
110
129
  const getNameFunc = getEntityName || makeDefaultGetEntityName(intl, defaultMessages);
111
- const name = getNameFunc(entity, configCompanies);
130
+
131
+ // Find the feed name using the logic from generateLabel in otp.ts
132
+ let feedName: string | undefined;
133
+ if (feeds && entity.id) {
134
+ const feedId = entity.id.split(":")[0];
135
+ const feed = feeds.find(f => f.feedId === feedId);
136
+ feedName = feed?.publisher?.name;
137
+ }
138
+
139
+ const name = getNameFunc(entity, configCompanies, feedName);
112
140
 
113
141
  const stationNetwork = "networks" in entity && (coreUtils.itinerary.getCompaniesLabelFromNetworks(entity?.networks || [], configCompanies) || entity?.networks?.[0]);
114
142
 
115
143
  const bikesAvailablePresent = entityIsStation(entity)
116
144
  const entityIsStationHub = bikesAvailablePresent && entity?.bikesAvailable !== undefined && !entity?.isFloatingBike;
117
- const stopId = !bikesAvailablePresent && entity?.code || entity.id.split(":")[1] || entity.id
118
-
119
- // Double quotes make the query invalid, so remove them from the id just in case
145
+ const stopId = !bikesAvailablePresent && entity?.code;
120
146
  const id = `focus-${encodeURIComponent(entity.id).replace(/%/g, "")}-popup`
121
147
 
122
148
  return (
@@ -158,4 +184,5 @@ export function MapPopup({ closePopup = () => {}, configCompanies, entity, getEn
158
184
  );
159
185
  }
160
186
 
161
- export default MapPopup;
187
+ export default MapPopup;
188
+ export { type StopIdAgencyMap };
package/src/styled.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import styled from "styled-components";
2
2
 
3
3
  /* eslint-disable-next-line import/prefer-default-export */
4
- export const ViewStopButton = styled.button`
4
+ export const ViewStopButton = styled.button<{ stopId?: string }>`
5
5
  background: none;
6
6
  border-bottom: none;
7
- border-left: 1px solid #000;
7
+ ${props =>
8
+ props.stopId != null
9
+ ? "border-left: 1px solid #000; margin-left: 5px;"
10
+ : "border-left: none; padding-left: 0;"};
8
11
  border-right: none;
9
12
  border-top: none;
10
13
  color: #008;
11
14
  font-family: inherit;
12
- margin-left: 5px;
13
15
  padding-top: 0;
14
16
  `;
package/src/util.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { Company, Station, Stop } from "@opentripplanner/types";
1
+ import { Agency, Company, Station, Stop } from "@opentripplanner/types";
2
2
  import { IntlShape } from "react-intl";
3
3
  import coreUtils from "@opentripplanner/core-utils";
4
4
 
5
+ export type StopIdAgencyMap = Record<string, Agency>;
6
+
5
7
  // eslint-disable-next-line import/prefer-default-export
6
8
  export function makeDefaultGetEntityName(
7
9
  intl: IntlShape,
@@ -9,7 +11,8 @@ export function makeDefaultGetEntityName(
9
11
  ) {
10
12
  return function defaultGetEntityName(
11
13
  entity: Station | Stop,
12
- configCompanies: Company[]
14
+ configCompanies: Company[],
15
+ feedName?: string
13
16
  ): string | null {
14
17
  // TODO: Stop generating this / passing it to the car string? Is it needed?
15
18
  // In English we say "Car: " instead
@@ -63,6 +66,8 @@ export function makeDefaultGetEntityName(
63
66
  },
64
67
  { name: stationName }
65
68
  );
69
+ } else if (feedName && "code" in entity) {
70
+ stationName = `${stationName} (${feedName} ${entity.code})`;
66
71
  }
67
72
  return stationName;
68
73
  };
package/tsconfig.json CHANGED
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "extends": "../../tsconfig.json",
3
-
4
3
  "compilerOptions": {
5
4
  "composite": true,
6
5
  "outDir": "./lib",