Repository: Automattic/wp-calypso
Pull request #102441: Remove LoadingEllipsis from initial document render
Author: login: jsnajdr
Labels: name: Janitorial
Created: 2025-04-07T08:51:46Z | Merged: 2025-04-16T07:46:48Z

While reviewing #102347 I noticed that the `LoadingEllipsis` variant in `chooseLoadingLogo` is no longer used, because the `loading_ellipsis` query param is no longer added anywhere.

The `loading_ellipsis` param was added in #57050 by @p-jackson and then removed at some time when Stepper was being rolled out.

The PR has two parts:
- remove the `useLoadingEllipsis` option from `chooseLoadingLogo`
- extract the placeholder rendering code to a separate `LoadingPlaceholder` component

One followup idea is to somehow clean up the props of the new `LoadingPlaceholder` component. Currently it's a random bag of values that we happen to need to determine the logo to display.

## Testing instructions:
This affects the placeholder that the server sends in the initial HTML document and which is displayed while the JavaScript SPA is booting. Verify that the placeholder still renders fine, on various initial routes.



Diff:
diff --git a/client/document/index.jsx b/client/document/index.jsx
index d33b2f48551b..e3e9b5b2bf6a 100644
--- a/client/document/index.jsx
+++ b/client/document/index.jsx
@@ -1,4 +1,4 @@
-import path from 'path';
+import { parse } from 'path';
 import config from '@automattic/calypso-config';
 import { isLocaleRtl } from '@automattic/i18n-utils';
 import { Step } from '@automattic/onboarding';
@@ -18,7 +18,6 @@ import EnvironmentBadge, {
 import Head from 'calypso/components/head';
 import JetpackLogo from 'calypso/components/jetpack-logo';
 import Loading from 'calypso/components/loading';
-import { LoadingEllipsis } from 'calypso/components/loading-ellipsis';
 import WooCommerceLogo from 'calypso/components/woocommerce-logo';
 import WordPressLogo from 'calypso/components/wordpress-logo';
 import isA8CForAgencies from 'calypso/lib/a8c-for-agencies/is-a8c-for-agencies';
@@ -74,7 +73,7 @@ class Document extends Component {
 
 		const installedChunks = entrypoint.js
 			.concat( chunkFiles.js )
-			.map( ( chunk ) => path.parse( chunk ).name );
+			.map( ( chunk ) => parse( chunk ).name );
 
 		const inlineScript =
 			`var COMMIT_SHA = ${ jsonStringifyForHtml( commitSha ) };\n` +
@@ -134,15 +133,6 @@ class Document extends Component {
 			}
 		}
 
-		const shouldNotShowLoadingLogo =
-			sectionName === 'checkout' || sectionName === 'stepper' || sectionName === 'signup';
-
-		const LoadingLogo = chooseLoadingLogo( this.props, {
-			isWpMobileApp: app?.isWpMobileApp,
-			isWcMobileApp: app?.isWcMobileApp,
-			isWCCOM,
-		} );
-
 		return (
 			<html
 				lang={ lang }
@@ -197,17 +187,12 @@ class Document extends Component {
 								} ) }
 							>
 								<div className="layout__content">
-									{ shouldNotShowLoadingLogo ? (
-										<>
-											{ showStepContainerV2Loader ? (
-												<Step.Loading />
-											) : (
-												<Loading className="wpcom-loading__boot" />
-											) }
-										</>
-									) : (
-										<LoadingLogo size={ 72 } className="wpcom-site__logo" />
-									) }
+									<LoadingPlaceholder
+										app={ app }
+										sectionName={ sectionName }
+										isWCCOM={ isWCCOM }
+										showStepContainerV2Loader={ showStepContainerV2Loader }
+									/>
 								</div>
 							</div>
 						</div>
@@ -312,12 +297,27 @@ class Document extends Component {
 		);
 	}
 }
+function LoadingPlaceholder( { app, sectionName, isWCCOM, showStepContainerV2Loader } ) {
+	const shouldNotShowLoadingLogo =
+		sectionName === 'checkout' || sectionName === 'stepper' || sectionName === 'signup';
 
-function chooseLoadingLogo( { useLoadingEllipsis }, { isWpMobileApp, isWcMobileApp, isWCCOM } ) {
-	if ( useLoadingEllipsis ) {
-		return LoadingEllipsis;
+	if ( shouldNotShowLoadingLogo ) {
+		return showStepContainerV2Loader ? (
+			<Step.Loading />
+		) : (
+			<Loading className="wpcom-loading__boot" />
+		);
 	}
 
+	const LoadingLogo = chooseLoadingLogo( {
+		isWpMobileApp: app?.isWpMobileApp,
+		isWcMobileApp: app?.isWcMobileApp,
+		isWCCOM,
+	} );
+	return <LoadingLogo size={ 72 } className="wpcom-site__logo" />;
+}
+
+function chooseLoadingLogo( { isWpMobileApp, isWcMobileApp, isWCCOM } ) {
 	if ( isWcMobileApp || isWCCOM ) {
 		return WooCommerceLogo;
 	}
diff --git a/client/server/pages/index.js b/client/server/pages/index.js
index 589d88d356a5..8a00a319c813 100644
--- a/client/server/pages/index.js
+++ b/client/server/pages/index.js
@@ -197,7 +197,6 @@ function getDefaultContext( request, response, entrypoint = 'entry-main' ) {
 			config.isEnabled( 'use-translation-chunks' ) ||
 			flags.includes( 'use-translation-chunks' ) ||
 			request.query.hasOwnProperty( 'useTranslationChunks' ),
-		useLoadingEllipsis: !! request.query.loading_ellipsis,
 		showGdprBanner,
 		showStepContainerV2Loader: isInStepContainerV2FlowContext( request.path, request.query ),
 	} );


Reviews:
user: login: tyxla
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T11:47:03Z
user: login: jsnajdr
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T12:00:26Z
user: login: jsnajdr
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T14:33:44Z
user: login: tyxla
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T15:07:51Z
user: login: zaguiini
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T20:16:02Z
user: login: zaguiini
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T20:16:49Z
user: login: tyxla
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T11:44:42Z
user: login: p-jackson
body: I'm glad that `?loading_ellipsis` is going. I used to be careful to tiptoe around existing UI, trying not to change anything while implementing the design I was working on. Which is where this query param came from.
Now we're making a more concerted effort to reduce arbitrary differences, which I love. Not there yet though :)
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T09:18:58Z
user: login: p-jackson
body: Tested side by side with production, with feature flag enabled and disabled. No change. LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T09:25:52Z

Review comments:
diff_hunk: @@ -197,17 +187,12 @@ class Document extends Component {
 								} ) }
 							>
 								<div className="layout__content">
-									{ shouldNotShowLoadingLogo ? (
-										<>
-											{ showStepContainerV2Loader ? (
-												<Step.Loading />
-											) : (
-												<Loading className="wpcom-loading__boot" />
-											) }
-										</>
-									) : (
-										<LoadingLogo size={ 72 } className="wpcom-site__logo" />
-									) }
+									<LoadingPlaceholder
+										app={ app }
+										sectionName={ sectionName }
+										isWCCOM={ isWCCOM }
+										showStepContainerV2Loader={ showStepContainerV2Loader }
path: client/document/index.jsx
user: login: tyxla
body: This was leaky before and still feels leaky. What does the "step container" has to do with the generic document layout component? Ideally, nothing, and `showStepContainerV2Loader` would be taken care of by the loading component itself. 
created_at: 2025-04-07T11:41:33Z
author_association: MEMBER
diff_hunk: @@ -197,7 +197,6 @@ function getDefaultContext( request, response, entrypoint = 'entry-main' ) {
 			config.isEnabled( 'use-translation-chunks' ) ||
 			flags.includes( 'use-translation-chunks' ) ||
 			request.query.hasOwnProperty( 'useTranslationChunks' ),
-		useLoadingEllipsis: !! request.query.loading_ellipsis,
 		showGdprBanner,
 		showStepContainerV2Loader: isInStepContainerV2FlowContext( request.path, request.query ),
path: client/server/pages/index.js
user: login: tyxla
body: I wish we would have less of these very specific props in this generic location. This would only be useful for the site container component in stepper, so ideally, it shouldn't belong here.
created_at: 2025-04-07T11:42:16Z
author_association: MEMBER
diff_hunk: @@ -312,12 +297,27 @@ class Document extends Component {
 		);
 	}
 }
+function LoadingPlaceholder( { app, sectionName, isWCCOM, showStepContainerV2Loader } ) {
+	const shouldNotShowLoadingLogo =
+		sectionName === 'checkout' || sectionName === 'stepper' || sectionName === 'signup';
 
-function chooseLoadingLogo( { useLoadingEllipsis }, { isWpMobileApp, isWcMobileApp, isWCCOM } ) {
-	if ( useLoadingEllipsis ) {
-		return LoadingEllipsis;
+	if ( shouldNotShowLoadingLogo ) {
+		return showStepContainerV2Loader ? (
+			<Step.Loading />
+		) : (
+			<Loading className="wpcom-loading__boot" />
+		);
 	}
 
+	const LoadingLogo = chooseLoadingLogo( {
+		isWpMobileApp: app?.isWpMobileApp,
+		isWcMobileApp: app?.isWcMobileApp,
+		isWCCOM,
+	} );
+	return <LoadingLogo size={ 72 } className="wpcom-site__logo" />;
path: client/document/index.jsx
user: login: tyxla
body: Seems like we still have a few different ways to indicate loading. 

I wish we had just one, following the work this project did:

https://github.com/Automattic/wp-calypso/issues/99256

Also see p58i-kIo-p2

I see you mentioned this is a potential follow-up - I agree it makes sense to have it separately.
created_at: 2025-04-07T11:45:10Z
author_association: MEMBER
diff_hunk: @@ -197,7 +197,6 @@ function getDefaultContext( request, response, entrypoint = 'entry-main' ) {
 			config.isEnabled( 'use-translation-chunks' ) ||
 			flags.includes( 'use-translation-chunks' ) ||
 			request.query.hasOwnProperty( 'useTranslationChunks' ),
-		useLoadingEllipsis: !! request.query.loading_ellipsis,
path: client/server/pages/index.js
user: login: tyxla
body: Good riddance 👍 
created_at: 2025-04-07T11:45:33Z
author_association: MEMBER
diff_hunk: @@ -197,17 +187,12 @@ class Document extends Component {
 								} ) }
 							>
 								<div className="layout__content">
-									{ shouldNotShowLoadingLogo ? (
-										<>
-											{ showStepContainerV2Loader ? (
-												<Step.Loading />
-											) : (
-												<Loading className="wpcom-loading__boot" />
-											) }
-										</>
-									) : (
-										<LoadingLogo size={ 72 } className="wpcom-site__logo" />
-									) }
+									<LoadingPlaceholder
+										app={ app }
+										sectionName={ sectionName }
+										isWCCOM={ isWCCOM }
+										showStepContainerV2Loader={ showStepContainerV2Loader }
path: client/document/index.jsx
user: login: jsnajdr
body: Yes, it's leaky and now it's also much better visible: we're explicitly passing these weird props instead of all the weirdness being mixed with everything else in the `Document` component.

Ultimately all these props are derived from the initial URL and the user agent. `showStepContainerV2Loader` is computed from `path` and `query`. `isWCCOM` is derived from the `client_id` OAuth query param. `sectionName` is also computed from `path`. `app.isWpMobileApp` and `app.isWcMobileApp` are derived from the `user-agent` header that the mobile apps send.

So, the ideal props would be `<Placeholder path={p} query={q} userAgent={ua} />`.

And that makes sense: different pages have different look and feel and branding, and so the placeholder displayed when they're loading can also be different. Maybe it could be a part of the `section` definition.
created_at: 2025-04-07T12:00:26Z
author_association: MEMBER
diff_hunk: @@ -197,17 +187,12 @@ class Document extends Component {
 								} ) }
 							>
 								<div className="layout__content">
-									{ shouldNotShowLoadingLogo ? (
-										<>
-											{ showStepContainerV2Loader ? (
-												<Step.Loading />
-											) : (
-												<Loading className="wpcom-loading__boot" />
-											) }
-										</>
-									) : (
-										<LoadingLogo size={ 72 } className="wpcom-site__logo" />
-									) }
+									<LoadingPlaceholder
+										app={ app }
+										sectionName={ sectionName }
+										isWCCOM={ isWCCOM }
+										showStepContainerV2Loader={ showStepContainerV2Loader }
path: client/document/index.jsx
user: login: jsnajdr
body: Another way how to think about the placeholders is that it's the SSR content that the server renders for a particular route. Some routes are full SSR, rendering an entire page, and here the SSR would render just the placeholder.
created_at: 2025-04-07T14:33:44Z
author_association: MEMBER
diff_hunk: @@ -197,17 +187,12 @@ class Document extends Component {
 								} ) }
 							>
 								<div className="layout__content">
-									{ shouldNotShowLoadingLogo ? (
-										<>
-											{ showStepContainerV2Loader ? (
-												<Step.Loading />
-											) : (
-												<Loading className="wpcom-loading__boot" />
-											) }
-										</>
-									) : (
-										<LoadingLogo size={ 72 } className="wpcom-site__logo" />
-									) }
+									<LoadingPlaceholder
+										app={ app }
+										sectionName={ sectionName }
+										isWCCOM={ isWCCOM }
+										showStepContainerV2Loader={ showStepContainerV2Loader }
path: client/document/index.jsx
user: login: tyxla
body: > So, the ideal props would be <Placeholder path={p} query={q} userAgent={ua} />.

Yep, I think that would be a good step forward, keeping the unrelated details away.

Is this something you're thinking of doing here in this PR, perhaps in a follow-up?
created_at: 2025-04-07T15:07:51Z
author_association: MEMBER
diff_hunk: @@ -312,12 +297,27 @@ class Document extends Component {
 		);
 	}
 }
+function LoadingPlaceholder( { app, sectionName, isWCCOM, showStepContainerV2Loader } ) {
+	const shouldNotShowLoadingLogo =
+		sectionName === 'checkout' || sectionName === 'stepper' || sectionName === 'signup';
 
-function chooseLoadingLogo( { useLoadingEllipsis }, { isWpMobileApp, isWcMobileApp, isWCCOM } ) {
-	if ( useLoadingEllipsis ) {
-		return LoadingEllipsis;
+	if ( shouldNotShowLoadingLogo ) {
+		return showStepContainerV2Loader ? (
+			<Step.Loading />
+		) : (
+			<Loading className="wpcom-loading__boot" />
+		);
 	}
 
+	const LoadingLogo = chooseLoadingLogo( {
+		isWpMobileApp: app?.isWpMobileApp,
+		isWcMobileApp: app?.isWcMobileApp,
+		isWCCOM,
+	} );
+	return <LoadingLogo size={ 72 } className="wpcom-site__logo" />;
path: client/document/index.jsx
user: login: zaguiini
body: We'll still have different ways because the loading component depends on the route, and we'll render pages on the server. 🤔 
created_at: 2025-04-07T20:16:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -312,12 +297,27 @@ class Document extends Component {
 		);
 	}
 }
+function LoadingPlaceholder( { app, sectionName, isWCCOM, showStepContainerV2Loader } ) {
+	const shouldNotShowLoadingLogo =
+		sectionName === 'checkout' || sectionName === 'stepper' || sectionName === 'signup';
 
-function chooseLoadingLogo( { useLoadingEllipsis }, { isWpMobileApp, isWcMobileApp, isWCCOM } ) {
-	if ( useLoadingEllipsis ) {
-		return LoadingEllipsis;
+	if ( shouldNotShowLoadingLogo ) {
+		return showStepContainerV2Loader ? (
path: client/document/index.jsx
user: login: zaguiini
body: I think it would be pretty safe to remove this ternary since `showStepContainerV2Loader` being true means we're in checkout or stepper. 

So we can rewrite it like:

```ts
if(showStepContainerV2Loader) {
 return <Step.Loading />
}

...rest
```
created_at: 2025-04-07T20:16:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -312,12 +297,27 @@ class Document extends Component {
 		);
 	}
 }
+function LoadingPlaceholder( { app, sectionName, isWCCOM, showStepContainerV2Loader } ) {
+	const shouldNotShowLoadingLogo =
+		sectionName === 'checkout' || sectionName === 'stepper' || sectionName === 'signup';
 
-function chooseLoadingLogo( { useLoadingEllipsis }, { isWpMobileApp, isWcMobileApp, isWCCOM } ) {
-	if ( useLoadingEllipsis ) {
-		return LoadingEllipsis;
+	if ( shouldNotShowLoadingLogo ) {
+		return showStepContainerV2Loader ? (
+			<Step.Loading />
+		) : (
+			<Loading className="wpcom-loading__boot" />
+		);
 	}
 
+	const LoadingLogo = chooseLoadingLogo( {
+		isWpMobileApp: app?.isWpMobileApp,
+		isWcMobileApp: app?.isWcMobileApp,
+		isWCCOM,
+	} );
+	return <LoadingLogo size={ 72 } className="wpcom-site__logo" />;
path: client/document/index.jsx
user: login: tyxla
body: I'd say this is an implementation detail; I don't see a good reason why there wouldn't be consistency between loading screens on the server and on the client. It signals we have more consistency work to do, and some of it might actually have to be in collaboration with the design team.
created_at: 2025-04-08T11:44:41Z
author_association: MEMBER
diff_hunk: @@ -197,7 +197,6 @@ function getDefaultContext( request, response, entrypoint = 'entry-main' ) {
 			config.isEnabled( 'use-translation-chunks' ) ||
 			flags.includes( 'use-translation-chunks' ) ||
 			request.query.hasOwnProperty( 'useTranslationChunks' ),
-		useLoadingEllipsis: !! request.query.loading_ellipsis,
 		showGdprBanner,
 		showStepContainerV2Loader: isInStepContainerV2FlowContext( request.path, request.query ),
path: client/server/pages/index.js
user: login: p-jackson
body: I was thinking that this "default context" is being added in a middleware. A middleware that is added to all section routes.

Since we have this context data which is only needed on some routes, perhaps we could add it using a middleware which is only installed on relevant routes?

Currently all sections are set up in `handleSectionPath()` and so they share the same middleware. But we could maybe add a parameter to pass in your own middleware for a specific section?
created_at: 2025-04-09T08:53:54Z
author_association: MEMBER
diff_hunk: @@ -312,12 +297,27 @@ class Document extends Component {
 		);
 	}
 }
+function LoadingPlaceholder( { app, sectionName, isWCCOM, showStepContainerV2Loader } ) {
+	const shouldNotShowLoadingLogo =
+		sectionName === 'checkout' || sectionName === 'stepper' || sectionName === 'signup';
 
-function chooseLoadingLogo( { useLoadingEllipsis }, { isWpMobileApp, isWcMobileApp, isWCCOM } ) {
-	if ( useLoadingEllipsis ) {
-		return LoadingEllipsis;
+	if ( shouldNotShowLoadingLogo ) {
+		return showStepContainerV2Loader ? (
path: client/document/index.jsx
user: login: p-jackson
body: Although this is still needed in the short term for the `onboarding/step-container-v2` feature flag to work?
created_at: 2025-04-09T09:01:21Z
author_association: MEMBER
diff_hunk: @@ -197,17 +187,12 @@ class Document extends Component {
 								} ) }
 							>
 								<div className="layout__content">
-									{ shouldNotShowLoadingLogo ? (
-										<>
-											{ showStepContainerV2Loader ? (
-												<Step.Loading />
-											) : (
-												<Loading className="wpcom-loading__boot" />
-											) }
-										</>
-									) : (
-										<LoadingLogo size={ 72 } className="wpcom-site__logo" />
-									) }
+									<LoadingPlaceholder
+										app={ app }
+										sectionName={ sectionName }
+										isWCCOM={ isWCCOM }
+										showStepContainerV2Loader={ showStepContainerV2Loader }
path: client/document/index.jsx
user: login: p-jackson
body: > So, the ideal props would be .

I think you could be right. But as a counter argument, if I imagine all our components are nice and hygenic and work perfectly in isolation; like in Storybook. Then if I was instantiating the `<Placeholder>` in Storybook I wouldn't image I'd include URLs and user agents in order to render it. Maybe just a simple prop that switched between Jetpack and WordPress.com logos.

The idea of having the business logic at the controller/router layer, and having a "dumb" component, doesn't seem too bad? There's still far too many props that need to be drilled down from the controller to the component of course. It seems like the five booleans (`shouldNotShowLoadingLogo`, `showStepContainerV2Loader`, `isWpMobileApp`, `isWcMobileApp`, `isWCCOM`) could actually be a single enum.
created_at: 2025-04-09T09:14:42Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-07T08:51:59Z
body: <details>
<summary>Calypso Live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-140685">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-140685%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-140685">https://calypso.live?image=registry.a8c.com/calypso/app:build-140685</a>
</td>
</tr>
</table>
</details>
<details>
<summary>Jetpack Cloud live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-140685&env=jetpack">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-140685%26env%3Djetpack%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-140685&env=jetpack">https://calypso.live?image=registry.a8c.com/calypso/app:build-140685&env=jetpack</a>
</td>
</tr>
</table>
</details>
<details>
<summary>Automattic for Agencies live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-140685&env=a8c-for-agencies">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-140685%26env%3Da8c-for-agencies%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-140685&env=a8c-for-agencies">https://calypso.live?image=registry.a8c.com/calypso/app:build-140685&env=a8c-for-agencies</a>
</td>
</tr>
</table>
</details>
<!--calypso-live-watermark:apr@v1-->
author_association: NONE
user: login: matticbot
created_at: 2025-04-07T08:52:51Z
body: <!-- icfy-watermark: c52822 -->
This PR does not affect the size of JS and CSS bundles shipped to the user's browser.

Generated by performance advisor bot at [iscalypsofastyet.com](http://iscalypsofastyet.com).
author_association: CONTRIBUTOR
user: login: matticbot
created_at: 2025-04-07T09:01:37Z
body: **This PR modifies the release build for the following Calypso Apps:**

_For info about this notification, see here: PCYsg-OT6-p2_

* blaze-dashboard
* notifications
* wpcom-block-editor

To test WordPress.com changes, run `install-plugin.sh $pluginSlug remove/loading-ellipsis-query-param` on your sandbox.
<!--calypso-app-artifacts-watermark:apr@v1-->
author_association: CONTRIBUTOR
user: login: jsnajdr
created_at: 2025-04-16T07:29:26Z
body: I'm going to merge this PR now. The main idea was to:
1. Remove support for the `loading_ellipsis` query param
2. Extract a local `LoadingPlaceholder` component from the giant `Document` component

There are many ideas to improve the placeholders further, but let's not block a good PR because of that.

> But as a counter argument, if I imagine all our components are nice and hygenic and work perfectly in isolation; like in Storybook. Then if I was instantiating the `<Placeholder>` in Storybook I wouldn't image I'd include URLs and user agents in order to render it.

@p-jackson Yes, we could completely separate the `chooseLoadingLogo` step (determining the placeholder to render from URL and headers) and the `<Placeholder />` step (actually rendering the graphics). We'll probably eventually do that.

But my motivation for extracting the `LoadingPlaceholder` component wasn't as ambitious as making it an isolated, Storybook-worthy component. I'm merely extracting a chunk of code from the big `Document` component into a separate one. That's a problem we run into often: creating giant React components with hundreds of lines of code, and not being aggressive enough with extracting hooks and sub-components. Even when they're supposed to be local and non-reusable.
author_association: MEMBER