From 41564f694b04b0f3cfbce99812c05c5985008f0a Mon Sep 17 00:00:00 2001 From: dochiana <146454910+dochiana@users.noreply.github.com> Date: Thu, 19 Oct 2023 00:07:20 +0300 Subject: [PATCH] Feature als 1591 web responsive (#67) * fixed the banner issue when user connected back once it is closed (#21) * fixed dynamic text issue on auto when toggling units * unauth simulation, analytics, localization, translation script and update default CF template * add footer in TriggeredByEnum and fix tests (#39) * localization fixes for terms page, auth geofence box * add pageViewDuration in LEAVE_PAGE event (#40) * Improve leave page event attr (#44) * fix pageViewDuration * add pageViewIdentifier to track actions per page view * placeholder for the loading state during the search operation. (#45) * merged lib approach * translated chinese in language dropdown * improve pageViewIdentifier storage (#46) * fixed text responsive issues with button (#42) * updated README * Dynamically Localize Number Formats for International User Support (#47) * fixed text & added new script to update translation (#48) * updated regex to support geofence name in multiple languages * Updated CF templates (#49) * Remove useRecordViewPage from DemoPlaceholderPage and amend page identifier key (#50) * Update main-cf-template.yaml * Update main-cf-template.yaml * fixes callback to show auth tracker box * removed log * Improve analytics (#51) * log session_end event and improve page identifiers storage * remove pageViewDuration * amend pageViewIdentifier * Add Open Data Maps to demo app (#52) * Addeede open data provider * completed adding open data provider * fixed attribute color issue with visualization dark mode * fixed build issues and updated name for translation script * Feat/change country approach (#53) * change the country fetching approach, also remove initiateAnalytics * add COUNTRY_EVALUATION_URL env variable * fix env variables * return undefined if the country code is Unknown getCountryCode * revert stopsession debouce time and remove logs * set default COUNTRY_EVALUATION_URL * unauth simulation fixes - removed geofence border, fixed animation, updated icons * Add the ability to dynamic evaluate and assign fastest region (#54) * change env variable for multiple identity pool ids (#56) * change env variable for multiple identity pool ids * fix useEffect error in useRecordViewPage * Fixed unauth simulation ui issues (#55) * fixed unauth simulation ui issues * removed unused import * fixed unauth tracking history left icon alignment * fixed notification icon size * Allowed duplicates geeofence notifications for unauth * fixed button hover for primary button * updated env and websocket urls approach * removed log * updated env * library format changed from es to cjs (#57) * Revert "library format changed from es to cjs (#57)" (#58) This reverts commit c4f2fa23497c43be589ab23745b710d50abc1fcb. * updated language icon ins settings modal * added option in settings modal to select region * updated translations * add geofence notification case in cypress tests (#60) * fixed dot issue in arabic and hebrew, fixed the RTL issue with short distance unit (#61) * fixed dot issue in arabic and hebrew, fixed the rtl issue with short unit * fixed short units translation * fixed paused simulation issue when active the other routes & fixed spelling for trackers * improved the short distance unit ui * region and opendata ui fix * fixes for unauth simulation * Added kinesis resources & updated pinpoint event stream (#62) Co-authored-by: Harshit Wadhawan * updated readme * updated readme * fixed translation issues (#63) * introduced react-spring-bottom-sheet * add unit test for Pinpoint Analytics (#64) * add security.txt (#65) * switched to npm and locked dep versions * added CSP to meta tag * implemented the Explore UI for mobile and tablet * added manifest-src to CSP meta tag * added manifest-src data to CSP meta tag * Update default-unauth-resources-template.yaml * updated readme * added fix for unauth creds expiry issue * Update default-unauth-resources-template.yaml * updated CSP * Improve enahnce analytics test (#68) * mock countryUtil.getCountryCode * add more failure test cases in PinpointAnalytics test * remove recording of search value in location search * removed CSP meta tag * updated default CF template * potential fix for multi notifications for unauth and auth simulations, reset map to default when region switched * udpated translations * completed map style responsive UI * updated E2E tests * added cory's feedback * updated e2e tests * fixed failing E2E tests * updated CSP * on going search box * fixed POI Card * fixed issues with direction * updated attribution setting logic * added hash to script-src * on going routes ui * [ALS-1636] geofence tooltip position update * search issue, udpated short unit notation * updated websocket URL logic * updated default CF template * [ALS-1643] render geofences on list view * ongoing routes * ongoing responsiveness task for tracker & geofence * tracker & geofence * built UI for tracker & geofence for tablet and mobile * fixed search issue * applied edit icon along with mobile icon for simulation * fixed close icon for geofence * fixed & complete settings & about UI for tablet and mobile * built routes/direction * improved experience * removed extra // onBlur={() => isBothInputFilled && onFocus(InputType.FROM, true)} * removed unsued import * fixed arrow up down issue with routes * Merge branch 'main' into feature_ALS-1591_web-responsive_main * fixed logo position * cleanup * hid the mapbutton, tracker and geofence on the map according to responsiveness need * cleanup * fixed closing of unath simulation * fixed type issues for build * [ALS-1591] updated state issue * fixed few issues with responsiveness * removed console.log * removed extra css * fixed scroll issue with search * map focus on routing fix * blank white space fix * changed mobile breakpoint to 430px * fixed map provider search param issue and current location issue * fixed sticky search bar and blank space issue * fixed poi card styling * fixed welcome modal, about modal * fixed scrolling issue * fixed close button issue with auth tracker * fixes for unauth simulation UI * changed explore min height * fixed ui issues with scrolling and padding * fixed few things * fixed route UI for mobile and tablet * fixed map styles UI issues along with closing issue on ios and updated a text * uncommented connect to aws click handler * fixed button active state on tablet and mobile * fixed button active state on tablet and mobile * fixed pull refresh issue * fixed pull refresh issue * fixed welcome modal UI, dropdown and connect AWS modal * removed extra css * fixed welcome modal UI and About modal UI * fixed auth simulation zoom and focus issues, added links to logo * fixed routes steps * fixed map styles opening * implemented arrow icons on bottom sheet header * fixed routes step * fixed vancouver bounds for tablet and mobile * fixed few issues * implemented scrollbar hidding and fixed attribute button styling * removed body overflow hidden * enabled route history for all devices * fixed logo issue * [ALS-1671] fixed space issue for numbers in french * removed log * updated failing tests * updated github workflow * updated failing tests * mocked reload * refined secrets * refined secrets * updated e2e test * updated workflow files * reverted workflow file changes * updated attributions and data provider search param * loggin env for all workflows * logging env for all workflows * removed log * logging env in workflow * reverted logging env in workflow * updated security-tests .env.example * responsive fixes * fixed few issues * fix height of auth tracker/geofence * cleanup * added close icon in bottom sheet header & fixed ui * fixed build failure * fixed closing issue on routes and poi card * cleanup * fixed minor issues * fixed placeholder glitch for mobile * cleanup * fixed minor issues raised in regression * fixed setting modal initial option issue * fixed replaceAll issue * fixed replaceAll issue * resolved build errors * updated toast implementation to be overlapping * fixed socket banner padding issue * fixed exit simulation modal, bottom sheet expanding issue with search, routes and map styles * removed comment * fixed scroll issue in bottom sheet, glitch issue with bottom sheet * fixes failing tests and updated map styles responsive logic * fixes failing tests and updated map styles responsive logic * unauth simulation exit modal fix * fixed map styles issues on tablet during unauth simulation, added and updated notifications for enter and exit events during unauth simulaiton * removed geofence and tracker shortcuts when unauth simulation enabled * implemented 40 percent height on bottom sheet * fixed search placeholder * fixed geofence and tracker shortcut functionality * fixed opening issue with routes and explore ui * updated fitbounds for route on mobile screen * fixed my location selection issue * minor fix * fixed poi card issue * removed background * resolved unauth simulation issue * fitbounds functionality update, and updated unit tests * removed log * poi card issue * fixed height issue on ipad * mapstyles search fix * fixed routes and search issues with height * fixed routes and search issues with height * unauth exit modal for mobile and tablet fix * updated simulation speed * fixed aws connect modal keyboard closing and routes issue * fixed route not found on poi card * hide GrabMaps when unauth simulation is open * fixed tootip greyed out issue * resolved region issue with connecting AWS account --------- Co-authored-by: Dabeer Raza Co-authored-by: Ahmad Azizi <91204996+its-aazizi@users.noreply.github.com> Co-authored-by: NihaalAftab-Makeen <86765805+NihaalAftab-Makeen@users.noreply.github.com> Co-authored-by: harshit-makeen <118467076+harshit-makeen@users.noreply.github.com> Co-authored-by: Harshit Wadhawan Co-authored-by: wadhawh <130486914+wadhawh@users.noreply.github.com> --- .../ConnectAwsAccountModal.test.tsx | 15 +-- .../ConnectAwsAccountModal.tsx | 66 ++++++------ .../GrabConfirmationModal.test.tsx | 19 ++-- .../GrabConfirmationModal.tsx | 17 +-- .../molecules/InputField/InputField.tsx | 5 +- .../molecules/MapButtons/MapButtons.tsx | 1 - src/atomicui/molecules/Popup/Popup.tsx | 35 +++--- .../AuthGeofenceBox/AuthGeofenceBox.tsx | 102 +++++++++--------- .../ResponsiveBottomSheet.tsx | 7 +- src/atomicui/organisms/RouteBox/RouteBox.tsx | 8 +- src/atomicui/organisms/RouteBox/styles.scss | 13 +++ .../SettingsModal/SettingsModal.test.tsx | 72 ++++++------- .../organisms/SettingsModal/SettingsModal.tsx | 71 ++++-------- .../UnauthSimulation/UnauthSimulation.tsx | 1 + src/atomicui/pages/DemoPage/DemoPage.tsx | 29 +++-- src/hooks/useAmplifyAuth.ts | 28 ++++- src/stores/useAmplifyAuthStore.ts | 6 +- 17 files changed, 266 insertions(+), 229 deletions(-) diff --git a/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.test.tsx b/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.test.tsx index cf0b2221..2669d844 100644 --- a/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.test.tsx +++ b/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.test.tsx @@ -12,7 +12,7 @@ Object.defineProperty(window, "location", { value: { reload: mockReload } }); -const props: ConnectAwsAccountModalProps = { +const mockProps: ConnectAwsAccountModalProps = { open: true, onClose: jest.fn(), handleCurrentLocationAndViewpoint: jest.fn() @@ -25,7 +25,10 @@ const mockUseAmplifyAuthData = { setIsUserAwsAccountConnected: jest.fn(), clearCredentials: jest.fn(), onLogin: jest.fn(), - validateFormValues: jest.fn() + validateFormValues: jest.fn(), + stackRegion: "ap-southeast-1", + cloudFormationLink: "https://link.com", + handleStackRegion: jest.fn() }; const mockUseAmplifyMapData = { @@ -57,7 +60,7 @@ describe("", () => { const renderComponent = () => render( - + ); @@ -121,7 +124,7 @@ describe("", () => { fireEvent.click(getByTestId("continue-to-explore")); }); waitFor(() => { - expect(props.onClose).toHaveBeenCalled(); + expect(mockProps.onClose).toHaveBeenCalled(); expect(mockReload).toHaveBeenCalled(); }); }); @@ -135,7 +138,7 @@ describe("", () => { fireEvent.click(getByTestId("sign-in-button")); }); waitFor(() => { - expect(props.onClose).toHaveBeenCalled(); + expect(mockProps.onClose).toHaveBeenCalled(); expect(mockUseAmplifyAuthData.onLogin).toHaveBeenCalled(); }); }); @@ -154,7 +157,7 @@ describe("", () => { fireEvent.click(getByTestId("modal-container")); }); waitFor(() => { - expect(props.onClose).toHaveBeenCalled(); + expect(mockProps.onClose).toHaveBeenCalled(); expect(mockUseAmplifyAuthData.onLogin).toHaveBeenCalled(); }); }); diff --git a/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.tsx b/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.tsx index d3ad1b85..e6822751 100644 --- a/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.tsx +++ b/src/atomicui/molecules/ConnectAwsAccountModal/ConnectAwsAccountModal.tsx @@ -1,7 +1,7 @@ /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. */ /* SPDX-License-Identifier: MIT-0 */ -import React, { useCallback, useEffect, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Button, Flex, Link, Text, View } from "@aws-amplify/ui-react"; import { IconAwsCloudFormation, IconCheckMarkCircle } from "@demo/assets"; @@ -13,17 +13,15 @@ import useDeviceMediaQuery from "@demo/hooks/useDeviceMediaQuery"; import { ConnectFormValuesType, EsriMapEnum, MapProviderEnum } from "@demo/types"; import { AnalyticsEventActionsEnum, EventTypeEnum, TriggeredByEnum } from "@demo/types/Enums"; import { record } from "@demo/utils/analyticsUtils"; -import { transformCloudFormationLink } from "@demo/utils/transformCloudFormationLink"; +import { isAndroid, isIOS } from "react-device-detect"; import { useTranslation } from "react-i18next"; import "./styles.scss"; const { - ENV: { CF_TEMPLATE }, ROUTES: { HELP }, MAP_RESOURCES: { GRAB_SUPPORTED_AWS_REGIONS }, LINKS: { AWS_TERMS_AND_CONDITIONS } } = appConfig; - let scrollTimeout: NodeJS.Timer | undefined; export interface ConnectAwsAccountModalProps { @@ -44,16 +42,16 @@ const ConnectAwsAccountModal: React.FC = ({ UserPoolId: "", WebSocketUrl: "" }); - const [cloudFormationLink, setCloudFormationLink] = useState(CF_TEMPLATE); - const [stackRegion, setStackRegion] = useState<{ value: string; label: string }>(); const { - region, isUserAwsAccountConnected, setConnectFormValues, setIsUserAwsAccountConnected, clearCredentials, onLogin, - validateFormValues + validateFormValues, + stackRegion, + cloudFormationLink, + handleStackRegion } = useAmplifyAuth(); const { resetStore: resetAwsStore } = useAws(); const { mapProvider: currentMapProvider, setMapProvider, setMapStyle } = useAmplifyMap(); @@ -62,32 +60,30 @@ const ConnectAwsAccountModal: React.FC = ({ const langDir = i18n.dir(); const isLtr = langDir === "ltr"; const isOverflowing = ["de", "es", "fr", "it", "pt-BR"].includes(i18n.language); - const { isDesktop } = useDeviceMediaQuery(); - - const handleStackRegion = useCallback( - (option: { value: string; label: string }) => { - const { label, value } = option; + const { isDesktop, isDesktopBrowser } = useDeviceMediaQuery(); + const contentRef = useRef(null); - if (isDesktop) { - setStackRegion(option); - } else { - const translatedLabel = t(label); - const l = translatedLabel.slice(0, translatedLabel.indexOf(")") + 1); - setStackRegion({ label: l, value }); + const handleScroll = useCallback(() => { + if (contentRef.current) { + for (const key of Object.keys(formValues)) { + const inputField = document.querySelector(`input[name=${key}]`) as HTMLInputElement; + if (inputField) inputField.blur(); } - }, - [isDesktop, t] - ); + } + }, [formValues]); useEffect(() => { - const regionOption = region && regionsData.find(option => option.value === region); + if (!isDesktop && (isAndroid || isIOS) && open) { + setTimeout(() => { + const currentContentRef = contentRef.current; + if (currentContentRef) currentContentRef.addEventListener("touchmove", handleScroll); - if (regionOption) { - const newUrl = transformCloudFormationLink(region); - setCloudFormationLink(newUrl); - handleStackRegion(regionOption); + return () => { + if (currentContentRef) currentContentRef.removeEventListener("touchmove", handleScroll); + }; + }, 500); } - }, [region, handleStackRegion]); + }, [handleScroll, isDesktop, isDesktopBrowser, contentRef, open]); useEffect(() => { if (isOverflowing) { @@ -112,11 +108,12 @@ const ConnectAwsAccountModal: React.FC = ({ isUserAwsAccountConnected && window.location.reload(); }; - const _onSelect = (option: { value: string; label: string }) => { - const newUrl = transformCloudFormationLink(option.value); - setCloudFormationLink(newUrl); - handleStackRegion(option); - }; + const _onSelect = useCallback( + (option: { value: string; label: string }) => { + handleStackRegion(option); + }, + [handleStackRegion] + ); const isBtnEnabled = useMemo( () => keyArr.filter(key => !!formValues[key as keyof typeof formValues]).length === keyArr.length, @@ -198,7 +195,7 @@ const ConnectAwsAccountModal: React.FC = ({ onClose={handleModalClose} className="connect-aws-account-modal" content={ - + = ({ value={formValues[key as keyof ConnectFormValuesType]} onChange={e => onChangeFormValues(key, e.target.value.trim())} dir={langDir} + name={key} /> ); })} diff --git a/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.test.tsx b/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.test.tsx index a1cdd4d8..0479ea22 100644 --- a/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.test.tsx +++ b/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.test.tsx @@ -4,18 +4,17 @@ import { I18nextProvider } from "react-i18next"; import GrabConfirmationModal from "./GrabConfirmationModal"; -describe("GrabConfirmationModal", () => { - const props = { - open: true, - onClose: jest.fn(), - onConfirm: jest.fn(), - isUnauthSimulationOpen: false - }; +const mockProps = { + open: true, + onClose: jest.fn(), + onConfirm: jest.fn() +}; +describe("GrabConfirmationModal", () => { const renderComponent = () => { return render( - + ); }; @@ -28,12 +27,12 @@ describe("GrabConfirmationModal", () => { it("triggers onClose when Cancel button is clicked", () => { const { getByText } = renderComponent(); fireEvent.click(getByText("Cancel")); - expect(props.onClose).toHaveBeenCalled(); + expect(mockProps.onClose).toHaveBeenCalled(); }); it("triggers onConfirm when Enable Grab button is clicked", () => { const { getByTestId } = renderComponent(); fireEvent.click(getByTestId("confirmation-button")); - expect(props.onConfirm).toHaveBeenCalled(); + expect(mockProps.onConfirm).toHaveBeenCalled(); }); }); diff --git a/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.tsx b/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.tsx index 81bfc587..5300cfc9 100644 --- a/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.tsx +++ b/src/atomicui/molecules/GrabConfirmationModal/GrabConfirmationModal.tsx @@ -19,7 +19,6 @@ interface GrabConfirmationModalProps { onConfirm: () => void; showDoNotAskAgainCheckbox?: boolean; onConfirmationCheckboxOnChange?: (e: boolean) => void; - isUnauthSimulationOpen: boolean; } const GrabConfirmationModal: React.FC = ({ @@ -27,14 +26,13 @@ const GrabConfirmationModal: React.FC = ({ onClose, onConfirm, showDoNotAskAgainCheckbox, - onConfirmationCheckboxOnChange, - isUnauthSimulationOpen + onConfirmationCheckboxOnChange }) => { const { t } = useTranslation(); return ( = ({ > {t("grab_cm__desc.text")} - {isUnauthSimulationOpen && ( - - {t("grab_cm__unauth_simulaiton.text")} - - )} } onConfirm={onConfirm} diff --git a/src/atomicui/molecules/InputField/InputField.tsx b/src/atomicui/molecules/InputField/InputField.tsx index c3c9c736..7c006d6c 100644 --- a/src/atomicui/molecules/InputField/InputField.tsx +++ b/src/atomicui/molecules/InputField/InputField.tsx @@ -23,6 +23,7 @@ interface InputFieldProps { onBlur?: FocusEventHandler; onKeyDown?: KeyboardEventHandler; searchInputRef?: React.RefObject; + name?: string; } const InputField: React.FC = ({ @@ -41,7 +42,8 @@ const InputField: React.FC = ({ onBlur, innerStartComponent, searchInputRef, - onKeyDown + onKeyDown, + name }) => { return ( @@ -65,6 +67,7 @@ const InputField: React.FC = ({ onFocus={onFocus} onBlur={onBlur} onKeyDown={onKeyDown} + name={name} /> {innerEndComponent} diff --git a/src/atomicui/molecules/MapButtons/MapButtons.tsx b/src/atomicui/molecules/MapButtons/MapButtons.tsx index 8258c34c..c4aeb7e8 100644 --- a/src/atomicui/molecules/MapButtons/MapButtons.tsx +++ b/src/atomicui/molecules/MapButtons/MapButtons.tsx @@ -470,7 +470,6 @@ const MapButtons: React.FC = ({ value={searchValue} onChange={e => setSearchValue(e.target.value)} onClick={() => { - console.log("clicked"); isHandDevice && setSearchWidth(searchDesktopWidth); !!showFilter && setShowFilter(false); diff --git a/src/atomicui/molecules/Popup/Popup.tsx b/src/atomicui/molecules/Popup/Popup.tsx index 90d75cae..618ba3ce 100644 --- a/src/atomicui/molecules/Popup/Popup.tsx +++ b/src/atomicui/molecules/Popup/Popup.tsx @@ -51,6 +51,7 @@ const Popup: React.FC = ({ active, info, select, onClosePopUp, setInfo }) const isLtr = langDir === "ltr"; const isLanguageRTL = ["ar", "he"].includes(currentLang); const POICardRef = useRef(null); + const [isLoading, setIsLoading] = useState(true); const geodesicDistance = useMemo( () => @@ -102,8 +103,13 @@ const Popup: React.FC = ({ active, info, select, onClosePopUp, setInfo }) DistanceUnit: currentMapUnit === METRIC ? KILOMETERS : MILES, TravelMode: TravelMode.CAR }; - const r = await getRoute(params as CalculateRouteRequest, TriggeredByEnum.PLACES_POPUP); - setRouteData(r); + try { + setIsLoading(true); + const r = await getRoute(params as CalculateRouteRequest, TriggeredByEnum.PLACES_POPUP); + setRouteData(r); + } finally { + setIsLoading(false); + } }, [currentLocationData, longitude, latitude, currentMapUnit, getRoute]); useEffect(() => { @@ -175,15 +181,19 @@ const Popup: React.FC = ({ active, info, select, onClosePopUp, setInfo }) return ( - - {localizeGeodesicDistance} - - - {geodesicDistanceUnit} - + {!isLoading && ( + <> + + {localizeGeodesicDistance} + + + {geodesicDistanceUnit} + + + )} - {t("popup__route_not_found.text")} + {!isLoading && t("popup__route_not_found.text")} ); @@ -224,11 +234,12 @@ const Popup: React.FC = ({ active, info, select, onClosePopUp, setInfo }) routeData, isLtr, t, + isLanguageRTL, localizeGeodesicDistance, geodesicDistanceUnit, currentMapUnit, - currentLang, - isLanguageRTL + isLoading, + currentLang ]); const address = useMemo(() => { @@ -310,7 +321,7 @@ const Popup: React.FC = ({ active, info, select, onClosePopUp, setInfo }) useEffect(() => { if (!isDesktop) { - const ch = POICardRef?.current?.clientHeight || 230; + const ch = POICardRef?.current?.clientHeight || 140; ui !== ResponsiveUIEnum.poi_card && setUI(ResponsiveUIEnum.poi_card); setPOICard(); setBottomSheetMinHeight(ch + 60); diff --git a/src/atomicui/organisms/AuthGeofenceBox/AuthGeofenceBox.tsx b/src/atomicui/organisms/AuthGeofenceBox/AuthGeofenceBox.tsx index d5ce60df..f46e4264 100644 --- a/src/atomicui/organisms/AuthGeofenceBox/AuthGeofenceBox.tsx +++ b/src/atomicui/organisms/AuthGeofenceBox/AuthGeofenceBox.tsx @@ -542,58 +542,60 @@ const AuthGeofenceBox: React.FC = ({ const line = turf.lineString(circle.geometry.coordinates[0]); return ( - {} : () => onClickGeofenceItem(GeofenceId, Center, Radius)} - data-tooltip-id="geofence-item" - data-tooltip-place="right" - data-tooltip-position-strategy="fixed" - data-tooltip-content={isDisabled ? t("tooltip__disabled_geofence.text") : ""} - > - - {isDisabled ? : } - - {GeofenceId} - -
{} : e => onDelete(e, GeofenceId)} + <> + {} : () => onClickGeofenceItem(GeofenceId, Center, Radius)} + data-tooltip-id="geofence-item" + data-tooltip-place="right" + data-tooltip-position-strategy="fixed" + data-tooltip-content={isDisabled ? t("tooltip__disabled_geofence.text") : ""} > - -
- {!isDisabled && ( -
- - - - - - + {isDisabled ? : } + + {GeofenceId} + +
{} : e => onDelete(e, GeofenceId)} + > +
- )} - + {!isDisabled && ( +
+ + + + + + +
+ )} + + + ); } }, diff --git a/src/atomicui/organisms/ResponsiveBottomSheet/ResponsiveBottomSheet.tsx b/src/atomicui/organisms/ResponsiveBottomSheet/ResponsiveBottomSheet.tsx index e4a33607..39f713af 100644 --- a/src/atomicui/organisms/ResponsiveBottomSheet/ResponsiveBottomSheet.tsx +++ b/src/atomicui/organisms/ResponsiveBottomSheet/ResponsiveBottomSheet.tsx @@ -69,6 +69,7 @@ interface IProps { setShowRouteBox: (b: boolean) => void; isExpandRouteOptionsMobile: boolean; setExpandRouteOptionsMobile: (b: boolean) => void; + setSearchBoxValue: React.Dispatch>; } const ResponsiveBottomSheet: FC = ({ @@ -109,7 +110,8 @@ const ResponsiveBottomSheet: FC = ({ isEditingAuthRoute, setShowRouteBox, isExpandRouteOptionsMobile, - setExpandRouteOptionsMobile + setExpandRouteOptionsMobile, + setSearchBoxValue }) => { const { isDesktop, isTablet, isMax556, isDesktopBrowser } = useDeviceMediaQuery(); const { unauthNotifications, isAddingGeofence } = useAwsGeofence(); @@ -283,7 +285,8 @@ const ResponsiveBottomSheet: FC = ({ resetAwsRouteStore(); setShowRouteBox(false); setUI(ResponsiveUIEnum.explore); - }, [setUI, resetAwsRouteStore, setShowRouteBox]); + setSearchBoxValue(""); + }, [setUI, resetAwsRouteStore, setShowRouteBox, setSearchBoxValue]); const handleClose = useCallback(() => { from === MenuItemEnum.GEOFENCE diff --git a/src/atomicui/organisms/RouteBox/RouteBox.tsx b/src/atomicui/organisms/RouteBox/RouteBox.tsx index 310cef13..548bb70c 100644 --- a/src/atomicui/organisms/RouteBox/RouteBox.tsx +++ b/src/atomicui/organisms/RouteBox/RouteBox.tsx @@ -1032,8 +1032,12 @@ const RouteBox: React.FC = ({ handleTravelModeChange(mode)} + className={`travel-mode ${travelMode === mode ? "selected" : ""} ${ + !duration ? "no-duration" : "" + }`} + onClick={() => { + duration && handleTravelModeChange(mode); + }} > {duration ? {duration} : null} diff --git a/src/atomicui/organisms/RouteBox/styles.scss b/src/atomicui/organisms/RouteBox/styles.scss index 4ba2e1bf..97d625a8 100644 --- a/src/atomicui/organisms/RouteBox/styles.scss +++ b/src/atomicui/organisms/RouteBox/styles.scss @@ -52,6 +52,11 @@ border-radius: 0.62rem; flex-shrink: 0; + &.no-duration { + background-color: var(--light-color-2); + pointer-events: none; + } + svg { min-width: 1.23rem; min-height: 1.23rem; @@ -126,6 +131,14 @@ background-color: rgba(0, 130, 150, 0.08) !important; border: none !important; + &.no-duration { + background-color: var(--light-color-2) !important; + border: 1px solid var(--grey-color-3) !important; + svg { + fill: #8e8e93 !important; + } + } + svg { fill: #008296 !important; } diff --git a/src/atomicui/organisms/SettingsModal/SettingsModal.test.tsx b/src/atomicui/organisms/SettingsModal/SettingsModal.test.tsx index 9c99477e..3b40097f 100644 --- a/src/atomicui/organisms/SettingsModal/SettingsModal.test.tsx +++ b/src/atomicui/organisms/SettingsModal/SettingsModal.test.tsx @@ -11,6 +11,41 @@ Object.defineProperty(window, "location", { value: { reload: jest.fn() } }); +const mockProps = { + renderedUpon: "", + openStylesCard: false, + setOpenStylesCard: jest.fn(), + onCloseSidebar: jest.fn(), + onOpenSignInModal: jest.fn(), + isGrabVisible: true, + showGrabDisclaimerModal: false, + onShowGridLoader: jest.fn(), + handleMapStyleChange: jest.fn(), + searchValue: "", + setSearchValue: jest.fn(), + selectedFilters: { + Providers: [], + Attribute: [], + Type: [] + }, + setSelectedFilters: jest.fn(), + isLoading: false, + onlyMapStyles: true, + resetSearchAndFilters: jest.fn(), + showOpenDataDisclaimerModal: false, + isAuthGeofenceBoxOpen: false, + onSetShowAuthGeofenceBox: jest.fn(), + isAuthTrackerDisclaimerModalOpen: false, + isAuthTrackerBoxOpen: false, + onShowAuthTrackerDisclaimerModal: jest.fn(), + onSetShowAuthTrackerBox: jest.fn(), + onShowUnauthSimulationDisclaimerModal: jest.fn(), + isUnauthGeofenceBoxOpen: false, + isUnauthTrackerBoxOpen: false, + onSetShowUnauthGeofenceBox: jest.fn(), + onSetShowUnauthTrackerBox: jest.fn() +}; + describe("", () => { let settingsModal: HTMLElement; @@ -20,41 +55,6 @@ describe("", () => { const handleCurrentLocationAndViewpoint = jest.fn(); const resetSearchAndFilters = jest.fn(); - const props = { - renderedUpon: "", - openStylesCard: false, - setOpenStylesCard: jest.fn(), - onCloseSidebar: jest.fn(), - onOpenSignInModal: jest.fn(), - isGrabVisible: true, - showGrabDisclaimerModal: false, - onShowGridLoader: jest.fn(), - handleMapStyleChange: jest.fn(), - searchValue: "", - setSearchValue: jest.fn(), - selectedFilters: { - Providers: [], - Attribute: [], - Type: [] - }, - setSelectedFilters: jest.fn(), - isLoading: false, - onlyMapStyles: true, - resetSearchAndFilters: jest.fn(), - showOpenDataDisclaimerModal: false, - isAuthGeofenceBoxOpen: false, - onSetShowAuthGeofenceBox: jest.fn(), - isAuthTrackerDisclaimerModalOpen: false, - isAuthTrackerBoxOpen: false, - onShowAuthTrackerDisclaimerModal: jest.fn(), - onSetShowAuthTrackerBox: jest.fn(), - onShowUnauthSimulationDisclaimerModal: jest.fn(), - isUnauthGeofenceBoxOpen: false, - isUnauthTrackerBoxOpen: false, - onSetShowUnauthGeofenceBox: jest.fn(), - onSetShowUnauthTrackerBox: jest.fn() - }; - const renderComponent = async (): Promise => { const renderedComponent = render( @@ -66,7 +66,7 @@ describe("", () => { handleMapProviderChange={handleMapProviderChange} handleCurrentLocationAndViewpoint={handleCurrentLocationAndViewpoint} resetSearchAndFilters={resetSearchAndFilters} - mapButtons={} + mapButtons={} /> ); diff --git a/src/atomicui/organisms/SettingsModal/SettingsModal.tsx b/src/atomicui/organisms/SettingsModal/SettingsModal.tsx index 8ea600ec..0c6cad43 100644 --- a/src/atomicui/organisms/SettingsModal/SettingsModal.tsx +++ b/src/atomicui/organisms/SettingsModal/SettingsModal.tsx @@ -1,7 +1,7 @@ /* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. */ /* SPDX-License-Identifier: MIT-0 */ -import React, { useCallback, useEffect, useMemo, useState } from "react"; +import React, { useCallback, useMemo, useState } from "react"; import { Button, CheckboxField, Divider, Flex, Link, Radio, Text, View } from "@aws-amplify/ui-react"; import { @@ -32,13 +32,11 @@ import { } from "@demo/types"; import { AnalyticsEventActionsEnum, EventTypeEnum, TriggeredByEnum } from "@demo/types/Enums"; import { record } from "@demo/utils/analyticsUtils"; -import { transformCloudFormationLink } from "@demo/utils/transformCloudFormationLink"; import { useTranslation } from "react-i18next"; import "./styles.scss"; const { POOLS, - ENV: { CF_TEMPLATE }, ROUTES: { HELP }, MAP_RESOURCES: { MAP_STYLES: { ESRI_STYLES, HERE_STYLES }, @@ -47,10 +45,6 @@ const { LINKS: { AWS_TERMS_AND_CONDITIONS }, PERSIST_STORAGE_KEYS: { FASTEST_REGION } } = appConfig; - -const fallbackRegion = Object.values(POOLS)[0]; -const region = localStorage.getItem(FASTEST_REGION) ?? fallbackRegion; -const defaultRegion = regionsData.find(option => option.value === region) as { value: string; label: string }; const { IMPERIAL, METRIC } = MapUnitEnum; const { ESRI, HERE, GRAB, OPEN_DATA } = MapProviderEnum; @@ -75,18 +69,6 @@ const SettingsModal: React.FC = ({ mapButtons, resetSearchAndFilters }) => { - const { - autoMapUnit, - setIsAutomaticMapUnit, - mapUnit: currentMapUnit, - setMapUnit, - mapProvider: currentMapProvider, - mapStyle: currentMapStyle, - setMapProvider, - setMapStyle, - resetStore: resetMapStore - } = useAmplifyMap(); - const { defaultRouteOptions, setDefaultRouteOptions, setSettingsOptions, settingsOptions } = usePersistedData(); const [formValues, setFormValues] = useState({ IdentityPoolId: "", UserDomain: "", @@ -94,8 +76,6 @@ const SettingsModal: React.FC = ({ UserPoolId: "", WebSocketUrl: "" }); - const [cloudFormationLink, setCloudFormationLink] = useState(CF_TEMPLATE); - const [stackRegion, setStackRegion] = useState<{ value: string; label: string }>(defaultRegion); const { isUserAwsAccountConnected, validateFormValues, @@ -109,8 +89,23 @@ const SettingsModal: React.FC = ({ onLogout, autoRegion, region: currentRegion, - setAutoRegion + setAutoRegion, + stackRegion, + cloudFormationLink, + handleStackRegion } = useAmplifyAuth(); + const { + autoMapUnit, + setIsAutomaticMapUnit, + mapUnit: currentMapUnit, + setMapUnit, + mapProvider: currentMapProvider, + mapStyle: currentMapStyle, + setMapProvider, + setMapStyle, + resetStore: resetMapStore + } = useAmplifyMap(); + const { defaultRouteOptions, setDefaultRouteOptions, setSettingsOptions, settingsOptions } = usePersistedData(); const { resetStore: resetAwsStore } = useAws(); const { detachPolicy } = useAwsIot(); const keyArr = Object.keys(formValues); @@ -118,34 +113,10 @@ const SettingsModal: React.FC = ({ const { t, i18n } = useTranslation(); const langDir = i18n.dir(); const isLtr = langDir === "ltr"; - const fastestRegion = localStorage.getItem(FASTEST_REGION) || ""; + const fallbackRegion = Object.values(POOLS)[0]; + const fastestRegion = localStorage.getItem(FASTEST_REGION) ?? fallbackRegion; const { isDesktop, isMobile } = useDeviceMediaQuery(); - const handleStackRegion = useCallback( - (option: { value: string; label: string }) => { - const { label, value } = option; - - if (isDesktop) { - setStackRegion(option); - } else { - const translatedLabel = t(label); - const l = translatedLabel.slice(0, translatedLabel.indexOf(")") + 1); - setStackRegion({ label: l, value }); - } - }, - [isDesktop, t] - ); - - useEffect(() => { - const regionOption = region && regionsData.find(option => option.value === region); - - if (regionOption) { - const newUrl = transformCloudFormationLink(region); - setCloudFormationLink(newUrl); - handleStackRegion(regionOption); - } - }, [handleStackRegion]); - const handleAutoMapUnitChange = useCallback(() => { setIsAutomaticMapUnit(true); resetAppState(); @@ -238,8 +209,6 @@ const SettingsModal: React.FC = ({ const _onSelect = useCallback( (option: { value: string; label: string }) => { - const newUrl = transformCloudFormationLink(option.value); - setCloudFormationLink(newUrl); handleStackRegion(option); }, [handleStackRegion] @@ -876,7 +845,7 @@ const SettingsModal: React.FC = ({ const modalCloseHandler = useCallback(() => { !isMobile && setSettingsOptions(SettingOptionEnum.UNITS); onClose(); - }, [isMobile, onClose, setSettingsOptions]); + }, [isMobile, setSettingsOptions, onClose]); return ( = ({ handleClose(); setHideGeofenceTrackerShortcut(false); setConfirmCloseSimulation(false); + !isDesktop && setUI(ResponsiveUIEnum.explore); }; const StartSimulation = useCallback(() => { diff --git a/src/atomicui/pages/DemoPage/DemoPage.tsx b/src/atomicui/pages/DemoPage/DemoPage.tsx index f1ba91aa..21e20b09 100644 --- a/src/atomicui/pages/DemoPage/DemoPage.tsx +++ b/src/atomicui/pages/DemoPage/DemoPage.tsx @@ -29,7 +29,7 @@ import { } from "@demo/atomicui/organisms"; import { DemoPlaceholderPage } from "@demo/atomicui/pages"; import { showToast } from "@demo/core"; -import { appConfig } from "@demo/core/constants"; +import { appConfig, regionsData } from "@demo/core/constants"; import BottomSheetHeights from "@demo/core/constants/bottomSheetHeights"; import { useAmplifyAuth, @@ -80,6 +80,7 @@ import "./styles.scss"; import { RefHandles } from "react-spring-bottom-sheet/dist/types"; const { + POOLS, PERSIST_STORAGE_KEYS: { SHOULD_CLEAR_CREDENTIALS, GEO_LOCATION_ALLOWED, FASTEST_REGION }, ROUTES: { DEMO }, MAP_RESOURCES: { MAX_BOUNDS, AMAZON_HQ, GRAB_SUPPORTED_AWS_REGIONS }, @@ -147,7 +148,8 @@ const DemoPage: React.FC = () => { handleCurrentSession, switchToGrabMapRegionStack, isUserAwsAccountConnected, - switchToDefaultRegionStack + switchToDefaultRegionStack, + handleStackRegion } = useAmplifyAuth(); const { locationClient, createLocationClient, iotClient, createIotClient, resetStore: resetAwsStore } = useAws(); const { attachPolicy } = useAwsIot(); @@ -193,13 +195,18 @@ const DemoPage: React.FC = () => { const isLtr = langDir === "ltr"; const shouldClearCredentials = localStorage.getItem(SHOULD_CLEAR_CREDENTIALS) === "true"; const geoLocateTopValue = `-${bottomSheetCurrentHeight / peggedRemValue + extraGeoLocateTop}rem`; - const fastestRegion = localStorage.getItem(FASTEST_REGION); + const fallbackRegion = Object.values(POOLS)[0]; + const fastestRegion = localStorage.getItem(FASTEST_REGION) ?? fallbackRegion; + const defaultRegion = regionsData.find(option => option.value === fastestRegion) as { value: string; label: string }; const isGrabAvailableInRegion = useMemo(() => !!region && GRAB_SUPPORTED_AWS_REGIONS.includes(region), [region]); const isGrabVisible = useMemo( - () => !isUserAwsAccountConnected || (isUserAwsAccountConnected && isGrabAvailableInRegion), - [isUserAwsAccountConnected, isGrabAvailableInRegion] + () => + !show.unauthGeofenceBox && + !show.unauthTrackerBox && + (!isUserAwsAccountConnected || (isUserAwsAccountConnected && isGrabAvailableInRegion)), + [show.unauthGeofenceBox, show.unauthTrackerBox, isUserAwsAccountConnected, isGrabAvailableInRegion] ); useEffect(() => { @@ -787,6 +794,16 @@ const DemoPage: React.FC = () => { } }, [currentMapProvider, isGrabVisible, setMapProvider, onMapProviderChange]); + /* Handled stack region and cloudformation link */ + useEffect(() => { + currentMapProvider === MapProviderEnum.GRAB + ? handleStackRegion({ + value: "ap-southeast-1", + label: "regions__ap_southeast_1.text" + }) + : handleStackRegion(defaultRegion); + }, [currentMapProvider, handleStackRegion, defaultRegion]); + const onMapStyleChange = useCallback( (mapStyle: EsriMapEnum | HereMapEnum | GrabMapEnum | OpenDataMapEnum) => { const splitArr = mapStyle.split("."); @@ -1187,6 +1204,7 @@ const DemoPage: React.FC = () => { setShowRouteBox={b => setShow(s => ({ ...s, routeBox: b }))} isExpandRouteOptionsMobile={expandRouteOptionsMobile} setExpandRouteOptionsMobile={setExpandRouteOptionsMobile} + setSearchBoxValue={setSearchBoxValue} /> { }} showDoNotAskAgainCheckbox onConfirmationCheckboxOnChange={setDoNotAskGrabDisclaimer} - isUnauthSimulationOpen={show.unauthGeofenceBox || show.unauthTrackerBox} /> { const { resetStore: resetAwsStore } = useAws(); const { resetStore: resetAmplifyMapStore } = useAmplifyMap(); const { t } = useTranslation(); + const { isDesktop } = useDeviceMediaQuery(); useEffect(() => { const localAppVersion = localStorage.getItem(LOCAL_APP_VERSION) || ""; @@ -307,6 +311,24 @@ const useAmplifyAuth = () => { setIdentityPoolIdRegionAndWebSocketUrl: (identityPoolId?: string, region?: string, webSocketUrl?: string) => { setState({ identityPoolId, region, webSocketUrl }); }, + setStackRegion: (stackRegion?: { value: string; label: string }) => { + setState({ stackRegion }); + }, + setCloudFormationLink: (cloudFormationLink: string) => { + setState({ cloudFormationLink }); + }, + handleStackRegion: (option: { value: string; label: string }) => { + const { label, value } = option; + const newUrl = transformCloudFormationLink(value); + + if (isDesktop) { + setState({ stackRegion: option, cloudFormationLink: newUrl }); + } else { + const translatedLabel = t(label); + const l = translatedLabel.slice(0, translatedLabel.indexOf(")") + 1); + setState({ stackRegion: { label: l, value }, cloudFormationLink: newUrl }); + } + }, resetStore: () => { setState({ credentials: undefined, @@ -316,7 +338,8 @@ const useAmplifyAuth = () => { userDomain: undefined, userPoolClientId: undefined, userPoolId: undefined, - webSocketUrl: undefined + webSocketUrl: undefined, + stackRegion: undefined }); setInitial(); } @@ -331,7 +354,8 @@ const useAmplifyAuth = () => { getCurrentSession, resetAmplifyMapStore, resetAwsStore, - t + t, + isDesktop ] ); diff --git a/src/stores/useAmplifyAuthStore.ts b/src/stores/useAmplifyAuthStore.ts index ab7ac96d..dc1579d0 100644 --- a/src/stores/useAmplifyAuthStore.ts +++ b/src/stores/useAmplifyAuthStore.ts @@ -9,6 +9,7 @@ import { AuthTokensType, IStateProps } from "@demo/types"; import createStore from "./createStore"; const { + ENV: { CF_TEMPLATE }, PERSIST_STORAGE_KEYS: { LOCAL_STORAGE_PREFIX, AMPLIFY_AUTH_DATA } } = appConfig; const localStorageKey = `${LOCAL_STORAGE_PREFIX}${AMPLIFY_AUTH_DATA}`; @@ -25,12 +26,15 @@ export interface AmplifyAuthStoreProps { userPoolId?: string; webSocketUrl?: string; autoRegion: boolean; + stackRegion?: { value: string; label: string }; + cloudFormationLink: string; } export const initialState: IStateProps = { isLoading: false, isUserAwsAccountConnected: false, - autoRegion: true + autoRegion: true, + cloudFormationLink: CF_TEMPLATE }; export default createStore(initialState, true, localStorageKey);