diff --git a/static/css/shared/_tiles.scss b/static/css/shared/_tiles.scss index abf0527967..3c64e29d37 100644 --- a/static/css/shared/_tiles.scss +++ b/static/css/shared/_tiles.scss @@ -123,6 +123,12 @@ $box-shadow-8dp: 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .ranking-unit-container { padding: 0 0.5rem; + display: flex; + flex-direction: column; + + .ranking-list { + flex-grow: 1; + } } /** diff --git a/static/js/components/tiles/chart_footer.tsx b/static/js/components/tiles/chart_footer.tsx new file mode 100644 index 0000000000..e5538a9e67 --- /dev/null +++ b/static/js/components/tiles/chart_footer.tsx @@ -0,0 +1,86 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Footer for charts in tiles. + */ + +import _ from "lodash"; +import React from "react"; + +import { NL_SOURCE_REPLACEMENTS } from "../../constants/app/nl_interface_constants"; +import { urlToDomain } from "../../shared/util"; +import { isNlInterface } from "../../utils/nl_interface_utils"; + +interface ChartFooterPropType { + // set of full urls of sources of the data in the chart + sources: Set; + handleEmbed?: () => void; +} + +/** + * Gets the JSX element for displaying a list of sources. + */ +function getSourcesJsx(sources: Set): JSX.Element[] { + if (!sources) { + return null; + } + + const sourceList: string[] = Array.from(sources); + const seenSourceDomains = new Set(); + const sourcesJsx = sourceList.map((source, index) => { + // HACK for updating source for NL interface + let processedSource = source; + if (isNlInterface()) { + processedSource = NL_SOURCE_REPLACEMENTS[source] || source; + } + const domain = urlToDomain(processedSource); + if (seenSourceDomains.has(domain)) { + return null; + } + seenSourceDomains.add(domain); + return ( + + {index > 0 ? ", " : ""} + {domain} + + ); + }); + return sourcesJsx; +} + +export function ChartFooter(props: ChartFooterPropType): JSX.Element { + return ( + + ); +} diff --git a/static/js/components/tiles/chart_tile.tsx b/static/js/components/tiles/chart_tile.tsx index 58b784f551..65a66fb97d 100644 --- a/static/js/components/tiles/chart_tile.tsx +++ b/static/js/components/tiles/chart_tile.tsx @@ -22,11 +22,8 @@ import _ from "lodash"; import React, { useRef } from "react"; import { ChartEmbed } from "../../place/chart_embed"; -import { - formatString, - getSourcesJsx, - ReplacementStrings, -} from "../../utils/tile_utils"; +import { formatString, ReplacementStrings } from "../../utils/tile_utils"; +import { ChartFooter } from "./chart_footer"; interface ChartTileContainerProp { title: string; @@ -55,26 +52,10 @@ export function ChartTileContainer(props: ChartTileContainerProp): JSX.Element { > {title &&

{title}

} {props.children} - + {props.allowEmbed && } ); @@ -98,7 +79,7 @@ export function ChartTileContainer(props: ChartTileContainerProp): JSX.Element { embedModalElement.current.show( svgXml, props.getDataCsv ? props.getDataCsv() : "", - svgWidth, + svgWidth || containerRef.current.offsetWidth, svgHeight, chartTitle, "", diff --git a/static/js/components/tiles/disaster_event_map_tile.tsx b/static/js/components/tiles/disaster_event_map_tile.tsx index f9935bac4d..8637ba97e7 100644 --- a/static/js/components/tiles/disaster_event_map_tile.tsx +++ b/static/js/components/tiles/disaster_event_map_tile.tsx @@ -19,7 +19,6 @@ */ import * as d3 from "d3"; -import { geoJson } from "leaflet"; import _ from "lodash"; import React, { useContext, useEffect, useRef, useState } from "react"; diff --git a/static/js/components/tiles/ranking_tile.tsx b/static/js/components/tiles/ranking_tile.tsx index 85fb60e14a..e77bf955b8 100644 --- a/static/js/components/tiles/ranking_tile.tsx +++ b/static/js/components/tiles/ranking_tile.tsx @@ -34,11 +34,11 @@ import { getPlaceDisplayNames, getPlaceNames } from "../../utils/place_utils"; import { formatNumber } from "../../utils/string_utils"; import { formatString, - getSourcesJsx, getStatVarName, getUnitString, } from "../../utils/tile_utils"; import { RankingUnit } from "../ranking_unit"; +import { ChartFooter } from "./chart_footer"; const RANKING_COUNT = 5; @@ -81,7 +81,6 @@ export function RankingTile(props: RankingTilePropType): JSX.Element { const rankingCount = props.rankingMetadata.rankingCount || RANKING_COUNT; const isMultiColumn = props.rankingMetadata.showMultiColumn; const svNames = props.statVarSpec.map((sv) => sv.name); - // TODO: Make use of ChartTileContainer for the footer section. return (
- + handleEmbed(points.reverse())} + />
)} {props.rankingMetadata.showLowest && ( @@ -167,21 +155,10 @@ export function RankingTile(props: RankingTilePropType): JSX.Element { svNames={isMultiColumn ? svNames : undefined} formatNumberFn={formatNumber} /> - + handleEmbed(points)} + /> )} @@ -191,11 +168,7 @@ export function RankingTile(props: RankingTilePropType): JSX.Element { ); - function handleEmbed( - e: React.MouseEvent, - rankingPoints: RankingPoint[] - ): void { - e.preventDefault(); + function handleEmbed(rankingPoints: RankingPoint[]): void { embedModalElement.current.show( "", rankingPointsToCsv(rankingPoints), diff --git a/static/js/components/tiles/top_event_tile.tsx b/static/js/components/tiles/top_event_tile.tsx index 7d02f3d33e..2f7c395ad5 100644 --- a/static/js/components/tiles/top_event_tile.tsx +++ b/static/js/components/tiles/top_event_tile.tsx @@ -37,6 +37,7 @@ import { stringifyFn } from "../../utils/axios"; import { rankingPointsToCsv } from "../../utils/chart_csv_utils"; import { getPlaceNames } from "../../utils/place_utils"; import { formatNumber } from "../../utils/string_utils"; +import { ChartFooter } from "./chart_footer"; const RANKING_COUNT = 10; const MIN_PERCENT_PLACE_NAMES = 0.4; @@ -92,6 +93,10 @@ export function TopEventTile(props: TopEventTilePropType): JSX.Element { MIN_PERCENT_PLACE_NAMES; const showNameColumn = topEvents.filter((event) => !isUnnamedEvent(event.placeName)).length > 0; + const sources = new Set(); + Object.values(props.disasterEventData.provenanceInfo).forEach((provInfo) => { + sources.add(provInfo.provenanceUrl); + }); return ( )} @@ -331,11 +330,7 @@ export function TopEventTile(props: TopEventTilePropType): JSX.Element { return filteredPoints.slice(0, RANKING_COUNT); } - function handleEmbed( - e: React.MouseEvent, - topEvents: DisasterEventPoint[] - ): void { - e.preventDefault(); + function handleEmbed(topEvents: DisasterEventPoint[]): void { const rankingPoints = topEvents.map((point) => { return { placeDcid: point.placeDcid, diff --git a/static/js/utils/tile_utils.tsx b/static/js/utils/tile_utils.tsx index 5f130b44ca..14a8a57933 100644 --- a/static/js/utils/tile_utils.tsx +++ b/static/js/utils/tile_utils.tsx @@ -18,13 +18,8 @@ * Util functions used by tile components. */ -import React from "react"; - -import { NL_SOURCE_REPLACEMENTS } from "../constants/app/nl_interface_constants"; import { getStatsVarLabel } from "../shared/stats_var_labels"; import { StatVarSpec } from "../shared/types"; -import { urlToDomain } from "../shared/util"; -import { isNlInterface } from "./nl_interface_utils"; export interface ReplacementStrings { place: string; @@ -72,38 +67,6 @@ export function getStatVarName( return label; } -/** - * Gets the JSX element for displaying a list of sources. - * @param sources sources to include in the element - */ -export function getSourcesJsx(sources: Set): JSX.Element[] { - if (!sources) { - return null; - } - - const sourceList: string[] = Array.from(sources); - const seenSourceDomains = new Set(); - const sourcesJsx = sourceList.map((source, index) => { - // HACK for updating source for NL interface - let processedSource = source; - if (isNlInterface()) { - processedSource = NL_SOURCE_REPLACEMENTS[source] || source; - } - const domain = urlToDomain(processedSource); - if (seenSourceDomains.has(domain)) { - return null; - } - seenSourceDomains.add(domain); - return ( - - {index > 0 ? ", " : ""} - {domain} - - ); - }); - return sourcesJsx; -} - /** * Gets the unit given the unit for the stat var and the dcid of the denominator * TODO(chejennifer): clean up all the getUnit functions in this repo