Skip to content

Commit

Permalink
✨ add support for sub-areas in tag graph
Browse files Browse the repository at this point in the history
  • Loading branch information
ikesau committed Jan 31, 2025
1 parent 232aeef commit ad88120
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 38 deletions.
19 changes: 18 additions & 1 deletion db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,20 @@ export function getImageUsage(trx: KnexReadonlyTransaction): Promise<
)
}

// A topic is any tag that has a slug.
// We want to keep tags that have topic children (i.e. areas and sub-areas) but not leaf nodes that aren't topics
function checkDoesFlatTagGraphNodeHaveAnyTopicChildren(
node: FlatTagGraphNode,
flatTagGraph: FlatTagGraph
): boolean {
if (node.isTopic) return true
const children = flatTagGraph[node.childId]
if (!children) return false
return children.some((child) =>
checkDoesFlatTagGraphNodeHaveAnyTopicChildren(child, flatTagGraph)
)
}

export async function generateTopicTagGraph(
knex: KnexReadonlyTransaction
): Promise<TagGraphRoot> {
Expand All @@ -896,7 +910,10 @@ export async function generateTopicTagGraph(
(acc: FlatTagGraph, [parentId, children]) => {
acc[Number(parentId)] = children.filter((child) => {
if (child.parentId === __rootId) return true
return child.isTopic
return checkDoesFlatTagGraphNodeHaveAnyTopicChildren(
child,
parents
)
})
return acc
},
Expand Down
40 changes: 35 additions & 5 deletions packages/@ourworldindata/utils/src/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1981,11 +1981,41 @@ export function createTagGraph(
return recursivelySetChildren(tagGraph) as TagGraphRoot
}

export const getAllTopicsInArea = (area: TagGraphNode): TagGraphNode[] => {
return [
...area.children,
...area.children.flatMap((child) => getAllTopicsInArea(child)),
]
export const getAllChildrenOfArea = (area: TagGraphNode): TagGraphNode[] => {
const children = []
for (const child of area.children) {
children.push(child)
children.push(...getAllChildrenOfArea(child))
}
return children
}

/**
* topicTagGraph.json includes sub-areas: non-topic tags that have topic children
* e.g. "Health" is an area, "Life & Death" is a sub-area, and "Life Expectancy" is a topic,
* This function flattens the graph by removing sub-areas and moving their children up to the area level
* e.g. "Life Expectancy" becomes a child of "Health" instead of "Life & Death"
* We need this because the all-topics section on the homepage renders sub-areas, but the site nav doesn't
* Note that topics can have children (e.g. "Air Pollution" is a topic, and "Indoor Air Pollution" is a sub-topic)
* Such cases are not flattened here, but in the frontend with getAllChildrenOfArea
*/
export function flattenNonTopicNodes(tagGraph: TagGraphRoot): TagGraphRoot {
const flattenNodes = (nodes: TagGraphNode[]): TagGraphNode[] =>
nodes.flatMap((node) =>
!node.isTopic && node.children.length
? flattenNodes(node.children)
: node.isTopic
? [{ ...node, children: flattenNodes(node.children) }]
: []
)

return {
...tagGraph,
children: tagGraph.children.map((area) => ({
...area,
children: flattenNodes(area.children),
})),
}
}

export function formatInlineList(
Expand Down
3 changes: 2 additions & 1 deletion packages/@ourworldindata/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ export {
commafyNumber,
isFiniteWithGuard,
createTagGraph,
getAllTopicsInArea,
getAllChildrenOfArea,
flattenNonTopicNodes,
formatInlineList,
lazy,
getParentVariableIdFromChartConfig,
Expand Down
2 changes: 1 addition & 1 deletion site/SiteConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
faLinkedin,
faBluesky,
} from "@fortawesome/free-brands-svg-icons"
import { CategoryWithEntries, SubNavId } from "@ourworldindata/types"
import { SubNavId } from "@ourworldindata/types"

// See https://cdnjs.cloudflare.com/polyfill/ for a list of all supported features
const polyfillFeatures = [
Expand Down
4 changes: 2 additions & 2 deletions site/SiteMobileArea.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useRef } from "react"
import { SiteNavigationTopic } from "./SiteNavigationTopic.js"
import { TagGraphNode, getAllTopicsInArea } from "@ourworldindata/utils"
import { TagGraphNode, getAllChildrenOfArea } from "@ourworldindata/utils"
import { SiteNavigationToggle } from "./SiteNavigationToggle.js"

export const SiteMobileArea = ({
Expand Down Expand Up @@ -28,7 +28,7 @@ export const SiteMobileArea = ({
onToggle={() => toggleArea(area)}
dropdown={
<ul>
{getAllTopicsInArea(area).map((topic) => (
{getAllChildrenOfArea(area).map((topic) => (
<SiteNavigationTopic
key={topic.slug}
topic={topic}
Expand Down
4 changes: 2 additions & 2 deletions site/SiteNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js"
import { SiteNavigationTopics } from "./SiteNavigationTopics.js"
import { SiteLogos } from "./SiteLogos.js"
import { SiteAbout } from "./SiteAbout.js"
import { TagGraphRoot } from "@ourworldindata/utils"
import { flattenNonTopicNodes, TagGraphRoot } from "@ourworldindata/utils"
import { SiteResources } from "./SiteResources.js"
import { SiteSearchNavigation } from "./SiteSearchNavigation.js"
import { SiteMobileMenu } from "./SiteMobileMenu.js"
Expand Down Expand Up @@ -42,7 +42,7 @@ export const SiteNavigation = ({
const fetchTagGraph = async () => {
const response = await fetch("/topicTagGraph.json")
const tagGraph = await response.json()
setTagGraph(tagGraph)
setTagGraph(flattenNonTopicNodes(tagGraph))
}
if (!tagGraph) fetchTagGraph().catch(console.error)
}, [tagGraph, setTagGraph])
Expand Down
6 changes: 3 additions & 3 deletions site/SiteNavigationTopics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useLayoutEffect } from "react"
import {
TagGraphNode,
TagGraphRoot,
getAllTopicsInArea,
getAllChildrenOfArea,
} from "@ourworldindata/utils"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js"
import { faArrowRight } from "@fortawesome/free-solid-svg-icons"
Expand All @@ -28,7 +28,7 @@ export const SiteNavigationTopics = ({
// using useLayoutEffect to avoid a flash of the wrong number of columns when switching categories
useLayoutEffect(() => {
if (activeArea) {
const topics = getAllTopicsInArea(activeArea)
const topics = getAllChildrenOfArea(activeArea)
const numColumns = Math.ceil(topics.length / 10)
setNumTopicColumns(numColumns)
}
Expand Down Expand Up @@ -79,7 +79,7 @@ export const SiteNavigationTopics = ({
})}
onClick={stopPropagation}
>
{getAllTopicsInArea(activeArea).map((topic) => (
{getAllChildrenOfArea(activeArea).map((topic) => (
<SiteNavigationTopic key={topic.slug} topic={topic} />
))}
</ul>
Expand Down
29 changes: 19 additions & 10 deletions site/gdocs/pages/Homepage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { NewsletterSubscriptionForm } from "../../NewsletterSubscription.js"
import { ArticleBlocks } from "../components/ArticleBlocks.js"
import { OwidGdocHomepageContent } from "@ourworldindata/types"
import { RSS_FEEDS, SOCIALS } from "../../SiteConstants.js"
import { getAllTopicsInArea } from "@ourworldindata/utils"
import { getAllChildrenOfArea } from "@ourworldindata/utils"
import { AttachmentsContext } from "../AttachmentsContext.js"

export interface HomepageProps {
Expand Down Expand Up @@ -62,7 +62,7 @@ const AllTopicsSection = () => {
// We have to flatten the areas because we can't nest <ul> elements and have them render correctly
const flattenedAreas = tagGraph.children.map((area) => ({
...area,
children: getAllTopicsInArea(area),
children: getAllChildrenOfArea(area),
}))

return (
Expand All @@ -83,14 +83,23 @@ const AllTopicsSection = () => {
{area.name}
</h2>
<ul className="homepage-topic__topic-list display-3-regular">
{area.children.map(({ slug, name }) => (
<li
className="homepage-topic__topic-entry"
key={`topic-entry-${slug}`}
>
<a href={`/${slug}`}>{name}</a>
</li>
))}
{area.children.map(({ slug, name }) =>
slug ? (
<li
className="homepage-topic__topic-entry"
key={`topic-entry-${slug}`}
>
<a href={`/${slug}`}>{name}</a>
</li>
) : (
<li
className="homepage-topic__subtopic"
key={`subarea-${name}`}
>
{name}
</li>
)
)}
</ul>
</section>
))}
Expand Down
13 changes: 0 additions & 13 deletions site/gdocs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import {
OwidGdocPostContent,
OwidGdocMinimalPostInterface,
LinkedIndicator,
CategoryWithEntries,
EntryMeta,
OwidGdocDataInsightContent,
OwidGdocLinkType,
SubNavId,
Expand Down Expand Up @@ -172,17 +170,6 @@ export function getShortPageCitation(
})} (${publishedAt?.getFullYear()}) - “${title}”`
}

export const allTopicsInCategory = (
category: CategoryWithEntries
): EntryMeta[] => {
return [
...category.entries,
...(category.subcategories ?? []).flatMap(
(subcategory) => subcategory.entries
),
]
}

export const getSubnavItem = (
id: string | undefined,
subnavItems: SubnavItem[]
Expand Down

0 comments on commit ad88120

Please sign in to comment.