diff --git a/adminSiteClient/gdocsDeploy.ts b/adminSiteClient/gdocsDeploy.ts index 1c59e6d509e..7c738e49317 100644 --- a/adminSiteClient/gdocsDeploy.ts +++ b/adminSiteClient/gdocsDeploy.ts @@ -86,6 +86,7 @@ export const checkIsLightningUpdate = ( "atom-excerpt": false, // requires updating the atom feed / blog roll "atom-title": false, // requires updating the atom feed / blog roll "featured-image": false, // requires updating references to this article + "deprecation-notice": false, // requires updating references to this article authors: false, // requires updating references to this article excerpt: false, // requires updating references to this article faqs: false, // requires updating datapages diff --git a/adminSiteClient/gdocsValidation.ts b/adminSiteClient/gdocsValidation.ts index cc3d2e8458b..de14af70eb3 100644 --- a/adminSiteClient/gdocsValidation.ts +++ b/adminSiteClient/gdocsValidation.ts @@ -53,6 +53,22 @@ function validateContentType(gdoc: OwidGdoc, errors: OwidGdocErrorMessage[]) { } } +function validateDeprecationNotice( + gdoc: OwidGdoc, + errors: OwidGdocErrorMessage[] +) { + if ( + "deprecation-notice" in gdoc.content && + gdoc.content.type !== OwidGdocType.Article + ) { + errors.push({ + property: "deprecation-notice", + type: OwidGdocErrorMessageType.Error, + message: "Deprecation notice is only supported in articles.", + }) + } +} + function validateBody(gdoc: OwidGdoc, errors: OwidGdocErrorMessage[]) { if (!gdoc.content.body) { errors.push(getMissingContentPropertyError("body")) @@ -236,6 +252,7 @@ export const getErrors = (gdoc: OwidGdoc): OwidGdocErrorMessage[] => { validateBody(gdoc, errors) validatePublishedAt(gdoc, errors) validateContentType(gdoc, errors) + validateDeprecationNotice(gdoc, errors) if (checkIsGdocPost(gdoc)) { validateRefs(gdoc, errors) diff --git a/baker/SiteBaker.tsx b/baker/SiteBaker.tsx index f14f3da5160..645e81a67e9 100644 --- a/baker/SiteBaker.tsx +++ b/baker/SiteBaker.tsx @@ -55,6 +55,7 @@ import { excludeUndefined, grabMetadataForGdocLinkedIndicator, GrapherTabOption, + DEFAULT_THUMBNAIL_FILENAME, } from "@ourworldindata/utils" import { execWrapper } from "../db/execWrapper.js" @@ -344,7 +345,7 @@ export class SiteBaker { title: cur.title || "", thumbnail: cur.thumbnail || - `${BAKED_BASE_URL}/default-thumbnail.jpg`, + `${BAKED_BASE_URL}/${DEFAULT_THUMBNAIL_FILENAME}`, tags: [], })) ) diff --git a/baker/algolia/algoliaUtils.tsx b/baker/algolia/algoliaUtils.tsx index 8803b3cc8be..38555207bf1 100644 --- a/baker/algolia/algoliaUtils.tsx +++ b/baker/algolia/algoliaUtils.tsx @@ -13,6 +13,9 @@ import { DbPlainTag, OwidGdocPostInterface, getThumbnailPath, + ARCHVED_THUMBNAIL_FILENAME, + DEFAULT_GDOC_FEATURED_IMAGE, + DEFAULT_THUMBNAIL_FILENAME, } from "@ourworldindata/utils" import { formatPost } from "../formatWordpressPost.js" import ReactDOMServer from "react-dom/server.js" @@ -65,7 +68,7 @@ function generateCountryRecords( content: `All available indicators for ${country.name}.`, views_7d: pageviews[`/country/${country.slug}`]?.views_7d ?? 0, documentType: "country-page" as const, - thumbnailUrl: "/default-thumbnail.jpg", + thumbnailUrl: `/${DEFAULT_THUMBNAIL_FILENAME}`, } const score = computeScore(record) return { ...record, score } @@ -144,7 +147,7 @@ async function generateWordpressRecords( content: c, tags: tags.map((t) => t.name), thumbnailUrl: formatUrls( - post.thumbnailUrl ?? "/default-thumbnail.jpg" + post.thumbnailUrl ?? `/${DEFAULT_THUMBNAIL_FILENAME}` ), views_7d: pageviews[`/${post.path}`]?.views_7d ?? 0, documentType: "wordpress" as const, @@ -157,6 +160,16 @@ async function generateWordpressRecords( return records } +function getGdocThumbnailUrl(gdoc: OwidGdocPostInterface): string { + if (gdoc.content["deprecation-notice"]) { + return `/${ARCHVED_THUMBNAIL_FILENAME}` + } + if (gdoc.content["featured-image"]) { + return getThumbnailPath(gdoc.content["featured-image"]) + } + return `/${DEFAULT_GDOC_FEATURED_IMAGE}` +} + function generateGdocRecords( gdocs: OwidGdocPostInterface[], pageviews: Record @@ -199,10 +212,7 @@ function generateGdocRecords( const chunks = generateChunksFromHtmlText(renderedPostContent) const postTypeAndImportance = getPostTypeAndImportance(gdoc) let i = 0 - - const thumbnailUrl = gdoc.content["featured-image"] - ? getThumbnailPath(gdoc.content["featured-image"]) - : "/default-thumbnail.jpg" + const thumbnailUrl = getGdocThumbnailUrl(gdoc) for (const chunk of chunks) { const record = { diff --git a/baker/siteRenderers.tsx b/baker/siteRenderers.tsx index e0f2c9f4ad4..0126b7d1e7d 100644 --- a/baker/siteRenderers.tsx +++ b/baker/siteRenderers.tsx @@ -47,6 +47,7 @@ import { } from "@ourworldindata/utils" import { extractFormattingOptions } from "../serverUtils/wordpressUtils.js" import { + DEFAULT_THUMBNAIL_FILENAME, DbPlainChart, DbRawChartConfig, FormattingOptions, @@ -882,6 +883,6 @@ const renderGrapherThumbnailByResolvedChartSlug = ( const renderExplorerDefaultThumbnail = (): string => { return ReactDOMServer.renderToStaticMarkup( - + ) } diff --git a/db/model/Gdoc/GdocBase.ts b/db/model/Gdoc/GdocBase.ts index b63ae07fcc7..2e01233d0ad 100644 --- a/db/model/Gdoc/GdocBase.ts +++ b/db/model/Gdoc/GdocBase.ts @@ -50,6 +50,8 @@ import { } from "../Variable.js" import { createLinkFromUrl } from "../Link.js" import { + ARCHVED_THUMBNAIL_FILENAME, + DEFAULT_THUMBNAIL_FILENAME, LatestDataInsight, LinkedAuthor, OwidGdoc, @@ -623,7 +625,7 @@ export class GdocBase implements OwidGdocBaseInterface { title: explorer?.title ?? "", subtitle: explorer?.subtitle ?? "", resolvedUrl: `${BAKED_BASE_URL}/${EXPLORERS_ROUTE_FOLDER}/${originalSlug}`, - thumbnail: `${BAKED_BASE_URL}/default-thumbnail.jpg`, + thumbnail: `${BAKED_BASE_URL}/${DEFAULT_THUMBNAIL_FILENAME}`, tags: explorer.tags, } return linkedChart @@ -879,7 +881,10 @@ export async function getMinimalGdocPostsByIds( content ->> '$.subtitle' as subtitle, content ->> '$.excerpt' as excerpt, type, - content ->> '$."featured-image"' as "featured-image" + CASE + WHEN content ->> '$."deprecation-notice"' IS NOT NULL THEN '${ARCHVED_THUMBNAIL_FILENAME}' + ELSE content ->> '$."featured-image"' + END as "featured-image" FROM posts_gdocs WHERE id in (:ids)`, { ids } diff --git a/db/model/Gdoc/GdocFactory.ts b/db/model/Gdoc/GdocFactory.ts index 05c7632bb42..82e66cba6bc 100644 --- a/db/model/Gdoc/GdocFactory.ts +++ b/db/model/Gdoc/GdocFactory.ts @@ -1,6 +1,7 @@ import { get, groupBy } from "lodash" import { match, P } from "ts-pattern" import { + ARCHVED_THUMBNAIL_FILENAME, DATA_INSIGHTS_INDEX_PAGE_SIZE, DbEnrichedPostGdoc, DbInsertPostGdocLink, @@ -210,7 +211,10 @@ export async function getAllMinimalGdocBaseObjects( content ->> '$.subtitle' as subtitle, content ->> '$.excerpt' as excerpt, type, - content ->> '$."featured-image"' as "featured-image" + CASE + WHEN content ->> '$."deprecation-notice"' IS NOT NULL THEN '${ARCHVED_THUMBNAIL_FILENAME}' + ELSE content ->> '$."featured-image"' + END as "featured-image" FROM posts_gdocs WHERE published = 1 AND publishedAt <= NOW()`, diff --git a/db/model/Gdoc/GdocPost.ts b/db/model/Gdoc/GdocPost.ts index 838665a34b9..072db2cfeab 100644 --- a/db/model/Gdoc/GdocPost.ts +++ b/db/model/Gdoc/GdocPost.ts @@ -68,6 +68,11 @@ export class GdocPost extends GdocBase implements OwidGdocPostInterface { enrichedBlocks.push(...refBlocks) } + const deprecationNotice = gdoc.content["deprecation-notice"] + if (deprecationNotice) { + enrichedBlocks.push(...deprecationNotice) + } + return enrichedBlocks } diff --git a/db/model/Gdoc/archieToEnriched.ts b/db/model/Gdoc/archieToEnriched.ts index 554e1958978..38b6e1d79d3 100644 --- a/db/model/Gdoc/archieToEnriched.ts +++ b/db/model/Gdoc/archieToEnriched.ts @@ -24,7 +24,11 @@ import { isEmpty, } from "@ourworldindata/utils" import { convertHeadingTextToId } from "@ourworldindata/components" -import { parseRawBlocksToEnrichedBlocks, parseRefs } from "./rawToEnriched.js" +import { + parseRawBlocksToEnrichedBlocks, + parseRefs, + parseText, +} from "./rawToEnriched.js" import urlSlug from "url-slug" import { extractUrl, parseAuthors, spansToSimpleString } from "./gdocUtils.js" import { htmlToSimpleTextBlock } from "./htmlToEnriched.js" @@ -316,6 +320,10 @@ export const archieToEnriched = ( // Parse elements of the ArchieML into enrichedBlocks parsed.body = compact(parsed.body.map(parseRawBlocksToEnrichedBlocks)) + const deprecationNotice = parsed["deprecation-notice"] + if (deprecationNotice) { + parsed["deprecation-notice"] = compact(deprecationNotice.map(parseText)) + } const parsedRefs = parseRefs({ refs: [...(parsed.refs ?? []), ...rawInlineRefs], diff --git a/db/model/Post.ts b/db/model/Post.ts index 548fe02e87c..0af38ffdd06 100644 --- a/db/model/Post.ts +++ b/db/model/Post.ts @@ -23,6 +23,8 @@ import { DbEnrichedLatestWork, parseLatestWork, DbPlainTag, + DEFAULT_THUMBNAIL_FILENAME, + ARCHVED_THUMBNAIL_FILENAME, } from "@ourworldindata/types" import { uniqBy, sortBy, memoize, orderBy } from "@ourworldindata/utils" import { Knex } from "knex" @@ -263,10 +265,12 @@ export const getFullPost = async ( content: excludeContent ? "" : postApi.content.rendered, excerpt: decodeHTML(postApi.excerpt.rendered), imageUrl: `${BAKED_BASE_URL}${ - postApi.featured_media_paths.medium_large ?? "/default-thumbnail.jpg" + postApi.featured_media_paths.medium_large ?? + `/${DEFAULT_THUMBNAIL_FILENAME}` }`, thumbnailUrl: `${BAKED_BASE_URL}${ - postApi.featured_media_paths?.thumbnail ?? "/default-thumbnail.jpg" + postApi.featured_media_paths?.thumbnail ?? + `/${DEFAULT_THUMBNAIL_FILENAME}` }`, imageId: postApi.featured_media, relatedCharts: @@ -308,6 +312,16 @@ export const getBlogIndex = memoize( } ) +function getGdocThumbnail(gdoc: OwidGdocPostInterface): string { + let thumbnailPath = `/${DEFAULT_THUMBNAIL_FILENAME}` + if (gdoc.content["deprecation-notice"]) { + thumbnailPath = `/${ARCHVED_THUMBNAIL_FILENAME}` + } else if (gdoc.content["featured-image"]) { + thumbnailPath = `${IMAGES_DIRECTORY}${gdoc.content["featured-image"]}` + } + return `${BAKED_BASE_URL}${thumbnailPath}` +} + export const mapGdocsToWordpressPosts = ( gdocs: OwidGdocPostInterface[] ): IndexPost[] => { @@ -319,9 +333,7 @@ export const mapGdocsToWordpressPosts = ( modifiedDate: gdoc.updatedAt as Date, authors: gdoc.content.authors, excerpt: gdoc.content["atom-excerpt"] || gdoc.content.excerpt, - imageUrl: gdoc.content["featured-image"] - ? `${BAKED_BASE_URL}${IMAGES_DIRECTORY}${gdoc.content["featured-image"]}` - : `${BAKED_BASE_URL}/default-thumbnail.jpg`, + imageUrl: getGdocThumbnail(gdoc), })) } @@ -614,7 +626,7 @@ export const getLatestWorkByAuthor = async ( knex: Knex, author: string ): Promise => { - const rawLatestWorkLinks: DbRawLatestWork[] = await db.knexRaw( + const rawLatestWorkLinks = await db.knexRaw( knex, `-- sql SELECT @@ -623,7 +635,10 @@ export const getLatestWorkByAuthor = async ( pg.content->>'$.title' AS title, pg.content->>'$.subtitle' AS subtitle, pg.content->>'$.authors' AS authors, - pg.content->>'$."featured-image"' AS "featured-image", + CASE + WHEN content ->> '$."deprecation-notice"' IS NOT NULL THEN '${ARCHVED_THUMBNAIL_FILENAME}' + ELSE content ->> '$."featured-image"' + END as "featured-image" pg.publishedAt FROM posts_gdocs pg diff --git a/functions/donation/_utils/checkout.ts b/functions/donation/_utils/checkout.ts index 12c796ec32b..9c0bcbc2cd3 100644 --- a/functions/donation/_utils/checkout.ts +++ b/functions/donation/_utils/checkout.ts @@ -1,5 +1,6 @@ import Stripe from "stripe" import { + DEFAULT_THUMBNAIL_FILENAME, DonationRequest, getErrorMessageDonation, JsonError, @@ -92,7 +93,7 @@ export async function createCheckoutSession( product_data: { name: "Monthly donation", images: [ - "https://ourworldindata.org/default-thumbnail.jpg", + `https://ourworldindata.org/${DEFAULT_THUMBNAIL_FILENAME}`, ], }, recurring: { diff --git a/packages/@ourworldindata/components/src/styles/variables.scss b/packages/@ourworldindata/components/src/styles/variables.scss index 1487b854c93..e0d9e6979af 100644 --- a/packages/@ourworldindata/components/src/styles/variables.scss +++ b/packages/@ourworldindata/components/src/styles/variables.scss @@ -147,6 +147,7 @@ $xxlg: 1536px; */ $zindex-input: 1; +$zindex-deprecation-notice: 10; $zindex-global-entity-select: 11; $zindex-footnote: 15; $zindex-sidebar: 20; diff --git a/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts b/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts index 7060f22d64b..6f15c14f15a 100644 --- a/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts +++ b/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts @@ -285,6 +285,7 @@ export interface OwidGdocPostContent { excerpt?: string refs?: { definitions: RefDictionary; errors: OwidGdocErrorMessage[] } summary?: EnrichedBlockText[] + "deprecation-notice"?: EnrichedBlockText[] "hide-citation"?: boolean toc?: TocHeadingWithTitleSupertitle[] "cover-image"?: string diff --git a/packages/@ourworldindata/types/src/gdocTypes/GdocConstants.ts b/packages/@ourworldindata/types/src/gdocTypes/GdocConstants.ts index ebf3cf13122..b5ddb142678 100644 --- a/packages/@ourworldindata/types/src/gdocTypes/GdocConstants.ts +++ b/packages/@ourworldindata/types/src/gdocTypes/GdocConstants.ts @@ -26,4 +26,10 @@ export const gdocUrlRegex = export const gdocIdRegex = /^[0-9A-Za-z\-_]{44}$/ +// This file is saved in Drive in the Unattributed Images folder +// Somewhat fragile, should be fixed as part of https://github.com/owid/owid-grapher/issues/2485 export const DEFAULT_GDOC_FEATURED_IMAGE = "default-featured-image.png" + +export const DEFAULT_THUMBNAIL_FILENAME = "default-thumbnail.jpg" + +export const ARCHVED_THUMBNAIL_FILENAME = "archived-thumbnail.jpg" diff --git a/packages/@ourworldindata/types/src/index.ts b/packages/@ourworldindata/types/src/index.ts index 9c19d9dd527..f75df1bca72 100644 --- a/packages/@ourworldindata/types/src/index.ts +++ b/packages/@ourworldindata/types/src/index.ts @@ -346,6 +346,8 @@ export { gdocUrlRegex, gdocIdRegex, DEFAULT_GDOC_FEATURED_IMAGE, + DEFAULT_THUMBNAIL_FILENAME, + ARCHVED_THUMBNAIL_FILENAME, } from "./gdocTypes/GdocConstants.js" export { type OwidVariableWithSource, diff --git a/packages/@ourworldindata/utils/src/image.ts b/packages/@ourworldindata/utils/src/image.ts index eb430323b01..080a7ffd9c5 100644 --- a/packages/@ourworldindata/utils/src/image.ts +++ b/packages/@ourworldindata/utils/src/image.ts @@ -151,15 +151,13 @@ export function getFeaturedImageFilename(gdoc: OwidGdoc): string | undefined { content: { type: P.union( OwidGdocType.Fragment, - // undefined so that we use default-thumbnail.jpg as defined in Head.tsx OwidGdocType.Homepage, undefined ), }, }, - () => { - return undefined - } + // This will fallback to DEFAULT_THUMBNAIL_FILENAME in Head.tsx + () => undefined ) .exhaustive() } diff --git a/public/archived-thumbnail.jpg b/public/archived-thumbnail.jpg new file mode 100644 index 00000000000..28e2c4c36c6 Binary files /dev/null and b/public/archived-thumbnail.jpg differ diff --git a/site/DataPageV2Content.tsx b/site/DataPageV2Content.tsx index eaffddccc86..8cb1c996938 100644 --- a/site/DataPageV2Content.tsx +++ b/site/DataPageV2Content.tsx @@ -14,6 +14,7 @@ import { GrapherInterface, joinTitleFragments, ImageMetadata, + DEFAULT_THUMBNAIL_FILENAME, } from "@ourworldindata/utils" import { AttachmentsContext, DocumentContext } from "./gdocs/OwidGdoc.js" import StickyNav from "./blocks/StickyNav.js" @@ -40,7 +41,7 @@ const DatapageResearchThumbnail = ({ urlOrFilename: string | undefined | null }) => { if (!urlOrFilename) { - urlOrFilename = `${BAKED_BASE_URL}/default-thumbnail.jpg` + urlOrFilename = `${BAKED_BASE_URL}/${DEFAULT_THUMBNAIL_FILENAME}` } if (urlOrFilename.startsWith("http")) { return ( diff --git a/site/Head.tsx b/site/Head.tsx index 61821b9933a..575c57774b5 100644 --- a/site/Head.tsx +++ b/site/Head.tsx @@ -2,6 +2,7 @@ import React from "react" import { viteAssetsForSite } from "./viteUtils.js" import { GOOGLE_TAG_MANAGER_ID } from "../settings/clientSettings.js" import { NoJSDetector } from "./NoJSDetector.js" +import { DEFAULT_THUMBNAIL_FILENAME } from "@ourworldindata/types" export const GTMScriptTags = ({ gtmId }: { gtmId: string }) => { if (!gtmId || /["']/.test(gtmId)) return null @@ -51,7 +52,8 @@ export const Head = (props: { const pageDesc = props.pageDesc || "Research and data to make progress against the world’s largest problems" - const imageUrl = props.imageUrl || `${baseUrl}/default-thumbnail.jpg` + const imageUrl = + props.imageUrl || `${baseUrl}/${DEFAULT_THUMBNAIL_FILENAME}` const atom = props.atom ?? { title: "Atom feed for Our World in Data", href: "/atom.xml", @@ -63,7 +65,7 @@ export const Head = (props: { {fullPageTitle} diff --git a/site/gdocs/OwidGdocPage.tsx b/site/gdocs/OwidGdocPage.tsx index 78c4857b63f..de9bb2d7ad3 100644 --- a/site/gdocs/OwidGdocPage.tsx +++ b/site/gdocs/OwidGdocPage.tsx @@ -14,7 +14,11 @@ import { import { getCanonicalUrl, getPageTitle } from "@ourworldindata/components" import { DebugProvider } from "./DebugContext.js" import { match, P } from "ts-pattern" -import { EnrichedBlockText, IMAGES_DIRECTORY } from "@ourworldindata/types" +import { + ARCHVED_THUMBNAIL_FILENAME, + EnrichedBlockText, + IMAGES_DIRECTORY, +} from "@ourworldindata/types" import { DATA_INSIGHT_ATOM_FEED_PROPS } from "./utils.js" import { Html } from "../Html.js" @@ -89,18 +93,23 @@ export default function OwidGdocPage({ const isDataInsight = gdoc.content.type === OwidGdocType.DataInsight const isAuthor = gdoc.content.type === OwidGdocType.Author + let imageUrl + if ( + gdoc.content.type === OwidGdocType.Article && + gdoc.content["deprecation-notice"] + ) { + imageUrl = `${baseUrl}/${ARCHVED_THUMBNAIL_FILENAME}` + } else if (featuredImageFilename) { + imageUrl = `${baseUrl}${IMAGES_DIRECTORY}${featuredImageFilename}` + } + return ( diff --git a/site/gdocs/components/OwidGdocHeader.tsx b/site/gdocs/components/OwidGdocHeader.tsx index fbcdec15155..c97ba5a92ac 100644 --- a/site/gdocs/components/OwidGdocHeader.tsx +++ b/site/gdocs/components/OwidGdocHeader.tsx @@ -18,10 +18,12 @@ function OwidArticleHeader({ content, publishedAt, breadcrumbs, + isDeprecated, }: { content: OwidGdocPostContent publishedAt: Date | null breadcrumbs?: BreadcrumbItem[] + isDeprecated?: boolean }) { const coverStyle = content["cover-color"] ? { backgroundColor: `var(--${content["cover-color"]})` } @@ -75,7 +77,13 @@ function OwidArticleHeader({ ) : null}
-
+
{content.authors.length > 0 && (
@@ -86,25 +94,27 @@ function OwidArticleHeader({ (publishedAt && formatDate(publishedAt))}
-
- {!content["hide-citation"] && ( + {!isDeprecated && ( + +
+ )}
@@ -158,6 +168,7 @@ export function OwidGdocHeader(props: { content: OwidGdocPostContent publishedAt: Date | null breadcrumbs?: BreadcrumbItem[] + isDeprecated?: boolean }) { if (props.content.type === OwidGdocType.Article) return diff --git a/site/gdocs/components/ProminentLink.tsx b/site/gdocs/components/ProminentLink.tsx index f2ead43cd08..95f5e74f1f0 100644 --- a/site/gdocs/components/ProminentLink.tsx +++ b/site/gdocs/components/ProminentLink.tsx @@ -7,6 +7,10 @@ import { useLinkedChart, useLinkedDocument } from "../utils.js" import { DocumentContext } from "../OwidGdoc.js" import { BlockErrorFallback } from "./BlockErrorBoundary.js" import { BAKED_GRAPHER_EXPORTS_BASE_URL } from "../../../settings/clientSettings.js" +import { + ARCHVED_THUMBNAIL_FILENAME, + DEFAULT_THUMBNAIL_FILENAME, +} from "@ourworldindata/types" export const ProminentLink = (props: { url: string @@ -66,7 +70,8 @@ export const ProminentLink = (props: { const Thumbnail = ({ thumbnail }: { thumbnail: string }) => { if ( thumbnail.startsWith(BAKED_GRAPHER_EXPORTS_BASE_URL) || - thumbnail.endsWith("default-thumbnail.jpg") + thumbnail.endsWith(ARCHVED_THUMBNAIL_FILENAME) || + thumbnail.endsWith(DEFAULT_THUMBNAIL_FILENAME) ) { return } else { diff --git a/site/gdocs/components/centered-article.scss b/site/gdocs/components/centered-article.scss index 8cd669a62f7..69b4fef9b14 100644 --- a/site/gdocs/components/centered-article.scss +++ b/site/gdocs/components/centered-article.scss @@ -195,6 +195,63 @@ $banner-height: 200px; margin-bottom: 32px; } +.deprecation-notice { + --bg-color: #fff5d8; + position: sticky; + top: 0; + left: 0; + right: 0; + background-color: var(--bg-color); + border-radius: 4px; + padding: 16px; + padding-bottom: 0; + margin-bottom: 24px; + z-index: $zindex-deprecation-notice; + + &::before { + content: ""; + position: absolute; + top: 0; + left: 50%; + right: 50%; + height: 100%; + background-color: var(--bg-color); + transition: all 0.3s ease; + z-index: -1; + } + + &--sticky::before { + left: -50vw; + right: -50vw; + } + + .article-block__text, + .article-block__list, + .article-block__html, + .article-block__numbered-list { + @include body-3-regular; + } +} + +.deprecation-notice__heading { + @include h4-semibold; + margin-top: 0; + margin-bottom: 8px; + color: $vermillion; +} + +.deprecation-notice__icon { + margin-right: 8px; +} + +.citation-deprecated-notice { + font-weight: 700; + + &__highlight { + color: $vermillion; + } +} + .centered-article-header { background: $white; margin: 80px 0 24px; @@ -470,14 +527,14 @@ h3.article-block__heading.has-supertitle { } @include sm-up { - > div:first-child { + > div:not(:last-child) { border-right: 1px solid $blue-10; } } @include sm-only { padding: 16px 0; - > div:first-child { + > div:not(:last-child) { border-bottom: 1px solid $blue-10; padding-bottom: 16px; margin-bottom: 16px; @@ -557,7 +614,6 @@ h3.article-block__heading.has-supertitle { padding-top: 48px; h3 { margin-top: 0; - text-align: center; } p { margin-bottom: 16px; diff --git a/site/gdocs/pages/GdocPost.tsx b/site/gdocs/pages/GdocPost.tsx index 08f2d02dd03..23e33326df0 100644 --- a/site/gdocs/pages/GdocPost.tsx +++ b/site/gdocs/pages/GdocPost.tsx @@ -1,5 +1,8 @@ import React from "react" import cx from "classnames" +import { useIntersectionObserver } from "usehooks-ts" +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import { faBoxArchive } from "@fortawesome/free-solid-svg-icons" import { ArticleBlocks } from "../components/ArticleBlocks.js" import Footnotes from "../components/Footnotes.js" import { @@ -9,6 +12,7 @@ import { isEmpty, OwidGdocType, formatAuthors, + EnrichedBlockText, } from "@ourworldindata/utils" import { CodeSnippet } from "@ourworldindata/components" import { BAKED_BASE_URL } from "../../../settings/clientSettings.js" @@ -56,6 +60,9 @@ export function GdocPost({ ) const citationText = `${shortPageCitation} Published online at OurWorldInData.org. Retrieved from: '${`${BAKED_BASE_URL}/${slug}`}' [Online Resource]` const hasSidebarToc = content["sidebar-toc"] + const isDeprecated = + postType === OwidGdocType.Article && + Boolean(content["deprecation-notice"]) const bibtex = `@article{owid-${slug.replace(/\//g, "-")}, author = {${formatAuthors({ @@ -85,7 +92,11 @@ export function GdocPost({ content={content} publishedAt={publishedAt} breadcrumbs={breadcrumbs ?? undefined} + isDeprecated={isDeprecated} /> + {isDeprecated && content["deprecation-notice"] && ( + + )} {hasSidebarToc && content.toc ? ( ) : null} - {content.type === "topic-page" && stickyNavLinks?.length ? ( + {postType === OwidGdocType.TopicPage && stickyNavLinks?.length ? (