Skip to content

Commit

Permalink
Merge pull request #87 from UrbanInstitute/feature-loading-component
Browse files Browse the repository at this point in the history
Feature: loading wrapper component
  • Loading branch information
benkates authored May 7, 2024
2 parents e3784d1 + e91a5a8 commit cddb11d
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Next

- Add LoadingWrapper component to provide a loading graphic for components that need it

## v0.8.0

- Simplify CSS in Toggle component
Expand Down
4 changes: 4 additions & 0 deletions src/docs/DatawrapperSwitching.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ It's useful to switch between Datawrapper iframes. Below, we demonstrate two use
### Switching with buttons

<Canvas of={stories.Buttons} sourceState="shown" />

### Switching with buttons (and loading wrapper)

<Canvas of={stories.Loading} sourceState="shown" />
32 changes: 32 additions & 0 deletions src/docs/stories/DatawrapperSwitching.stories.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
</script>

<script>
import LoadingWrapper from "$lib/LoadingWrapper/LoadingWrapper.svelte";
import { Story } from "@storybook/addon-svelte-csf";
import DatawrapperIframe from "$lib/DatawrapperIframe/DatawrapperIframe.svelte";
import BasicDropdown from "$lib/BasicDropdown/BasicDropdown.svelte";
Expand All @@ -18,6 +20,8 @@
{ value: "91Q0t", label: "Chart #2" },
{ value: "eaD2D", label: "Chart #3" }
];
let selectedChartLoading = "WsGs1";
</script>

<Story
Expand Down Expand Up @@ -83,3 +87,31 @@
<DatawrapperIframe datawrapperId={selectedChart} />
{/key}
</Story>

<Story
name="Loading"
source={`
{#key selectedChart}
<LoadingWrapper let:setChildLoaded let:setChildLoading>
<DatawrapperIframe
datawrapperId={selectedChart}
on:startrender={setChildLoading}
on:visrendered={setChildLoaded}
/>
</LoadingWrapper>
{/key}
`}
>
<Button on:click={() => (selectedChart = "Toh1S")}>Chart #1</Button>
<Button on:click={() => (selectedChart = "rgLU1")}>Chart #2</Button>

{#key selectedChart}
<LoadingWrapper let:setChildLoaded let:setChildLoading>
<DatawrapperIframe
datawrapperId={selectedChartLoading}
on:startrender={setChildLoading}
on:visrendered={setChildLoaded}
/>
</LoadingWrapper>
{/key}
</Story>
2 changes: 1 addition & 1 deletion src/lib/DatawrapperIframe/DatawrapperIframe.stories.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
docs: {
description: {
component:
"Datawrapper iframe with <a href='https://developer.datawrapper.de/docs/listening-to-chart-interaction-events' target='_blank'>event dispatching</a> enabled. All interaction events are accessible via <code>on:eventname</code> (<b>no periods</b>) on the `DatawrapperIframe` Svelte component itself.<br/><br/>The complete event list and associated descriptions can be found <a href='https://developer.datawrapper.de/docs/listening-to-chart-interaction-events#visualization-events' target='_blank'>here</a>.<br/><br/>In April 2024, the <code>vis.rendered</code> event was added to the Datawrapper event list (per the Urban team's request) in order to track when a visualization had been rendered/painted on the page. This is useful if there is a longer loading visualization (like a large map) and you'd like to indicate to the user that the visualization is still loading."
"Datawrapper iframe with <a href='https://developer.datawrapper.de/docs/listening-to-chart-interaction-events' target='_blank'>event dispatching</a> enabled. All interaction events are accessible via <code>on:eventname</code> (<b>no periods</b>) on the `DatawrapperIframe` Svelte component itself. The complete event list and associated descriptions can be found <a href='https://developer.datawrapper.de/docs/listening-to-chart-interaction-events#visualization-events' target='_blank'>here</a>.<br/><br/>Examples of how to setup \"switching\" between Datawrapper charts with a dropdown or button controls can be found <a href='/docs/examples-datawrapper-switching--docs'>in the Examples section here</a>.<br/><br/>In April 2024, the <code>vis.rendered</code> event was added to the Datawrapper event list (per the Urban team's request) in order to track when a visualization had been rendered/painted on the page. This is useful if there is a longer loading visualization (like a large map) and you'd like to indicate to the user that the visualization is still loading.<br><br>The `startrender` event is available via the component's `beforeUpdate()` function in order to track when the iframe starts to load. This can be combined with the `vis.rendered` event to track when the visualization has been rendered/painted on the page in combination with the <a href='/docs/components-loadingwrapper--docs' >LoadingWrapper</a> component."
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/lib/DatawrapperIframe/DatawrapperIframe.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
import { createEventDispatcher, onDestroy } from "svelte";
import { createEventDispatcher, onDestroy, beforeUpdate, afterUpdate } from "svelte";
import datawrapper from "./events";
import datawrapperEventList from "./datawrapper-event-list.json";
Expand Down Expand Up @@ -49,6 +49,12 @@
});
}
// dispatch an even when the iframe starts to load
// (does not user beforeUpdate because it is not triggered on first load)
afterUpdate(() => {
dispatch("startrender");
});
// turn off interaction events on destroy
// (prevents multiple events when loading multiple iframes)
onDestroy(() => {
Expand Down
71 changes: 71 additions & 0 deletions src/lib/LoadingWrapper/LoadingWrapper.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script context="module">
import { onMount } from "svelte";
import LoadingWrapper from "./LoadingWrapper.svelte";
import LogoUrbanWide from "../LogoUrbanWide/LogoUrbanWide.svelte";
import DatawrapperIframe from "../DatawrapperIframe/DatawrapperIframe.svelte";
export const meta = {
title: "Components/LoadingWrapper",
description: "Wrapper to display a loading spinner graphic while content is loading",
component: LoadingWrapper,
tags: ["autodocs"],
parameters: {
docs: {
description: {
component:
'Wrapper to display a loading spinner graphic while content is loading. Exposes `setChildLoading()` and `setChildLoaded()` to be used by children as an alternative method of setting `isChildLoading` boolean. Accepts an alternative graphic for the "graphic" named slot.'
}
}
}
};
</script>

<script>
import { Story } from "@storybook/addon-svelte-csf";
// function to create a fake await for 2sec
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// set loading for paragraph examples, sleep for 2.5 sec on mount
$: isChildLoading = true;
onMount(() => {
sleep(2500).then(() => {
isChildLoading = false;
});
});
</script>

<Story name="Default">
<LoadingWrapper {isChildLoading}>
<span
>Amet est Lorem qui ullamco laboris velit. Incididunt est sunt exercitation qui ea. Officia
Lorem est labore amet irure nostrud. Exercitation Lorem do consectetur enim esse quis mollit
cupidatat aliqua magna. Ipsum irure anim commodo Lorem.
</span>
</LoadingWrapper>
</Story>

<Story name="Custom graphic">
<LoadingWrapper {isChildLoading}>
<LogoUrbanWide slot="graphic" />
<span
>Amet est Lorem qui ullamco laboris velit. Incididunt est sunt exercitation qui ea. Officia
Lorem est labore amet irure nostrud. Exercitation Lorem do consectetur enim esse quis mollit
cupidatat aliqua magna. Ipsum irure anim commodo Lorem.
</span>
</LoadingWrapper>
</Story>

<Story name="Datawrapper example">
<LoadingWrapper let:setChildLoading let:setChildLoaded>
<DatawrapperIframe
title="This is a title for the visualization"
ariaLabel="This is an accessible title for the visualization"
datawrapperId="qF5No"
on:startrender={setChildLoading}
on:visrendered={setChildLoaded}
/>
</LoadingWrapper>
</Story>
59 changes: 59 additions & 0 deletions src/lib/LoadingWrapper/LoadingWrapper.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<script>
import urbanColors from "../utils/urbanColors.js";
import LogoUrbanAnimated from "../LogoUrbanAnimated/LogoUrbanAnimated.svelte";
/**
* Boolean indicating whether the child component is loading or not
* @type {boolean}
*/
export let isChildLoading = true;
/**
* The background color of the loading container (can be null for transparent background)
* @type {string | null} ["white"]
*/
export let backgroundColor = urbanColors.white;
// define functions to expose that set isChildLoading
let setChildLoaded = () => {
isChildLoading = false;
};
let setChildLoading = () => {
isChildLoading = true;
};
</script>

<div class="container">
{#if isChildLoading}
<div class="loading" id="loading" style:background-color={backgroundColor}>
<slot name="graphic">
<LogoUrbanAnimated width={50} duration="1500ms" />
</slot>
</div>
{/if}
<slot {setChildLoading} {setChildLoaded} />
</div>

<style>
.container {
position: relative;
width: 100%;
}
.loading {
/* absolute position */
left: 0;
position: absolute;
top: 0;
/* take full size */
height: 100%;
width: 100%;
/* center */
align-items: center;
display: flex;
justify-content: center;
}
</style>
1 change: 1 addition & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export { default as Scrolly } from "./Scrolly/Scrolly.svelte";
export { default as Analytics, logClickToGA } from "./Analytics/Analytics.svelte";
export { default as Headline } from "./Headline/Headline.svelte";
export { default as PymChild } from "./Pym/PymChild.svelte";
export { default as LoadingWrapper } from "./LoadingWrapper/LoadingWrapper.svelte";

0 comments on commit cddb11d

Please sign in to comment.