Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add grapher image fallback to GDoc pages #3635

Merged
merged 3 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions baker/formatWordpressPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ import { renderExpandableParagraphs } from "../site/blocks/ExpandableParagraph.j
import {
formatUrls,
getBodyHtml,
GRAPHER_PREVIEW_CLASS,
SUMMARY_CLASSNAME,
} from "../site/formatting.js"
import { GRAPHER_PREVIEW_CLASS } from "../site/SiteConstants.js"
import { INTERACTIVE_ICON_SVG } from "../site/InteractionNotice.js"
import { renderKeyInsights, renderProminentLinks } from "./siteRenderers.js"
import { KEY_INSIGHTS_CLASS_NAME } from "../site/blocks/KeyInsights.js"
import { RELATED_CHARTS_CLASS_NAME } from "../site/blocks/RelatedCharts.js"
Expand Down Expand Up @@ -82,12 +83,6 @@ const initMathJax = () => {

const formatMathJax = initMathJax()

// A modifed FontAwesome icon
const INTERACTIVE_ICON_SVG = `<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="hand-pointer" class="svg-inline--fa fa-hand-pointer fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 617">
<path fill="currentColor" d="M448,344.59v96a40.36,40.36,0,0,1-1.06,9.16l-32,136A40,40,0,0,1,376,616.59H168a40,40,0,0,1-32.35-16.47l-128-176a40,40,0,0,1,64.7-47.06L104,420.58v-276a40,40,0,0,1,80,0v200h8v-40a40,40,0,1,1,80,0v40h8v-24a40,40,0,1,1,80,0v24h8a40,40,0,1,1,80,0Zm-256,80h-8v96h8Zm88,0h-8v96h8Zm88,0h-8v96h8Z" transform="translate(0 -0.41)"/>
<path fill="currentColor" opacity="0.6" d="M239.76,234.78A27.5,27.5,0,0,1,217,192a87.76,87.76,0,1,0-145.9,0A27.5,27.5,0,1,1,25.37,222.6,142.17,142.17,0,0,1,1.24,143.17C1.24,64.45,65.28.41,144,.41s142.76,64,142.76,142.76a142.17,142.17,0,0,1-24.13,79.43A27.47,27.47,0,0,1,239.76,234.78Z" transform="translate(0 -0.41)"/>
</svg>`

const extractLatex = (html: string): [string, string[]] => {
const latexBlocks: string[] = []
html = html.replace(/\[latex\]([\s\S]*?)\[\/latex\]/gm, (_, latex) => {
Expand Down
2 changes: 1 addition & 1 deletion site/DataPageContent.scss
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@
}

figure[data-grapher-src] {
height: 575px;
height: $grapher-height;
}
}

Expand Down
5 changes: 0 additions & 5 deletions site/DataPageV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,6 @@ export const DataPageV2 = (props: {
<meta property="og:image:width" content={imageWidth} />
<meta property="og:image:height" content={imageHeight} />
<IFrameDetector />
<noscript>
<style>{`
figure[data-grapher-src] { display: none !important; }
`}</style>
</noscript>
<link rel="preconnect" href={dataApiOrigin} />
{variableIds.flatMap((variableId) =>
[
Expand Down
4 changes: 4 additions & 0 deletions site/GrapherImage.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.GrapherImage {
display: block;
border: 1px solid #f2f2f2;
}
30 changes: 30 additions & 0 deletions site/GrapherImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react"

import {
DEFAULT_GRAPHER_HEIGHT,
DEFAULT_GRAPHER_WIDTH,
} from "@ourworldindata/grapher"
import { BAKED_GRAPHER_EXPORTS_BASE_URL } from "../settings/clientSettings.js"

export default function GrapherImage({
alt,
slug,
noFormatting,
}: {
slug: string
alt?: string
noFormatting?: boolean
}) {
return (
<img
className="GrapherImage"
src={`${BAKED_GRAPHER_EXPORTS_BASE_URL}/${slug}.svg`}
alt={alt}
width={DEFAULT_GRAPHER_WIDTH}
height={DEFAULT_GRAPHER_HEIGHT}
loading="lazy"
data-no-lightbox
data-no-img-formatting={noFormatting}
/>
)
}
11 changes: 7 additions & 4 deletions site/GrapherPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import React from "react"
import urljoin from "url-join"
import {
ADMIN_BASE_URL,
BAKED_GRAPHER_EXPORTS_BASE_URL,
BAKED_GRAPHER_URL,
DATA_API_URL,
} from "../settings/clientSettings.js"
Expand All @@ -29,6 +28,7 @@ import { IFrameDetector } from "./IframeDetector.js"
import { RelatedArticles } from "./RelatedArticles/RelatedArticles.js"
import { SiteFooter } from "./SiteFooter.js"
import { SiteHeader } from "./SiteHeader.js"
import GrapherImage from "./GrapherImage.js"

export const GrapherPage = (props: {
grapher: GrapherInterface
Expand Down Expand Up @@ -123,9 +123,12 @@ window.Grapher.renderSingleGrapherOnGrapherPage(jsonConfig)`
<LoadingIndicator />
</figure>
<noscript id="fallback">
<img
src={`${BAKED_GRAPHER_EXPORTS_BASE_URL}/${grapher.slug}.svg`}
/>
{grapher.slug && (
<GrapherImage
slug={grapher.slug}
alt={grapher.title}
/>
)}
<p>Interactive visualization requires JavaScript</p>
</noscript>

Expand Down
23 changes: 8 additions & 15 deletions site/GrapherWithFallback.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import {
DEFAULT_GRAPHER_HEIGHT,
DEFAULT_GRAPHER_WIDTH,
Grapher,
} from "@ourworldindata/grapher"
import { Grapher } from "@ourworldindata/grapher"
import React from "react"
import { GrapherFigureView } from "./GrapherFigureView.js"
import { BAKED_GRAPHER_EXPORTS_BASE_URL } from "../settings/clientSettings.js"
import cx from "classnames"
import { GRAPHER_PREVIEW_CLASS } from "./SiteConstants.js"
import GrapherImage from "./GrapherImage.js"

export const GrapherWithFallback = ({
grapher,
Expand All @@ -29,16 +26,12 @@ export const GrapherWithFallback = ({
// grapher is loading
<figure
data-grapher-src
className="GrapherWithFallback__fallback"
className={cx(
GRAPHER_PREVIEW_CLASS,
"GrapherWithFallback__fallback"
)}
>
<img
src={`${BAKED_GRAPHER_EXPORTS_BASE_URL}/${
slug ?? ""
}.svg`}
width={DEFAULT_GRAPHER_WIDTH}
height={DEFAULT_GRAPHER_HEIGHT}
loading="lazy"
/>
{slug && <GrapherImage slug={slug} />}
</figure>
)}
</>
Expand Down
20 changes: 20 additions & 0 deletions site/InteractionNotice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react"

// A modified FontAwesome icon. This is a string instead of JSX, so it can be
// also used outside of React when baking WordPress content.
export const INTERACTIVE_ICON_SVG = `<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="hand-pointer" class="svg-inline--fa fa-hand-pointer fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 617">
<path fill="currentColor" d="M448,344.59v96a40.36,40.36,0,0,1-1.06,9.16l-32,136A40,40,0,0,1,376,616.59H168a40,40,0,0,1-32.35-16.47l-128-176a40,40,0,0,1,64.7-47.06L104,420.58v-276a40,40,0,0,1,80,0v200h8v-40a40,40,0,1,1,80,0v40h8v-24a40,40,0,1,1,80,0v24h8a40,40,0,1,1,80,0Zm-256,80h-8v96h8Zm88,0h-8v96h8Zm88,0h-8v96h8Z" transform="translate(0 -0.41)"/>
<path fill="currentColor" opacity="0.6" d="M239.76,234.78A27.5,27.5,0,0,1,217,192a87.76,87.76,0,1,0-145.9,0A27.5,27.5,0,1,1,25.37,222.6,142.17,142.17,0,0,1,1.24,143.17C1.24,64.45,65.28.41,144,.41s142.76,64,142.76,142.76a142.17,142.17,0,0,1-24.13,79.43A27.47,27.47,0,0,1,239.76,234.78Z" transform="translate(0 -0.41)"/>
</svg>`

export default function InteractionNotice() {
return (
<div className="interactionNotice">
<span
className="icon"
dangerouslySetInnerHTML={{ __html: INTERACTIVE_ICON_SVG }}
/>
<span className="label">Click to open interactive version</span>
</div>
)
}
2 changes: 2 additions & 0 deletions site/SiteConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ export const POLYFILL_URL: string = `https://cdnjs.cloudflare.com/polyfill/v3/po
)}`

export const DEFAULT_LOCAL_BAKE_DIR = "localBake"

export const GRAPHER_PREVIEW_CLASS = "grapherPreview"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved here to be importable from both client and server.

19 changes: 3 additions & 16 deletions site/blocks/AllChartsListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import {
DEFAULT_GRAPHER_HEIGHT,
DEFAULT_GRAPHER_WIDTH,
} from "@ourworldindata/grapher"
import { BasicChartInformation } from "@ourworldindata/utils"
import React from "react"
import {
BAKED_BASE_URL,
BAKED_GRAPHER_EXPORTS_BASE_URL,
} from "../../settings/clientSettings.js"
import { BAKED_BASE_URL } from "../../settings/clientSettings.js"
import GrapherImage from "../GrapherImage.js"

export const AllChartsListItem = ({
chart,
Expand All @@ -24,14 +18,7 @@ export const AllChartsListItem = ({
href={`${BAKED_BASE_URL}/grapher/${chart.slug}`}
onClick={onClick}
>
<img
src={`${BAKED_GRAPHER_EXPORTS_BASE_URL}/${chart.slug}.svg`}
loading="lazy"
data-no-lightbox
data-no-img-formatting
width={DEFAULT_GRAPHER_WIDTH}
height={DEFAULT_GRAPHER_HEIGHT}
></img>
<GrapherImage slug={chart.slug} noFormatting />
<span>{chart.title}</span>
</a>
{chart.variantName ? (
Expand Down
4 changes: 2 additions & 2 deletions site/blocks/RelatedCharts.jsdom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ it("renders active chart links and loads respective chart on click", () => {
)
).toHaveLength(1)

wrapper.find("a").forEach((link, idx) => {
wrapper.find("li > a").forEach((link, idx) => {
// Chart 2 has a higher priority, so the charts should be in reverse order: `Chart 2, Chart 1`
const expectedChartIdx = 1 - idx
link.simulate("click")
expect(wrapper.find("figure")).toHaveLength(1)
expect(
wrapper.find(
`figure[data-grapher-src="${BAKED_BASE_URL}/grapher/${charts[expectedChartIdx].slug}"]`
`figure[data-grapher-src="${BAKED_BASE_URL}/grapher/${charts[expectedChartIdx]?.slug}"]`
)
).toHaveLength(1)
// should have forced re-render by changing the `key`
Expand Down
37 changes: 26 additions & 11 deletions site/blocks/RelatedCharts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useEmbedChart } from "../hooks.js"
import { GalleryArrow, GalleryArrowDirection } from "./GalleryArrow.js"
import { AllChartsListItem } from "./AllChartsListItem.js"
import { BAKED_BASE_URL } from "../../settings/clientSettings.js"
import { GRAPHER_PREVIEW_CLASS } from "../SiteConstants.js"
import GrapherImage from "../GrapherImage.js"

export const RELATED_CHARTS_CLASS_NAME = "related-charts"

Expand All @@ -30,7 +32,8 @@ export const RelatedCharts = ({

const isFirstSlideActive = activeChartIdx === 0
const isLastSlideActive = activeChartIdx === sortedCharts.length - 1
const activeChartSlug = sortedCharts[activeChartIdx]?.slug
const activeChart = sortedCharts[activeChartIdx]
const activeChartSlug = activeChart?.slug

const onClickItem = (event: React.MouseEvent, idx: number) => {
// Allow opening charts in new tab/window with ⌘+CLICK
Expand All @@ -42,18 +45,34 @@ export const RelatedCharts = ({

useEmbedChart(activeChartIdx, refChartContainer)

const grapherUrl = `${BAKED_BASE_URL}/grapher/${activeChartSlug}`

const figure = (
<figure
className={GRAPHER_PREVIEW_CLASS}
// Use unique `key` to force React to re-render tree
key={activeChartSlug}
data-grapher-src={grapherUrl}
>
<noscript>
<a href={grapherUrl}>
<GrapherImage
slug={activeChartSlug}
alt={activeChart?.title}
/>
</a>
</noscript>
</figure>
)

const singleChartView = (
<div className={RELATED_CHARTS_CLASS_NAME}>
<div className="grid grid-cols-12">
<div
className="related-charts__chart span-cols-7 span-md-cols-12"
ref={refChartContainer}
>
<figure
// Use unique `key` to force React to re-render tree
key={activeChartSlug}
data-grapher-src={`${BAKED_BASE_URL}/grapher/${activeChartSlug}`}
/>
{figure}
</div>
</div>
</div>
Expand All @@ -78,11 +97,7 @@ export const RelatedCharts = ({
className="related-charts__chart span-cols-7 span-md-cols-12"
ref={refChartContainer}
>
<figure
// Use unique `key` to force React to re-render tree
key={activeChartSlug}
data-grapher-src={`${BAKED_BASE_URL}/grapher/${activeChartSlug}`}
/>
{figure}
<div className="gallery-navigation">
<GalleryArrow
disabled={isFirstSlideActive}
Expand Down
5 changes: 4 additions & 1 deletion site/collections/CollectionsPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@

figure {
width: 100%;
height: $grapher-height;
margin: 0;
margin-bottom: 48px;
}

figure:not(.grapherPreview) {
height: $grapher-height;
}
}
35 changes: 27 additions & 8 deletions site/collections/DynamicCollection.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import cx from "classnames"
import React from "react"
import ReactDOM from "react-dom"
import { BAKED_BASE_URL } from "../../settings/clientSettings.js"
Expand All @@ -12,6 +13,9 @@ import {
import { observer } from "mobx-react"
import { WindowGraphers } from "./DynamicCollectionPage.js"
import { Grapher } from "@ourworldindata/grapher"
import { GRAPHER_PREVIEW_CLASS } from "../SiteConstants.js"
import InteractionNotice from "../InteractionNotice.js"
import GrapherImage from "../GrapherImage.js"

interface DynamicCollectionProps {
baseUrl: string
Expand Down Expand Up @@ -117,14 +121,29 @@ export class DynamicCollection extends React.Component<DynamicCollectionProps> {
<div className="grid span-cols-12">
{this.initialDynamicCollection
.split(" ")
.map((chartSlug, index) => (
<figure
key={index}
data-grapher-src={`${this.props.baseUrl}/grapher/${chartSlug}`}
data-grapher-index={index}
className="span-cols-6 span-md-cols-12"
/>
))}
.map((chartSlug, index) => {
const grapherUrl = `${this.props.baseUrl}/grapher/${chartSlug}`
return (
<figure
key={index}
className={cx(
GRAPHER_PREVIEW_CLASS,
"span-cols-6 span-md-cols-12"
)}
data-grapher-src={grapherUrl}
data-grapher-index={index}
>
<a
href={grapherUrl}
target="_blank"
rel="noopener"
>
<GrapherImage slug={chartSlug} />
<InteractionNotice />
</a>
</figure>
)
})}
</div>
)
}
Expand Down
Loading