Skip to content

Commit

Permalink
Data page: About this data (updated design) (#2914)
Browse files Browse the repository at this point in the history
Updates the design of the About this data section below the chart.
  • Loading branch information
danyx23 authored Nov 27, 2023
2 parents 99feeb6 + 27562f0 commit 3233b5c
Show file tree
Hide file tree
Showing 14 changed files with 608 additions and 348 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react"
import cx from "classnames"
import {
dayjs,
OwidProcessingLevel,
Expand All @@ -9,186 +8,92 @@ import {
import { DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID } from "../SharedDataPageConstants.js"
import { SimpleMarkdownText } from "../SimpleMarkdownText.js"

interface IndicatorKeyDataProps {
export const makeSource = ({
attribution,
owidProcessingLevel,
isEmbeddedInADataPage,
}: {
attribution?: string
dateRange?: string
lastUpdated?: string
nextUpdate?: string
unit?: string
owidProcessingLevel?: OwidProcessingLevel
link?: string
unitConversionFactor?: number
isEmbeddedInADataPage?: boolean // true by default

// styling
hideTopBorder?: boolean
hideBottomBorder?: boolean
}

export const IndicatorKeyData = (props: IndicatorKeyDataProps) => {
const isEmbeddedInADataPage = props.isEmbeddedInADataPage ?? true
const processingLevelPhrase = getPhraseForProcessingLevel(
props.owidProcessingLevel
isEmbeddedInADataPage?: boolean
}): React.ReactNode => {
if (!attribution) return null
const isEmbedded = isEmbeddedInADataPage ?? true
const processingLevelPhrase =
getPhraseForProcessingLevel(owidProcessingLevel)
return (
<>
<SimpleMarkdownText text={attribution} useParagraphs={false} />
{" - "}
{isEmbedded ? (
<a href={`#${DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID}`}>
{processingLevelPhrase}
</a>
) : (
processingLevelPhrase
)}{" "}
by Our World in Data
</>
)
}

const lastUpdated = dayjs(props.lastUpdated ?? "", ["YYYY-MM-DD", "YYYY"])
const showLastUpdated = lastUpdated.isValid()
export const makeLastUpdated = ({
lastUpdated,
}: {
lastUpdated?: string
}): React.ReactNode => {
const date = dayjs(lastUpdated ?? "", ["YYYY-MM-DD", "YYYY"])
if (!date.isValid()) return null
return date.format("MMMM D, YYYY")
}

const nextUpdate = dayjs(props.nextUpdate ?? "", ["YYYY-MM-DD"])
const showNextUpdate = nextUpdate.isValid()
export const makeNextUpdate = ({
nextUpdate,
}: {
nextUpdate?: string
}): React.ReactNode => {
const date = dayjs(nextUpdate ?? "", ["YYYY-MM-DD"])
if (!date.isValid()) return null
return date.format("MMMM YYYY")
}

const showUnitConversionFactor =
props.unitConversionFactor && props.unitConversionFactor !== 1
export const makeDateRange = ({
dateRange,
}: {
dateRange?: string
}): React.ReactNode => {
if (!dateRange) return null
return getDateRange(dateRange)
}

const linkFragments = splitSourceTextIntoFragments(props.link)
const link = linkFragments.join("\n")
export const makeUnit = ({ unit }: { unit?: string }): React.ReactNode => {
if (!unit) return null
return unit
}

const keyDataCount = count(
props.attribution,
showLastUpdated,
showNextUpdate,
props.dateRange,
props.unit,
showUnitConversionFactor,
link
)
const hasSingleRow =
keyDataCount === 1 ||
(keyDataCount === 2 && !props.attribution && !link)
export const makeUnitConversionFactor = ({
unitConversionFactor,
}: {
unitConversionFactor?: number
}): React.ReactNode => {
if (!unitConversionFactor || unitConversionFactor === 1) return null
return unitConversionFactor
}

export const makeLinks = ({ link }: { link?: string }): React.ReactNode => {
if (!link) return null
const linkFragments = splitSourceTextIntoFragments(link)
return (
<div
className={cx("indicator-key-data", {
"indicator-key-data--top-border":
!hasSingleRow && !props.hideTopBorder,
"indicator-key-data--bottom-border":
!hasSingleRow && !props.hideBottomBorder,
})}
>
{props.attribution && (
<div className="indicator-key-data-item indicator-key-data-item--span">
<div className="indicator-key-data-item__title">Source</div>
<div className="indicator-key-data-item__content">
<SimpleMarkdownText text={props.attribution} />
{" - "}
{isEmbeddedInADataPage ? (
<a
href={`#${DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID}`}
>
{processingLevelPhrase}
</a>
) : (
processingLevelPhrase
)}{" "}
by Our World in Data
</div>
</div>
)}
{showLastUpdated && (
<div
className={cx("indicator-key-data-item", {
"indicator-key-data-item--span":
!showNextUpdate &&
!props.dateRange &&
!props.unit &&
!showUnitConversionFactor,
})}
>
<div className="indicator-key-data-item__title">
Last updated
</div>
<div className="indicator-key-data-item__content">
{lastUpdated.format("MMMM D, YYYY")}
</div>
</div>
)}
{showNextUpdate && (
<div
className={cx("indicator-key-data-item", {
"indicator-key-data-item--span":
!props.dateRange &&
!showLastUpdated &&
!props.unit &&
!showUnitConversionFactor,
})}
>
<div className="indicator-key-data-item__title">
Next expected update
</div>
<div className="indicator-key-data-item__content">
{nextUpdate.format("MMMM YYYY")}
</div>
</div>
)}
{props.dateRange && (
<div
className={cx("indicator-key-data-item", {
"indicator-key-data-item--span":
!props.unit &&
!showUnitConversionFactor &&
isEven(count(showLastUpdated, showNextUpdate)),
})}
>
<div className="indicator-key-data-item__title">
Date range
</div>
<div className="indicator-key-data-item__content">
{getDateRange(props.dateRange)}
</div>
</div>
)}
{props.unit && (
<div
className={cx("indicator-key-data-item", {
"indicator-key-data-item--span":
!showUnitConversionFactor &&
isEven(
count(
props.lastUpdated,
props.nextUpdate,
props.dateRange
)
),
})}
>
<div className="indicator-key-data-item__title">Unit</div>
<div className="indicator-key-data-item__content">
{props.unit}
</div>
</div>
)}
{showUnitConversionFactor && (
<div
className={cx("indicator-key-data-item", {
"indicator-key-data-item--span": isEven(
count(
props.lastUpdated,
props.nextUpdate,
props.dateRange,
props.unit
)
),
})}
>
<div className="indicator-key-data-item__title">
Unit conversion factor
</div>
<div className="indicator-key-data-item__content">
{props.unitConversionFactor}
</div>
</div>
)}
{link && (
<div className="indicator-key-data-item indicator-key-data-item--span">
<div className="indicator-key-data-item__title">
{linkFragments.length > 1 ? "Links" : "Link"}
</div>
<div className="indicator-key-data-item__content">
<SimpleMarkdownText text={link} />
</div>
</div>
)}
</div>
<>
{linkFragments.map((text, index) => (
<>
<span>
<SimpleMarkdownText text={text} useParagraphs={false} />
</span>
{index < linkFragments.length - 1 && <br />}
</>
))}
</>
)
}

Expand Down Expand Up @@ -234,6 +139,3 @@ const getDateRange = (dateRange: string): string | null => {
}
return null
}

const count = (...args: any[]) => args.filter((arg) => arg).length
const isEven = (n: number) => n % 2 === 0
20 changes: 20 additions & 0 deletions packages/@ourworldindata/components/src/SimpleMarkdownText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { computed } from "mobx"
import { Remark } from "react-remark"
import { remarkPlainLinks } from "./markdown/remarkPlainLinks.js"
import visit from "unist-util-visit"

type SimpleMarkdownTextProps = {
text: string
useParagraphs?: boolean // by default, text is wrapped in <p> tags
}

function transformDodLinks() {
Expand Down Expand Up @@ -38,11 +40,29 @@ export class SimpleMarkdownText extends React.Component<SimpleMarkdownTextProps>
return this.props.text
}

@computed get useParagraphs(): boolean {
return this.props.useParagraphs ?? true
}

@computed get rehypeReactOptions(): any {
if (!this.useParagraphs) {
// "unwrap" the text by rendering <p /> as a react fragment
return {
components: {
p: (props: any) => <React.Fragment {...props} />,
},
}
}

return undefined
}

render(): JSX.Element | null {
return (
<Remark
rehypePlugins={[transformDodLinks]}
remarkPlugins={[remarkPlainLinks]}
rehypeReactOptions={this.rehypeReactOptions}
>
{this.text}
</Remark>
Expand Down
Loading

0 comments on commit 3233b5c

Please sign in to comment.