diff --git a/baker/SiteBaker.tsx b/baker/SiteBaker.tsx index 63c8719e8da..291450fd346 100644 --- a/baker/SiteBaker.tsx +++ b/baker/SiteBaker.tsx @@ -56,6 +56,7 @@ import { DATA_INSIGHTS_INDEX_PAGE_SIZE, OwidGdocMinimalPostInterface, excludeUndefined, + grabMetadataForGdocLinkedIndicator, } from "@ourworldindata/utils" import { execWrapper } from "../db/execWrapper.js" @@ -345,7 +346,7 @@ export class SiteBaker { const metadata = await getVariableMetadata(indicatorId) return { id: indicatorId, - titlePublic: metadata.presentation?.titlePublic, + ...grabMetadataForGdocLinkedIndicator(metadata), } }) ) diff --git a/db/model/Gdoc/GdocBase.ts b/db/model/Gdoc/GdocBase.ts index 6b8910d5a12..bbf38e42684 100644 --- a/db/model/Gdoc/GdocBase.ts +++ b/db/model/Gdoc/GdocBase.ts @@ -35,6 +35,7 @@ import { MinimalDataInsightInterface, OwidGdocMinimalPostInterface, urlToSlug, + grabMetadataForGdocLinkedIndicator, } from "@ourworldindata/utils" import { BAKED_GRAPHER_URL } from "../../../settings/serverSettings.js" import { google } from "googleapis" @@ -618,7 +619,7 @@ export class GdocBase extends BaseEntity implements OwidGdocBaseInterface { ) const linkedIndicator: LinkedIndicator = { id: linkedChart.indicatorId, - titlePublic: metadata.presentation?.titlePublic, + ...grabMetadataForGdocLinkedIndicator(metadata), } return linkedIndicator }) diff --git a/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx b/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx index eae6ba63347..b8457d9d404 100644 --- a/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx +++ b/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx @@ -12,21 +12,23 @@ export const makeSource = ({ attribution, owidProcessingLevel, isEmbeddedInADataPage, + hideProcessingLevel = false, }: { attribution?: string owidProcessingLevel?: OwidProcessingLevel isEmbeddedInADataPage?: boolean + hideProcessingLevel?: boolean }): React.ReactNode => { if (!attribution) return null const isEmbedded = isEmbeddedInADataPage ?? true const processingLevelPhrase = getPhraseForProcessingLevel(owidProcessingLevel) - const hideProcessingPhase = + const hideProcessingPhrase = hideProcessingLevel || attribution.toLowerCase() === "our world in data" return ( <> - {!hideProcessingPhase && ( + {!hideProcessingPhrase && ( <> {" – "} {isEmbedded ? ( diff --git a/packages/@ourworldindata/types/src/OwidVariable.ts b/packages/@ourworldindata/types/src/OwidVariable.ts index 6aeb2d91dfd..a49e227733a 100644 --- a/packages/@ourworldindata/types/src/OwidVariable.ts +++ b/packages/@ourworldindata/types/src/OwidVariable.ts @@ -1,6 +1,7 @@ import { OwidOrigin } from "./OwidOrigin.js" import { OwidSource } from "./OwidSource.js" import { OwidVariableDisplayConfigInterface } from "./OwidVariableDisplayConfigInterface.js" +import { GrapherInterface } from "./grapherTypes/GrapherTypes.js" export interface OwidVariableWithSource { id: number @@ -71,7 +72,7 @@ export interface OwidVariablePresentation { attribution?: string topicTagsLinks?: string[] faqs?: FaqLink[] - grapherConfigETL?: string + grapherConfigETL?: GrapherInterface } export type OwidProcessingLevel = "minor" | "major" diff --git a/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts b/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts index 4ac70a04f50..ea767d8a705 100644 --- a/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts +++ b/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts @@ -28,7 +28,10 @@ export interface LinkedChart { export interface LinkedIndicator { id: number - titlePublic?: string + title: string + dateRange?: string + lastUpdated?: string + attributionUnshortened?: string } export enum OwidGdocType { diff --git a/packages/@ourworldindata/utils/src/index.ts b/packages/@ourworldindata/utils/src/index.ts index 731970dc23b..32784da5e36 100644 --- a/packages/@ourworldindata/utils/src/index.ts +++ b/packages/@ourworldindata/utils/src/index.ts @@ -131,6 +131,8 @@ export { formatSourceDate, getCitationLong, getCitationShort, + grabMetadataForGdocLinkedIndicator, + getAttributionUnshortened, } from "./metadataHelpers.js" export { diff --git a/packages/@ourworldindata/utils/src/metadataHelpers.ts b/packages/@ourworldindata/utils/src/metadataHelpers.ts index bb1a344321c..12c98a5deea 100644 --- a/packages/@ourworldindata/utils/src/metadataHelpers.ts +++ b/packages/@ourworldindata/utils/src/metadataHelpers.ts @@ -5,6 +5,8 @@ import { DisplaySource, IndicatorTitleWithFragments, OwidSource, + OwidVariableWithSourceAndDimension, + LinkedIndicator, } from "@ourworldindata/types" import { compact, uniq, last, excludeUndefined } from "./Util" import dayjs from "./dayjs.js" @@ -194,6 +196,7 @@ const getYearSuffixFromOrigin = (o: OwidOrigin): string => { if (year) return ` (${year})` else return "" } + export const getCitationShort = ( origins: OwidOrigin[], attributions: string[], @@ -261,6 +264,7 @@ export const getCitationLong = ( canonicalUrl ? `Retrieved ${today} from ${canonicalUrl}` : undefined, ]).join(" ") } + export const formatSourceDate = ( date: string | undefined, format: string @@ -269,3 +273,36 @@ export const formatSourceDate = ( if (!parsedDate.isValid()) return date || null return parsedDate.format(format) } + +export function getAttributionUnshortened({ + origins, + attributions, +}: { + origins: OwidOrigin[] + attributions: string[] +}): string { + const producersWithYear = uniq( + origins.map((o) => `${o.producer}${getYearSuffixFromOrigin(o)}`) + ) + const attributionFragments = attributions ?? producersWithYear + return attributionFragments.join("; ") +} + +export function grabMetadataForGdocLinkedIndicator( + metadata: OwidVariableWithSourceAndDimension +): Omit { + return { + title: + metadata.presentation?.titlePublic ?? + metadata.presentation?.grapherConfigETL?.title ?? + metadata.display?.name ?? + metadata.name ?? + "", + dateRange: metadata.timespan, + lastUpdated: getLastUpdatedFromVariable(metadata), + attributionUnshortened: getAttributionUnshortened({ + attributions: getAttributionFragmentsFromVariable(metadata), + origins: metadata.origins ?? [], + }), + } +} diff --git a/site/DataPageV2Content.tsx b/site/DataPageV2Content.tsx index e10f31f2bff..a3dfb34b30f 100644 --- a/site/DataPageV2Content.tsx +++ b/site/DataPageV2Content.tsx @@ -34,12 +34,12 @@ import { DataPageRelatedResearch, isEmpty, excludeUndefined, - OwidOrigin, DataPageDataV2, getCitationShort, GrapherInterface, getCitationLong, joinTitleFragments, + getAttributionUnshortened, } from "@ourworldindata/utils" import { AttachmentsContext, DocumentContext } from "./gdocs/OwidGdoc.js" import StickyNav from "./blocks/StickyNav.js" @@ -124,24 +124,12 @@ export const DataPageV2Content = ({ datapageData.descriptionKey && datapageData.descriptionKey.length > 0 const sourcesForDisplay = prepareSourcesForDisplay(datapageData) - const getYearSuffixFromOrigin = (o: OwidOrigin) => { - const year = o.dateAccessed - ? dayjs(o.dateAccessed, ["YYYY-MM-DD", "YYYY"]).year() - : o.datePublished - ? dayjs(o.datePublished, ["YYYY-MM-DD", "YYYY"]).year() - : undefined - if (year) return ` (${year})` - else return "" - } const producers = uniq(datapageData.origins.map((o) => `${o.producer}`)) - const producersWithYear = uniq( - datapageData.origins.map( - (o) => `${o.producer}${getYearSuffixFromOrigin(o)}` - ) - ) - const attributionFragments = datapageData.attributions ?? producersWithYear - const attributionUnshortened = attributionFragments.join("; ") + const attributionUnshortened = getAttributionUnshortened({ + attributions: datapageData.attributions, + origins: datapageData.origins, + }) const citationShort = getCitationShort( datapageData.origins, datapageData.attributions, @@ -749,7 +737,7 @@ const KeyDataTable = (props: { const links = makeLinks({ link: datapageData.source?.link }) return ( -
+
{datapageData.descriptionShort && (
diff --git a/site/gdocs/components/ArticleBlock.tsx b/site/gdocs/components/ArticleBlock.tsx index dbd8506547f..925733200d3 100644 --- a/site/gdocs/components/ArticleBlock.tsx +++ b/site/gdocs/components/ArticleBlock.tsx @@ -70,7 +70,7 @@ const layouts: { [key in Container]: Layouts} = { ["image--narrow"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 col-sm-start-2 span-sm-cols-12", ["image--wide"]: "col-start-4 span-cols-8 col-md-start-2 span-md-cols-12", ["image-caption"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2", - ["key-indicator"]: "col-start-5 span-cols-6", + ["key-indicator"]: "col-start-2 span-cols-12", ["key-insights"]: "col-start-2 span-cols-12", ["list"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2", ["numbered-list"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2", diff --git a/site/gdocs/components/KeyIndicator.scss b/site/gdocs/components/KeyIndicator.scss new file mode 100644 index 00000000000..71ef90c90c6 --- /dev/null +++ b/site/gdocs/components/KeyIndicator.scss @@ -0,0 +1,68 @@ +.key-indicator { + $frame-padding: 24px; + $padding: 16px; + + background-color: $blue-10; + padding: $frame-padding; + margin-bottom: $frame-padding; + + .indicator-title { + @include body-2-semibold; + color: $blue-60; + margin-bottom: 14px; + } + + .narrative-title { + @include h3-bold; + color: $blue-90; + margin-top: 0; + margin-bottom: 8px; + } + + .blurb { + @include body-2-regular; + color: $blue-90; + margin-bottom: $padding; + } + + .blurb a { + @include owid-link-90; + } + + .metadata--border-top { + border-top: solid rgba(164, 182, 202, 0.5); + padding-top: $padding; + } + + .metadata-entry { + @include body-3-medium; + margin-bottom: $padding; + } + + .metadata-entry__title { + color: $blue-50; + } + + .metadata-entry__value { + color: $blue-90; + } + + .datapage-link { + @include body-3-medium; + + display: block; + @include sm-up { + display: inline-block; + } + + height: 40px; + padding: 8px 24px; + text-align: center; + color: $white; + background-color: $blue-60; + cursor: pointer; + border: none; + + margin-top: $padding; + } +} diff --git a/site/gdocs/components/KeyIndicator.tsx b/site/gdocs/components/KeyIndicator.tsx index f64d33986d0..1bae644ce86 100644 --- a/site/gdocs/components/KeyIndicator.tsx +++ b/site/gdocs/components/KeyIndicator.tsx @@ -1,5 +1,16 @@ import React from "react" -import { EnrichedBlockKeyIndicator } from "@ourworldindata/types" +import cx from "classnames" + +import { + EnrichedBlockKeyIndicator, + EnrichedBlockText, +} from "@ourworldindata/types" +import { + makeSource, + makeDateRange, + makeLastUpdated, +} from "@ourworldindata/components" + import Chart from "./Chart.js" import Paragraph from "./Paragraph.js" import { useLinkedChart, useLinkedIndicator } from "../utils.js" @@ -19,30 +30,78 @@ export default function KeyIndicator({ if (!linkedChart) return null if (!linkedIndicator) return null + const source = makeSource({ + attribution: linkedIndicator.attributionUnshortened, + hideProcessingLevel: true, + }) + const dateRange = makeDateRange({ + dateRange: linkedIndicator.dateRange, + }) + const lastUpdated = makeLastUpdated({ + lastUpdated: linkedIndicator.lastUpdated, + }) + return ( -
-
- Custom title: {d.title} -
-
- Default title: {linkedChart?.title} -
- {d.blurb && ( -
- Blurb: - {d.blurb.map((textBlock, i) => ( - - ))} +
+
+
{linkedIndicator.title}
+ {d.title &&

{d.title}

} + {d.blurb && ( +
+ {d.blurb.map( + (textBlock: EnrichedBlockText, i: number) => ( + + ) + )} +
+ )} +
+ {source && ( +
+
Source
+
+ {source} +
+
+ )} + {dateRange && ( +
+
+ Date range +
+
+ {dateRange} +
+
+ )} + {lastUpdated && ( +
+
+ Last updated +
+
+ {lastUpdated} +
+
+ )}
- )} + + Explore and learn more about this data + +
-
- Linked indicator with metadata: - {JSON.stringify(linkedIndicator, null, 2)} -
) } diff --git a/site/owid.scss b/site/owid.scss index 1838caee6a9..6be7a3a8209 100644 --- a/site/owid.scss +++ b/site/owid.scss @@ -94,6 +94,7 @@ @import "./gdocs/components/AdditionalCharts.scss"; @import "./gdocs/components/ResearchAndWriting.scss"; @import "./gdocs/components/Chart.scss"; +@import "./gdocs/components/KeyIndicator.scss"; @import "./DataPage.scss"; @import "./DataPageContent.scss"; @import "./detailsOnDemand.scss";