diff --git a/packages/@ourworldindata/components/src/CodeSnippet/CodeSnippet.tsx b/packages/@ourworldindata/components/src/CodeSnippet/CodeSnippet.tsx index ba7d0fa3f6f..58a3259385c 100644 --- a/packages/@ourworldindata/components/src/CodeSnippet/CodeSnippet.tsx +++ b/packages/@ourworldindata/components/src/CodeSnippet/CodeSnippet.tsx @@ -5,15 +5,18 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js" import { faCopy } from "@fortawesome/free-solid-svg-icons" import { canWriteToClipboard } from "@ourworldindata/utils" import cx from "classnames" +import { SimpleMarkdownText } from "../SimpleMarkdownText" export const CodeSnippet = ({ code, theme = "dark", isTruncated = false, + useMarkdown = false, }: { code: string theme?: "dark" | "light" isTruncated?: boolean + useMarkdown?: boolean }) => { const [canCopy, setCanCopy] = useState(false) const [hasCopied, setHasCopied] = useState(false) @@ -44,7 +47,11 @@ export const CodeSnippet = ({ "wp-code-snippet__code--is-truncated": isTruncated, })} > - {code} + {useMarkdown ? ( + + ) : ( + code + )} {canCopy && ( diff --git a/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.scss b/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.scss index f70bba6d824..79917d3abb5 100644 --- a/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.scss +++ b/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.scss @@ -1,7 +1,7 @@ .ExpandableToggle { --title: var(--expandable-toggle-title, currentColor); - --content: var(--expandable-toggle-content, #{$blue-60}); --border: var(--expandable-toggle-border, #d0dae3); + --content: var(--expandable-toggle-content, #{$blue-60}); --button: var(--expandable-toggle-button, #{$blue-90}); --button-hover: var(--expandable-toggle-button-hover, #{$blue-60}); @@ -34,6 +34,11 @@ } } +.ExpandableToggle--teaser .ExpandableToggle__button, +.ExpandableToggle--open .ExpandableToggle__button { + padding-bottom: 8px; +} + .ExpandableToggle__title { @include h4-semibold; margin: 0; @@ -66,17 +71,21 @@ font-size: var(--content-size); } + > *:first-child { + margin-top: 0; + } + > *:last-child { margin-bottom: 0; } } -.ExpandableToggle__content--teaser { +.ExpandableToggle--teaser .ExpandableToggle__content { height: 96px; -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent); } -.ExpandableToggle__content--open { +.ExpandableToggle--open .ExpandableToggle__content { padding-bottom: 16px; height: auto; -webkit-mask-image: none; diff --git a/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.tsx b/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.tsx index a8acf621e0e..370d6f56738 100644 --- a/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.tsx +++ b/packages/@ourworldindata/components/src/ExpandableToggle/ExpandableToggle.tsx @@ -27,6 +27,8 @@ export const ExpandableToggle = ({
-
- {content} -
+
{content}
) } diff --git a/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx b/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx index 81c96335cc8..6850018d793 100644 --- a/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx +++ b/packages/@ourworldindata/components/src/IndicatorKeyData/IndicatorKeyData.tsx @@ -1,9 +1,9 @@ import React from "react" import { - dayjs, OwidProcessingLevel, getPhraseForProcessingLevel, splitSourceTextIntoFragments, + formatSourceDate, } from "@ourworldindata/utils" import { DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID } from "../SharedDataPageConstants.js" import { SimpleMarkdownText } from "../SimpleMarkdownText.js" @@ -21,18 +21,26 @@ export const makeSource = ({ const isEmbedded = isEmbeddedInADataPage ?? true const processingLevelPhrase = getPhraseForProcessingLevel(owidProcessingLevel) + const hideProcessingPhase = + attribution.toLowerCase() === "our world in data" return ( <> - {" - "} - {isEmbedded ? ( - - {processingLevelPhrase} - - ) : ( - processingLevelPhrase - )}{" "} - by Our World in Data + {!hideProcessingPhase && ( + <> + {" – "} + {isEmbedded ? ( + + {processingLevelPhrase} + + ) : ( + processingLevelPhrase + )}{" "} + by Our World in Data + + )} ) } @@ -42,9 +50,7 @@ export const makeLastUpdated = ({ }: { lastUpdated?: string }): React.ReactNode => { - const date = dayjs(lastUpdated ?? "", ["YYYY-MM-DD", "YYYY"]) - if (!date.isValid()) return null - return date.format("MMMM D, YYYY") + return formatSourceDate(lastUpdated, "MMMM D, YYYY") } export const makeNextUpdate = ({ @@ -52,9 +58,7 @@ export const makeNextUpdate = ({ }: { nextUpdate?: string }): React.ReactNode => { - const date = dayjs(nextUpdate ?? "", ["YYYY-MM-DD"]) - if (!date.isValid()) return null - return date.format("MMMM YYYY") + return formatSourceDate(nextUpdate, "MMMM YYYY") } export const makeDateRange = ({ diff --git a/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.scss b/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.scss index 878f4a6df0e..e2b43a3efe3 100644 --- a/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.scss +++ b/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.scss @@ -1,26 +1,33 @@ .indicator-processing { .data-processing { - --content: var(--data-processing-content, currentColor); + --content: var(--data-processing-content, #{$blue-60}); --content-size: var(--data-processing-content-size, 0.875rem); margin-top: 0; margin-bottom: 24px; + color: var(--content); > p { @include body-3-medium; margin: 0 0 16px; - color: var(--content); font-size: var(--content-size); } } .indicator-processing__link { - @include expandable-paragraph__expand-button--full; + @include body-3-medium; + color: inherit; + text-decoration: underline; + text-underline-offset: 4px; + + &:hover { + text-decoration: none; + } svg { - display: none; // used in Grapher's sources modal font-size: 0.75em; - margin-left: 12px; + margin-left: 6px; + transform: rotate(-90deg); } } @@ -48,6 +55,32 @@ color: var(--content); margin: 0; font-size: var(--content-size); + + ul { + margin-left: 1em; + + li { + margin: 1em 0; + &:first-child { + margin-top: 0; + } + &:last-child { + margin-bottom: 0; + } + } + } + + > *:first-child { + margin-top: 0; + } + + > *:last-child { + margin-bottom: 0; + } + + ul li p { + margin: 0; + } } a { diff --git a/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.tsx b/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.tsx index d17fa8cad3b..1d0be62418a 100644 --- a/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.tsx +++ b/packages/@ourworldindata/components/src/IndicatorProcessing/IndicatorProcessing.tsx @@ -41,11 +41,11 @@ export const IndicatorProcessing = (props: IndicatorProcessingProps) => {
Notes on our processing step for this indicator
-

+

-

+
)} diff --git a/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.scss b/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.scss index af4615610fb..b4c4639d587 100644 --- a/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.scss +++ b/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.scss @@ -1,49 +1,90 @@ -.indicator-source { - .description { - margin: 0; - margin-bottom: 16px; - } +.indicator-sources { + --description-heading: var( + --indicator-sources-description-heading, + #{$blue-90} + ); + --key-data-title: var(--indicator-sources-key-data-title, #{$blue-90}); + --content: var(--indicator-sources-content, #{$blue-60}); + + .source { + color: var(--content); + + .description { + margin-bottom: 16px; + + > *:first-child { + margin-top: 0; + } - .source-key-data-blocks { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); - row-gap: 8px; - column-gap: 16px; + > *:last-child { + margin-bottom: 0; + } - .wp-code-snippet { - margin-top: 8px; - margin-bottom: 0; + h1, + h2, + h3, + h4, + h5, + h6 { + color: var(--description-heading); + font-size: 1em; + margin-top: 14px; + margin-bottom: 6px; + } + + p { + margin: 6px 0; + } + + ol { + margin-left: 1.2em; + } + + ul { + margin-left: 1em; + } } - } - .source-key-data { - --title: var(--source-key-data-title, #{$blue-90}); - } + .source-key-data-blocks { + --code-snippet-text: #{$blue-50}; - .source-key-data--span-2 { - grid-column: span 2; - } + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + gap: 16px; - .source-key-data__title { - color: var(--title); - white-space: nowrap; - } + .wp-code-snippet { + margin-top: 16px; + margin-bottom: 0; + } + } - .source-key-data__content--hide-overflow { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } + .source-key-data--span-2 { + grid-column: span 2; + } - a { - @include owid-link-90; - color: inherit; + .source-key-data__title { + color: var(--key-data-title); + white-space: nowrap; + } + + .source-key-data__content { + > p:first-of-type { + margin-top: 0; + } + > p:last-of-type { + margin-bottom: 0; + } + } + + a { + @include owid-link-90; + color: inherit; + } } } .NonExpandable { --title: var(--non-expandable-title, currentColor); - --content: var(--non-expandable-content, #{$blue-60}); --border: var(--non-expandable-border, #d0dae3); --title-size: var(--non-expandable-title-size, 1rem); @@ -68,7 +109,6 @@ &__content { @include body-3-medium; - color: var(--content); font-size: var(--content-size); } } diff --git a/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.tsx b/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.tsx index 472e1e1b5f4..2f213eb7ba0 100644 --- a/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.tsx +++ b/packages/@ourworldindata/components/src/IndicatorSources/IndicatorSources.tsx @@ -1,7 +1,7 @@ import React from "react" import cx from "classnames" import { ExpandableToggle } from "../ExpandableToggle/ExpandableToggle.js" -import { DisplaySource, dayjs, uniqBy } from "@ourworldindata/utils" +import { DisplaySource, uniqBy, formatSourceDate } from "@ourworldindata/utils" import { SimpleMarkdownText } from "../SimpleMarkdownText.js" import { CodeSnippet } from "../CodeSnippet/CodeSnippet.js" import { REUSE_THIS_WORK_SECTION_ID } from "../SharedDataPageConstants.js" @@ -16,7 +16,11 @@ export const IndicatorSources = (props: IndicatorSourcesProps) => { const uniqueSources = uniqBy(props.sources, "label") return ( -
+
{uniqueSources.map((source: DisplaySource, idx: number) => { const isStacked = idx !== uniqueSources.length - 1 const content = ( @@ -25,9 +29,9 @@ export const IndicatorSources = (props: IndicatorSourcesProps) => { isEmbeddedInADataPage={isEmbeddedInADataPage} /> ) - return source.description || - source.citation || - source.dataPublishedBy ? ( + const useExpandableToggle = + source.description || source.citation + return useExpandableToggle ? ( { const { source } = props - const retrievedOn = source.retrievedOn - ? dayjs(source.retrievedOn).format("MMMM D, YYYY") - : undefined + const retrievedOn = formatSourceDate(source.retrievedOn, "MMMM D, YYYY") const showKeyInfo = source.dataPublishedBy || retrievedOn || (source.retrievedFrom && source.retrievedFrom.length > 0) || source.citation return ( -
+
{source.description && ( -

+

-

+
)} {showKeyInfo && (
@@ -92,61 +94,75 @@ const SourceContent = (props: {
Data published by
-
{source.dataPublishedBy}
+
+ +
)} {retrievedOn && ( -
+
Retrieved on
-
{retrievedOn}
+
+ {retrievedOn} +
)} {source.retrievedFrom && source.retrievedFrom.length > 0 && ( -
+
Retrieved from
- {source.retrievedFrom.map((url: string) => ( - - ))} +
+ +
)} {source.citation && ( -
+
Citation
- This is the citation of the original data obtained - from the source, prior to any processing or - adaptation by Our World in Data.{" "} - {props.isEmbeddedInADataPage && ( - <> - To cite data downloaded from this page, - please use the suggested citation given in{" "} - - Reuse This Work - {" "} - below. - - )} - +
+ This is the citation of the original data + obtained from the source, prior to any + processing or adaptation by Our World in Data.{" "} + {props.isEmbeddedInADataPage && ( + <> + To cite data downloaded from this page, + please use the suggested citation given + in{" "} + + Reuse This Work + {" "} + below. + + )} + +
)}
diff --git a/packages/@ourworldindata/components/src/SharedDataPageConstants.ts b/packages/@ourworldindata/components/src/SharedDataPageConstants.ts index 9c9be698ec2..bc3e12c8420 100644 --- a/packages/@ourworldindata/components/src/SharedDataPageConstants.ts +++ b/packages/@ourworldindata/components/src/SharedDataPageConstants.ts @@ -1,3 +1,4 @@ +export const DATAPAGE_ABOUT_THIS_DATA_SECTION_ID = "about-this-data" as const export const DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID = "sources-and-processing" as const export const REUSE_THIS_WORK_SECTION_ID = "reuse-this-work" diff --git a/packages/@ourworldindata/components/src/Tabs/ExpandableTabs.scss b/packages/@ourworldindata/components/src/Tabs/ExpandableTabs.scss deleted file mode 100644 index 5adc6a60f82..00000000000 --- a/packages/@ourworldindata/components/src/Tabs/ExpandableTabs.scss +++ /dev/null @@ -1,9 +0,0 @@ -.ExpandableTabs__button { - color: #5b5b5b; - border-color: #f2f2f2 !important; // !important overwrites style definitions in grapher.scss - background: #f2f2f2 !important; - - svg { - margin-right: 6px; - } -} diff --git a/packages/@ourworldindata/components/src/Tabs/Tabs.scss b/packages/@ourworldindata/components/src/Tabs/Tabs.scss deleted file mode 100644 index e2659adedfe..00000000000 --- a/packages/@ourworldindata/components/src/Tabs/Tabs.scss +++ /dev/null @@ -1,42 +0,0 @@ -.Tabs { - --border: var(--tab-border, #{$blue-10}); - --text: var(--tab-text, #858585); - --text-active: var(--text-text-active, #{$blue-90}); - --background: var(--tab-background, #fff); - --background-active: var(--tab-background-active, #{$accent-pale-blue}); - - margin: 16px 0; - - &--horizontal-scroll { - overflow-x: auto; - white-space: nowrap; - -webkit-overflow-scrolling: touch; - scrollbar-width: none; - } - - &::-webkit-scrollbar { - display: none; - } - - &__tab { - margin: 0 8px 8px 0; - padding: 8px 16px; - border: 1px solid var(--border) !important; // !important overwrites style definitions in grapher.scss - background: var(--background) !important; - color: var(--text); - font-size: 0.8125rem; - font-weight: 500; - letter-spacing: 0.01em; - white-space: nowrap; - max-width: 240px; - text-overflow: ellipsis; - overflow: hidden; - cursor: pointer; - } - - &__tab--active { - background: var(--background-active) !important; - border-color: var(--background-active) !important; - color: var(--text-active); - } -} diff --git a/packages/@ourworldindata/components/src/index.ts b/packages/@ourworldindata/components/src/index.ts index 073c1c8ac21..35b288d6d40 100644 --- a/packages/@ourworldindata/components/src/index.ts +++ b/packages/@ourworldindata/components/src/index.ts @@ -17,8 +17,6 @@ export { } from "./GdocsUtils.js" export { ExpandableToggle } from "./ExpandableToggle/ExpandableToggle.js" -export { Tabs } from "./Tabs/Tabs.js" -export { ExpandableTabs } from "./Tabs/ExpandableTabs.js" export { makeSource, @@ -41,6 +39,7 @@ export { } from "./CodeSnippet/CodeSnippet.js" export { + DATAPAGE_ABOUT_THIS_DATA_SECTION_ID, DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID, REUSE_THIS_WORK_SECTION_ID, } from "./SharedDataPageConstants.js" diff --git a/packages/@ourworldindata/components/src/styles/typography.scss b/packages/@ourworldindata/components/src/styles/typography.scss index f98ed4289cb..fac2260ee05 100644 --- a/packages/@ourworldindata/components/src/styles/typography.scss +++ b/packages/@ourworldindata/components/src/styles/typography.scss @@ -345,6 +345,7 @@ body { font-size: 0.8125rem; font-weight: 500; letter-spacing: 0.01em; + line-height: 1.25; } .label-2-medium { diff --git a/packages/@ourworldindata/grapher/src/core/grapher.scss b/packages/@ourworldindata/grapher/src/core/grapher.scss index c1f888833c7..9c80d0cb68f 100644 --- a/packages/@ourworldindata/grapher/src/core/grapher.scss +++ b/packages/@ourworldindata/grapher/src/core/grapher.scss @@ -6,8 +6,6 @@ @import "../../../components/src/CodeSnippet/code-snippet.scss"; @import "../../../components/src/ExpandableToggle/ExpandableToggle.scss"; -@import "../../../components/src/Tabs/Tabs.scss"; -@import "../../../components/src/Tabs/ExpandableTabs.scss"; @import "../../../components/src/IndicatorSources/IndicatorSources.scss"; @import "../../../components/src/IndicatorProcessing/IndicatorProcessing.scss"; @@ -73,6 +71,8 @@ $zindex-controls-drawer: 140; @import "../header/Header.scss"; @import "../modal/SourcesKeyDataTable.scss"; @import "../modal/SourcesDescriptions.scss"; + @import "../tabs/Tabs.scss"; + @import "../tabs/ExpandableTabs.scss"; } // These rules are currently used elsewhere in the site. e.g. Explorers diff --git a/packages/@ourworldindata/grapher/src/footer/Footer.tsx b/packages/@ourworldindata/grapher/src/footer/Footer.tsx index 7eb481e30f0..364cbe58e3f 100644 --- a/packages/@ourworldindata/grapher/src/footer/Footer.tsx +++ b/packages/@ourworldindata/grapher/src/footer/Footer.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react" import parseUrl from "url-parse" import { Bounds, DEFAULT_BOUNDS, getRelativeMouse } from "@ourworldindata/utils" import { - DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID, + DATAPAGE_ABOUT_THIS_DATA_SECTION_ID, MarkdownTextWrap, TextWrap, } from "@ourworldindata/components" @@ -409,7 +409,7 @@ export class Footer< return (

{sources.renderHTML()} - {" - "} + {" – "} { contentRef: React.RefObject = React.createRef() @@ -32,6 +34,10 @@ export class Modal extends React.Component<{ return this.props.alignVertical ?? "center" } + @computed private get showStickyHeader(): boolean { + return this.props.showStickyHeader || !!this.title + } + @action.bound onDocumentClick(e: MouseEvent): void { const tagName = (e.target as HTMLElement).tagName const isTargetInteractive = ["A", "BUTTON", "INPUT"].includes(tagName) @@ -95,8 +101,12 @@ export class Modal extends React.Component<{ style={contentStyle} ref={this.contentRef} > - {this.title ? ( -

+ {this.showStickyHeader ? ( +
{this.title}
{dismissButton}
diff --git a/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.scss b/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.scss index c4b0c58c42a..63384e330d1 100644 --- a/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.scss +++ b/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.scss @@ -1,13 +1,14 @@ .sources-descriptions { - .key-info { + margin-top: 24px; + + .sources-description-key { background-color: $gray-10; - border-left: 4px solid $blue-10; margin: 24px 0; - padding: 32px; + padding: 24px; &__title { - @include h5-black-caps; - color: #a1a1a1; + @include h2-semibold; + color: $dark-text; margin-top: 0; margin-bottom: 16px; } @@ -17,9 +18,12 @@ @include body-2-regular; font-size: 0.875rem; font-weight: 500; + color: $dark-text; + margin: 0; } &__content ul { + margin-left: 1em; li { margin-bottom: 1em; @@ -31,14 +35,20 @@ &__learn-more { @include body-3-medium; - margin-top: 24px; + display: inline-block; + margin-top: 16px; text-decoration: underline; text-underline-offset: 4px; + color: $dark-text; &:hover { text-decoration: none; } + &:visited { + color: $dark-text; + } + svg { font-size: 0.75em; margin-left: 12px; @@ -48,6 +58,16 @@ } } + .expandable-info-blocks { + p { + margin: 0; + + + p { + margin-top: 8px; + } + } + } + a { @include owid-link-60; color: inherit; diff --git a/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.tsx b/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.tsx index ebfdcdcaec6..3718bc01a43 100644 --- a/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.tsx +++ b/packages/@ourworldindata/grapher/src/modal/SourcesDescriptions.tsx @@ -22,11 +22,11 @@ export const SourcesDescriptions = (props: SourcesDescriptionsProps) => { return (
{props.descriptionKey && props.descriptionKey.length > 0 && ( -
-

+
+

What you should know about this data

-
+
{props.descriptionKey.length === 1 ? ( {
  • {" "}
  • ))} @@ -44,7 +45,10 @@ export const SourcesDescriptions = (props: SourcesDescriptionsProps) => { )}
    {isEmbeddedInADataPage && props.hasFaqEntries && ( -
    + Learn more in the FAQs @@ -60,11 +64,9 @@ export const SourcesDescriptions = (props: SourcesDescriptionsProps) => { : "How does the producer of this data describe this data?" } content={ -
    - -
    + } isExpandedDefault={ !(props.descriptionShort || props.descriptionKey) @@ -76,11 +78,9 @@ export const SourcesDescriptions = (props: SourcesDescriptionsProps) => { - -
    + } /> )} diff --git a/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.scss b/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.scss index 43ae8d1020d..2e6ead84d80 100644 --- a/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.scss +++ b/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.scss @@ -1,59 +1,48 @@ .sources-key-data-table { - $border: #e7e7e7; - - @include label-2-medium; margin-top: 16px; - &--top-border { - border-top: 1px solid $border; - } - - &--bottom-border { - border-bottom: 1px solid $border; - } - display: grid; grid-template-columns: repeat(2, 1fr); + gap: 16px; .key-data { - display: flex; - padding: 16px 24px 16px 0; - - @include sm-only { - display: block; - } - - &:not(.key-data-source) { - border-top: 1px solid $border; - } + @include label-2-medium; &__title { - flex: 0 0 140px; // using a fixed width here to make sure the content is aligned - margin-right: 24px; + margin-bottom: 4px; color: $light-text; - - @include sm-only { - margin-right: 0; - } } &__content { - color: var(--content); + color: $dark-text; } &--span { grid-column: 1 / -1; } + + a { + text-underline-offset: 2px; + } } a { - @include owid-link-90; color: inherit; + text-decoration: underline; + + &:hover { + text-decoration: none; + } } } -&.GrapherComponentNarrow .sources-key-data-table { +&.GrapherComponentSemiNarrow .sources-key-data-table { .key-data { - display: block; + grid-column: 1 / -1; + } + + .key-data__title { + margin-right: 0; + margin-bottom: 4px; } } diff --git a/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.tsx b/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.tsx index f41d385d14c..4c8bad94e14 100644 --- a/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.tsx +++ b/packages/@ourworldindata/grapher/src/modal/SourcesKeyDataTable.tsx @@ -1,6 +1,6 @@ import React from "react" import cx from "classnames" -import { OwidProcessingLevel } from "@ourworldindata/utils" +import { OwidProcessingLevel, excludeNull, range } from "@ourworldindata/utils" import { makeSource, makeLastUpdated, @@ -21,10 +21,6 @@ interface SourcesKeyDataTableProps { link?: string unitConversionFactor?: number isEmbeddedInADataPage?: boolean // true by default - - // styling - hideTopBorder?: boolean - hideBottomBorder?: boolean } export const SourcesKeyDataTable = (props: SourcesKeyDataTableProps) => { @@ -36,98 +32,59 @@ export const SourcesKeyDataTable = (props: SourcesKeyDataTableProps) => { const unitConversionFactor = makeUnitConversionFactor(props) const links = makeLinks(props) + const keyDataWithoutSource = excludeNull([ + lastUpdated ? { label: "Last updated", content: lastUpdated } : null, + nextUpdate + ? { label: "Next expected update", content: nextUpdate } + : null, + dateRange ? { label: "Date range", content: dateRange } : null, + unit ? { label: "Unit", content: unit } : null, + unitConversionFactor + ? { + label: "Unit conversion factor", + content: unitConversionFactor, + } + : null, + links ? { label: "Links", content: links } : null, + ]) + + const rows = range(0, keyDataWithoutSource.length, 2).map( + (index: number) => [ + keyDataWithoutSource[index], + keyDataWithoutSource[index + 1], + ] + ) + return ( -
    +
    {source && ( -
    +
    Source
    {source}
    )} - {lastUpdated && ( -
    -
    Last updated
    -
    {lastUpdated}
    -
    - )} - {nextUpdate && ( -
    -
    Next expected update
    -
    {nextUpdate}
    -
    - )} - {dateRange && ( -
    -
    Date range
    -
    {dateRange}
    -
    - )} - {unit && ( -
    -
    Unit
    -
    {unit}
    -
    - )} - {unitConversionFactor && ( -
    -
    - Unit conversion factor + {rows.map(([first, second]) => ( + +
    +
    {first.label}
    +
    {first.content}
    -
    - {unitConversionFactor} -
    -
    - )} - {links && ( -
    -
    Links
    -
    {links}
    -
    - )} + {second && ( +
    +
    + {second.label} +
    +
    + {second.content} +
    +
    + )} + + ))}
    ) } - -const count = (...args: any[]) => args.filter((arg) => arg).length -const isEven = (n: number) => n % 2 === 0 diff --git a/packages/@ourworldindata/grapher/src/modal/SourcesModal.scss b/packages/@ourworldindata/grapher/src/modal/SourcesModal.scss index e745c61c3eb..867d59cd552 100644 --- a/packages/@ourworldindata/grapher/src/modal/SourcesModal.scss +++ b/packages/@ourworldindata/grapher/src/modal/SourcesModal.scss @@ -1,13 +1,15 @@ .SourcesModalContent { // keep in sync with variables in SourcesModal.tsx - $max-width: 832px; + $max-content-width: 640px; + $tab-padding: 16px; + $tab-font-size: 13px; + $tab-gap: 8px; padding-bottom: $modal-padding; - max-width: $max-width; + max-width: $max-content-width; margin: 0 auto; $border: #e7e7e7; - $box-title: #a1a1a1; .note-multiple-indicators { margin-top: 0; @@ -18,12 +20,13 @@ } .source { - color: $dark-text; + color: $light-text; h2 { @include h1-semibold; margin-top: 0; margin-bottom: 8px; + color: $dark-text; @include sm-up { font-size: 1.5rem; @@ -34,6 +37,7 @@ @include body-3-medium; margin-top: 8px; margin-bottom: 16px; + color: $dark-text; } a { @@ -47,16 +51,32 @@ .heading { @include h2-semibold; margin-top: 32px; - margin-bottom: 8px; + margin-bottom: 16px; + color: $dark-text; + + &--tight { + margin-bottom: 8px; + } } .indicator-sources { - .ExpandableToggle:first-of-type { + --indicator-sources-description-heading: #{$dark-text}; + --indicator-sources-key-data-title: #{$dark-text}; + --indicator-sources-content: #{$light-text}; + + .ExpandableToggle:first-of-type, + .NonExpandable:first-of-type { border-top: none; + padding-top: 0; + + .ExpandableToggle__button { + padding-top: 0; + } } - .source-key-data { - --source-key-data-title: #{$dark-text}; + &--single .NonExpandable { + border: none; + padding: 0; } } @@ -69,8 +89,8 @@ .indicator-processing-callout { --indicator-processing-background: #{$gray-10}; - --indicator-processing-title: #{$box-title}; - --indicator-processing-content: #{$dark-text}; + --indicator-processing-title: #{$dark-text}; + --indicator-processing-content: #{$light-text}; margin-left: 0; margin-right: 0; @@ -79,14 +99,13 @@ .ExpandableToggle { --expandable-toggle-border: #{$border}; --expandable-toggle-title: #{$dark-text}; + --expandable-toggle-content: #{$light-text}; --expandable-toggle-button: #{$dark-text}; --expandable-toggle-button-hover: #{$light-text}; - --expandable-toggle-content: #{$light-text}; } .NonExpandable { --non-expandable-title: #{$dark-text}; - --non-expandable-content: #{$light-text}; --non-expandable-border: #{$border}; } @@ -99,12 +118,10 @@ --code-snippet-button-active: #{$light-text}; } - // keep in sync with measureTabWidth() in SourcesModal.tsx .Tabs__tab { - font-size: 13px; - padding: 8px 16px; - margin-right: 8px; - max-width: 240px; + font-size: $tab-font-size; + padding: 8px $tab-padding; + margin-right: $tab-gap; } .indicator-processing .indicator-processing__link { @@ -113,7 +130,7 @@ height: auto; padding: 0; text-align: left; - color: inherit; + color: $dark-text; background-color: transparent; text-decoration: underline; text-underline-offset: 4px; @@ -123,8 +140,8 @@ } svg { - display: inline-block; - transform: rotate(-90deg); + //display: inline-block; + //transform: rotate(-90deg); } } } diff --git a/packages/@ourworldindata/grapher/src/modal/SourcesModal.tsx b/packages/@ourworldindata/grapher/src/modal/SourcesModal.tsx index 3672a89feae..0842bb51a40 100644 --- a/packages/@ourworldindata/grapher/src/modal/SourcesModal.tsx +++ b/packages/@ourworldindata/grapher/src/modal/SourcesModal.tsx @@ -2,6 +2,7 @@ import { Bounds, DEFAULT_BOUNDS, uniq, + uniqBy, sum, zip, getAttributionFragmentsFromVariable, @@ -13,8 +14,6 @@ import { OwidSource, } from "@ourworldindata/utils" import { - Tabs, - ExpandableTabs, IndicatorSources, IndicatorProcessing, } from "@ourworldindata/components" @@ -26,12 +25,19 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js" import { CoreColumn, OwidColumnDef } from "@ourworldindata/core-table" import { Modal } from "./Modal" import { SourcesKeyDataTable } from "./SourcesKeyDataTable" -import { SourcesDescriptions } from "./SourcesDescriptions.js" +import { SourcesDescriptions } from "./SourcesDescriptions" +import { Tabs } from "../tabs/Tabs" +import { ExpandableTabs } from "../tabs/ExpandableTabs" +import { LoadingIndicator } from "../loadingIndicator/LoadingIndicator" // keep in sync with variables in SourcesModal.scss -const MAX_WIDTH = 832 +const MAX_CONTENT_WIDTH = 640 +const TAB_PADDING = 16 +const TAB_FONT_SIZE = 13 +const TAB_GAP = 8 export interface SourcesModalManager { + isReady?: boolean adminBaseUrl?: string columnsWithSourcesExtensive: CoreColumn[] showAdminControls?: boolean @@ -71,8 +77,20 @@ export class SourcesModal extends React.Component< } @computed private get modalBounds(): Bounds { + const maxWidth = MAX_CONTENT_WIDTH + 220 // using 15px instead of 16px to make sure the modal fully covers the OWID logo in the header - return this.tabBounds.pad(15) + const padWidth = Math.max(15, (this.tabBounds.width - maxWidth) / 2) + return this.tabBounds.padHeight(15).padWidth(padWidth) + } + + @computed private get showStickyModalHeader(): boolean { + const modalWidth = this.modalBounds.width - 2 * this.modalPadding + const dismissButtonWidth = 32 + return (modalWidth - MAX_CONTENT_WIDTH) / 2 < dismissButtonWidth + } + + @computed private get modalPadding(): number { + return 1.5 * (this.manager.fontSize ?? 16) } @computed private get editBaseUrl(): string | undefined { @@ -84,6 +102,19 @@ export class SourcesModal extends React.Component< return this.manager.columnsWithSourcesExtensive } + @computed private get deduplicatedColumn(): CoreColumn | undefined { + const sources = this.columns.map((column) => new Source({ column })) + + // no need to deduplicate if there is only one source + if (sources.length <= 1) return undefined + + // deduplicate on all visible information + const uniqueSources = uniqBy(sources, (source) => + visibleSourceInformation(source) + ) + return uniqueSources.length === 1 ? this.columns[0] : undefined + } + @computed private get tabLabels(): string[] { return this.columns.map((column) => column.nonEmptyDisplayName) } @@ -101,6 +132,19 @@ export class SourcesModal extends React.Component< ) } + private renderDeduplicatedSource(): JSX.Element | null { + if (!this.deduplicatedColumn) return null + return ( + + ) + } + private renderTabs(): JSX.Element { const activeIndex = this.state.activeTabIndex const setActiveIndex = (index: number) => @@ -108,38 +152,61 @@ export class SourcesModal extends React.Component< activeTabIndex: index, }) - if (this.manager.isNarrow) + // tabs are clipped to this width + const maxTabWidth = 240 + + // on mobile, we show a horizontally scrolling tabs + if (this.manager.isNarrow) { return ( ) + } - // width available for tabs - const modalPadding = 1.5 * (this.manager.fontSize ?? 16) + // maximum width available for tabs const maxWidth = Math.min( - MAX_WIDTH, - this.modalBounds.width - 2 * modalPadding - 10 // wiggle room + MAX_CONTENT_WIDTH, + this.modalBounds.width - 2 * this.modalPadding - 10 // wiggle room ) const labelWidths = this.tabLabels.map( - (label) => measureTabWidth(label) + 8 // right padding + (label) => measureTabWidth(label) + TAB_GAP + ) + + // check if all tab labels fit into a single line + if (sum(labelWidths) <= maxWidth) { + return ( + + ) + } + + const clippedLabelWidths = this.tabLabels.map( + (label) => Math.min(measureTabWidth(label), maxTabWidth) + TAB_GAP ) - // check if all tabs fit into a single line - if (sum(labelWidths) <= maxWidth) + // check if all tab labels fit into a single line when they are clipped + if (sum(clippedLabelWidths) <= maxWidth) { return ( ) + } - // get a subset of tabs that fit into a single line + // compute the subset of tabs that fit into a single line const getVisibleLabels = (labels: string[]) => { // take width of the "Show more" button into account let width = @@ -148,7 +215,7 @@ export class SourcesModal extends React.Component< 6 // icon padding const visibleLabels: string[] = [] - for (const [label, labelWidth] of zip(labels, labelWidths)) { + for (const [label, labelWidth] of zip(labels, clippedLabelWidths)) { width += labelWidth as number if (width > maxWidth) break visibleLabels.push(label as string) @@ -159,15 +226,17 @@ export class SourcesModal extends React.Component< // if only a single label would be visible, we prefer tabs with horizontal scrolling const visibleLabels = getVisibleLabels(this.tabLabels) - if (visibleLabels.length <= 1) + if (visibleLabels.length <= 1) { return ( ) + } return ( ) } + private renderMultipleSources(): JSX.Element { + return ( + <> +

    + This chart is composed of multiple indicators. Select an + indicator for more information. +

    + {this.renderTabs()} + {this.renderSource(this.columns[this.state.activeTabIndex])} + + ) + } + + private renderModalContent(): JSX.Element | null { + if (this.deduplicatedColumn) { + return this.renderDeduplicatedSource() + } + + return this.columns.length === 1 + ? this.renderSource(this.columns[0]) + : this.renderMultipleSources() + } + render(): JSX.Element { return ( (this.manager.isSourcesModalOpen = false) )} bounds={this.modalBounds} + isHeightFixed={true} + showStickyHeader={this.showStickyModalHeader} >
    - {this.columns.length === 1 ? ( - this.renderSource(this.columns[0]) + {this.manager.isReady ? ( + this.renderModalContent() ) : ( - <> -

    - This data includes several indicators. Select an - indicator for more information. -

    - {this.renderTabs()} - {this.renderSource( - this.columns[this.state.activeTabIndex] - )} - + )}
    @@ -214,16 +300,20 @@ export class Source extends React.Component<{ editBaseUrl?: string isEmbeddedInADataPage?: boolean }> { - @computed private get def(): OwidColumnDef { - return this.props.column.def + @computed get column(): CoreColumn { + return this.props.column + } + + @computed get def(): OwidColumnDef & { source?: OwidSource } { + return { ...this.column.def, source: this.column.source } } @computed private get source(): OwidSource { - return this.props.column.source ?? {} + return this.def.source ?? {} } @computed private get title(): string { - return this.props.column.nonEmptyDisplayName + return this.column.nonEmptyDisplayName } @computed private get editUrl(): string | undefined { @@ -237,23 +327,23 @@ export class Source extends React.Component<{ return uniq(excludeUndefined(this.def.origins.map((o) => o.producer))) } - @computed private get attributions(): string | undefined { + @computed get attributions(): string | undefined { const attributionFragments = getAttributionFragmentsFromVariable(this.def) ?? this.producers if (attributionFragments.length === 0) return undefined return attributionFragments.join(", ") } - @computed private get lastUpdated(): string | undefined { + @computed get lastUpdated(): string | undefined { return getLastUpdatedFromVariable(this.def) } - @computed private get nextUpdate(): string | undefined { + @computed get nextUpdate(): string | undefined { return getNextUpdateFromVariable(this.def) } - @computed private get unit(): string | undefined { - return this.def.display?.unit ?? this.def.unit + @computed get unit(): string | undefined { + return this.column.unit } @computed private get datapageHasFAQSection(): boolean { @@ -273,17 +363,35 @@ export class Source extends React.Component<{ return prepareSourcesForDisplay(this.def) } + @computed protected get sourcesSectionHeading(): string { + return "The data of this indicator is based on the following sources:" + } + + @computed private get hideSourcesForDisplay(): boolean { + // the indictaor with id = 123 is the "Continent" variable curated by OWID. + // it's used in many charts but doesn't come with useful source information. + // that's why we hide the sources section for this indicator for now, + // but we might decide to show it in the future + return this.def.owidVariableId === 123 + } + + protected renderTitle(): JSX.Element { + return ( +

    + {this.title}{" "} + {this.editUrl && ( + + + + )} +

    + ) + } + render(): JSX.Element { return (
    -

    - {this.title}{" "} - {this.editUrl && ( - - - - )} -

    + {this.renderTitle()} {this.def.descriptionShort && (

    {this.def.descriptionShort}

    )} @@ -295,16 +403,8 @@ export class Source extends React.Component<{ nextUpdate={this.nextUpdate} unit={this.unit} link={this.source.link} - unitConversionFactor={ - this.props.column.unitConversionFactor - } + unitConversionFactor={this.column.unitConversionFactor} isEmbeddedInADataPage={this.props.isEmbeddedInADataPage} - hideTopBorder={!this.def.descriptionShort} - hideBottomBorder={ - this.showDescriptions && - (!this.def.descriptionKey || - this.def.descriptionKey.length === 0) - } /> {this.showDescriptions && ( )} - {this.sourcesForDisplay && + {!this.hideSourcesForDisplay && + this.sourcesForDisplay && this.sourcesForDisplay.length > 0 && ( <>

    - This data is based on the following sources: + {this.sourcesSectionHeading}

    )} -

    +

    How we process data at Our World in Data:

    About this data

    + } + + @computed get sourcesSectionHeading(): string { + return "This data is based on the following sources" + } +} + +const visibleSourceInformation = (source: Source): string => { + return [ + // used in key data table + source.attributions, + source.def.timespan, + source.lastUpdated, + source.nextUpdate, + source.unit, + source.def.sourceLink, + source.column.unitConversionFactor, + + // descriptions + source.def.descriptionShort, + source.def.descriptionKey, + source.def.descriptionFromProducer, + source.def.additionalInfo, + + // old source information + source.def.sourceName, + source.def.dataPublishedBy, + source.def.retrievedDate, + source.def.description, + + // origins + source.def.origins + ?.map((origin) => [ + origin.producer, + origin.title, + origin.description, + origin.dateAccessed, + origin.urlMain, + origin.citationFull, + ]) + .join("-"), + ].join("-") +} + const measureTabWidth = (label: string): number => { - const maxWidth = 240 - const computedWidth = - 2 * 16 + // padding - Bounds.forText(label, { fontSize: 13 }).width + + return ( + 2 * TAB_PADDING + + Bounds.forText(label, { fontSize: TAB_FONT_SIZE }).width + 2 // border - return Math.min(maxWidth, computedWidth) + ) } diff --git a/packages/@ourworldindata/grapher/src/tabs/ExpandableTabs.scss b/packages/@ourworldindata/grapher/src/tabs/ExpandableTabs.scss new file mode 100644 index 00000000000..5bd9f9d9097 --- /dev/null +++ b/packages/@ourworldindata/grapher/src/tabs/ExpandableTabs.scss @@ -0,0 +1,17 @@ +.ExpandableTabs:not(.ExpandableTabs--expanded) { + white-space: nowrap; +} + +.ExpandableTabs__button { + color: $dark-text; + border-color: #f2f2f2; + background: #f2f2f2; + + &:hover { + background: #e7e7e7; + } + + svg { + margin-right: 6px; + } +} diff --git a/packages/@ourworldindata/components/src/Tabs/ExpandableTabs.tsx b/packages/@ourworldindata/grapher/src/tabs/ExpandableTabs.tsx similarity index 67% rename from packages/@ourworldindata/components/src/Tabs/ExpandableTabs.tsx rename to packages/@ourworldindata/grapher/src/tabs/ExpandableTabs.tsx index e4772bb1f24..fefc229c848 100644 --- a/packages/@ourworldindata/components/src/Tabs/ExpandableTabs.tsx +++ b/packages/@ourworldindata/grapher/src/tabs/ExpandableTabs.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js" import { faPlus, faMinus } from "@fortawesome/free-solid-svg-icons" +import cx from "classnames" import { Tabs } from "./Tabs" export const ExpandableTabs = ({ @@ -9,12 +10,14 @@ export const ExpandableTabs = ({ setActiveIndex, isExpandedDefault = false, getVisibleLabels = (labels: string[]) => labels.slice(0, 3), + maxTabWidth = 240, }: { labels: string[] activeIndex: number setActiveIndex: (index: number) => void isExpandedDefault?: boolean getVisibleLabels?: (tabLabels: string[]) => string[] + maxTabWidth?: number | null // if null, don't clip labels }) => { const [isExpanded, setExpanded] = useState(isExpandedDefault) @@ -32,11 +35,18 @@ export const ExpandableTabs = ({ ) return ( - +
    + +
    ) } diff --git a/packages/@ourworldindata/grapher/src/tabs/Tabs.scss b/packages/@ourworldindata/grapher/src/tabs/Tabs.scss new file mode 100644 index 00000000000..a97ffa470a7 --- /dev/null +++ b/packages/@ourworldindata/grapher/src/tabs/Tabs.scss @@ -0,0 +1,38 @@ +.Tabs { + margin: 16px 0; + + &--horizontal-scroll { + overflow-x: auto; + white-space: nowrap; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + } + + &::-webkit-scrollbar { + display: none; + } + + &__tab { + margin: 0 8px 8px 0; + padding: 8px 16px; + border: 1px solid $blue-10; + background: #fff; + color: $light-text; + font-size: 0.8125rem; + font-weight: 500; + letter-spacing: 0.01em; + white-space: nowrap; + + &:hover { + cursor: pointer; + background-color: $gray-10; + border-color: $gray-10; + } + + &[aria-selected="true"] { + background: $accent-pale-blue; + border-color: $accent-pale-blue; + color: $blue-90; + } + } +} diff --git a/packages/@ourworldindata/components/src/Tabs/Tabs.tsx b/packages/@ourworldindata/grapher/src/tabs/Tabs.tsx similarity index 83% rename from packages/@ourworldindata/components/src/Tabs/Tabs.tsx rename to packages/@ourworldindata/grapher/src/tabs/Tabs.tsx index 162c38ad3bb..a43388be1fe 100644 --- a/packages/@ourworldindata/components/src/Tabs/Tabs.tsx +++ b/packages/@ourworldindata/grapher/src/tabs/Tabs.tsx @@ -6,12 +6,14 @@ export const Tabs = ({ activeIndex, setActiveIndex, horizontalScroll = false, + maxTabWidth = 240, slot, }: { labels: string[] activeIndex: number setActiveIndex: (label: number) => void horizontalScroll?: boolean + maxTabWidth?: number | null // if null, don't clip labels slot?: JSX.Element }) => { const container = useRef(null) @@ -45,6 +47,15 @@ export const Tabs = ({ if (activeTabElement) activeTabElement.focus() } + let style: React.CSSProperties | undefined = undefined + if (maxTabWidth !== null && Number.isFinite(maxTabWidth)) { + style = { + maxWidth: maxTabWidth, + textOverflow: "ellipsis", + overflow: "hidden", + } + } + return (
    { let label = origin.producer ?? "" if (origin.title && origin.title !== label) { - label += " - " + origin.title + label += " – " + origin.title } return { label, description: origin.description, retrievedOn: origin.dateAccessed, - retrievedFrom: origin.urlMain ? [origin.urlMain] : undefined, + retrievedFrom: origin.urlMain, citation: origin.citationFull, } } @@ -171,7 +171,7 @@ export const prepareSourcesForDisplay = ( description, dataPublishedBy: source?.dataPublishedBy, retrievedOn: source?.retrievedDate, - retrievedFrom: splitSourceTextIntoFragments(source?.link), + retrievedFrom: source?.link, }) } @@ -183,3 +183,12 @@ export const prepareSourcesForDisplay = ( return sourcesForDisplay } + +export const formatSourceDate = ( + date: string | undefined, + format: string +): string | null => { + const parsedDate = dayjs(date ?? "", ["YYYY-MM-DD", "DD/MM/YYYY"]) + if (!parsedDate.isValid()) return date || null + return parsedDate.format(format) +} diff --git a/packages/@ourworldindata/utils/src/owidTypes.ts b/packages/@ourworldindata/utils/src/owidTypes.ts index bce0c0eacaa..1293bc8b035 100644 --- a/packages/@ourworldindata/utils/src/owidTypes.ts +++ b/packages/@ourworldindata/utils/src/owidTypes.ts @@ -1737,6 +1737,6 @@ export interface DisplaySource { description?: string dataPublishedBy?: string retrievedOn?: string - retrievedFrom?: string[] + retrievedFrom?: string citation?: string } diff --git a/site/DataPageContent.scss b/site/DataPageContent.scss index 14e761e3cde..a79b3e8df38 100644 --- a/site/DataPageContent.scss +++ b/site/DataPageContent.scss @@ -171,14 +171,14 @@ ul { padding-left: 1em; - } - li { - @include body-2-regular; - margin-bottom: 1em; + li { + @include body-2-regular; + margin-bottom: 1em; - &:last-child { - margin-bottom: 0; + &:last-child { + margin-bottom: 0; + } } } } @@ -192,6 +192,25 @@ } } + .key-info__expandable-descriptions { + p { + margin: 8px 0; + + &:first-of-type { + margin-top: 0; + } + + &:last-of-type { + margin-bottom: 0; + } + } + + a { + @include owid-link-60; + color: inherit; + } + } + .key-info__key-description + .key-info__expandable-descriptions { margin-top: 24px; } @@ -424,7 +443,7 @@ .reuse__list-item, .citation__paragraph { @include body-3-medium; - margin: 0 0 16px; + margin: 0 0 8px; } .citation__how-to-header { diff --git a/site/DataPageV2Content.tsx b/site/DataPageV2Content.tsx index 6fd4d8b0d79..64c288acb0a 100644 --- a/site/DataPageV2Content.tsx +++ b/site/DataPageV2Content.tsx @@ -6,6 +6,7 @@ import { CodeSnippet, REUSE_THIS_WORK_SECTION_ID, IndicatorSources, + DATAPAGE_ABOUT_THIS_DATA_SECTION_ID, DATAPAGE_SOURCES_AND_PROCESSING_SECTION_ID, IndicatorProcessing, SimpleMarkdownText, @@ -265,7 +266,7 @@ export const DataPageV2Content = ({ datapageData.source?.additionalInfo ? ( <>

    What you should know about this @@ -365,7 +366,7 @@ export const DataPageV2Content = ({ <>

    About this data

    @@ -678,6 +679,9 @@ export const DataPageV2Content = ({ citationShort } theme="light" + useMarkdown={ + true + } /> )} @@ -694,6 +698,9 @@ export const DataPageV2Content = ({ citationLong } theme="light" + useMarkdown={ + true + } /> )} @@ -716,6 +723,7 @@ export const DataPageV2Content = ({
    )}