@duffel/components 3.0.7-canary → 3.1.2--prototype

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.
Files changed (232) hide show
  1. package/.eslintrc.js +8 -0
  2. package/.github/CODEOWNERS +4 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  4. package/.github/renovate.json +1 -5
  5. package/.github/workflows/release.yml +3 -0
  6. package/.storybook/__snapshots__/Storyshots.test.js.snap +20384 -533
  7. package/.tool-versions +1 -1
  8. package/README.md +16 -2
  9. package/config/esbuild.base.config.js +6 -2
  10. package/config/esbuild.cdn.config.js +2 -1
  11. package/config/esbuild.dev.config.js +2 -1
  12. package/config/esbuild.react.config.js +1 -1
  13. package/data/airports.csv +9084 -0
  14. package/data/cities.csv +256 -0
  15. package/package.json +11 -1
  16. package/react-dist/index.js +51 -21
  17. package/scripts/generate-fixture.ts +13 -8
  18. package/scripts/setup-suggestion-data.ts +100 -0
  19. package/scripts/upload-to-cdn.sh +1 -1
  20. package/src/components/{Card.tsx → DuffelAncillaries/Card.tsx} +1 -1
  21. package/src/components/{Counter.tsx → DuffelAncillaries/Counter.tsx} +1 -1
  22. package/src/components/{DuffelAncillaries.tsx → DuffelAncillaries/DuffelAncillaries.tsx} +68 -64
  23. package/src/components/{DuffelAncillariesCustomElement.tsx → DuffelAncillaries/DuffelAncillariesCustomElement.tsx} +2 -2
  24. package/src/components/{bags → DuffelAncillaries/bags}/BaggageSelectionCard.tsx +10 -5
  25. package/src/components/{bags → DuffelAncillaries/bags}/BaggageSelectionController.tsx +2 -2
  26. package/src/components/{bags → DuffelAncillaries/bags}/BaggageSelectionModal.tsx +4 -4
  27. package/src/components/{bags → DuffelAncillaries/bags}/BaggageSelectionModalBody.tsx +3 -3
  28. package/src/components/{bags → DuffelAncillaries/bags}/BaggageSelectionModalBodyPassenger.tsx +4 -4
  29. package/src/components/{bags → DuffelAncillaries/bags}/BaggageSelectionModalFooter.tsx +23 -16
  30. package/src/components/{bags → DuffelAncillaries/bags}/BaggageSelectionModalHeader.tsx +24 -18
  31. package/src/components/{bags → DuffelAncillaries/bags}/IncludedBaggageBanner.tsx +1 -1
  32. package/src/components/{cancel_for_any_reason → DuffelAncillaries/cancel_for_any_reason}/CfarSelectionCard.tsx +4 -4
  33. package/src/components/{cancel_for_any_reason → DuffelAncillaries/cancel_for_any_reason}/CfarSelectionModal.tsx +3 -3
  34. package/src/components/{cancel_for_any_reason → DuffelAncillaries/cancel_for_any_reason}/CfarSelectionModalBody.tsx +3 -3
  35. package/src/components/{cancel_for_any_reason → DuffelAncillaries/cancel_for_any_reason}/CfarSelectionModalBodyListItem.tsx +1 -1
  36. package/src/components/{cancel_for_any_reason → DuffelAncillaries/cancel_for_any_reason}/CfarSelectionModalFooter.tsx +5 -5
  37. package/src/components/{seats → DuffelAncillaries/seats}/Amenity.tsx +2 -2
  38. package/src/components/{seats → DuffelAncillaries/seats}/DeckSelect.tsx +1 -1
  39. package/src/components/{seats → DuffelAncillaries/seats}/Element.tsx +2 -2
  40. package/src/components/{seats → DuffelAncillaries/seats}/ExitElement.tsx +1 -1
  41. package/src/components/{seats → DuffelAncillaries/seats}/Legend.tsx +2 -2
  42. package/src/components/{seats → DuffelAncillaries/seats}/Row.tsx +2 -2
  43. package/src/components/{seats → DuffelAncillaries/seats}/RowSection.tsx +5 -2
  44. package/src/components/{seats → DuffelAncillaries/seats}/SeatElement.tsx +3 -3
  45. package/src/components/{seats → DuffelAncillaries/seats}/SeatInfo.tsx +1 -1
  46. package/src/components/{seats → DuffelAncillaries/seats}/SeatMap.tsx +6 -2
  47. package/src/components/{seats → DuffelAncillaries/seats}/SeatMapUnavailable.tsx +1 -1
  48. package/src/components/{seats → DuffelAncillaries/seats}/SeatSelectionCard.tsx +5 -5
  49. package/src/components/{seats → DuffelAncillaries/seats}/SeatSelectionModal.tsx +5 -5
  50. package/src/components/{seats → DuffelAncillaries/seats}/SeatSelectionModalBody.tsx +1 -1
  51. package/src/components/{seats → DuffelAncillaries/seats}/SeatSelectionModalFooter.tsx +24 -17
  52. package/src/components/{seats → DuffelAncillaries/seats}/SeatSelectionModalHeader.tsx +30 -20
  53. package/src/components/{seats → DuffelAncillaries/seats}/SeatUnavailable.tsx +2 -2
  54. package/src/components/DuffelPayments/DuffelPayments.tsx +224 -0
  55. package/src/components/DuffelPayments/DuffelPaymentsCustomElement.tsx +130 -0
  56. package/src/components/PlacesLookup/PlacesLookup.tsx +123 -0
  57. package/src/components/ShowData/ShowData.tsx +38 -0
  58. package/src/components/ShowData/ShowDataCustomElement.tsx +85 -0
  59. package/src/components/{Button.tsx → shared/Button.tsx} +4 -3
  60. package/src/components/{ErrorBoundary.tsx → shared/ErrorBoundary.tsx} +2 -2
  61. package/src/components/{Icon.tsx → shared/Icon.tsx} +11 -11
  62. package/src/components/{IconButton.tsx → shared/IconButton.tsx} +1 -1
  63. package/src/components/{Modal.tsx → shared/Modal.tsx} +5 -1
  64. package/src/components/{NonIdealState.tsx → shared/NonIdealState.tsx} +1 -1
  65. package/src/custom-elements.ts +6 -1
  66. package/src/examples/client-side/index.html +1 -1
  67. package/src/examples/full-stack/index.html +1 -1
  68. package/src/examples/full-stack/server.mjs +1 -0
  69. package/src/examples/just-typescript/src/index.html +2 -2
  70. package/src/examples/just-typescript/src/index.ts +2 -1
  71. package/src/examples/payments-custom-element/README.md +17 -0
  72. package/src/examples/payments-custom-element/index.html +43 -0
  73. package/src/examples/payments-just-typescript/README.md +37 -0
  74. package/src/examples/payments-just-typescript/package.json +16 -0
  75. package/src/examples/payments-just-typescript/src/index.html +23 -0
  76. package/src/examples/payments-just-typescript/src/index.ts +18 -0
  77. package/src/examples/react-app/src/index.tsx +11 -6
  78. package/src/fixtures/offers/off_1.json +1 -10
  79. package/src/index.ts +3 -1
  80. package/src/lib/captureErrorInSentry.ts +2 -20
  81. package/src/lib/fetchFromDuffelAPI.ts +36 -6
  82. package/src/lib/formatDate.ts +3 -4
  83. package/src/lib/getBaggageServiceDescription.ts +1 -6
  84. package/src/lib/getPassengerName.ts +4 -0
  85. package/src/lib/getTotalAmountForServices.ts +1 -1
  86. package/src/lib/hasHighLuminance.ts +9 -0
  87. package/src/lib/logging.ts +52 -32
  88. package/src/lib/retrieveOffer.ts +13 -6
  89. package/src/lib/retrieveSeatMaps.ts +13 -8
  90. package/src/stories/BaggageSelectionModalHeader.stories.tsx +1 -1
  91. package/src/stories/Button.stories.tsx +33 -2
  92. package/src/stories/DuffelAncillaries.stories.tsx +42 -2
  93. package/src/stories/DuffelPayments.stories.tsx +34 -0
  94. package/src/stories/Icon.stories.tsx +3 -2
  95. package/src/stories/IconButton.stories.tsx +1 -1
  96. package/src/stories/PlacesLookup.stories.tsx +22 -0
  97. package/src/stories/ShowData.stories.tsx +16 -0
  98. package/src/styles/components/Button.css +11 -3
  99. package/src/styles/components/Card.css +3 -3
  100. package/src/styles/components/CfarSelectionModal.css +1 -1
  101. package/src/styles/components/DuffelPayments.css +42 -0
  102. package/src/styles/components/Legend.css +10 -6
  103. package/src/styles/components/LoadingState.css +8 -2
  104. package/src/styles/components/Modal.css +2 -1
  105. package/src/styles/components/PassengerSelect.css +8 -2
  106. package/src/styles/components/PlacesLookup.css +36 -0
  107. package/src/styles/components/Seat.css +9 -7
  108. package/src/styles/components/SeatInfo.css +1 -1
  109. package/src/styles/components/Tabs.css +5 -2
  110. package/src/styles/global.css +2 -0
  111. package/src/tests/components/DuffelAncillaries.test.tsx +1 -1
  112. package/src/tests/lib/createPriceFormatters.test.tsx +1 -1
  113. package/src/tests/lib/formatAvailableServices.test.tsx +1 -1
  114. package/src/tests/lib/formatSeatMaps.test.tsx +2 -2
  115. package/src/tests/lib/getCurrencyForServices.test.tsx +1 -1
  116. package/src/tests/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.test.ts +1 -1
  117. package/src/tests/lib/logging.test.tsx +14 -14
  118. package/src/tests/lib/moneyStringFormatter.test.tsx +1 -1
  119. package/src/tests/lib/validateProps.test.tsx +1 -1
  120. package/src/types/DuffelAncillariesProps.ts +1 -1
  121. package/react-dist/components/AnimatedLoaderEllipsis.d.ts +0 -2
  122. package/react-dist/components/Button.d.ts +0 -23
  123. package/react-dist/components/Card.d.ts +0 -14
  124. package/react-dist/components/Counter.d.ts +0 -10
  125. package/react-dist/components/DuffelAncillaries.d.ts +0 -3
  126. package/react-dist/components/DuffelAncillariesCustomElement.d.ts +0 -13
  127. package/react-dist/components/ErrorBoundary.d.ts +0 -13
  128. package/react-dist/components/FetchOfferErrorState.d.ts +0 -5
  129. package/react-dist/components/Icon.d.ts +0 -44
  130. package/react-dist/components/IconButton.d.ts +0 -16
  131. package/react-dist/components/Modal.d.ts +0 -11
  132. package/react-dist/components/NonIdealState.d.ts +0 -4
  133. package/react-dist/components/Stamp.d.ts +0 -7
  134. package/react-dist/components/Tabs.d.ts +0 -16
  135. package/react-dist/components/bags/BaggageSelectionCard.d.ts +0 -11
  136. package/react-dist/components/bags/BaggageSelectionController.d.ts +0 -13
  137. package/react-dist/components/bags/BaggageSelectionModal.d.ts +0 -11
  138. package/react-dist/components/bags/BaggageSelectionModalBody.d.ts +0 -11
  139. package/react-dist/components/bags/BaggageSelectionModalBodyPassenger.d.ts +0 -13
  140. package/react-dist/components/bags/BaggageSelectionModalFooter.d.ts +0 -14
  141. package/react-dist/components/bags/BaggageSelectionModalHeader.d.ts +0 -9
  142. package/react-dist/components/bags/IncludedBaggageBanner.d.ts +0 -7
  143. package/react-dist/components/cancel_for_any_reason/CfarSelectionCard.d.ts +0 -10
  144. package/react-dist/components/cancel_for_any_reason/CfarSelectionModal.d.ts +0 -11
  145. package/react-dist/components/cancel_for_any_reason/CfarSelectionModalBody.d.ts +0 -7
  146. package/react-dist/components/cancel_for_any_reason/CfarSelectionModalBodyListItem.d.ts +0 -4
  147. package/react-dist/components/cancel_for_any_reason/CfarSelectionModalFooter.d.ts +0 -11
  148. package/react-dist/components/cancel_for_any_reason/CfarSelectionModalHeader.d.ts +0 -2
  149. package/react-dist/components/seats/Amenity.d.ts +0 -6
  150. package/react-dist/components/seats/DeckSelect.d.ts +0 -15
  151. package/react-dist/components/seats/Element.d.ts +0 -15
  152. package/react-dist/components/seats/EmptyElement.d.ts +0 -2
  153. package/react-dist/components/seats/ExitElement.d.ts +0 -6
  154. package/react-dist/components/seats/Legend.d.ts +0 -12
  155. package/react-dist/components/seats/Row.d.ts +0 -13
  156. package/react-dist/components/seats/RowSection.d.ts +0 -17
  157. package/react-dist/components/seats/SeatElement.d.ts +0 -13
  158. package/react-dist/components/seats/SeatInfo.d.ts +0 -7
  159. package/react-dist/components/seats/SeatMap.d.ts +0 -12
  160. package/react-dist/components/seats/SeatMapUnavailable.d.ts +0 -2
  161. package/react-dist/components/seats/SeatSelectionCard.d.ts +0 -13
  162. package/react-dist/components/seats/SeatSelectionModal.d.ts +0 -13
  163. package/react-dist/components/seats/SeatSelectionModalBody.d.ts +0 -4
  164. package/react-dist/components/seats/SeatSelectionModalFooter.d.ts +0 -16
  165. package/react-dist/components/seats/SeatSelectionModalHeader.d.ts +0 -10
  166. package/react-dist/components/seats/SeatUnavailable.d.ts +0 -5
  167. package/react-dist/custom-elements.d.ts +0 -5
  168. package/react-dist/custom-elements.js +0 -36
  169. package/react-dist/custom-elements.js.map +0 -7
  170. package/react-dist/index.d.ts +0 -6
  171. package/react-dist/index.js.map +0 -7
  172. package/react-dist/lib/captureErrorInSentry.d.ts +0 -1
  173. package/react-dist/lib/compileCreateOrderPayload.d.ts +0 -14
  174. package/react-dist/lib/createPriceFormatters.d.ts +0 -12
  175. package/react-dist/lib/fetchFromDuffelAPI.d.ts +0 -1
  176. package/react-dist/lib/fetchFromFixtures.d.ts +0 -4
  177. package/react-dist/lib/formatAvailableServices.d.ts +0 -12
  178. package/react-dist/lib/formatDate.d.ts +0 -2
  179. package/react-dist/lib/formatSeatMaps.d.ts +0 -4
  180. package/react-dist/lib/getBaggageServiceDescription.d.ts +0 -2
  181. package/react-dist/lib/getCabinsForSegmentAndDeck.d.ts +0 -2
  182. package/react-dist/lib/getCurrencyForSeatMaps.d.ts +0 -10
  183. package/react-dist/lib/getCurrencyForServices.d.ts +0 -11
  184. package/react-dist/lib/getFirstSeatElementMatchingCriteria.d.ts +0 -3
  185. package/react-dist/lib/getPassengerBySegmentList.d.ts +0 -6
  186. package/react-dist/lib/getPassengerInitials.d.ts +0 -1
  187. package/react-dist/lib/getPassengerMapById.d.ts +0 -3
  188. package/react-dist/lib/getPassengerName.d.ts +0 -3
  189. package/react-dist/lib/getRowNumber.d.ts +0 -2
  190. package/react-dist/lib/getSegmentList.d.ts +0 -2
  191. package/react-dist/lib/getServicePriceMapById.d.ts +0 -3
  192. package/react-dist/lib/getSymbols.d.ts +0 -2
  193. package/react-dist/lib/getTotalAmountForServices.d.ts +0 -6
  194. package/react-dist/lib/getTotalQuantity.d.ts +0 -2
  195. package/react-dist/lib/hasService.d.ts +0 -2
  196. package/react-dist/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.d.ts +0 -3
  197. package/react-dist/lib/hasWings.d.ts +0 -2
  198. package/react-dist/lib/isBaggageService.d.ts +0 -2
  199. package/react-dist/lib/isCancelForAnyReasonService.d.ts +0 -2
  200. package/react-dist/lib/isFixtureOfferId.d.ts +0 -2
  201. package/react-dist/lib/isPayloadComplete.d.ts +0 -2
  202. package/react-dist/lib/isSeatElement.d.ts +0 -2
  203. package/react-dist/lib/logging.d.ts +0 -53
  204. package/react-dist/lib/moneyStringFormatter.d.ts +0 -8
  205. package/react-dist/lib/offerIsExpired.d.ts +0 -2
  206. package/react-dist/lib/retrieveOffer.d.ts +0 -2
  207. package/react-dist/lib/retrieveOfferFromDuffelAPI.d.ts +0 -1
  208. package/react-dist/lib/retrieveSeatMaps.d.ts +0 -2
  209. package/react-dist/lib/retrieveSeatMapsFromDuffelAPI.d.ts +0 -1
  210. package/react-dist/lib/setBodyScrollability.d.ts +0 -1
  211. package/react-dist/lib/validateProps.d.ts +0 -7
  212. package/react-dist/lib/withPlural.d.ts +0 -1
  213. package/react-dist/types/Aircraft.d.ts +0 -14
  214. package/react-dist/types/Airline.d.ts +0 -14
  215. package/react-dist/types/Airport.d.ts +0 -44
  216. package/react-dist/types/City.d.ts +0 -18
  217. package/react-dist/types/CreateOrderPayload.d.ts +0 -72
  218. package/react-dist/types/CurrencyConversion.d.ts +0 -10
  219. package/react-dist/types/DuffelAncillariesProps.d.ts +0 -70
  220. package/react-dist/types/Offer.d.ts +0 -711
  221. package/react-dist/types/Order.d.ts +0 -8
  222. package/react-dist/types/Place.d.ts +0 -8
  223. package/react-dist/types/SeatMap.d.ts +0 -190
  224. package/react-dist/types/index.d.ts +0 -11
  225. package/src/examples/just-typescript/yarn.lock +0 -154
  226. package/src/examples/react-app/yarn.lock +0 -219
  227. /package/src/components/{cancel_for_any_reason → DuffelAncillaries/cancel_for_any_reason}/CfarSelectionModalHeader.tsx +0 -0
  228. /package/src/components/{seats → DuffelAncillaries/seats}/EmptyElement.tsx +0 -0
  229. /package/src/components/{AnimatedLoaderEllipsis.tsx → shared/AnimatedLoaderEllipsis.tsx} +0 -0
  230. /package/src/components/{FetchOfferErrorState.tsx → shared/FetchOfferErrorState.tsx} +0 -0
  231. /package/src/components/{Stamp.tsx → shared/Stamp.tsx} +0 -0
  232. /package/src/components/{Tabs.tsx → shared/Tabs.tsx} +0 -0
@@ -0,0 +1,130 @@
1
+ import { StripeError } from "@stripe/stripe-js";
2
+ import { createRoot, Root } from "react-dom/client";
3
+ import { DuffelPayments, DuffelPaymentsProps } from "./DuffelPayments";
4
+
5
+ declare global {
6
+ // eslint-disable-next-line @typescript-eslint/no-namespace
7
+ namespace JSX {
8
+ interface IntrinsicElements {
9
+ "duffel-payments": React.DetailedHTMLProps<
10
+ React.HTMLAttributes<HTMLElement>,
11
+ HTMLElement
12
+ >;
13
+ }
14
+ }
15
+ }
16
+
17
+ const CUSTOM_ELEMENT_TAG = "duffel-payments";
18
+
19
+ type DuffelPaymentsCustomElementRenderArguments = Pick<
20
+ DuffelPaymentsProps,
21
+ "paymentIntentClientToken" | "styles"
22
+ >;
23
+
24
+ class DuffelPaymentsCustomElement extends HTMLElement {
25
+ /**
26
+ * The React root for displaying content inside a browser DOM element.
27
+ */
28
+ private root!: Root;
29
+
30
+ /**
31
+ * `connectedCallback` is called to initialise the custom element
32
+ */
33
+ connectedCallback() {
34
+ const container = document.createElement("div");
35
+ this.appendChild(container);
36
+
37
+ this.root = createRoot(container);
38
+ }
39
+
40
+ /**
41
+ * When this function is called, it will render/re-render
42
+ * the `DuffelPayments` component with the given props.
43
+ */
44
+ public render(withProps: DuffelPaymentsCustomElementRenderArguments) {
45
+ if (!this.root) {
46
+ throw "It was not possible to render `duffel-payments` because `this.root` is missing.";
47
+ }
48
+
49
+ this.root.render(
50
+ <DuffelPayments
51
+ {...withProps}
52
+ onSuccessfulPayment={() => {
53
+ this.dispatchEvent(
54
+ new CustomEvent("onSuccessfulPayment", {
55
+ composed: true,
56
+ })
57
+ );
58
+ }}
59
+ onFailedPayment={(error: StripeError) => {
60
+ this.dispatchEvent(
61
+ new CustomEvent("onFailedPayment", {
62
+ detail: { error },
63
+ composed: true,
64
+ })
65
+ );
66
+ }}
67
+ />
68
+ );
69
+ }
70
+ }
71
+
72
+ window.customElements.get(CUSTOM_ELEMENT_TAG) ||
73
+ window.customElements.define(CUSTOM_ELEMENT_TAG, DuffelPaymentsCustomElement);
74
+
75
+ function tryToGetDuffelPaymentsCustomElement(
76
+ caller: string
77
+ ): DuffelPaymentsCustomElement {
78
+ const element =
79
+ document.querySelector<DuffelPaymentsCustomElement>(CUSTOM_ELEMENT_TAG);
80
+ if (!element) {
81
+ throw new Error(
82
+ `Could not find duffel-payments element in the DOM. Maybe you need to call ${caller} after 'window.onload'?`
83
+ );
84
+ }
85
+ return element;
86
+ }
87
+
88
+ export function renderDuffelPaymentsCustomElement(
89
+ props: DuffelPaymentsCustomElementRenderArguments
90
+ ) {
91
+ const element = tryToGetDuffelPaymentsCustomElement(
92
+ "renderDuffelPaymentsCustomElement"
93
+ );
94
+ element.render(props);
95
+ }
96
+
97
+ export function onDuffelPaymentsSuccessfulPayment(
98
+ onSuccessfulPayment: DuffelPaymentsProps["onSuccessfulPayment"]
99
+ ) {
100
+ const element = tryToGetDuffelPaymentsCustomElement(
101
+ "onDuffelPaymentsPayloadReady"
102
+ );
103
+
104
+ // using `as EventListener` here because typescript doesn't know the event type for `onPayloadReady`
105
+ // There's a few different suggestions to resolve this seemed good enough
106
+ // You can learn more here: https://github.com/microsoft/TypeScript/issues/28357
107
+ element.addEventListener(
108
+ "onPayloadReady",
109
+ onSuccessfulPayment as EventListener
110
+ );
111
+ }
112
+
113
+ type OnFailedPaymentCustomEvent = CustomEvent<{
114
+ error: StripeError;
115
+ }>;
116
+ export function onDuffelPaymentsFailedPayment(
117
+ onFailedPayment: DuffelPaymentsProps["onFailedPayment"]
118
+ ) {
119
+ const element = tryToGetDuffelPaymentsCustomElement(
120
+ "onDuffelPaymentsPayloadReady"
121
+ );
122
+ const eventListener = (event: OnFailedPaymentCustomEvent) => {
123
+ onFailedPayment(event.detail.error);
124
+ };
125
+
126
+ // using `as EventListener` here because typescript doesn't know the event type for `onPayloadReady`
127
+ // There's a few different suggestions to resolve this seemed good enough
128
+ // You can learn more here: https://github.com/microsoft/TypeScript/issues/28357
129
+ element.addEventListener("onPayloadReady", eventListener as EventListener);
130
+ }
@@ -0,0 +1,123 @@
1
+ import Fuse from "fuse.js";
2
+ import { debounce } from "lodash";
3
+ import fetch from "node-fetch";
4
+ import React from "react";
5
+ import { Icon } from "../shared/Icon";
6
+
7
+ const DATA_SOURCE = "https://assets.duffel.com/data/iata-lookup-v2.json";
8
+
9
+ interface City {
10
+ type: "city";
11
+ name: string;
12
+ iata_code: string;
13
+ }
14
+ interface Airport {
15
+ type: "airport";
16
+ name: string;
17
+ iata_code: string;
18
+ latitude: string;
19
+ longitude: string;
20
+ }
21
+ type Place = City | Airport;
22
+
23
+ const mapDataRowsIntoObjects = (data: string[]): Place[] =>
24
+ data.map((row) => {
25
+ const [type, name, iata_code, latitude, longitude] = row.split(",");
26
+ if (type === "C") return { type: "city", name, iata_code } as City;
27
+ else
28
+ return {
29
+ type: "airport",
30
+ name,
31
+ iata_code,
32
+ latitude,
33
+ longitude,
34
+ } as Airport;
35
+ });
36
+
37
+ export interface PlacesLookupProps {
38
+ onPlaceSelected: (selection: Place) => void;
39
+ placeholder?: string;
40
+ }
41
+
42
+ export const PlacesLookup: React.FC<PlacesLookupProps> = ({
43
+ onPlaceSelected,
44
+ placeholder = "Lookup city or airport",
45
+ }) => {
46
+ const [shouldShowPopover, setShouldShowPopover] =
47
+ React.useState<boolean>(true);
48
+ const [inputValue, setInputValue] = React.useState<string>("");
49
+ const [lookupData, setLookupData] = React.useState<Fuse<Place>>(new Fuse([]));
50
+ const [lookupResults, setLookupResults] = React.useState<
51
+ Fuse.FuseResult<Place>[]
52
+ >([]);
53
+
54
+ const runLookup = debounce((newInputValue: string) => {
55
+ setLookupResults(
56
+ lookupData.search(newInputValue, {
57
+ limit: 10,
58
+ })
59
+ );
60
+ }, 300);
61
+
62
+ React.useEffect(() => {
63
+ fetch(DATA_SOURCE)
64
+ .then((response) => response.json())
65
+ .then((data) =>
66
+ setLookupData(
67
+ new Fuse<Place>(mapDataRowsIntoObjects(data), {
68
+ threshold: 0.2,
69
+ keys: ["name", "iata_code"],
70
+ })
71
+ )
72
+ );
73
+ }, []);
74
+
75
+ return (
76
+ <div className="places-lookup">
77
+ <input
78
+ className="places-lookup-input"
79
+ placeholder={placeholder}
80
+ type="text"
81
+ value={inputValue}
82
+ onChange={(e) => {
83
+ if (!shouldShowPopover) setShouldShowPopover(true);
84
+ setInputValue(e.target.value);
85
+ runLookup(e.target.value);
86
+ }}
87
+ />
88
+ {shouldShowPopover &&
89
+ inputValue.length > 0 &&
90
+ lookupResults.length > 0 && (
91
+ <div
92
+ className="places-lookup-popover"
93
+ style={{ display: "flex", flexDirection: "column" }}
94
+ >
95
+ {lookupResults.map(({ item }, index) => (
96
+ <button
97
+ className="places-lookup-popover__item"
98
+ key={item.iata_code + index}
99
+ onClick={() => {
100
+ setShouldShowPopover(false);
101
+ onPlaceSelected(item);
102
+ setInputValue(item.name);
103
+ }}
104
+ >
105
+ <span className="places-lookup-popover__icon-and-name-container">
106
+ <Icon
107
+ style={{ fill: "var(--GREY-400)", marginTop: "-2px" }}
108
+ name={
109
+ item.type === "airport" ? "flight_takeoff" : "apartment"
110
+ }
111
+ />
112
+ {item.name}
113
+ </span>{" "}
114
+ <span style={{ color: "var(--GREY-600)", fontSize: "12px" }}>
115
+ {item.iata_code}
116
+ </span>
117
+ </button>
118
+ ))}
119
+ </div>
120
+ )}
121
+ </div>
122
+ );
123
+ };
@@ -0,0 +1,38 @@
1
+ import { Button } from "@components/shared/Button";
2
+ import React from "react";
3
+
4
+ function isJsonString(dataString: string) {
5
+ try {
6
+ JSON.parse(dataString);
7
+ } catch (e) {
8
+ return false;
9
+ }
10
+ return true;
11
+ }
12
+
13
+ export interface ShowDataProps {
14
+ data: any;
15
+ onFinished: (newData: any) => void;
16
+ }
17
+
18
+ export const ShowData: React.FC<ShowDataProps> = ({ data, onFinished }) => {
19
+ const [textareaData, setTextareaData] = React.useState("{}");
20
+ const isValid = isJsonString(textareaData);
21
+
22
+ return (
23
+ <div>
24
+ <pre>{JSON.stringify(data, null, 2)}</pre>
25
+ <textarea
26
+ value={textareaData}
27
+ onChange={(e) => setTextareaData(e.target.value)}
28
+ />
29
+ <Button
30
+ disabled={!isValid}
31
+ onClick={() => onFinished(JSON.parse(textareaData))}
32
+ >
33
+ Click me to trigger `onFinished` event
34
+ </Button>
35
+ {!isValid && <p style={{ color: "tomato" }}>Invalid JSON</p>}
36
+ </div>
37
+ );
38
+ };
@@ -0,0 +1,85 @@
1
+ import { createRoot, Root } from "react-dom/client";
2
+ import { ShowData, ShowDataProps } from "./ShowData";
3
+ declare global {
4
+ // eslint-disable-next-line @typescript-eslint/no-namespace
5
+ namespace JSX {
6
+ interface IntrinsicElements {
7
+ "show-data": React.DetailedHTMLProps<
8
+ React.HTMLAttributes<HTMLElement>,
9
+ HTMLElement
10
+ >;
11
+ }
12
+ }
13
+ }
14
+
15
+ const CUSTOM_ELEMENT_TAG = "show-data";
16
+
17
+ type ShowDataRenderArguments = Pick<ShowDataProps, "data">;
18
+
19
+ class ShowDataCustomElement extends HTMLElement {
20
+ /**
21
+ * The React root for displaying content inside a browser DOM element.
22
+ */
23
+ private root!: Root;
24
+
25
+ /**
26
+ * `connectedCallback` is called to initialise the custom element
27
+ */
28
+ connectedCallback() {
29
+ const container = document.createElement("div");
30
+ this.appendChild(container);
31
+
32
+ this.root = createRoot(container);
33
+ }
34
+
35
+ /**
36
+ * When this function is called, it will render/re-render
37
+ * the `DuffelPayments` component with the given props.
38
+ */
39
+ public render(withProps: ShowDataRenderArguments) {
40
+ if (!this.root) {
41
+ throw "It was not possible to render `duffel-payments` because `this.root` is missing.";
42
+ }
43
+
44
+ this.root.render(
45
+ <ShowData
46
+ {...withProps}
47
+ onFinished={() => {
48
+ this.dispatchEvent(
49
+ new CustomEvent("onFinished", {
50
+ composed: true,
51
+ })
52
+ );
53
+ }}
54
+ />
55
+ );
56
+ }
57
+ }
58
+
59
+ window.customElements.get(CUSTOM_ELEMENT_TAG) ||
60
+ window.customElements.define(CUSTOM_ELEMENT_TAG, ShowDataCustomElement);
61
+
62
+ function tryToGetShowData(caller: string): ShowDataCustomElement {
63
+ const element =
64
+ document.querySelector<ShowDataCustomElement>(CUSTOM_ELEMENT_TAG);
65
+ if (!element) {
66
+ throw new Error(
67
+ `Could not find duffel-payments element in the DOM. Maybe you need to call ${caller} after 'window.onload'?`
68
+ );
69
+ }
70
+ return element;
71
+ }
72
+
73
+ export function renderShowData(props: ShowDataRenderArguments) {
74
+ const element = tryToGetShowData("renderShowData");
75
+ element.render(props);
76
+ }
77
+
78
+ export function onShowDataFinished(onFinished: ShowDataProps["onFinished"]) {
79
+ const element = tryToGetShowData("onShowDataFinished");
80
+
81
+ // using `as EventListener` here because typescript doesn't know the event type for `onPayloadReady`
82
+ // There's a few different suggestions to resolve this seemed good enough
83
+ // You can learn more here: https://github.com/microsoft/TypeScript/issues/28357
84
+ element.addEventListener("onFinished", onFinished as EventListener);
85
+ }
@@ -1,4 +1,4 @@
1
- import { Icon, IconName } from "@components/Icon";
1
+ import { Icon, IconName } from "@components/shared/Icon";
2
2
  import classNames from "classnames";
3
3
  import * as React from "react";
4
4
 
@@ -25,7 +25,7 @@ type NativeButtonProps = React.DetailedHTMLProps<
25
25
  export interface ButtonProps
26
26
  extends Pick<
27
27
  NativeButtonProps,
28
- "id" | "onClick" | "disabled" | "children" | "className"
28
+ "id" | "onClick" | "disabled" | "children" | "className" | "type"
29
29
  > {
30
30
  "data-testid"?: string;
31
31
  iconBefore?: IconName;
@@ -39,10 +39,11 @@ export const Button: React.FC<ButtonProps> = ({
39
39
  size = 40,
40
40
  children,
41
41
  className,
42
+ type = "button",
42
43
  ...nativeButtonProps
43
44
  }) => (
44
45
  <button
45
- type="button"
46
+ type={type}
46
47
  className={classNames(
47
48
  "button",
48
49
  BUTTON_VARIANTS[variant],
@@ -13,9 +13,9 @@ export class ErrorBoundary extends React.Component<{
13
13
  return { hasError: true };
14
14
  }
15
15
 
16
- componentDidCatch(error: Error, context: Record<any, any>) {
16
+ componentDidCatch(error: Error) {
17
17
  // You can also log the error to an error reporting service
18
- captureErrorInSentry(error, context);
18
+ captureErrorInSentry(error);
19
19
  }
20
20
 
21
21
  render() {
@@ -1,3 +1,4 @@
1
+ import { log } from "@lib/logging";
1
2
  import * as React from "react";
2
3
 
3
4
  /* eslint-disable react/no-unknown-property */
@@ -6,6 +7,9 @@ export const ICON_MAP = {
6
7
  add: (
7
8
  <path d="M18 13h-5v5c0 .55-.45 1-1 1s-1-.45-1-1v-5H6c-.55 0-1-.45-1-1s.45-1 1-1h5V6c0-.55.45-1 1-1s1 .45 1 1v5h5c.55 0 1 .45 1 1s-.45 1-1 1z" />
8
9
  ),
10
+ apartment: (
11
+ <path d="M3.8496 19.9752V7.2744H7.4496V3.6744H15.9504V10.8744H20.1504V19.9752H13.4496V16.3752H10.5504V19.9752H3.8496ZM5.1504 18.6744H7.4496V16.3752H5.1504V18.6744ZM5.1504 14.7744H7.4496V12.4752H5.1504V14.7744ZM5.1504 10.8744H7.4496V8.5752H5.1504V10.8744ZM8.7504 14.7744H11.0496V12.4752H8.7504V14.7744ZM8.7504 10.8744H11.0496V8.5752H8.7504V10.8744ZM8.7504 7.2744H11.0496V4.9752H8.7504V7.2744ZM12.3504 14.7744H14.6496V12.4752H12.3504V14.7744ZM12.3504 10.8744H14.6496V8.5752H12.3504V10.8744ZM12.3504 7.2744H14.6496V4.9752H12.3504V7.2744ZM16.5504 18.6744H18.8496V16.3752H16.5504V18.6744ZM16.5504 14.7744H18.8496V12.4752H16.5504V14.7744Z" />
12
+ ),
9
13
  arrow_forward: (
10
14
  <path d="M5 13h11.17l-4.88 4.88c-.39.39-.39 1.03 0 1.42.39.39 1.02.39 1.41 0l6.59-6.59c.39-.39.39-1.02 0-1.41l-6.58-6.6c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L16.17 11H5c-.55 0-1 .45-1 1s.45 1 1 1z" />
11
15
  ),
@@ -15,6 +19,7 @@ export const ICON_MAP = {
15
19
  arrow_right: (
16
20
  <path d="M15 19L13.6 17.6L18.2 13H2V11H18.2L13.6 6.4L15 5L22 12L15 19Z" />
17
21
  ),
22
+
18
23
  autorenew: (
19
24
  <path d="M5.69728 14.4104C5.49326 13.9833 5.32604 13.5257 5.19562 13.0374C5.06521 12.5492 5 12.0367 5 11.5C5 9.56481 5.69083 7.91679 7.07249 6.55591C8.45416 5.19504 10.147 4.53946 12.1511 4.58918H12.7613L11.0742 2.92359L12.0097 2L15.2869 5.23549L12.0097 8.47099L11.0742 7.54739L12.7613 5.88181H12.1511C10.5021 5.84866 9.11624 6.38632 7.99347 7.49479C6.87069 8.60326 6.30931 9.93833 6.30931 11.5C6.30931 11.8455 6.33804 12.1824 6.3955 12.5106C6.45296 12.8389 6.53915 13.1573 6.65407 13.4658L5.69728 14.4104ZM11.9903 21L8.71309 17.7645L11.9903 14.529L12.9258 15.4526L11.2387 17.1182H11.8489C13.4979 17.1513 14.8838 16.6137 16.0065 15.5052C17.1293 14.3967 17.6907 13.0617 17.6907 11.5C17.6907 11.1545 17.662 10.8176 17.6045 10.4894C17.547 10.1611 17.4609 9.84273 17.3459 9.53421L18.3027 8.58959C18.5067 9.01665 18.674 9.47431 18.8044 9.96256C18.9348 10.4508 19 10.9633 19 11.5C19 13.4186 18.3092 15.0625 16.9275 16.4317C15.5458 17.8008 13.853 18.4605 11.8489 18.4108H11.2387L12.9258 20.0764L11.9903 21Z" />
20
25
  ),
@@ -52,6 +57,9 @@ export const ICON_MAP = {
52
57
  flight_class: (
53
58
  <path d="M14.2596 12.5C13.7737 12.5 13.3606 12.3298 13.0202 11.9894C12.6798 11.649 12.5096 11.2359 12.5096 10.75V6.25C12.5096 5.7641 12.6798 5.35096 13.0202 5.01058C13.3606 4.67019 13.7737 4.5 14.2596 4.5H15.75C16.2359 4.5 16.649 4.67019 16.9894 5.01058C17.3298 5.35096 17.5 5.7641 17.5 6.25V10.75C17.5 11.2359 17.3298 11.649 16.9894 11.9894C16.649 12.3298 16.2359 12.5 15.75 12.5H14.2596ZM14.2596 11H15.75C15.8205 11 15.8798 10.9759 15.9278 10.9279C15.9759 10.8798 16 10.8205 16 10.75V6.25C16 6.17948 15.9759 6.12018 15.9278 6.0721C15.8798 6.02402 15.8205 5.99998 15.75 5.99998H14.2596C14.1891 5.99998 14.1298 6.02402 14.0817 6.0721C14.0336 6.12018 14.0096 6.17948 14.0096 6.25V10.75C14.0096 10.8205 14.0336 10.8798 14.0817 10.9279C14.1298 10.9759 14.1891 11 14.2596 11ZM9.5673 17.5C9.21345 17.5 8.89678 17.398 8.6173 17.1942C8.33782 16.9903 8.14423 16.7198 8.03655 16.3827L5.5 8.02883V4.5H6.99997V7.99998L9.49997 16H17.7596V17.5H9.5673ZM8.25 20.5V19H17.75V20.5H8.25ZM14.2596 5.99998H16H14.0096H14.2596Z" />
54
59
  ),
60
+ flight_takeoff: (
61
+ <path d="M3.77521 20.1498V18.849H20.0748V20.1498H3.77521ZM5.45041 15.2742L2.94961 11.0994L4.05001 10.8246L5.90041 12.399L10.05 11.2998L6.27481 4.89902L7.72561 4.62422L14.1 10.1994L19.4004 8.77382C19.75 8.67462 20.0832 8.72062 20.4 8.91182C20.7168 9.10382 20.9252 9.37462 21.0252 9.72422C21.1084 10.0746 21.0624 10.4038 20.8872 10.7118C20.7128 11.0198 20.4504 11.2242 20.1 11.325L5.45041 15.2742Z" />
62
+ ),
55
63
  galley: (
56
64
  <path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z" />
57
65
  ),
@@ -68,20 +76,12 @@ export const ICON_MAP = {
68
76
  <path d="M14.6674 10.0001V2.66675C14.6674 1.56008 13.7741 0.666748 12.6674 0.666748C11.5607 0.666748 10.6674 1.56008 10.6674 2.66675V7.57341L21.1074 18.0134L25.3341 19.3334V16.6667L14.6674 10.0001ZM1.33407 5.02675L7.9874 11.6801L0.000732422 16.6667V19.3334L10.6674 16.0001V23.3334L8.00073 25.3334V27.3334L12.6674 26.0001L17.3341 27.3334V25.3334L14.6674 23.3334V18.3601L22.3074 26.0001L24.0007 24.3067L3.0274 3.33341L1.33407 5.02675Z" />
69
77
  ),
70
78
  no_bag: (
71
- <path
72
- fill-rule="evenodd"
73
- clip-rule="evenodd"
74
- d="M16.8992 6.12037H14.9392V3.18101C14.9385 2.92148 14.8351 2.67278 14.6516 2.48926C14.4681 2.30574 14.2194 2.20234 13.9599 2.20166H10.0399C9.78034 2.20234 9.53163 2.30574 9.34811 2.48926C9.16459 2.67278 9.06119 2.92148 9.06051 3.18101V6.12037H8.20711L8.34715 6.25804L14.3973 12.0871V9.06102H15.9676V13.6L17.3022 14.8859L18.8592 16.4748V8.08037C18.8579 7.56096 18.6509 7.06322 18.2836 6.69594C17.9164 6.32867 17.4186 6.12173 16.8992 6.12037ZM5.14051 8.09559L3.77173 6.71825L5.00821 5.48945L7.16586 7.6606L13.9581 14.4953L15.8932 16.4426L19.7441 20.3175L20.2279 20.8043L18.9914 22.0331L17.635 20.6683C17.4035 20.7625 17.154 20.8126 16.8992 20.8133C16.904 20.945 16.8822 21.0763 16.8351 21.1993C16.788 21.3224 16.7166 21.4347 16.6251 21.5296C16.5336 21.6244 16.424 21.6999 16.3027 21.7514C16.1814 21.8029 16.051 21.8295 15.9192 21.8295C15.7874 21.8295 15.657 21.8029 15.5357 21.7514C15.4145 21.6999 15.3048 21.6244 15.2133 21.5296C15.1219 21.4347 15.0504 21.3224 15.0034 21.1993C14.9563 21.0763 14.9344 20.945 14.9392 20.8133H9.06051C9.06529 20.945 9.04348 21.0763 8.99638 21.1993C8.94929 21.3224 8.87787 21.4347 8.7864 21.5296C8.69492 21.6244 8.58527 21.6999 8.46399 21.7514C8.34271 21.8029 8.21229 21.8295 8.08051 21.8295C7.94874 21.8295 7.81832 21.8029 7.69703 21.7514C7.57575 21.6999 7.4661 21.6244 7.37463 21.5296C7.28316 21.4347 7.21174 21.3224 7.16464 21.1993C7.11754 21.0763 7.09573 20.945 7.10051 20.8133C6.58222 20.8119 6.08545 20.6059 5.71835 20.24C5.35126 19.8741 5.14357 19.378 5.14051 18.8597V8.09559ZM14.8632 17.8791L14.3973 17.4103V17.8791H14.8632ZM9.596 12.579V17.8791H8.03083V11.004L9.596 12.579ZM10.5302 3.67134H13.4695V6.12295H10.5302V3.67134Z"
75
- />
79
+ <path d="M16.8992 6.12037H14.9392V3.18101C14.9385 2.92148 14.8351 2.67278 14.6516 2.48926C14.4681 2.30574 14.2194 2.20234 13.9599 2.20166H10.0399C9.78034 2.20234 9.53163 2.30574 9.34811 2.48926C9.16459 2.67278 9.06119 2.92148 9.06051 3.18101V6.12037H8.20711L8.34715 6.25804L14.3973 12.0871V9.06102H15.9676V13.6L17.3022 14.8859L18.8592 16.4748V8.08037C18.8579 7.56096 18.6509 7.06322 18.2836 6.69594C17.9164 6.32867 17.4186 6.12173 16.8992 6.12037ZM5.14051 8.09559L3.77173 6.71825L5.00821 5.48945L7.16586 7.6606L13.9581 14.4953L15.8932 16.4426L19.7441 20.3175L20.2279 20.8043L18.9914 22.0331L17.635 20.6683C17.4035 20.7625 17.154 20.8126 16.8992 20.8133C16.904 20.945 16.8822 21.0763 16.8351 21.1993C16.788 21.3224 16.7166 21.4347 16.6251 21.5296C16.5336 21.6244 16.424 21.6999 16.3027 21.7514C16.1814 21.8029 16.051 21.8295 15.9192 21.8295C15.7874 21.8295 15.657 21.8029 15.5357 21.7514C15.4145 21.6999 15.3048 21.6244 15.2133 21.5296C15.1219 21.4347 15.0504 21.3224 15.0034 21.1993C14.9563 21.0763 14.9344 20.945 14.9392 20.8133H9.06051C9.06529 20.945 9.04348 21.0763 8.99638 21.1993C8.94929 21.3224 8.87787 21.4347 8.7864 21.5296C8.69492 21.6244 8.58527 21.6999 8.46399 21.7514C8.34271 21.8029 8.21229 21.8295 8.08051 21.8295C7.94874 21.8295 7.81832 21.8029 7.69703 21.7514C7.57575 21.6999 7.4661 21.6244 7.37463 21.5296C7.28316 21.4347 7.21174 21.3224 7.16464 21.1993C7.11754 21.0763 7.09573 20.945 7.10051 20.8133C6.58222 20.8119 6.08545 20.6059 5.71835 20.24C5.35126 19.8741 5.14357 19.378 5.14051 18.8597V8.09559ZM14.8632 17.8791L14.3973 17.4103V17.8791H14.8632ZM9.596 12.579V17.8791H8.03083V11.004L9.596 12.579ZM10.5302 3.67134H13.4695V6.12295H10.5302V3.67134Z" />
76
80
  ),
77
81
  no_seat: (
78
82
  <>
79
83
  <path d="M25.1667 21.9733L23.4733 23.6667L2.5 2.69333L4.19333 1L7.16667 3.97333L16.5267 13.3333L19.1933 16L24.5 21.3067L25.1667 21.9733Z" />
80
- <path
81
- fillRule="evenodd"
82
- clipRule="evenodd"
83
- d="M7.16667 24H3.16667V16H19.1933L24.5 21.3067V24H20.5V20H7.16667V24ZM27.1667 9.33333H23.1667V13.3333H27.1667V9.33333ZM4.5 9.33333H0.5V13.3333H4.5V9.33333ZM20.3333 13.3333H20.5V2.66667C20.5 1.2 19.3 0 17.8333 0H9.83333C9.05784 0 8.3569 0.335483 7.86848 0.868484L20.3333 13.3333ZM16.5267 13.3333L7.16667 3.97333V13.3333H16.5267Z"
84
- />
84
+ <path d="M7.16667 24H3.16667V16H19.1933L24.5 21.3067V24H20.5V20H7.16667V24ZM27.1667 9.33333H23.1667V13.3333H27.1667V9.33333ZM4.5 9.33333H0.5V13.3333H4.5V9.33333ZM20.3333 13.3333H20.5V2.66667C20.5 1.2 19.3 0 17.8333 0H9.83333C9.05784 0 8.3569 0.335483 7.86848 0.868484L20.3333 13.3333ZM16.5267 13.3333L7.16667 3.97333V13.3333H16.5267Z" />
85
85
  </>
86
86
  ),
87
87
  north_east: (
@@ -109,7 +109,7 @@ export type IconName = keyof typeof ICON_MAP;
109
109
 
110
110
  const getIconPath = (name: IconName) => {
111
111
  if (!(name in ICON_MAP)) {
112
- console.warn(`The icon "${name}" is missing from ICON_MAP`);
112
+ log(`The icon "${name}" is missing from ICON_MAP`);
113
113
  return null;
114
114
  }
115
115
  return ICON_MAP[name];
@@ -1,6 +1,6 @@
1
- import { Icon, IconName } from "@components/Icon";
2
1
  import classNames from "classnames";
3
2
  import * as React from "react";
3
+ import { Icon, IconName } from "./Icon";
4
4
 
5
5
  const ICON_BUTTON_VARIANTS = {
6
6
  primary: "icon-button--primary",
@@ -16,7 +16,11 @@ export const Modal: React.FC<ModalProps> = ({ children, onClose, isOpen }) => {
16
16
  }, [isOpen]);
17
17
 
18
18
  return (
19
- <div className={classNames("modal", isOpen && "modal--open")}>
19
+ <div
20
+ className={classNames("modal", isOpen && "modal--open")}
21
+ // setting inline style to avoid modal content to flash unstyled before stylesheet is loaded
22
+ style={{ opacity: 0 }}
23
+ >
20
24
  <div role="presentation" className={"modal--content"}>
21
25
  {children}
22
26
 
@@ -1,4 +1,4 @@
1
- import * as React from "react";
1
+ import React from "react";
2
2
 
3
3
  export const NonIdealState: React.FC<
4
4
  React.PropsWithChildren<{ style?: React.CSSProperties }>
@@ -5,4 +5,9 @@
5
5
  export {
6
6
  onDuffelAncillariesPayloadReady,
7
7
  renderDuffelAncillariesCustomElement,
8
- } from "./components/DuffelAncillariesCustomElement";
8
+ } from "./components/DuffelAncillaries/DuffelAncillariesCustomElement";
9
+ export {
10
+ onDuffelPaymentsFailedPayment,
11
+ onDuffelPaymentsSuccessfulPayment,
12
+ renderDuffelPaymentsCustomElement,
13
+ } from "./components/DuffelPayments/DuffelPaymentsCustomElement";
@@ -11,7 +11,7 @@
11
11
  />
12
12
 
13
13
  <!-- 1. This script loads duffel-components -->
14
- <script src="http://localhost:8000/index.js"></script>
14
+ <script src="http://localhost:8000/duffel-ancillaries.js"></script>
15
15
  </head>
16
16
 
17
17
  <body style="font-family: sans-serif">
@@ -11,7 +11,7 @@
11
11
  />
12
12
 
13
13
  <!-- 1. This script loads duffel-components -->
14
- <script src="http://localhost:8000/index.js"></script>
14
+ <script src="http://localhost:8000/duffel-ancillaries.js"></script>
15
15
  </head>
16
16
 
17
17
  <body style="font-family: sans-serif">
@@ -154,4 +154,5 @@ http
154
154
  })
155
155
  .listen(6262);
156
156
 
157
+ // eslint-disable-next-line
157
158
  console.log(`\n🐄 Serving example on http://localhost:6262`);
@@ -11,11 +11,11 @@
11
11
  />
12
12
 
13
13
  <script src="../dist/index.js" defer></script>
14
- <title>React app example</title>
14
+ <title>ancillaries component just typescript example</title>
15
15
  </head>
16
16
  <body>
17
17
  <h1 style="margin-bottom: 2rem">
18
- Duffel ancillaries component react-app example
18
+ Duffel ancillaries component just typescript example
19
19
  </h1>
20
20
 
21
21
  <duffel-ancillaries></duffel-ancillaries>
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  onDuffelAncillariesPayloadReady,
3
3
  renderDuffelAncillariesCustomElement,
4
- } from "duffel-components";
4
+ } from "duffel-components/custom-elements";
5
5
 
6
6
  window.onload = () => {
7
7
  renderDuffelAncillariesCustomElement({
@@ -29,6 +29,7 @@ window.onload = () => {
29
29
  ],
30
30
  });
31
31
  onDuffelAncillariesPayloadReady((data, metadata) => {
32
+ /* eslint-disable */
32
33
  console.table(data);
33
34
  console.table(metadata);
34
35
  });
@@ -0,0 +1,17 @@
1
+ # Duffel payments custom element example
2
+
3
+ ## Setup
4
+
5
+ ```sh
6
+ # .env.local
7
+
8
+ # The url for the component CDN.
9
+ # This is used to load both the styles an
10
+ # COMPONENT_CDN=https://assets.duffel.com/components/ancillaries/VERSION # production
11
+ COMPONENT_CDN=http://localhost:8000 # development
12
+ ```
13
+
14
+ ## Run the example
15
+
16
+ 1. Serve the Duffel component bundle and watch for changes to rebuild on port `8000` using `yarn dev`.
17
+ 2. Open `src/examples/payments-custom-element/index.html` on your browser.
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Payment page example</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link
7
+ rel="icon"
8
+ type="image/png"
9
+ sizes="96x96"
10
+ href="https://duffel.com/images/favicon/favicon-96x96.png"
11
+ />
12
+
13
+ <!-- 1. This script loads duffel-components -->
14
+ <script src="http://localhost:8000/duffel-payments.js"></script>
15
+ </head>
16
+
17
+ <body style="font-family: sans-serif">
18
+ <h1 style="margin-bottom: 2rem">Payment page example</h1>
19
+
20
+ <!-- 2. Add the custom element to your markup where you want to render the payments card -->
21
+ <duffel-payments></duffel-payments>
22
+ </body>
23
+ <script>
24
+ const duffelpaymentsElement = document.querySelector("duffel-payments");
25
+
26
+ // 3. Render the component with the required data, you can safely call this function as many times as you want.
27
+ duffelpaymentsElement.render({
28
+ paymentIntentClientToken:
29
+ "eyJjbGllbnRfc2VjcmV0IjoicGlfM0psczlVQWcySmhFeTh2WTBSTm1MU0JkX3NlY3JldF9QUW9yZXNuU3laeWJadGRiejZwNzBCbUdPIiwicHVibGlzaGFibGVfa2V5IjoicGtfdGVzdF9EQUJLY0E2Vzh6OTc0cTdPSWY0YmJ2MVQwMEpwRmMyOUpWIn0=",
30
+ debug: true,
31
+ });
32
+
33
+ // 4. Listen to 'onSuccessfulPayment' event on the component:
34
+ duffelpaymentsElement.addEventListener("onSuccessfulPayment", () =>
35
+ console.log("onPayloadReady\n")
36
+ );
37
+
38
+ // 5. If we run into an issue with the payment, you can listen to 'onFailedPayment' event:
39
+ duffelpaymentsElement.addEventListener("onFailedPayment", (event) =>
40
+ console.log("onPayloadReady\n", event.detail)
41
+ );
42
+ </script>
43
+ </html>