@duffel/components 2.7.20 → 3.0.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.
- package/.circleci/config.yml +67 -0
- package/.eslintrc.js +47 -0
- package/.github/renovate.json +16 -0
- package/.github/workflows/autoapprove.yml +18 -0
- package/.github/workflows/release.yml +89 -0
- package/.husky/post-commit +4 -0
- package/.husky/pre-commit +4 -0
- package/.nvmrc +1 -0
- package/.prettierignore +2 -0
- package/.storybook/Storyshots.test.js +3 -0
- package/.storybook/__snapshots__/Storyshots.test.js.snap +48318 -0
- package/.storybook/main.ts +33 -0
- package/.storybook/preview.tsx +28 -0
- package/.tool-versions +1 -0
- package/CONTRIBUTING.md +83 -0
- package/README.md +53 -15
- package/__mocks__/styleMock.js +6 -0
- package/babel.config.js +20 -0
- package/commitlint.config.js +4 -0
- package/config/esbuild.base.config.js +18 -0
- package/config/esbuild.cdn.config.js +51 -0
- package/config/esbuild.dev.config.js +46 -0
- package/config/esbuild.react.config.js +42 -0
- package/jest.config.ts +14 -0
- package/package.json +135 -193
- package/react-dist/components/DuffelAncillaries/Card.d.ts +14 -0
- package/react-dist/components/DuffelAncillaries/Counter.d.ts +10 -0
- package/react-dist/components/DuffelAncillaries/DuffelAncillaries.d.ts +3 -0
- package/react-dist/components/DuffelAncillaries/DuffelAncillariesCustomElement.d.ts +13 -0
- package/react-dist/components/DuffelAncillaries/bags/BaggageSelectionCard.d.ts +11 -0
- package/react-dist/components/DuffelAncillaries/bags/BaggageSelectionController.d.ts +13 -0
- package/react-dist/components/DuffelAncillaries/bags/BaggageSelectionModal.d.ts +11 -0
- package/react-dist/components/DuffelAncillaries/bags/BaggageSelectionModalBody.d.ts +11 -0
- package/react-dist/components/DuffelAncillaries/bags/BaggageSelectionModalBodyPassenger.d.ts +13 -0
- package/react-dist/components/DuffelAncillaries/bags/BaggageSelectionModalFooter.d.ts +14 -0
- package/react-dist/components/DuffelAncillaries/bags/BaggageSelectionModalHeader.d.ts +9 -0
- package/react-dist/components/DuffelAncillaries/bags/IncludedBaggageBanner.d.ts +7 -0
- package/react-dist/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionCard.d.ts +10 -0
- package/react-dist/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModal.d.ts +11 -0
- package/react-dist/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalBody.d.ts +7 -0
- package/react-dist/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalBodyListItem.d.ts +4 -0
- package/react-dist/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalFooter.d.ts +11 -0
- package/react-dist/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalHeader.d.ts +2 -0
- package/react-dist/components/DuffelAncillaries/seats/Amenity.d.ts +6 -0
- package/react-dist/components/DuffelAncillaries/seats/DeckSelect.d.ts +15 -0
- package/react-dist/components/DuffelAncillaries/seats/Element.d.ts +15 -0
- package/react-dist/components/DuffelAncillaries/seats/EmptyElement.d.ts +2 -0
- package/react-dist/components/DuffelAncillaries/seats/ExitElement.d.ts +6 -0
- package/react-dist/components/DuffelAncillaries/seats/Legend.d.ts +12 -0
- package/react-dist/components/DuffelAncillaries/seats/Row.d.ts +13 -0
- package/react-dist/components/DuffelAncillaries/seats/RowSection.d.ts +17 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatElement.d.ts +13 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatInfo.d.ts +7 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatMap.d.ts +12 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatMapUnavailable.d.ts +2 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatSelectionCard.d.ts +13 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatSelectionModal.d.ts +13 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatSelectionModalBody.d.ts +4 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatSelectionModalFooter.d.ts +16 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatSelectionModalHeader.d.ts +10 -0
- package/react-dist/components/DuffelAncillaries/seats/SeatUnavailable.d.ts +5 -0
- package/react-dist/components/DuffelPayments/DuffelPayments.d.ts +11 -0
- package/react-dist/components/DuffelPayments/DuffelPaymentsCustomElement.d.ts +14 -0
- package/react-dist/components/shared/AnimatedLoaderEllipsis.d.ts +2 -0
- package/react-dist/components/shared/Button.d.ts +23 -0
- package/react-dist/components/shared/ErrorBoundary.d.ts +13 -0
- package/react-dist/components/shared/FetchOfferErrorState.d.ts +5 -0
- package/react-dist/components/shared/Icon.d.ts +44 -0
- package/react-dist/components/shared/IconButton.d.ts +16 -0
- package/react-dist/components/shared/Modal.d.ts +11 -0
- package/react-dist/components/shared/NonIdealState.d.ts +4 -0
- package/react-dist/components/shared/Stamp.d.ts +7 -0
- package/react-dist/components/shared/Tabs.d.ts +16 -0
- package/react-dist/custom-elements.d.ts +6 -0
- package/react-dist/custom-elements.js +37 -0
- package/react-dist/custom-elements.js.map +7 -0
- package/react-dist/index.d.ts +7 -0
- package/react-dist/index.js +37 -0
- package/react-dist/index.js.map +7 -0
- package/react-dist/lib/captureErrorInSentry.d.ts +1 -0
- package/react-dist/lib/compileCreateOrderPayload.d.ts +14 -0
- package/react-dist/lib/createPriceFormatters.d.ts +12 -0
- package/react-dist/lib/fetchFromDuffelAPI.d.ts +8 -0
- package/react-dist/lib/fetchFromFixtures.d.ts +4 -0
- package/react-dist/lib/formatAvailableServices.d.ts +12 -0
- package/react-dist/lib/formatDate.d.ts +2 -0
- package/react-dist/lib/formatSeatMaps.d.ts +4 -0
- package/react-dist/lib/getBaggageServiceDescription.d.ts +2 -0
- package/react-dist/lib/getCabinsForSegmentAndDeck.d.ts +2 -0
- package/react-dist/lib/getCurrencyForSeatMaps.d.ts +10 -0
- package/react-dist/lib/getCurrencyForServices.d.ts +11 -0
- package/react-dist/lib/getFirstSeatElementMatchingCriteria.d.ts +3 -0
- package/react-dist/lib/getPassengerBySegmentList.d.ts +6 -0
- package/react-dist/lib/getPassengerInitials.d.ts +1 -0
- package/react-dist/lib/getPassengerMapById.d.ts +3 -0
- package/react-dist/lib/getPassengerName.d.ts +3 -0
- package/react-dist/lib/getRowNumber.d.ts +2 -0
- package/react-dist/lib/getSegmentList.d.ts +2 -0
- package/react-dist/lib/getServicePriceMapById.d.ts +3 -0
- package/react-dist/lib/getSymbols.d.ts +2 -0
- package/react-dist/lib/getTotalAmountForServices.d.ts +6 -0
- package/react-dist/lib/getTotalQuantity.d.ts +2 -0
- package/react-dist/lib/hasService.d.ts +2 -0
- package/react-dist/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.d.ts +3 -0
- package/react-dist/lib/hasWings.d.ts +2 -0
- package/react-dist/lib/isBaggageService.d.ts +2 -0
- package/react-dist/lib/isCancelForAnyReasonService.d.ts +2 -0
- package/react-dist/lib/isFixtureOfferId.d.ts +2 -0
- package/react-dist/lib/isPayloadComplete.d.ts +2 -0
- package/react-dist/lib/isSeatElement.d.ts +2 -0
- package/react-dist/lib/logging.d.ts +46 -0
- package/react-dist/lib/moneyStringFormatter.d.ts +8 -0
- package/react-dist/lib/offerIsExpired.d.ts +2 -0
- package/react-dist/lib/retrieveOffer.d.ts +2 -0
- package/react-dist/lib/retrieveOfferFromDuffelAPI.d.ts +1 -0
- package/react-dist/lib/retrieveSeatMaps.d.ts +2 -0
- package/react-dist/lib/retrieveSeatMapsFromDuffelAPI.d.ts +1 -0
- package/react-dist/lib/setBodyScrollability.d.ts +1 -0
- package/react-dist/lib/validateProps.d.ts +7 -0
- package/react-dist/lib/withPlural.d.ts +1 -0
- package/react-dist/types/Aircraft.d.ts +14 -0
- package/react-dist/types/Airline.d.ts +14 -0
- package/react-dist/types/Airport.d.ts +44 -0
- package/react-dist/types/City.d.ts +18 -0
- package/react-dist/types/CreateOrderPayload.d.ts +72 -0
- package/react-dist/types/CurrencyConversion.d.ts +10 -0
- package/react-dist/types/DuffelAncillariesProps.d.ts +70 -0
- package/react-dist/types/Offer.d.ts +711 -0
- package/react-dist/types/Order.d.ts +8 -0
- package/react-dist/types/Place.d.ts +8 -0
- package/react-dist/types/SeatMap.d.ts +190 -0
- package/react-dist/types/index.d.ts +11 -0
- package/scripts/generate-fixture.ts +200 -0
- package/scripts/upload-to-cdn.sh +34 -0
- package/scripts.tsconfig.json +11 -0
- package/src/components/DuffelAncillaries/Card.tsx +126 -0
- package/src/components/DuffelAncillaries/Counter.tsx +40 -0
- package/src/components/DuffelAncillaries/DuffelAncillaries.tsx +344 -0
- package/src/components/DuffelAncillaries/DuffelAncillariesCustomElement.tsx +124 -0
- package/src/components/DuffelAncillaries/bags/BaggageSelectionCard.tsx +96 -0
- package/src/components/DuffelAncillaries/bags/BaggageSelectionController.tsx +88 -0
- package/src/components/DuffelAncillaries/bags/BaggageSelectionModal.tsx +81 -0
- package/src/components/DuffelAncillaries/bags/BaggageSelectionModalBody.tsx +60 -0
- package/src/components/DuffelAncillaries/bags/BaggageSelectionModalBodyPassenger.tsx +122 -0
- package/src/components/DuffelAncillaries/bags/BaggageSelectionModalFooter.tsx +81 -0
- package/src/components/DuffelAncillaries/bags/BaggageSelectionModalHeader.tsx +76 -0
- package/src/components/DuffelAncillaries/bags/IncludedBaggageBanner.tsx +51 -0
- package/src/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionCard.tsx +90 -0
- package/src/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModal.tsx +63 -0
- package/src/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalBody.tsx +56 -0
- package/src/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalBodyListItem.tsx +11 -0
- package/src/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalFooter.tsx +74 -0
- package/src/components/DuffelAncillaries/cancel_for_any_reason/CfarSelectionModalHeader.tsx +9 -0
- package/src/components/DuffelAncillaries/seats/Amenity.tsx +21 -0
- package/src/components/DuffelAncillaries/seats/DeckSelect.tsx +27 -0
- package/src/components/DuffelAncillaries/seats/Element.tsx +52 -0
- package/src/components/DuffelAncillaries/seats/EmptyElement.tsx +5 -0
- package/src/components/DuffelAncillaries/seats/ExitElement.tsx +17 -0
- package/src/components/DuffelAncillaries/seats/Legend.tsx +60 -0
- package/src/components/DuffelAncillaries/seats/Row.tsx +47 -0
- package/src/components/DuffelAncillaries/seats/RowSection.tsx +78 -0
- package/src/components/DuffelAncillaries/seats/SeatElement.tsx +120 -0
- package/src/components/DuffelAncillaries/seats/SeatInfo.tsx +32 -0
- package/src/components/DuffelAncillaries/seats/SeatMap.tsx +81 -0
- package/src/components/DuffelAncillaries/seats/SeatMapUnavailable.tsx +21 -0
- package/src/components/DuffelAncillaries/seats/SeatSelectionCard.tsx +103 -0
- package/src/components/DuffelAncillaries/seats/SeatSelectionModal.tsx +142 -0
- package/src/components/DuffelAncillaries/seats/SeatSelectionModalBody.tsx +13 -0
- package/src/components/DuffelAncillaries/seats/SeatSelectionModalFooter.tsx +82 -0
- package/src/components/DuffelAncillaries/seats/SeatSelectionModalHeader.tsx +87 -0
- package/src/components/DuffelAncillaries/seats/SeatUnavailable.tsx +14 -0
- package/src/components/DuffelPayments/DuffelPayments.tsx +218 -0
- package/src/components/DuffelPayments/DuffelPaymentsCustomElement.tsx +130 -0
- package/src/components/shared/AnimatedLoaderEllipsis.tsx +5 -0
- package/src/components/shared/Button.tsx +63 -0
- package/src/components/shared/ErrorBoundary.tsx +54 -0
- package/src/components/shared/FetchOfferErrorState.tsx +35 -0
- package/src/components/shared/Icon.tsx +152 -0
- package/src/components/shared/IconButton.tsx +42 -0
- package/src/components/shared/Modal.tsx +40 -0
- package/src/components/shared/NonIdealState.tsx +28 -0
- package/src/components/shared/Stamp.tsx +29 -0
- package/src/components/shared/Tabs.tsx +36 -0
- package/src/custom-elements.ts +13 -0
- package/src/examples/client-side/README.md +30 -0
- package/src/examples/client-side/index.html +57 -0
- package/src/examples/full-stack/README.md +34 -0
- package/src/examples/full-stack/index.html +48 -0
- package/src/examples/full-stack/server.mjs +158 -0
- package/src/examples/just-typescript/README.md +37 -0
- package/src/examples/just-typescript/package.json +16 -0
- package/src/examples/just-typescript/src/index.html +23 -0
- package/src/examples/just-typescript/src/index.ts +36 -0
- package/src/examples/just-typescript/yarn.lock +154 -0
- package/src/examples/payments-custom-element/README.md +17 -0
- package/src/examples/payments-custom-element/index.html +43 -0
- package/src/examples/payments-just-typescript/README.md +37 -0
- package/src/examples/payments-just-typescript/package.json +16 -0
- package/src/examples/payments-just-typescript/src/index.html +23 -0
- package/src/examples/payments-just-typescript/src/index.ts +18 -0
- package/src/examples/payments-just-typescript/yarn.lock +154 -0
- package/src/examples/react-app/README.md +37 -0
- package/src/examples/react-app/package.json +20 -0
- package/src/examples/react-app/src/index.html +19 -0
- package/src/examples/react-app/src/index.tsx +48 -0
- package/src/examples/react-app/yarn.lock +219 -0
- package/src/fixtures/offers/off_0000AUde3KwTztSRK1cznH.json +497 -0
- package/src/fixtures/offers/off_0000AVx4lUFFKW8PsPeQeQ.json +307 -0
- package/src/fixtures/offers/off_1.json +497 -0
- package/src/fixtures/passengers/mock_passengers.ts +26 -0
- package/src/fixtures/seat-maps/off_0000AUde3KwTztSRK1cznH.json +6852 -0
- package/src/fixtures/seat-maps/off_0000AVx4lUFFKW8PsPeQeQ.json +1 -0
- package/src/fixtures/seat-maps/off_1.json +6852 -0
- package/src/index.ts +7 -0
- package/src/lib/captureErrorInSentry.ts +42 -0
- package/src/lib/compileCreateOrderPayload.ts +63 -0
- package/src/lib/createPriceFormatters.ts +73 -0
- package/src/lib/fetchFromDuffelAPI.ts +54 -0
- package/src/lib/fetchFromFixtures.ts +18 -0
- package/src/lib/formatAvailableServices.ts +91 -0
- package/src/lib/formatDate.ts +20 -0
- package/src/lib/formatSeatMaps.ts +81 -0
- package/src/lib/getBaggageServiceDescription.ts +42 -0
- package/src/lib/getCabinsForSegmentAndDeck.ts +4 -0
- package/src/lib/getCurrencyForSeatMaps.ts +22 -0
- package/src/lib/getCurrencyForServices.ts +24 -0
- package/src/lib/getFirstSeatElementMatchingCriteria.ts +22 -0
- package/src/lib/getPassengerBySegmentList.ts +10 -0
- package/src/lib/getPassengerInitials.ts +6 -0
- package/src/lib/getPassengerMapById.ts +17 -0
- package/src/lib/getPassengerName.ts +37 -0
- package/src/lib/getRowNumber.ts +16 -0
- package/src/lib/getSegmentList.ts +7 -0
- package/src/lib/getServicePriceMapById.ts +20 -0
- package/src/lib/getSymbols.ts +22 -0
- package/src/lib/getTotalAmountForServices.ts +72 -0
- package/src/lib/getTotalQuantity.ts +5 -0
- package/src/lib/hasService.ts +24 -0
- package/src/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.ts +35 -0
- package/src/lib/hasWings.ts +8 -0
- package/src/lib/isBaggageService.ts +8 -0
- package/src/lib/isCancelForAnyReasonService.ts +9 -0
- package/src/lib/isFixtureOfferId.ts +4 -0
- package/src/lib/isPayloadComplete.ts +11 -0
- package/src/lib/isSeatElement.ts +10 -0
- package/src/lib/logging.ts +120 -0
- package/src/lib/moneyStringFormatter.ts +34 -0
- package/src/lib/offerIsExpired.ts +5 -0
- package/src/lib/retrieveOffer.ts +54 -0
- package/src/lib/retrieveOfferFromDuffelAPI.ts +13 -0
- package/src/lib/retrieveSeatMaps.ts +55 -0
- package/src/lib/retrieveSeatMapsFromDuffelAPI.ts +13 -0
- package/src/lib/setBodyScrollability.ts +7 -0
- package/src/lib/validateProps.ts +37 -0
- package/src/lib/withPlural.ts +8 -0
- package/src/stories/BaggageSelectionModalHeader.stories.tsx +21 -0
- package/src/stories/Button.stories.tsx +69 -0
- package/src/stories/DuffelAncillaries.stories.tsx +126 -0
- package/src/stories/DuffelPayments.stories.tsx +34 -0
- package/src/stories/Icon.stories.tsx +35 -0
- package/src/stories/IconButton.stories.tsx +25 -0
- package/src/styles/colors.css +22 -0
- package/src/styles/components/Amenity.css +23 -0
- package/src/styles/components/BaggageDisplay.css +25 -0
- package/src/styles/components/Button.css +161 -0
- package/src/styles/components/Card.css +52 -0
- package/src/styles/components/CfarSelectionModal.css +34 -0
- package/src/styles/components/Counter.css +18 -0
- package/src/styles/components/DuffelPayments.css +42 -0
- package/src/styles/components/IconButton.css +63 -0
- package/src/styles/components/Legend.css +58 -0
- package/src/styles/components/Loader.css +37 -0
- package/src/styles/components/LoadingState.css +81 -0
- package/src/styles/components/Modal.css +84 -0
- package/src/styles/components/PassengerSelect.css +93 -0
- package/src/styles/components/PassengersLayout.css +90 -0
- package/src/styles/components/Row.css +70 -0
- package/src/styles/components/Seat.css +57 -0
- package/src/styles/components/SeatInfo.css +61 -0
- package/src/styles/components/SeatMap.css +24 -0
- package/src/styles/components/SeatSelect.css +92 -0
- package/src/styles/components/Segment.css +17 -0
- package/src/styles/components/SelectionSegment.css +10 -0
- package/src/styles/components/Summary.css +70 -0
- package/src/styles/components/Tabs.css +49 -0
- package/src/styles/flex.css +5 -0
- package/src/styles/font-families.css +47 -0
- package/src/styles/global.css +51 -0
- package/src/styles/margin.css +3 -0
- package/src/styles/spacing.css +18 -0
- package/src/styles/transitions.css +3 -0
- package/src/styles/typography.css +13 -0
- package/src/tests/components/DuffelAncillaries.test.tsx +342 -0
- package/src/tests/lib/createPriceFormatters.test.tsx +152 -0
- package/src/tests/lib/formatAvailableServices.test.tsx +79 -0
- package/src/tests/lib/formatSeatMaps.test.tsx +49 -0
- package/src/tests/lib/getCurrencyForServices.test.tsx +44 -0
- package/src/tests/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.test.ts +86 -0
- package/src/tests/lib/logging.test.tsx +32 -0
- package/src/tests/lib/moneyStringFormatter.test.tsx +12 -0
- package/src/tests/lib/validateProps.test.tsx +57 -0
- package/src/types/Aircraft.ts +16 -0
- package/src/types/Airline.ts +16 -0
- package/src/types/Airport.ts +54 -0
- package/src/types/City.ts +21 -0
- package/src/types/CreateOrderPayload.ts +99 -0
- package/src/types/CurrencyConversion.ts +10 -0
- package/src/types/DuffelAncillariesProps.ts +108 -0
- package/src/types/Offer.ts +851 -0
- package/src/types/Order.ts +6 -0
- package/src/types/Place.ts +6 -0
- package/src/types/SeatMap.ts +231 -0
- package/src/types/index.ts +11 -0
- package/tsconfig.json +52 -0
- package/LICENSE +0 -21
- package/dist/AdditionalBaggage.esm.js +0 -1
- package/dist/AdditionalBaggage.js +0 -1
- package/dist/AdditionalBaggage.min.css +0 -408
- package/dist/AdditionalBaggage.umd.min.js +0 -2
- package/dist/AdditionalBaggage.umd.min.js.LICENSE.txt +0 -60
- package/dist/AdditionalBaggageSelection.esm.js +0 -1
- package/dist/AdditionalBaggageSelection.js +0 -1
- package/dist/AdditionalBaggageSelection.min.css +0 -744
- package/dist/AdditionalBaggageSelection.umd.min.js +0 -2
- package/dist/AdditionalBaggageSelection.umd.min.js.LICENSE.txt +0 -93
- package/dist/CardPayment.esm.js +0 -2
- package/dist/CardPayment.esm.js.LICENSE.txt +0 -6
- package/dist/CardPayment.js +0 -2
- package/dist/CardPayment.js.LICENSE.txt +0 -6
- package/dist/CardPayment.min.css +0 -233
- package/dist/CardPayment.umd.min.js +0 -2
- package/dist/CardPayment.umd.min.js.LICENSE.txt +0 -61
- package/dist/SeatSelection.esm.js +0 -1
- package/dist/SeatSelection.js +0 -1
- package/dist/SeatSelection.min.css +0 -1127
- package/dist/SeatSelection.umd.min.js +0 -2
- package/dist/SeatSelection.umd.min.js.LICENSE.txt +0 -60
- package/dist/duffel-components.d.ts +0 -1614
- package/dist/duffel-components.esm.js +0 -2
- package/dist/duffel-components.esm.js.LICENSE.txt +0 -6
- package/dist/duffel-components.js +0 -2
- package/dist/duffel-components.js.LICENSE.txt +0 -6
- package/dist/duffel-components.min.css +0 -1280
- package/dist/duffel-components.umd.min.js +0 -2
- package/dist/duffel-components.umd.min.js.LICENSE.txt +0 -102
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seat maps are used to build a rich experience for your customers so they can select a seat as part of an order.
|
|
3
|
+
* A seat map includes the data for rendering seats in the relevant cabins, along with their total cost and other information such as disclosures.
|
|
4
|
+
* @link https://duffel.com/docs/api/seat-maps/schema
|
|
5
|
+
*/
|
|
6
|
+
export interface SeatMap {
|
|
7
|
+
/**
|
|
8
|
+
* Duffel's unique identifier for the seat map
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
/**
|
|
12
|
+
* Duffel's unique identifier for the slice. It identifies the slice of an offer (i.e. the same slice across offers will have different ids.)
|
|
13
|
+
*/
|
|
14
|
+
slice_id: string;
|
|
15
|
+
/**
|
|
16
|
+
* Duffel's unique identifier for the segment. It identifies the segment of an offer (i.e. the same segment across offers will have different ids).
|
|
17
|
+
*/
|
|
18
|
+
segment_id: string;
|
|
19
|
+
/**
|
|
20
|
+
* The list of cabins in this seat map.
|
|
21
|
+
* Cabins are ordered by deck from lowest to highest, and then within each deck from the front to back of the aircraft.
|
|
22
|
+
*/
|
|
23
|
+
cabins: SeatMapCabin[];
|
|
24
|
+
}
|
|
25
|
+
export interface SeatMapCabin {
|
|
26
|
+
/**
|
|
27
|
+
* Level 0 is the main deck and level 1 is the upper deck above that, which is found on some large aircraft.
|
|
28
|
+
*/
|
|
29
|
+
deck: number;
|
|
30
|
+
/**
|
|
31
|
+
* The cabin class that the passenger will travel in on this segment
|
|
32
|
+
*/
|
|
33
|
+
cabin_class: string;
|
|
34
|
+
/**
|
|
35
|
+
* Where the wings of the aircraft are in relation to rows in the cabin.
|
|
36
|
+
* The numbers correspond to the indices of the first and the last row which are overwing. You can use this to draw a visual representation of the wings to help users get a better idea of what they will see outside their window.
|
|
37
|
+
* The indices are 0 th-based and are for all rows, not just those that have seats.
|
|
38
|
+
* This is null when no rows of the cabin are overwing.
|
|
39
|
+
*/
|
|
40
|
+
wings: {
|
|
41
|
+
/**
|
|
42
|
+
* The index of the first row which is overwing, starting from the front of the aircraft.
|
|
43
|
+
*/
|
|
44
|
+
first_row_index: number;
|
|
45
|
+
/**
|
|
46
|
+
* The index of the last row which is overwing, starting from the front of the aircraft.
|
|
47
|
+
*/
|
|
48
|
+
last_row_index: number;
|
|
49
|
+
} | null;
|
|
50
|
+
/**
|
|
51
|
+
* The number of aisles in this cabin.
|
|
52
|
+
* If this is set to 1, each row of the cabin is split into two sections. If this is set to 2, each row of the cabin is split into three section.
|
|
53
|
+
*/
|
|
54
|
+
aisles: number;
|
|
55
|
+
/**
|
|
56
|
+
* A list of rows in this cabin.
|
|
57
|
+
* Row sections are broken up by aisles. Rows are ordered from front to back of the aircraft.
|
|
58
|
+
*/
|
|
59
|
+
rows: SeatMapCabinRow[];
|
|
60
|
+
}
|
|
61
|
+
export interface SeatMapCabinRow {
|
|
62
|
+
/**
|
|
63
|
+
* A list of sections.
|
|
64
|
+
* Each row is divided into sections by one or more aisles.
|
|
65
|
+
*/
|
|
66
|
+
sections: SeatMapCabinRowSection[];
|
|
67
|
+
}
|
|
68
|
+
export interface SeatMapCabinRowSection {
|
|
69
|
+
/**
|
|
70
|
+
* The elements that make up this section.
|
|
71
|
+
*/
|
|
72
|
+
elements: SeatMapCabinRowSectionElement[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A seat for a passenger. If the available_services list is empty (which will be represented as an empty list : []), the seat is unavailable.
|
|
76
|
+
* For display, all seats should be displayed with the same static width.
|
|
77
|
+
*/
|
|
78
|
+
export interface SeatMapCabinRowSectionElementSeat {
|
|
79
|
+
/**
|
|
80
|
+
* The type of this element.
|
|
81
|
+
*/
|
|
82
|
+
type: "seat";
|
|
83
|
+
/**
|
|
84
|
+
* The designator used to uniquely identify the seat, usually made up of a row number and a column letter
|
|
85
|
+
*/
|
|
86
|
+
designator: string;
|
|
87
|
+
/**
|
|
88
|
+
* A name which describes the type of seat, which you can display in your user interface to help customers to understand its features
|
|
89
|
+
*/
|
|
90
|
+
name?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Each disclosure is text, in English, provided by the airline that describes the terms and conditions of this seat. We recommend showing this in your user interface to make sure that customers understand any restrictions and limitations.
|
|
93
|
+
*/
|
|
94
|
+
disclosures: string[];
|
|
95
|
+
/**
|
|
96
|
+
* Seats are considered a special kind of service. There will be at most one service per seat per passenger. A seat can only be booked for one passenger. If a seat has no available services (which will be represented as an empty list : []) then it's unavailable.
|
|
97
|
+
*/
|
|
98
|
+
available_services: SeatMapCabinRowSectionAvailableService[];
|
|
99
|
+
}
|
|
100
|
+
export interface SeatMapCabinRowSectionAvailableService {
|
|
101
|
+
/**
|
|
102
|
+
* Duffel's unique identifier for the service
|
|
103
|
+
*/
|
|
104
|
+
id: string;
|
|
105
|
+
/**
|
|
106
|
+
* The passenger that this seat is for
|
|
107
|
+
*/
|
|
108
|
+
passenger_id: string;
|
|
109
|
+
/**
|
|
110
|
+
* The total price of the seat, including taxes
|
|
111
|
+
*/
|
|
112
|
+
total_amount: string;
|
|
113
|
+
/**
|
|
114
|
+
* The currency of the total_amount, as an ISO 4217 currency code
|
|
115
|
+
*/
|
|
116
|
+
total_currency: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A bassinet is a child's cradle. This element will be aligned with the corresponding seat in the following row.
|
|
120
|
+
* For display, this element should have the same width as a seat for proper alignment.
|
|
121
|
+
*/
|
|
122
|
+
export interface SeatMapCabinRowSectionElementBassinet {
|
|
123
|
+
/**
|
|
124
|
+
* The type of this element.
|
|
125
|
+
*/
|
|
126
|
+
type: "bassinet";
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* An empty space used for padding in some non-standard seat arrangements.
|
|
130
|
+
* For display, this element should have the same dimensions as a seat for proper alignment.
|
|
131
|
+
*/
|
|
132
|
+
export interface SeatMapCabinRowSectionElementEmpty {
|
|
133
|
+
/**
|
|
134
|
+
* The type of this element.
|
|
135
|
+
*/
|
|
136
|
+
type: "empty";
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* An exit row represents the extra wide legroom used to reach aircraft exits. There is one exit_row element per row section.
|
|
140
|
+
* Exit row elements only occur in their own row, so they can be displayed as one element across the whole row. Displaying an exit row element filling all available space in its section or using the same width as the seat is also reasonable.
|
|
141
|
+
*/
|
|
142
|
+
export interface SeatMapCabinRowSectionElementExitRow {
|
|
143
|
+
/**
|
|
144
|
+
* The type of this element.
|
|
145
|
+
*/
|
|
146
|
+
type: "exit_row";
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* A lavatory for use by passengers.
|
|
150
|
+
* For display, this element should ideally fill or shrink to available space in a row section. Displaying it with the same width as seat is also reasonable.
|
|
151
|
+
*/
|
|
152
|
+
export interface SeatMapCabinRowSectionElementLavatory {
|
|
153
|
+
/**
|
|
154
|
+
* The type of this element.
|
|
155
|
+
*/
|
|
156
|
+
type: "lavatory";
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* A galley is the compartment where food is cooked or prepared. These are conventionally marked with a teacup symbol.
|
|
160
|
+
* For display, this element should ideally fill or shrink to available space in a row section. Displaying it with the same width as seat is also reasonable.
|
|
161
|
+
*/
|
|
162
|
+
export interface SeatMapCabinRowSectionElementGalley {
|
|
163
|
+
/**
|
|
164
|
+
* The type of this element.
|
|
165
|
+
*/
|
|
166
|
+
type: "galley";
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* A closet used for storage. These are conventionally marked with a clothes hanger symbol.
|
|
170
|
+
* For display, this element should ideally fill or shrink to available space in a row section. Displaying it with the same width as seat is also reasonable.
|
|
171
|
+
*/
|
|
172
|
+
export interface SeatMapCabinRowSectionElementCloset {
|
|
173
|
+
/**
|
|
174
|
+
* The type of this element.
|
|
175
|
+
*/
|
|
176
|
+
type: "closet";
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* A set of stairs to another deck.
|
|
180
|
+
* For display, this element should ideally fill or shrink to available space in a row section. Displaying it with the same width as seat is also reasonable.
|
|
181
|
+
*/
|
|
182
|
+
export interface SeatMapCabinRowSectionElementStairs {
|
|
183
|
+
/**
|
|
184
|
+
* The type of this element.
|
|
185
|
+
*/
|
|
186
|
+
type: "stairs";
|
|
187
|
+
}
|
|
188
|
+
export type SeatMapCabinRowSectionElement = SeatMapCabinRowSectionElementSeat | SeatMapCabinRowSectionElementBassinet | SeatMapCabinRowSectionElementEmpty | SeatMapCabinRowSectionElementExitRow | SeatMapCabinRowSectionElementLavatory | SeatMapCabinRowSectionElementGalley | SeatMapCabinRowSectionElementCloset | SeatMapCabinRowSectionElementStairs;
|
|
189
|
+
export type SeatMapCabinRowSectionElementType = SeatMapCabinRowSectionElement["type"];
|
|
190
|
+
export type SeatMapCabinRowSectionElementAmenity = Exclude<SeatMapCabinRowSectionElementType, "empty" | "seat">;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./Aircraft";
|
|
2
|
+
export * from "./Airline";
|
|
3
|
+
export * from "./Airport";
|
|
4
|
+
export * from "./City";
|
|
5
|
+
export * from "./CreateOrderPayload";
|
|
6
|
+
export * from "./CurrencyConversion";
|
|
7
|
+
export * from "./DuffelAncillariesProps";
|
|
8
|
+
export * from "./Offer";
|
|
9
|
+
export * from "./Order";
|
|
10
|
+
export * from "./Place";
|
|
11
|
+
export * from "./SeatMap";
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { config } from "dotenv";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import prompts from "prompts";
|
|
4
|
+
import { withPlural } from "../src/lib/withPlural";
|
|
5
|
+
import { Offer } from "../src/types/Offer";
|
|
6
|
+
|
|
7
|
+
config({ path: ".env.local" });
|
|
8
|
+
|
|
9
|
+
const DUFFEL_API_TOKEN = process.env.DUFFEL_API_TOKEN || "";
|
|
10
|
+
const DUFFEL_API_URL = process.env.DUFFEL_API_URL || "";
|
|
11
|
+
const VERBOSE = process.env.VERBOSE === "true";
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line
|
|
14
|
+
const log = console.log;
|
|
15
|
+
|
|
16
|
+
if (DUFFEL_API_URL.includes("localhost"))
|
|
17
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
18
|
+
|
|
19
|
+
const duffelHeaders = {
|
|
20
|
+
"Duffel-Version": "v1",
|
|
21
|
+
"Accept-Encoding": "gzip",
|
|
22
|
+
Accept: "application/json",
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
Authorization: `Bearer ${DUFFEL_API_TOKEN}`,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const makeMockDateInTheFuture = (daysAhead) => {
|
|
28
|
+
const now = new Date(Date.now());
|
|
29
|
+
now.setDate(now.getDate() + daysAhead);
|
|
30
|
+
return now;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const createOfferRequest = async (
|
|
34
|
+
sliceInput: [string, string][],
|
|
35
|
+
adults: number,
|
|
36
|
+
requestedSources?: string[]
|
|
37
|
+
) => {
|
|
38
|
+
const data = {
|
|
39
|
+
slices: sliceInput.map(([origin, destination], index) => ({
|
|
40
|
+
origin,
|
|
41
|
+
destination,
|
|
42
|
+
departure_date: makeMockDateInTheFuture(7 * (index + 1))
|
|
43
|
+
.toISOString()
|
|
44
|
+
.split("T")[0],
|
|
45
|
+
})),
|
|
46
|
+
passengers: Array(adults).fill({ type: "adult" }),
|
|
47
|
+
...(requestedSources && { requested_sources: requestedSources }),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const response = await fetch(
|
|
51
|
+
DUFFEL_API_URL + "/air/offer_requests?return_offers=true",
|
|
52
|
+
{
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: duffelHeaders,
|
|
55
|
+
body: JSON.stringify({ data }),
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return await response.json();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getOffer = async (offerId: string): Promise<{ data: Offer }> => {
|
|
63
|
+
const response = await fetch(
|
|
64
|
+
process.env.DUFFEL_API_URL +
|
|
65
|
+
`/air/offers/${offerId}/?return_available_services=true`,
|
|
66
|
+
{ headers: duffelHeaders }
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return await response.json();
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const getSeatMaps = async (offerId: string) => {
|
|
73
|
+
const response = await fetch(
|
|
74
|
+
process.env.DUFFEL_API_URL + `/air/seat_maps?offer_id=${offerId}`,
|
|
75
|
+
{ headers: duffelHeaders }
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return await response.json();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const main = async () => {
|
|
82
|
+
try {
|
|
83
|
+
// prompt user for how many slices they want
|
|
84
|
+
const { sliceCount } = await prompts({
|
|
85
|
+
type: "number",
|
|
86
|
+
name: "sliceCount",
|
|
87
|
+
message: "How many slices do you want?",
|
|
88
|
+
initial: 2,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// for each slice, prompt for origin and destination
|
|
92
|
+
const sliceInput = new Array<[string, string]>(sliceCount);
|
|
93
|
+
for (let i = 0; i < sliceCount; i++) {
|
|
94
|
+
let origin = sliceInput[i - 1]?.[0];
|
|
95
|
+
|
|
96
|
+
log(`\nSlice #${i + 1}`);
|
|
97
|
+
const { value } = await prompts({
|
|
98
|
+
type: "text",
|
|
99
|
+
name: "value",
|
|
100
|
+
message: "What is the origin?",
|
|
101
|
+
limit: 3,
|
|
102
|
+
});
|
|
103
|
+
origin = value;
|
|
104
|
+
|
|
105
|
+
const { destination } = await prompts({
|
|
106
|
+
type: "text",
|
|
107
|
+
name: "destination",
|
|
108
|
+
message: "What is the destination?",
|
|
109
|
+
limit: 3,
|
|
110
|
+
});
|
|
111
|
+
sliceInput[i] = [origin, destination];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ask how many adults
|
|
115
|
+
log(`\n`);
|
|
116
|
+
const { adultCount } = await prompts({
|
|
117
|
+
type: "number",
|
|
118
|
+
name: "adultCount",
|
|
119
|
+
message: "How many adults traveling?",
|
|
120
|
+
initial: 1,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ask for requested sources
|
|
124
|
+
log(`\n`);
|
|
125
|
+
const { requestedSources } = await prompts({
|
|
126
|
+
type: "text",
|
|
127
|
+
name: "requestedSources",
|
|
128
|
+
message: "What sources do you want to request?",
|
|
129
|
+
initial: "duffel_airways",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// run search
|
|
133
|
+
const { data: offerRequest } = await createOfferRequest(
|
|
134
|
+
sliceInput,
|
|
135
|
+
adultCount,
|
|
136
|
+
requestedSources ? requestedSources.split(",") : undefined
|
|
137
|
+
);
|
|
138
|
+
if (VERBOSE) {
|
|
139
|
+
const airlines = new Set(
|
|
140
|
+
offerRequest.offers.map((offer) => offer.owner.iata_code)
|
|
141
|
+
);
|
|
142
|
+
log(
|
|
143
|
+
`Received ${withPlural(
|
|
144
|
+
offerRequest.offers.length,
|
|
145
|
+
"offer",
|
|
146
|
+
"offers"
|
|
147
|
+
)} back ` +
|
|
148
|
+
`from ${withPlural(
|
|
149
|
+
airlines.size,
|
|
150
|
+
"airline",
|
|
151
|
+
"airlines"
|
|
152
|
+
)}(${Array.from(airlines.values()).join(",")})`
|
|
153
|
+
);
|
|
154
|
+
log(
|
|
155
|
+
`Search completed, offer request ID: ${offerRequest.id}.\nUsing first offer to get services: ${offerRequest.offers[0].id}\n`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// get offer
|
|
160
|
+
const { data: firstOffer } = await getOffer(offerRequest.offers[0].id);
|
|
161
|
+
|
|
162
|
+
// save to src/fixtures/offer
|
|
163
|
+
const _description =
|
|
164
|
+
`This fixture was generated by scripts/generate-fixture.ts. ` +
|
|
165
|
+
`It includes an offer with ${withPlural(
|
|
166
|
+
adultCount,
|
|
167
|
+
"passenger",
|
|
168
|
+
"passengers"
|
|
169
|
+
)} and ` +
|
|
170
|
+
`${withPlural(sliceCount, "slice", "slices")}: ${sliceInput.join(" ⇢ ")}`;
|
|
171
|
+
|
|
172
|
+
// Set offer to expire in 10 years so that this fixture won't trigger the
|
|
173
|
+
// "expired" error in the future.
|
|
174
|
+
const expires_at = makeMockDateInTheFuture(365 * 10).toISOString();
|
|
175
|
+
|
|
176
|
+
fs.writeFileSync(
|
|
177
|
+
`src/fixtures/offers/${firstOffer.id}.json`,
|
|
178
|
+
JSON.stringify({ _description, ...firstOffer, expires_at }, null, 2)
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// get seat maps
|
|
182
|
+
const { data: seatMaps } = await getSeatMaps(firstOffer.id);
|
|
183
|
+
|
|
184
|
+
// save to src/fixtures/seat-maps
|
|
185
|
+
fs.writeFileSync(
|
|
186
|
+
`src/fixtures/seat-maps/${firstOffer.id}.json`,
|
|
187
|
+
JSON.stringify(seatMaps, null, 2)
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
log(`\n🐄 Fixtures saved for ${firstOffer.id}`);
|
|
191
|
+
log(` ↳ /src/fixtures/offer/${firstOffer.id}.json`);
|
|
192
|
+
log(` ↳ /src/fixtures/seat-maps/${firstOffer.id}.json\n`);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
// eslint-disable-next-line
|
|
195
|
+
console.error(err);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export default main();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Exit if a command fails
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
# Define where to upload files too
|
|
7
|
+
GCP_PREFIX=gs://duffel-assets/components
|
|
8
|
+
|
|
9
|
+
# Gets current version from package.json
|
|
10
|
+
VERSION=$(jq '.version' package.json -r)
|
|
11
|
+
|
|
12
|
+
# Check if version is empty
|
|
13
|
+
if [ -z "$VERSION" ]; then
|
|
14
|
+
echo "Error: version is empty"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Check if folder exists
|
|
19
|
+
if gsutil -q stat "$GCP_PREFIX/$VERSION/duffel-ancillaries.js"; then
|
|
20
|
+
# Confirm with user before overriding
|
|
21
|
+
read -p "Version \`$VERSION\` already exists. Do you want to override it? (\`Y\` to continue) " -n 1 -r
|
|
22
|
+
echo
|
|
23
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
24
|
+
# Delete old version from GCP
|
|
25
|
+
gcloud storage rm -r "$GCP_PREFIX/$VERSION"
|
|
26
|
+
else
|
|
27
|
+
echo "Aborting script."
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Uploads fixtures
|
|
33
|
+
gcloud storage cp -r cdn-dist "$GCP_PREFIX/$VERSION"
|
|
34
|
+
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Icon, IconName } from "@components/shared/Icon";
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
export interface CardProps {
|
|
6
|
+
buttonTitle: string;
|
|
7
|
+
title: string;
|
|
8
|
+
icon: IconName;
|
|
9
|
+
onClick?: (() => void) | null;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
copy: string;
|
|
12
|
+
isLoading?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
isSelected?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Card: React.FC<CardProps> = ({
|
|
18
|
+
buttonTitle,
|
|
19
|
+
title,
|
|
20
|
+
icon,
|
|
21
|
+
copy,
|
|
22
|
+
onClick,
|
|
23
|
+
children,
|
|
24
|
+
isLoading,
|
|
25
|
+
disabled,
|
|
26
|
+
isSelected,
|
|
27
|
+
}) => {
|
|
28
|
+
const hasChildren = React.Children.toArray(children).length > 0;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<button
|
|
32
|
+
title={buttonTitle}
|
|
33
|
+
{...(onClick && { onClick })}
|
|
34
|
+
disabled={disabled}
|
|
35
|
+
className={classNames(
|
|
36
|
+
"ancillary-card",
|
|
37
|
+
isLoading && "ancillary-card--loading"
|
|
38
|
+
)}
|
|
39
|
+
// We are using inline styles here because
|
|
40
|
+
// we don't want the cards to appear unstyled
|
|
41
|
+
// before the CSS stylesheet loads.
|
|
42
|
+
// This is important for this component since it
|
|
43
|
+
// be on visible on the page when it loads.
|
|
44
|
+
style={{
|
|
45
|
+
cursor: "pointer",
|
|
46
|
+
background: "transparent",
|
|
47
|
+
color: "var(--GREY-900)",
|
|
48
|
+
border: "solid 1px rgba(226, 226, 232, 1)",
|
|
49
|
+
display: "flex",
|
|
50
|
+
rowGap: "4px",
|
|
51
|
+
padding: "20px",
|
|
52
|
+
borderRadius: "8px",
|
|
53
|
+
justifyContent: "space-between",
|
|
54
|
+
flexWrap: "wrap",
|
|
55
|
+
flexDirection: "column",
|
|
56
|
+
width: "100%",
|
|
57
|
+
boxSizing: "border-box",
|
|
58
|
+
fontSize: "16px",
|
|
59
|
+
fontWeight: "400",
|
|
60
|
+
lineHeight: "24px",
|
|
61
|
+
letterSpacing: "0em",
|
|
62
|
+
transition:
|
|
63
|
+
"border-color 0.3s var(--TRANSITION-CUBIC-BEZIER) background-color 0.3s var(--TRANSITION-CUBIC-BEZIER)",
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
style={{
|
|
68
|
+
display: "flex",
|
|
69
|
+
alignItems: "flex-start",
|
|
70
|
+
marginBlock: "0",
|
|
71
|
+
textAlign: "start",
|
|
72
|
+
marginTop: "2px",
|
|
73
|
+
columnGap: "12px",
|
|
74
|
+
width: "100%",
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
<div>
|
|
78
|
+
{isSelected ? (
|
|
79
|
+
<Icon name="check" className="ancillary-card__selected-icon" />
|
|
80
|
+
) : (
|
|
81
|
+
<Icon name={icon} />
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div
|
|
86
|
+
style={{
|
|
87
|
+
display: "flex",
|
|
88
|
+
justifyContent: "space-between",
|
|
89
|
+
alignItems: "start",
|
|
90
|
+
width: "100%",
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<p
|
|
94
|
+
className="p1--semibold"
|
|
95
|
+
style={{
|
|
96
|
+
marginBlock: "0",
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
{title}
|
|
100
|
+
</p>
|
|
101
|
+
<div className="ancillary-card__children">
|
|
102
|
+
{hasChildren ? (
|
|
103
|
+
children
|
|
104
|
+
) : (
|
|
105
|
+
<Icon
|
|
106
|
+
name="expand_content"
|
|
107
|
+
className="ancillary-card__expand-icon"
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<p
|
|
114
|
+
className="p1--regular"
|
|
115
|
+
style={{
|
|
116
|
+
textAlign: "start",
|
|
117
|
+
color: "var(--GREY-600)",
|
|
118
|
+
marginLeft: "34px",
|
|
119
|
+
marginBlock: "0",
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
{copy}
|
|
123
|
+
</p>
|
|
124
|
+
</button>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { IconButton } from "@components/shared/IconButton";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
interface CounterProps {
|
|
5
|
+
id: string;
|
|
6
|
+
min: number;
|
|
7
|
+
max: number;
|
|
8
|
+
value: number;
|
|
9
|
+
onChange: (value: number) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Counter: React.FC<CounterProps> = ({
|
|
13
|
+
id,
|
|
14
|
+
min,
|
|
15
|
+
max,
|
|
16
|
+
value,
|
|
17
|
+
onChange,
|
|
18
|
+
}) => (
|
|
19
|
+
<div className="counter" id={id}>
|
|
20
|
+
<IconButton
|
|
21
|
+
icon="minus"
|
|
22
|
+
title="Remove one"
|
|
23
|
+
id={`${id}-minus`}
|
|
24
|
+
data-testid={`${id}-minus`}
|
|
25
|
+
variant="outlined"
|
|
26
|
+
disabled={value <= min}
|
|
27
|
+
onClick={() => onChange(Math.max(value - 1, min))}
|
|
28
|
+
/>
|
|
29
|
+
<div className="counter__count-label">{value}</div>
|
|
30
|
+
<IconButton
|
|
31
|
+
icon="add"
|
|
32
|
+
title="Add one"
|
|
33
|
+
id={`${id}-plus`}
|
|
34
|
+
data-testid={`${id}-plus`}
|
|
35
|
+
variant="outlined"
|
|
36
|
+
disabled={value >= max}
|
|
37
|
+
onClick={() => onChange(Math.min(value + 1, max))}
|
|
38
|
+
/>
|
|
39
|
+
</div>
|
|
40
|
+
);
|