From e02449ec092e5ae953a66a0796f7b9c885673b16 Mon Sep 17 00:00:00 2001 From: Julia Wu Date: Thu, 5 Dec 2024 09:31:29 -0800 Subject: [PATCH 1/5] add modal, implement map tile code, refactor stylesheet injection --- static/css/shared/modal.scss | 14 ++-- static/js/components/tiles/chart_footer.tsx | 15 ++++ static/js/components/tiles/chart_tile.tsx | 7 +- static/js/components/tiles/map_tile.tsx | 92 ++++++++++++++++++++ static/js/tools/shared/tile_code_modal.tsx | 93 +++++++++++++++++++++ static/library/constants.ts | 8 ++ static/library/index.ts | 24 +----- static/library/utils.ts | 30 +++++-- 8 files changed, 247 insertions(+), 36 deletions(-) create mode 100644 static/js/tools/shared/tile_code_modal.tsx diff --git a/static/css/shared/modal.scss b/static/css/shared/modal.scss index 8f181834b9..5b7afb2cf3 100644 --- a/static/css/shared/modal.scss +++ b/static/css/shared/modal.scss @@ -20,16 +20,20 @@ @import "node_modules/bootstrap/scss/variables"; @import "node_modules/bootstrap/scss/mixins"; -$border: 0.5px solid #dee2e6; +$border: 1px solid #dadce0; .modal textarea { - background: #efefef; - border-radius: 3px; + background: #fff; + border-radius: 8px; border: $border; display: flex; - font-family: monospace; + font-family: "Google Sans Text"; + font-size: 14px; + font-weight: 400; + line-height: 20px; margin: auto; - padding: 0.5rem; + padding: 8px; + width: 100%; } .modal-dialog { diff --git a/static/js/components/tiles/chart_footer.tsx b/static/js/components/tiles/chart_footer.tsx index 098a42fbe0..003f4492fe 100644 --- a/static/js/components/tiles/chart_footer.tsx +++ b/static/js/components/tiles/chart_footer.tsx @@ -26,17 +26,23 @@ import { GA_PARAM_TILE_TYPE, triggerGAEvent, } from "../../shared/ga_events"; +import { TileCodeModal } from "../../tools/shared/tile_code_modal"; // Number of characters in footnote to show before "show more" const FOOTNOTE_CHAR_LIMIT = 150; interface ChartFooterPropType { + // Reference to containing chart tile element + containerRef?: React.RefObject; + // Callback to download chart handleEmbed?: () => void; // Link to explore more. Only show explore button if this object is non-empty. exploreLink?: { displayText: string; url: string }; children?: React.ReactNode; // Text to show above buttons footnote?: string; + // Code to show API users for embedding the chart to their websites + sourceCode?: string; } export function ChartFooter(props: ChartFooterPropType): JSX.Element { @@ -83,6 +89,15 @@ export function ChartFooter(props: ChartFooterPropType): JSX.Element { )} + {props.sourceCode && ( +
+ code + +
+ )} {props.children} diff --git a/static/js/components/tiles/chart_tile.tsx b/static/js/components/tiles/chart_tile.tsx index cb9dddb232..73271741a0 100644 --- a/static/js/components/tiles/chart_tile.tsx +++ b/static/js/components/tiles/chart_tile.tsx @@ -36,6 +36,7 @@ import { import { NlChartFeedback } from "../nl_feedback"; import { ChartFooter } from "./chart_footer"; import { LoadingHeader } from "./loading_header"; + interface ChartTileContainerProp { id: string; isLoading?: boolean; @@ -68,6 +69,8 @@ interface ChartTileContainerProp { forwardRef?: MutableRefObject; // Optional: Chart height chartHeight?: number; + // Optional: Code to show when clicking "Embed this chart" + sourceCode?: string; } export function ChartTileContainer(props: ChartTileContainerProp): JSX.Element { @@ -117,9 +120,11 @@ export function ChartTileContainer(props: ChartTileContainerProp): JSX.Element { {props.children} @@ -129,7 +134,7 @@ export function ChartTileContainer(props: ChartTileContainerProp): JSX.Element { ); - // Handle when chart embed is clicked . + // Handle when chart download is clicked . function handleEmbed(): void { const chartTitle = props.title ? formatString(props.title, props.replacementStrings) diff --git a/static/js/components/tiles/map_tile.tsx b/static/js/components/tiles/map_tile.tsx index 460c3b8f71..db9e1754d2 100644 --- a/static/js/components/tiles/map_tile.tsx +++ b/static/js/components/tiles/map_tile.tsx @@ -70,6 +70,7 @@ import { getPointWithin, getSeriesWithin } from "../../utils/data_fetch_utils"; import { getDateRange } from "../../utils/string_utils"; import { clearContainer, + getChartTitle, getDenomInfo, getNoDataErrorMsg, getStatFormat, @@ -353,6 +354,7 @@ export function MapTile(props: MapTilePropType): JSX.Element { ? [props.dataSpecs[0].variable] : [props.statVarSpec] } + sourceCode={getWebComponentSourceCode(props, mapChartData)} > {showZoomButtons && !mapChartData.errorMsg && (
@@ -749,3 +751,93 @@ function getExploreLink(props: MapTilePropType): { url: `${props.apiRoot || ""}${URL_PATH}#${hash}`, }; } + +/** + * Get the HTML that can be used to embed this tile as a web component + * @param props this tile's props + * @returns HTML used to embed this tile as a web component + */ +function getWebComponentSourceCode( + props: MapTilePropType, + mapChartData: MapChartData +): string { + let perCapitaVariables = ""; + let variables = ""; + let places = props.place.dcid; + let enclosedPlaceTypes = props.enclosedPlaceType; + if (mapChartData) { + // Get all places and types in the chart + places = mapChartData.layerData.map((layer) => layer.place.dcid).join(" "); + enclosedPlaceTypes = mapChartData.layerData + .map((layer) => layer.enclosedPlaceType) + .join(" "); + // Get unique variables + variables = _.uniq( + mapChartData.layerData.map((layer) => { + return layer.variable.statVar; + }) + ).join(" "); + // Cet list of variables that are per capita + perCapitaVariables = mapChartData.layerData + .map((layer) => { + return layer.variable; + }) + .filter((variable) => { + return variable?.denom == "Count_Person"; + }) + .map((variable) => { + return variable?.statVar; + }) + .join(" "); + } + + // Check if a specific date is being used for all variables + let date = ""; + if ( + mapChartData?.layerData.length > 0 && + mapChartData.layerData.every((layer) => layer.variable.date) + ) { + date = mapChartData.layerData[0]?.variable?.date || ""; + } + + // Generate title with replacement strings filled in + const replacementStrings = + mapChartData && getReplacementStrings(props, mapChartData); + const header = getChartTitle(props.title, replacementStrings); + + let sourceCode = ` +`; + return sourceCode; +} diff --git a/static/js/tools/shared/tile_code_modal.tsx b/static/js/tools/shared/tile_code_modal.tsx new file mode 100644 index 0000000000..ccbef30cb0 --- /dev/null +++ b/static/js/tools/shared/tile_code_modal.tsx @@ -0,0 +1,93 @@ +/** + * Copyright 2024 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. + */ + +/** + * Displays a modal with code users can use to embed the tile + */ + +import React, { useState } from "react"; +import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; + +import { CopyButton } from "../../components/form_components/icon_buttons"; + +interface TileCodeModalPropType { + containerRef?: React.RefObject; + sourceCode: string; +} + +export function TileCodeModal(props: TileCodeModalPropType): JSX.Element { + const [modalOpen, setModalOpen] = useState(false); + const toggleModal = () => setModalOpen(!modalOpen); + + return ( + <> + { + event.preventDefault(); + setModalOpen(true); + }} + > + Embed this chart + + {modalOpen && ( + + }> + Embed this chart + +
+ Use the following code to embed this chart on your website. For{" "} + documentation, see our{" "} + + web component documentation + + . +
+ + + + + + + +
+ )} + + ); +} diff --git a/static/library/constants.ts b/static/library/constants.ts index 8b3351eb6d..dc8ea59f2d 100644 --- a/static/library/constants.ts +++ b/static/library/constants.ts @@ -17,6 +17,10 @@ /** Default website API endpoint for datacommons library */ export const DEFAULT_API_ENDPOINT = "https://datacommons.org"; +/** URL for Google Sans Text to inject into web components */ +export const GOOGLE_SANS_URL = + "https://fonts.googleapis.com/css2?family=Google+Sans:wght@300;400;500;700&family=Google+Sans+Text:wght@300;400;500;700&display=swap"; + /** URL for Material Icons Outlined stylesheet to inject into web components */ export const MATERIAL_ICONS_OUTLINED_STYLESHEET_URL = "https://fonts.googleapis.com/icon?family=Material+Icons+Outlined"; @@ -25,5 +29,9 @@ export const MATERIAL_ICONS_OUTLINED_STYLESHEET_URL = export const MATERIAL_ICONS_STYLESHEET_URL = "https://fonts.googleapis.com/icon?family=Material+Icons"; +/** URL for Material Symbols Outlined stylesheet to inject into web components */ +export const MATERIAL_SYMBOLS_OUTLINED_STYLESHEET_URL = + "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined"; + /** Sets denom to this value for perCapita web components */ export const DEFAULT_PER_CAPITA_DENOM = "Count_Person"; diff --git a/static/library/index.ts b/static/library/index.ts index fb03e74a12..a290d12a44 100644 --- a/static/library/index.ts +++ b/static/library/index.ts @@ -36,6 +36,7 @@ import { DatacommonsRankingComponent } from "./ranking_component"; import { DatacommonsScatterComponent } from "./scatter_component"; import { DatacommonsSliderComponent } from "./slider_component"; import { DatacommonsTextComponent } from "./text_component"; +import { addStylesheets } from "./utils"; globalThis.datacommons = { DatacommonsBarComponent, @@ -56,27 +57,8 @@ globalThis.datacommons = { }; /** - * Adds Google Sans and material icons to + * Adds Google Sans and material symbols/icons to * Web components can't load external fonts directly, so instead add them * directly to the parent page */ -function loadStyles() { - const googleSansEl = document.createElement("link"); - googleSansEl.href = - "https://fonts.googleapis.com/css2?family=Google+Sans:wght@300;400;500;700&family=Google+Sans+Text:wght@300;400;500;700&display=swap"; - googleSansEl.rel = "stylesheet"; - const materialIconsEl = document.createElement("link"); - materialIconsEl.href = - "https://fonts.googleapis.com/icon?family=Material+Icons&display=block"; - materialIconsEl.rel = "stylesheet"; - const materialIconsOutlinedEl = document.createElement("link"); - materialIconsOutlinedEl.href = - "https://fonts.googleapis.com/icon?family=Material+Icons+Outlined&display=block"; - materialIconsOutlinedEl.rel = "stylesheet"; - document.head.appendChild(googleSansEl); - document.head.appendChild(materialIconsEl); - document.head.appendChild(materialIconsOutlinedEl); -} - -// Append font styles to page -loadStyles(); +addStylesheets(document.head); diff --git a/static/library/utils.ts b/static/library/utils.ts index 9d56cacac0..d0a906b382 100644 --- a/static/library/utils.ts +++ b/static/library/utils.ts @@ -21,8 +21,10 @@ import { StyleSheetManager } from "styled-components"; import { DEFAULT_API_ENDPOINT, + GOOGLE_SANS_URL, MATERIAL_ICONS_OUTLINED_STYLESHEET_URL, MATERIAL_ICONS_STYLESHEET_URL, + MATERIAL_SYMBOLS_OUTLINED_STYLESHEET_URL, } from "./constants"; /** Library of helper functions shared across web components */ @@ -61,6 +63,24 @@ export function convertBooleanAttribute(attributeValue: string): boolean { return attributeValue.toLowerCase() !== "false"; } +/** + * Add stylesheets as link elements to the DOM + * @param container containing element to inject stylesheets into + */ +export function addStylesheets(container: HTMLElement): void { + for (const url of [ + GOOGLE_SANS_URL, + MATERIAL_ICONS_OUTLINED_STYLESHEET_URL, + MATERIAL_ICONS_STYLESHEET_URL, + MATERIAL_SYMBOLS_OUTLINED_STYLESHEET_URL, + ]) { + const link = document.createElement("link"); + link.setAttribute("rel", "stylesheet"); + link.setAttribute("href", url); + container.appendChild(link); + } +} + /** * Create the HTML web component for a tile. * @param tile React function to create the Tile's JSX @@ -76,15 +96,7 @@ export function createWebComponentElement( container.appendChild(styleHost); // Add stylesheet for material icons to the shadow DOM - for (const url of [ - MATERIAL_ICONS_OUTLINED_STYLESHEET_URL, - MATERIAL_ICONS_STYLESHEET_URL, - ]) { - const link = document.createElement("link"); - link.setAttribute("rel", "stylesheet"); - link.setAttribute("href", url); - container.appendChild(link); - } + addStylesheets(container); // Create mount point and render tile in it const mountPoint = document.createElement("div"); From 7481b6c57ba287117fc8e30951fd7ace0aaac4a2 Mon Sep 17 00:00:00 2001 From: Julia Wu Date: Thu, 5 Dec 2024 11:05:18 -0800 Subject: [PATCH 2/5] boilerplate for test --- static/js/components/tiles/map_tile.tsx | 67 ++++++++-------- static/js/utils/tests/tiles/map_tile.test.ts | 80 ++++++++++++++++++++ 2 files changed, 114 insertions(+), 33 deletions(-) create mode 100644 static/js/utils/tests/tiles/map_tile.test.ts diff --git a/static/js/components/tiles/map_tile.tsx b/static/js/components/tiles/map_tile.tsx index db9e1754d2..1e185fd3e3 100644 --- a/static/js/components/tiles/map_tile.tsx +++ b/static/js/components/tiles/map_tile.tsx @@ -354,7 +354,7 @@ export function MapTile(props: MapTilePropType): JSX.Element { ? [props.dataSpecs[0].variable] : [props.statVarSpec] } - sourceCode={getWebComponentSourceCode(props, mapChartData)} + sourceCode={getWebComponentSourceCode(mapChartData)} > {showZoomButtons && !mapChartData.errorMsg && (
@@ -757,40 +757,41 @@ function getExploreLink(props: MapTilePropType): { * @param props this tile's props * @returns HTML used to embed this tile as a web component */ -function getWebComponentSourceCode( - props: MapTilePropType, - mapChartData: MapChartData -): string { - let perCapitaVariables = ""; - let variables = ""; - let places = props.place.dcid; - let enclosedPlaceTypes = props.enclosedPlaceType; - if (mapChartData) { - // Get all places and types in the chart - places = mapChartData.layerData.map((layer) => layer.place.dcid).join(" "); - enclosedPlaceTypes = mapChartData.layerData - .map((layer) => layer.enclosedPlaceType) - .join(" "); - // Get unique variables - variables = _.uniq( - mapChartData.layerData.map((layer) => { - return layer.variable.statVar; - }) - ).join(" "); - // Cet list of variables that are per capita - perCapitaVariables = mapChartData.layerData - .map((layer) => { - return layer.variable; - }) - .filter((variable) => { - return variable?.denom == "Count_Person"; - }) - .map((variable) => { - return variable?.statVar; - }) - .join(" "); +export function getWebComponentSourceCode(mapChartData: MapChartData): string { + if (!mapChartData) { + return ""; } + const props = mapChartData.props; + + // Get all places and types in the chart + const places = mapChartData.layerData + .map((layer) => layer.place.dcid) + .join(" "); + const enclosedPlaceTypes = mapChartData.layerData + .map((layer) => layer.enclosedPlaceType) + .join(" "); + + // Get unique variables + const variables = _.uniq( + mapChartData.layerData.map((layer) => { + return layer.variable.statVar; + }) + ).join(" "); + + // Cet list of variables that are per capita + const perCapitaVariables = mapChartData.layerData + .map((layer) => { + return layer.variable; + }) + .filter((variable) => { + return variable?.denom == "Count_Person"; + }) + .map((variable) => { + return variable?.statVar; + }) + .join(" "); + // Check if a specific date is being used for all variables let date = ""; if ( diff --git a/static/js/utils/tests/tiles/map_tile.test.ts b/static/js/utils/tests/tiles/map_tile.test.ts new file mode 100644 index 0000000000..aca9343727 --- /dev/null +++ b/static/js/utils/tests/tiles/map_tile.test.ts @@ -0,0 +1,80 @@ +/** + * Copyright 2024 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. + */ + +import { + getWebComponentSourceCode, + MapChartData, + MapLayerData, + MapTilePropType, +} from "../../../components/tiles/map_tile"; +import { NamedTypedPlace, StatVarSpec } from "../../../shared/types"; + +const testPlace: NamedTypedPlace = { + dcid: "geoId/06", + name: "California", + types: ["State"], +}; + +const testStatVar: StatVarSpec = { + statVar: "Count_Person", + date: "2020", +}; + +const layer1: MapLayerData = { + geoJson: {}, + +}; + +const testPropsSimple: MapTilePropType = { + enclosedPlaceType: "County", + footnote: "Test Footnote", + id: "test-map-tile", + place: testPlace, + statVarSpec: testStatVar, + svgChartHeight: 200, + title: "Test Map Tile", +}; + +const testSingleLayerChartData: MapChartData = { + dateRange: "", + errorMsg: "", + isUsaPlace: true, + layerData: [layer1], + props: testPropsSimple, + sources: new Set(), +}; + +test("getWebComponentSourceCode", () => { + const cases: { + mapChartData: MapChartData; + expected: string; + }[] = [ + { + mapChartData: testSingleLayerChartData, + expected: "", + }, + ]; + + for (const c of cases) { + const sourceCode = getWebComponentSourceCode(c.mapChartData); + try { + expect(sourceCode).toEqual(c.expected); + } catch (e) { + console.log(`Failed for case with data: ${c.mapChartData}`); + throw e; + } + } +}); From e275585c184cc6ec4afac5b206617fc1dfedcb13 Mon Sep 17 00:00:00 2001 From: Julia Wu Date: Mon, 9 Dec 2024 09:49:37 -0800 Subject: [PATCH 3/5] add button styling --- static/css/base/_color.scss | 5 ++ .../form_components/icon_buttons.tsx | 48 ++++++++++++++----- static/js/components/tiles/map_tile.tsx | 2 +- static/js/tools/shared/tile_code_modal.tsx | 13 +++-- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/static/css/base/_color.scss b/static/css/base/_color.scss index b68cd4504c..1cba339f0b 100644 --- a/static/css/base/_color.scss +++ b/static/css/base/_color.scss @@ -65,4 +65,9 @@ $dc-primary-color: #467bd5; --button-background-color: var(--gm-3-white); --button-highlight-background-color: #ecf2fc; --table-link-color: var(--link-color); + --button-emphasized-text-color: var(--gm-3-white); + --button-emphasized-background-color: var(--gm-3-sys-light-primary); + --button-emphasized-highlight-background-color: var( + --gm-3-ref-secondary-secondary-30 + ); } diff --git a/static/js/components/form_components/icon_buttons.tsx b/static/js/components/form_components/icon_buttons.tsx index 6f92512ff9..02810cef84 100644 --- a/static/js/components/form_components/icon_buttons.tsx +++ b/static/js/components/form_components/icon_buttons.tsx @@ -30,44 +30,63 @@ const ICON_SELECTED_TIMEOUT = 2000; /* Base Button Component*/ -const StyledButton = styled.button` +export const StyledButton = styled.button` align-items: center; - background: var(--button-background-color, transparent); + background: ${(props) => props.theme.background}; border: 1px solid #747775; border-radius: 100px; - color: var(--button-text-color, black); + color: ${(props) => props.theme.color}; + cursor: pointer; display: flex; font-size: 14px; font-weight: 500; gap: 8px; justify-content: center; line-height: 20px; - padding: 10px 24px 10px 16px; + padding: 10px 24px; text-align: center; width: fit-content; &:hover { - background-color: var(--button-highlight-background-color, transparent); + background-color: ${(props) => props.theme.hoverBackgroundColor}; + box-shadow: ${(props) => props.theme.hoverBoxShadow}; } .icon { font-size: 18px; } `; +const defaultColorTheme = { + background: "var(--button-background-color, transparent)", + color: "var(--button-text-color, black)", + hoverBackgroundColor: "var(--button-highlight-background-color, transparent)", + hoverBoxShadow: "none", +}; + +const emphasizedColorTheme = { + background: "var(--button-emphasized-background-color, transparent)", + color: "var(--button-emphasized-text-color, black)", + hoverBackgroundColor: + "var(--button-emphasized-background-color, transparent)", + hoverBoxShadow: + "0px 1px 2px 0px rgba(0, 0, 0, 0.30), 0px 1px 3px 1px rgba(0, 0, 0, 0.15)", +}; + interface ButtonProps { + children?: React.ReactNode; // Class name to add to button class?: string; + // Whether to use emphasized styling + emphasized?: boolean; // Icon to show on button to the left of text icon?: string; // Icon to show temporarily when button is clicked iconWhenClicked?: string; - // Text to show on button - label: string; // Handler for what happens when button is clicked onClick: () => void; } -function IconButton(props: ButtonProps): JSX.Element { +export function IconButton(props: ButtonProps): JSX.Element { const [isClicked, setIsClicked] = useState(false); const timerRef = useRef(null); @@ -89,13 +108,14 @@ function IconButton(props: ButtonProps): JSX.Element { {props.icon && ( {(isClicked && props.iconWhenClicked) || props.icon} )} - {props.label} + {props.children} ); } @@ -113,8 +133,9 @@ export function CopyButton(props: CopyButtonProps): JSX.Element { icon="file_copy" iconWhenClicked="done" onClick={() => navigator.clipboard.writeText(props.textToCopy)} - label="Copy" - > + > + Copy + ); } @@ -133,7 +154,8 @@ export function DownloadButton(props: DownloadButtonProps): JSX.Element { icon="download" iconWhenClicked="download_done" onClick={() => saveToFile(props.filename, props.content)} - label="Download" - > + > + Download + ); } diff --git a/static/js/components/tiles/map_tile.tsx b/static/js/components/tiles/map_tile.tsx index 1e185fd3e3..4e659dd410 100644 --- a/static/js/components/tiles/map_tile.tsx +++ b/static/js/components/tiles/map_tile.tsx @@ -779,7 +779,7 @@ export function getWebComponentSourceCode(mapChartData: MapChartData): string { }) ).join(" "); - // Cet list of variables that are per capita + // Get list of variables that are per capita const perCapitaVariables = mapChartData.layerData .map((layer) => { return layer.variable; diff --git a/static/js/tools/shared/tile_code_modal.tsx b/static/js/tools/shared/tile_code_modal.tsx index ccbef30cb0..6f71bac4f1 100644 --- a/static/js/tools/shared/tile_code_modal.tsx +++ b/static/js/tools/shared/tile_code_modal.tsx @@ -19,9 +19,12 @@ */ import React, { useState } from "react"; -import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; +import { Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; -import { CopyButton } from "../../components/form_components/icon_buttons"; +import { + CopyButton, + IconButton, +} from "../../components/form_components/icon_buttons"; interface TileCodeModalPropType { containerRef?: React.RefObject; @@ -77,14 +80,14 @@ export function TileCodeModal(props: TileCodeModalPropType): JSX.Element { - + )} From 9fddcfc3c76afead1fd90f7b61f5ff32f6392c6f Mon Sep 17 00:00:00 2001 From: Julia Wu Date: Mon, 9 Dec 2024 15:12:39 -0800 Subject: [PATCH 4/5] add tests --- static/js/components/tiles/map_tile.tsx | 8 +- static/js/utils/tests/tiles/map_tile.test.ts | 99 ++++++++++++++++++-- 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/static/js/components/tiles/map_tile.tsx b/static/js/components/tiles/map_tile.tsx index 4e659dd410..21d5a1a8d9 100644 --- a/static/js/components/tiles/map_tile.tsx +++ b/static/js/components/tiles/map_tile.tsx @@ -808,10 +808,10 @@ export function getWebComponentSourceCode(mapChartData: MapChartData): string { let sourceCode = ` (), }; +const expectedSimpleSourceCode = ` +`; + +const testPlace2: NamedTypedPlace = { + dcid: "geoId/10", + name: "Delaware", + types: ["State"], +}; + +const testStatVar2: StatVarSpec = { + date: "2020", + denom: "Count_Person", + log: false, + scaling: 1, + statVar: "Count_Person_WithoutHealthInsurance", + unit: "", +}; + +const layer2: MapLayerData = { + enclosedPlaceType: "County", + geoJson: { + type: "FeatureCollection", + features: [], + properties: { currentGeo: "" }, + }, + place: testPlace2, + variable: testStatVar, +}; + +const layer3: MapLayerData = { + enclosedPlaceType: "County", + geoJson: { + type: "FeatureCollection", + features: [], + properties: { currentGeo: "" }, + }, + place: testPlace2, + variable: testStatVar2, +}; + +const testPropsMultiLayer: MapTilePropType = { + enclosedPlaceType: "County", + footnote: "Test Footnote", + id: "test-map-tile", + parentPlaces: [testPlace, testPlace2], + place: testPlace, + statVarSpec: testStatVar, + svgChartHeight: 200, + title: "Test Map Tile", +}; + +const testMultiLayerChartData: MapChartData = { + dateRange: "", + errorMsg: "", + isUsaPlace: true, + layerData: [layer1, layer2, layer3], + props: testPropsMultiLayer, + sources: new Set(), +}; + +const expectedMultiLayerSourceCode = ` +`; + test("getWebComponentSourceCode", () => { const cases: { mapChartData: MapChartData; @@ -64,7 +147,11 @@ test("getWebComponentSourceCode", () => { }[] = [ { mapChartData: testSingleLayerChartData, - expected: "", + expected: expectedSimpleSourceCode, + }, + { + mapChartData: testMultiLayerChartData, + expected: expectedMultiLayerSourceCode, }, ]; From 7c2046e4480a667b0743ff83ae30cbea2e143e82 Mon Sep 17 00:00:00 2001 From: Julia Wu Date: Tue, 10 Dec 2024 09:47:42 -0800 Subject: [PATCH 5/5] remove borders from modal and button --- static/css/shared/_metadata_modal.scss | 10 +++++----- static/css/shared/modal.scss | 2 +- static/js/components/form_components/icon_buttons.tsx | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/static/css/shared/_metadata_modal.scss b/static/css/shared/_metadata_modal.scss index 66294e09bf..4b93a37613 100644 --- a/static/css/shared/_metadata_modal.scss +++ b/static/css/shared/_metadata_modal.scss @@ -21,10 +21,10 @@ @use "./modal.scss"; @use "../base"; - .metadata-modal { // TODO(beets): Apply these styles to all modals. .modal-content { + border: none; border-radius: 28px; } .modal-header { @@ -53,7 +53,7 @@ padding: 0 24px 12px; /* body/medium */ - font-size: .9rem; + font-size: 0.9rem; font-weight: 400; line-height: 1.4; } @@ -65,7 +65,7 @@ color: var(--gm-3-sys-light-primary); /* label/large */ - font-size: .9rem; + font-size: 0.9rem; font-style: normal; font-weight: 500; line-height: 1.4; @@ -91,7 +91,7 @@ a { /* button/button */ - font-size: .9rem; + font-size: 0.9rem; font-style: normal; font-weight: 500; line-height: 1.4; @@ -100,4 +100,4 @@ } } } -} \ No newline at end of file +} diff --git a/static/css/shared/modal.scss b/static/css/shared/modal.scss index 5b7afb2cf3..fcdad036d9 100644 --- a/static/css/shared/modal.scss +++ b/static/css/shared/modal.scss @@ -27,7 +27,7 @@ $border: 1px solid #dadce0; border-radius: 8px; border: $border; display: flex; - font-family: "Google Sans Text"; + font-family: "Google Sans Text", sans-serif; font-size: 14px; font-weight: 400; line-height: 20px; diff --git a/static/js/components/form_components/icon_buttons.tsx b/static/js/components/form_components/icon_buttons.tsx index 02810cef84..688f8efec7 100644 --- a/static/js/components/form_components/icon_buttons.tsx +++ b/static/js/components/form_components/icon_buttons.tsx @@ -33,7 +33,7 @@ const ICON_SELECTED_TIMEOUT = 2000; export const StyledButton = styled.button` align-items: center; background: ${(props) => props.theme.background}; - border: 1px solid #747775; + border: ${(props) => props.theme.border}; border-radius: 100px; color: ${(props) => props.theme.color}; cursor: pointer; @@ -58,6 +58,7 @@ export const StyledButton = styled.button` const defaultColorTheme = { background: "var(--button-background-color, transparent)", + border: "1px solid #747775", color: "var(--button-text-color, black)", hoverBackgroundColor: "var(--button-highlight-background-color, transparent)", hoverBoxShadow: "none", @@ -65,6 +66,7 @@ const defaultColorTheme = { const emphasizedColorTheme = { background: "var(--button-emphasized-background-color, transparent)", + border: "1px solid var(--button-emphasized-background-color, transparent)", color: "var(--button-emphasized-text-color, black)", hoverBackgroundColor: "var(--button-emphasized-background-color, transparent)",