Skip to content

Commit

Permalink
Topics tab refactoring (#4826)
Browse files Browse the repository at this point in the history
Refactor the Topics menu to use a shared TopicItem for each of the
buttons.

Only show the Topic button for categories for which we know we have
data. We add a second call for the OVERVIEW chart config even when we're
on a different category because we need it to identify which topics we
have.

France: https://screenshot.googleplex.com/X4McSQ5qax7f9w7
USA: https://screenshot.googleplex.com/BUJQV8rxhfvvT2e
  • Loading branch information
gmechali authored Jan 3, 2025
1 parent bb7b11f commit dba2831
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 88 deletions.
13 changes: 11 additions & 2 deletions server/routes/dev_place/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ def place_charts(place_dcid: str):
full_chart_config = copy.deepcopy(current_app.config['CHART_CONFIG'])

# Filter chart config by category
overview_chart_config = [c for c in full_chart_config if c.get("isOverview")]
if place_category == OVERVIEW_CATEGORY:
chart_config = [c for c in full_chart_config if c.get("isOverview")]
chart_config = overview_chart_config
else:
chart_config = [
c for c in full_chart_config if c["category"] == place_category
Expand All @@ -103,6 +104,14 @@ def place_charts(place_dcid: str):
place_dcid=place_dcid,
child_place_type=child_place_type)

# Always execute the call for the overview category to fetch the valid categories.
filtered_chart_config_for_category = (
filtered_chart_config if place_category == OVERVIEW_CATEGORY else
place_utils.filter_chart_config_by_place_dcid(
chart_config=overview_chart_config,
place_dcid=place_dcid,
child_place_type=child_place_type))

# Translate chart config titles
translated_chart_config = place_utils.translate_chart_config(
filtered_chart_config)
Expand All @@ -113,7 +122,7 @@ def place_charts(place_dcid: str):

# Translate category strings
translated_category_strings = place_utils.get_translated_category_strings(
filtered_chart_config)
filtered_chart_config_for_category)

response = PlaceChartsApiResponse(
charts=charts,
Expand Down
3 changes: 3 additions & 0 deletions server/routes/dev_place/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
from flask_babel import gettext

from server.lib import fetch
from server.lib.cache import cache
from server.lib.i18n import DEFAULT_LOCALE
from server.routes import TIMEOUT
from server.routes.dev_place.types import Chart
from server.routes.dev_place.types import Place
import server.routes.shared_api.place as place_api
Expand Down Expand Up @@ -128,6 +130,7 @@ def get_place_type_with_parent_places_links(dcid: str) -> str:
return ''


@cache.memoize(timeout=TIMEOUT)
def filter_chart_config_by_place_dcid(chart_config: List[Dict],
place_dcid: str,
child_place_type=str):
Expand Down
168 changes: 82 additions & 86 deletions static/js/place/dev_place_main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,56 @@ const PlaceHeader = (props: {
);
};

/**
* Component that renders the individual topic navigation buttons.
* Shows buttons for the topics created and highlights the currently selected category.
*
* @param props.category The category for the current button
* @param props.selectedCategory The currently selected category
* @param props.forceDevPlaces Whether the flag to force dev places should be propagated.
* @param props.place The place object containing the DCID for generating URLs
* @returns Button component for the current topic
*/
const TopicItem = (props: {
category: string;
selectedCategory: string;
forceDevPlaces: boolean;
place: NamedTypedPlace;
}): React.JSX.Element => {
const { category, selectedCategory, forceDevPlaces, place } = props;

const createHref = (
category: string,
forceDevPlaces: boolean,
place: NamedTypedPlace
): string => {
const href = `/place/${place.dcid}`;
const params = new URLSearchParams();
const isOverview = category === "Overview";

if (!isOverview) {
params.set("category", category);
}
if (forceDevPlaces) {
params.set("force_dev_places", "true");
}
return params.size > 0 ? `${href}?${params.toString()}` : href;
};

return (
<div className="item-list-item">
<a
href={createHref(category, forceDevPlaces, place)}
className={`item-list-text + ${
selectedCategory === category ? " selected" : ""
}`}
>
{category}
</a>
</div>
);
};

/**
* Component that renders the topic navigation tabs.
* Shows tabs for Overview and different categories like Economics, Health, etc.
Expand All @@ -194,103 +244,34 @@ const PlaceHeader = (props: {
* @returns Navigation component with topic tabs
*/
const PlaceTopicTabs = ({
topics,
forceDevPlaces,
category,
place,
}: {
topics: string[];
forceDevPlaces: boolean;
category: string;
place: NamedTypedPlace;
}): React.JSX.Element => {
if (!topics || topics.length == 0) {
return <></>;
}

return (
<div className="explore-topics-box">
<span className="explore-relevant-topics">Relevant topics</span>
<div className="item-list-container">
<div className="item-list-inner">
<div className="item-list-item">
<a
className={`item-list-text ${
category === "Overview" ? "selected" : ""
}`}
href={`/place/${place.dcid}${
forceDevPlaces ? "?force_dev_places=true" : ""
}`}
>
Overview
</a>
</div>
<div className="item-list-item">
<a
className={`item-list-text ${
category === "Economics" ? "selected" : ""
}`}
href={`/place/${place.dcid}?category=Economics${
forceDevPlaces ? "&force_dev_places=true" : ""
}`}
>
Economics
</a>
</div>
<div className="item-list-item">
<a
className={`item-list-text ${
category === "Health" ? "selected" : ""
}`}
href={`/place/${place.dcid}?category=Health${
forceDevPlaces ? "&force_dev_places=true" : ""
}`}
>
Health
</a>
</div>
<div className="item-list-item">
<a
className={`item-list-text ${
category === "Equity" ? "selected" : ""
}`}
href={`/place/${place.dcid}?category=Equity${
forceDevPlaces ? "&force_dev_places=true" : ""
}`}
>
Equity
</a>
</div>
<div className="item-list-item">
<a
className={`item-list-text ${
category === "Demographics" ? "selected" : ""
}`}
href={`/place/${place.dcid}?category=Demographics${
forceDevPlaces ? "&force_dev_places=true" : ""
}`}
>
Demographics
</a>
</div>
<div className="item-list-item">
<a
className={`item-list-text ${
category === "Environment" ? "selected" : ""
}`}
href={`/place/${place.dcid}?category=Environment${
forceDevPlaces ? "&force_dev_places=true" : ""
}`}
>
Environment
</a>
</div>
<div className="item-list-item">
<a
className={`item-list-text ${
category === "Energy" ? "selected" : ""
}`}
href={`/place/${place.dcid}?category=Energy${
forceDevPlaces ? "&force_dev_places=true" : ""
}`}
>
Energy
</a>
</div>
{topics.map((topic) => (
<TopicItem
key={topic}
category={topic}
selectedCategory={category}
forceDevPlaces={forceDevPlaces}
place={place}
/>
))}
</div>
</div>
</div>
Expand Down Expand Up @@ -540,6 +521,7 @@ export const DevPlaceMain = (): React.JSX.Element => {
const [childPlaces, setChildPlaces] = useState<NamedTypedPlace[]>([]);
const [parentPlaces, setParentPlaces] = useState<NamedTypedPlace[]>([]);
const [pageConfig, setPageConfig] = useState<SubjectPageConfig>();
const [categories, setCategories] = useState<string[]>();

const urlParams = new URLSearchParams(window.location.search);
const category = urlParams.get("category") || "Overview";
Expand Down Expand Up @@ -586,16 +568,29 @@ export const DevPlaceMain = (): React.JSX.Element => {

setPlaceChartsApiResponse(placeChartsApiResponse);
setRelatedPlacesApiResponse(relatedPlacesApiResponse);
const pageConfig = placeChartsApiResponsesToPageConfig(
const config = placeChartsApiResponsesToPageConfig(
placeChartsApiResponse
);
setChildPlaceType(relatedPlacesApiResponse.childPlaceType);
setChildPlaces(relatedPlacesApiResponse.childPlaces);
setParentPlaces(relatedPlacesApiResponse.parentPlaces);
setPageConfig(pageConfig);
setPageConfig(config);
})();
}, [place]);

useEffect(() => {
if (placeChartsApiResponse && placeChartsApiResponse.charts) {
// TODO(gmechali): Refactor this to use the translations correctly.
// Move overview to be added in the response with translations. Use the
// translation in the tabs, but the english version in the URL.
setCategories(
["Overview"].concat(
Object.values(placeChartsApiResponse.translatedCategoryStrings)
)
);
}
}, [placeChartsApiResponse, setPlaceChartsApiResponse, setCategories]);

if (!place) {
return <div>Loading...</div>;
}
Expand All @@ -607,6 +602,7 @@ export const DevPlaceMain = (): React.JSX.Element => {
placeSubheader={placeSubheader}
/>
<PlaceTopicTabs
topics={categories}
category={category}
place={place}
forceDevPlaces={forceDevPlaces}
Expand Down

0 comments on commit dba2831

Please sign in to comment.