From 445e50fb982386b4432efbd5607622a917cd731b Mon Sep 17 00:00:00 2001 From: Ike Saunders Date: Fri, 24 Jan 2025 17:48:01 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20add=20support=20for=20sub-areas=20i?= =?UTF-8?q?n=20tag=20graph?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/db.ts | 19 +++++++++- packages/@ourworldindata/utils/src/Util.ts | 40 ++++++++++++++++++--- packages/@ourworldindata/utils/src/index.ts | 3 +- site/SiteMobileArea.tsx | 4 +-- site/SiteNavigation.tsx | 4 +-- site/SiteNavigationTopics.tsx | 6 ++-- site/gdocs/pages/Homepage.tsx | 29 +++++++++------ site/gdocs/utils.ts | 11 ------ 8 files changed, 81 insertions(+), 35 deletions(-) diff --git a/db/db.ts b/db/db.ts index 65c4e2d390..96931920b1 100644 --- a/db/db.ts +++ b/db/db.ts @@ -861,6 +861,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 { @@ -870,7 +884,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 }, diff --git a/packages/@ourworldindata/utils/src/Util.ts b/packages/@ourworldindata/utils/src/Util.ts index bd11023330..8ea35a3058 100644 --- a/packages/@ourworldindata/utils/src/Util.ts +++ b/packages/@ourworldindata/utils/src/Util.ts @@ -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( diff --git a/packages/@ourworldindata/utils/src/index.ts b/packages/@ourworldindata/utils/src/index.ts index 12a68e304b..13fe81f931 100644 --- a/packages/@ourworldindata/utils/src/index.ts +++ b/packages/@ourworldindata/utils/src/index.ts @@ -124,7 +124,8 @@ export { commafyNumber, isFiniteWithGuard, createTagGraph, - getAllTopicsInArea, + getAllChildrenOfArea, + flattenNonTopicNodes, formatInlineList, lazy, getParentVariableIdFromChartConfig, diff --git a/site/SiteMobileArea.tsx b/site/SiteMobileArea.tsx index acb809b7c9..a98fdddf75 100644 --- a/site/SiteMobileArea.tsx +++ b/site/SiteMobileArea.tsx @@ -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 = ({ @@ -28,7 +28,7 @@ export const SiteMobileArea = ({ onToggle={() => toggleArea(area)} dropdown={ diff --git a/site/gdocs/pages/Homepage.tsx b/site/gdocs/pages/Homepage.tsx index 95b30a267c..42a68c70c2 100644 --- a/site/gdocs/pages/Homepage.tsx +++ b/site/gdocs/pages/Homepage.tsx @@ -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 { @@ -62,7 +62,7 @@ const AllTopicsSection = () => { // We have to flatten the areas because we can't nest