From c7dc53435968d9fd60f075cec054da459e48dd8c Mon Sep 17 00:00:00 2001 From: Nicholas Blumberg <41446765+nick-next@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:06:47 -0800 Subject: [PATCH] Place redirect smoothing (#4732) ## Description This PR updates aspects of the flow, layout and styling of the redirect process for when a the search results give a "place overview" and the svSource is "unfulfilled", with the goal of removing redirect flashes and FOUCs. The following changes were made to make the process visually smoother: * In the event that the a page is poised to redirect (i.e., the above criteria is true), the loading state is maintained. This prevents the intermediate "Place Overview" page from displaying just before the redirect, removing the first flash. * In the place page that follows (for example, `place/country/CAN`), the layout was adjusted so that a FOUC was removed while the content was being loaded in. * The page overview now fades in when it loads, softening the appearance of the load. * A small spinner was added onto this page to give more of a loading sense. --------- Co-authored-by: Pablo Noel --- server/templates/place.html | 6 +++++- static/css/place/place.scss | 21 +++++++++++++++++++++ static/css/place/place_page.scss | 9 ++++++++- static/js/apps/explore/app.tsx | 8 ++++++-- static/js/place/place.ts | 7 ++++--- 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/server/templates/place.html b/server/templates/place.html index 7cc6efb7ae..5c2ff0336c 100644 --- a/server/templates/place.html +++ b/server/templates/place.html @@ -69,7 +69,11 @@

{{ place_summary }}
{# TRANSLATORS: A message shown on the page while the content is loading. #} -
{% trans %}Loading...{% endtrans %}
+
+ {# SVG: Material Icon: Progress Activity, Source: https://fonts.google.com/icons #} + +

{% trans %}Loading{% endtrans %}

+
diff --git a/static/css/place/place.scss b/static/css/place/place.scss index 02dfeb8d66..a1063304b9 100644 --- a/static/css/place/place.scss +++ b/static/css/place/place.scss @@ -76,3 +76,24 @@ h3 { #main-pane .button-dc { margin-top: -3px; } + +#page-loading { + display: flex; + align-items: center; + gap: 8px; + svg { + animation: rotating 2s linear infinite; + } + p { + margin: 3px 0 0 0; + } +} + +@keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/static/css/place/place_page.scss b/static/css/place/place_page.scss index 93b8658367..f5b77131e2 100644 --- a/static/css/place/place_page.scss +++ b/static/css/place/place_page.scss @@ -248,10 +248,17 @@ $chart-border: 0.5px solid #dee2e6; padding: 0.25rem; } +#main-pane { + opacity: 0; + transition: opacity 0.7s ease-in-out; +} + #sidebar-outer { border-top: $vertical-section-border; margin-top: $vertical-section-margin; padding-top: $vertical-section-margin; + opacity: 0; + transition: opacity 0.7s ease-in-out; @include media-breakpoint-up(lg) { border: none; @@ -269,7 +276,7 @@ $chart-border: 0.5px solid #dee2e6; #sidebar-region { padding-top: 15px; position: relative; - transition: transform 300ms linear; + transition: transform 300ms ease-in-out; } #sidebar-region.fixed { diff --git a/static/js/apps/explore/app.tsx b/static/js/apps/explore/app.tsx index f594094aa2..5dc2c08c84 100644 --- a/static/js/apps/explore/app.tsx +++ b/static/js/apps/explore/app.tsx @@ -211,12 +211,14 @@ export function App(props: { isDemo: boolean }): JSX.Element { sessionId: "session" in fulfillData ? fulfillData["session"]["id"] : "", svSource: fulfillData["svSource"], }; + let isPendingRedirect = false; if ( pageMetadata && pageMetadata.pageConfig && pageMetadata.pageConfig.categories ) { - if (shouldSkipPlaceOverview(pageMetadata)) { + isPendingRedirect = shouldSkipPlaceOverview(pageMetadata); + if (isPendingRedirect) { const placeDcid = pageMetadata.place.dcid; const url = `/place/${placeDcid}`; window.location.replace(url); @@ -262,7 +264,9 @@ export function App(props: { isDemo: boolean }): JSX.Element { pastSourceContext: fulfillData["pastSourceContext"], sessionId: pageMetadata.sessionId, }); - setLoadingStatus(LoadingStatus.SUCCESS); + setLoadingStatus( + isPendingRedirect ? LoadingStatus.LOADING : LoadingStatus.SUCCESS + ); } function handleHashChange(): void { diff --git a/static/js/place/place.ts b/static/js/place/place.ts index 235e9c092b..d627133cae 100644 --- a/static/js/place/place.ts +++ b/static/js/place/place.ts @@ -190,7 +190,11 @@ function renderPage(): void { return; } const loadingElem = document.getElementById("page-loading"); + const sidebarElem = document.getElementById("sidebar-outer"); + const mainPaneElem = document.getElementById("main-pane"); loadingElem.style.display = "none"; + sidebarElem.style.opacity = "1"; + mainPaneElem.style.opacity = "1"; const data: PageData = landingPageData; const isUsaPlace = isPlaceInUsa(dcid, data.parentPlaces); @@ -205,9 +209,6 @@ function renderPage(): void { }), document.getElementById("nl-search-bar") ); - } else { - // when NL search bar is hidden, need to adjust spacing - document.getElementById("nl-search-bar").style.height = "2rem"; } ReactDOM.render(