diff --git a/server/config/home_page/partners.json b/server/config/home_page/partners.json index 025fe7babf..e5cd287d95 100644 --- a/server/config/home_page/partners.json +++ b/server/config/home_page/partners.json @@ -1,4 +1,10 @@ [ + { + "id": "unSdg", + "title": "United Nations Sustainable Development Goals", + "sprite-index": 5, + "url": "https://unstats.un.org/UNSDWebsite/undatacommons/sdgs" + }, { "id": "techsoup", "title": "TechSoup", @@ -28,11 +34,5 @@ "title": "Stanford University", "sprite-index": 4, "url": "https://datacommons.stanford.edu" - }, - { - "id": "unSdg", - "title": "United Nations Sustainable Development Goals", - "sprite-index": 5, - "url": "https://unstats.un.org/UNSDWebsite/undatacommons/sdgs" } ] diff --git a/server/webdriver/shared_tests/browser_test.py b/server/webdriver/shared_tests/browser_test.py index a789b481a3..6035d4ba5b 100644 --- a/server/webdriver/shared_tests/browser_test.py +++ b/server/webdriver/shared_tests/browser_test.py @@ -44,16 +44,17 @@ def test_page_landing(self): self.assertIn(title_text, self.driver.title) # Wait for title to be present - title_locator = (By.CSS_SELECTOR, '#kg-title') + title_locator = (By.TAG_NAME, 'h1') WebDriverWait(self.driver, self.TIMEOUT_SEC).until( EC.text_to_be_present_in_element(title_locator, 'Knowledge Graph')) title_element = self.driver.find_element(*title_locator) - self.assertEqual("Knowledge Graph", title_element.text) + self.assertEqual( + "Knowledge Graph", title_element.text, + f"Expected title 'Knowledge Graph', but found: {title_element.text}") # Assert intro is correct - description_locator = (By.CSS_SELECTOR, '#kg-desc') - WebDriverWait(self.driver, self.TIMEOUT_SEC).until( - EC.presence_of_element_located(description_locator)) + description_locator = ( + By.XPATH, "//h1[text()='Knowledge Graph']/following-sibling::p") description_element = self.driver.find_element(*description_locator) expected_description_start = 'The Data Commons Knowledge Graph is constructed' self.assertTrue( diff --git a/server/webdriver/tests/homepage_test.py b/server/webdriver/tests/homepage_test.py index 53a3b469ec..5e9c43f291 100644 --- a/server/webdriver/tests/homepage_test.py +++ b/server/webdriver/tests/homepage_test.py @@ -35,7 +35,8 @@ def test_homepage_en_by_css(self): hero_msg = self.driver.find_elements(By.ID, 'hero')[0] self.assertTrue( hero_msg.text.startswith( - 'Data Commons aggregates and harmonizes global, open data')) + 'Data Commons brings together the world\'s public data, harmonized for your exploration' + )) def test_homepage_it(self): """Test homepage in IT.""" @@ -49,7 +50,8 @@ def test_homepage_it(self): hero_msg = self.driver.find_elements(By.ID, 'hero')[0] self.assertTrue( hero_msg.text.startswith( - 'Data Commons aggregates and harmonizes global, open data')) + 'Data Commons brings together the world\'s public data, harmonized for your exploration' + )) # def test_hero_all_langs(self): # """Test hero message translation in *all* languages. diff --git a/static/css/about.scss b/static/css/about.scss index b43b1f7e4c..73f10ab378 100644 --- a/static/css/about.scss +++ b/static/css/about.scss @@ -18,7 +18,6 @@ @use "./variables" as var; @import "base"; -@import "./content/media_text"; @import "./content/columns_text"; @import "./content/quote"; diff --git a/static/css/build.scss b/static/css/build.scss index 2d45b5b803..5b54bcef10 100644 --- a/static/css/build.scss +++ b/static/css/build.scss @@ -20,7 +20,6 @@ @import "base"; @import "./content/partners"; @import "./content/hero_columns"; -@import "./content/media_text"; @import "./content/quote"; @import "./content/simple_text"; @import "./content/slide_carousel"; diff --git a/static/css/content/hero_video.scss b/static/css/content/hero_video.scss index cde50f2451..204ade40ec 100644 --- a/static/css/content/hero_video.scss +++ b/static/css/content/hero_video.scss @@ -63,4 +63,4 @@ @include var.text_lg; } } -} \ No newline at end of file +} diff --git a/static/css/content/media_text.scss b/static/css/content/media_text.scss index 7ce9861b43..1f58a48e22 100644 --- a/static/css/content/media_text.scss +++ b/static/css/content/media_text.scss @@ -62,4 +62,3 @@ @include var.text_md; } } -} \ No newline at end of file diff --git a/static/css/content/tools.scss b/static/css/content/tools.scss index 1ce07afbf4..a239267c38 100644 --- a/static/css/content/tools.scss +++ b/static/css/content/tools.scss @@ -90,4 +90,4 @@ background-image: url('/images/icons/icon-scaterplot.svg') } } -} \ No newline at end of file +} diff --git a/static/css/homepage.scss b/static/css/homepage.scss index 64e4aca868..dee1721063 100644 --- a/static/css/homepage.scss +++ b/static/css/homepage.scss @@ -19,12 +19,6 @@ @use "./variables" as var; @import "base"; @import "./content/hero_video"; -@import "./content/sample_questions"; -@import "./content/tools"; -@import "./content/partners"; -@import "./content/build_your_own"; -@import "./content/chips"; -@import "./content/slide_carousel"; #homepage { // General Styles @@ -38,4 +32,4 @@ padding: var.$container-horizontal-padding calc(#{var.$spacing} * 3); } } -} \ No newline at end of file +} diff --git a/static/css/variables.scss b/static/css/variables.scss index aabd1a73ef..26b230d16b 100644 --- a/static/css/variables.scss +++ b/static/css/variables.scss @@ -169,7 +169,7 @@ $color-gray_pill_bckg:#E2E2EC; bottom: 0; left: 0; -webkit-mask-image: -webkit-radial-gradient(white, black); - mask-image: -webkit-radial-gradient(white, black); + mask-image: radial-gradient(white, black); -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; backface-visibility: hidden; diff --git a/static/images/icons/icon-api.svg b/static/images/icons/icon-api.svg deleted file mode 100644 index 2589a63de3..0000000000 --- a/static/images/icons/icon-api.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/static/images/icons/icon-download.svg b/static/images/icons/icon-download.svg deleted file mode 100644 index eb6caea105..0000000000 --- a/static/images/icons/icon-download.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/static/images/icons/icon-map.svg b/static/images/icons/icon-map.svg deleted file mode 100644 index 6bb6b119b2..0000000000 --- a/static/images/icons/icon-map.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/static/images/icons/icon-scaterplot.svg b/static/images/icons/icon-scaterplot.svg deleted file mode 100644 index a033dff5c6..0000000000 --- a/static/images/icons/icon-scaterplot.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/static/images/icons/icon-timeline.svg b/static/images/icons/icon-timeline.svg deleted file mode 100644 index 4eefd77cc9..0000000000 --- a/static/images/icons/icon-timeline.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/static/js/apps/about/app.tsx b/static/js/apps/about/app.tsx index f75245c25c..6128591efb 100644 --- a/static/js/apps/about/app.tsx +++ b/static/js/apps/about/app.tsx @@ -18,11 +18,17 @@ * Main component for the about your own Data Commons page */ +import { ThemeProvider } from "@emotion/react"; import React, { ReactElement } from "react"; -import MediaText from "../../components/content/media_text"; -import Quote from "../../components/content/quote"; +import { Section } from "../../components/elements/layout/section"; import { Routes } from "../../shared/types/base"; +import theme from "../../theme/theme"; +import { Collaborations } from "./components/collaborations"; +import { SplashQuote } from "./components/splash_quote"; +import { StayInTouch } from "./components/stay_in_touch"; +import { WhoCanUse } from "./components/who_can_use"; +import { WhyDataCommons } from "./components/why_data_commons"; interface AppProps { //the routes dictionary - this is used to convert routes to resolved urls @@ -35,139 +41,26 @@ interface AppProps { export function App({ routes }: AppProps): ReactElement { return ( - <> -
- -
-
-
-
-

Why Data Commons

-
-
-

- Many of the big challenges we face — climate change, increasing - inequities, epidemics of diabetes and other health conditions — - will need deep insights to solve. These insights will need to be - firmly grounded in data. Fortunately, a lot of this data is - already publicly available. Unfortunately, there is a difference - between data being public and data being easily usable by those - who need access to it. It is this gap that we are trying to - bridge. Google has organized and made easily accessible many kinds - of information — web pages, images, maps, videos and so on. Now we - are doing this for publicly available data. We have organized the - core of this data from a wide range of sources, ranging from - governmental statistical organizations like census bureaus to - organizations like the World Bank and the United Nations. And - recent advances in AI have enabled us to go much farther than we - had thought possible in making this data easily accessible - now - you can use natural language to access the data.{" "} -

-
-
-

- Data Commons synthesizes a single graph from these different data - sources. It links references to the same entities (such as cities, - counties, organizations, etc.) across different datasets to nodes - on the graph, so that users can access data about a particular - entity aggregated from different sources without data cleaning or - joining.{" "} -

-

- - We hope the data contained within Data Commons will be useful to - students, researchers, and enthusiasts across different - disciplines. - -

-
-
-
+ +
+ +
- - <> -

- {" "} - Data Commons can be accessed by anyone here at Datacommons.org. - Students, researchers, journalists, non profits, policymakers, and - private enterprises can access the tools and allow them to - manipulate and make decisions based on data without the need to know - how to code. Software developers can use the REST, Python and Google - Sheets APIs, all of which are free for educational, academic and - journalistic research purposes.{" "} -

- -
+
+ +
-
-
-
-

Collaborations

-
-
-

- Data Commons has benefited greatly from many collaborations. In - addition to help from US Department of Commerce (notably the - Census Bureau), we have received help from our many academic - collaborations, including, University of California San Francisco, - Stanford University, University of California Berkeley, Harvard - University and Indian Institute of Technology Madras. We have also - collaborated with nonprofits such as Techsoup, Feeding America, - and Resources for the Future. -

-
-
-

- We are looking for more collaborators, both for adding new data to - Data Commons and for building new and interesting applications of - Data Commons. Please fill out this form if you are interested in - working with us. -

-
-
-
+
+ +
-
-
-
-

Stay in touch

-

- Stay informed about the latest Data Commons developments: visit - our blog or sign up for our mailing list -

- - Join the mailing list - -
-
-

See Also

- -
-
-
- +
+ +
+ +
+ +
+
); } diff --git a/static/js/apps/about/components/collaborations.tsx b/static/js/apps/about/components/collaborations.tsx new file mode 100644 index 0000000000..eead6b38a3 --- /dev/null +++ b/static/js/apps/about/components/collaborations.tsx @@ -0,0 +1,50 @@ +/** + * 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. + */ + +/** + * A component to display the "Collaborations" section of the about page + */ + +import React, { ReactElement } from "react"; + +import { TextColumns } from "../../../components/content/text_columns"; + +export const Collaborations = (): ReactElement => { + return ( + + +

+ Data Commons has benefited greatly from many collaborations. In + addition to help from US Department of Commerce (notably the Census + Bureau), we have received help from our many academic collaborations, + including, University of California San Francisco, Stanford + University, University of California Berkeley, Harvard University and + Indian Institute of Technology Madras. We have also collaborated with + nonprofits such as Techsoup, Feeding America, and Resources for the + Future. +

+
+ +

+ We are looking for more collaborators, both for adding new data to + Data Commons and for building new and interesting applications of Data + Commons. Please fill out this form if you are interested in working + with us. +

+
+
+ ); +}; diff --git a/static/js/apps/about/components/splash_quote.tsx b/static/js/apps/about/components/splash_quote.tsx new file mode 100644 index 0000000000..b5b07e4374 --- /dev/null +++ b/static/js/apps/about/components/splash_quote.tsx @@ -0,0 +1,33 @@ +/** + * 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 React, { ReactElement } from "react"; + +import Quote from "../../../components/content/quote"; + +/** + * A component to display the splash quote section at the top of about page + */ + +export const SplashQuote = (): ReactElement => { + return ( + + ); +}; diff --git a/static/js/apps/about/components/stay_in_touch.tsx b/static/js/apps/about/components/stay_in_touch.tsx new file mode 100644 index 0000000000..17737cd73b --- /dev/null +++ b/static/js/apps/about/components/stay_in_touch.tsx @@ -0,0 +1,63 @@ +/** + * 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. + */ + +/** + * A component to display the "Stay in Touch" section of the about page + */ + +import React, { ReactElement } from "react"; + +import { TextColumns } from "../../../components/content/text_columns"; +import { Routes } from "../../../shared/types/base"; + +interface StayInTouchProps { + //the routes dictionary - this is used to convert routes to resolved urls + routes: Routes; +} + +export const StayInTouch = ({ routes }: StayInTouchProps): ReactElement => { + return ( + + +

Stay in touch

+

+ Stay informed about the latest Data Commons developments: visit our + blog or sign up for our mailing list +

+ + Join the mailing list + +
+ +

See Also

+ +
+
+ ); +}; diff --git a/static/js/apps/about/components/who_can_use.tsx b/static/js/apps/about/components/who_can_use.tsx new file mode 100644 index 0000000000..815d8779ad --- /dev/null +++ b/static/js/apps/about/components/who_can_use.tsx @@ -0,0 +1,52 @@ +/** + * 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. + */ + +/** + * A component to display the "Who Can Use It" section of the about page + */ + +/** @jsxImportSource @emotion/react */ + +import React, { ReactElement } from "react"; + +import { MediaText } from "../../../components/content/media_text"; +import { Routes } from "../../../shared/types/base"; + +interface WhoCanUseProps { + //the routes dictionary - this is used to convert routes to resolved urls + routes: Routes; +} + +export const WhoCanUse = ({ routes }: WhoCanUseProps): ReactElement => { + return ( + +

+ Data Commons can be accessed by anyone here at{" "} + Datacommons.org. Students, + researchers, journalists, non profits, policymakers, and private + enterprises can access the tools and allow them to manipulate and make + decisions based on data without the need to know how to code. Software + developers can use the REST, Python and Google Sheets APIs, all of which + are free for educational, academic and journalistic research purposes. +

+
+ ); +}; diff --git a/static/js/apps/about/components/why_data_commons.tsx b/static/js/apps/about/components/why_data_commons.tsx new file mode 100644 index 0000000000..1d78037f2e --- /dev/null +++ b/static/js/apps/about/components/why_data_commons.tsx @@ -0,0 +1,64 @@ +/** + * 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. + */ + +/** + * A component to display the "Why Data Commons" section of the about page + */ + +import React, { ReactElement } from "react"; + +import { TextColumns } from "../../../components/content/text_columns"; + +export const WhyDataCommons = (): ReactElement => { + return ( + + +

+ Many of the big challenges we face — climate change, increasing + inequities, epidemics of diabetes and other health conditions — will + need deep insights to solve. These insights will need to be firmly + grounded in data. Fortunately, a lot of this data is already publicly + available. Unfortunately, there is a difference between data being + public and data being easily usable by those who need access to it. It + is this gap that we are trying to bridge. Google has organized and + made easily accessible many kinds of information — web pages, images, + maps, videos and so on. Now we are doing this for publicly available + data. We have organized the core of this data from a wide range of + sources, ranging from governmental statistical organizations like + census bureaus to organizations like the World Bank and the United + Nations. And recent advances in AI have enabled us to go much farther + than we had thought possible in making this data easily accessible - + now you can use natural language to access the data. +

+
+ +

+ Data Commons synthesizes a single graph from these different data + sources. It links references to the same entities (such as cities, + counties, organizations, etc.) across different datasets to nodes on + the graph, so that users can access data about a particular entity + aggregated from different sources without data cleaning or joining.{" "} +

+

+ + We hope the data contained within Data Commons will be useful to + students, researchers, and enthusiasts across different disciplines. + +

+
+
+ ); +}; diff --git a/static/js/apps/browser_landing/app.tsx b/static/js/apps/browser_landing/app.tsx index 28a11a1580..31e094bd11 100644 --- a/static/js/apps/browser_landing/app.tsx +++ b/static/js/apps/browser_landing/app.tsx @@ -18,11 +18,14 @@ * Main component for the browser landing (knowledge graph) page */ +import { ThemeProvider } from "@emotion/react"; import React, { ReactElement } from "react"; import { IntroText } from "../../components/content/intro_text"; -import { KnowledgeGraphBrowser } from "../../components/knowledge_graph/knowledge_graph_browser"; +import { Section } from "../../components/elements/layout/section"; import { KnowledgeGraphCategory } from "../../shared/types/knowledge_graph"; +import theme from "../../theme/theme"; +import { KnowledgeGraphBrowser } from "./components/knowledge_graph_browser"; import knowledgeGraphData from "./knowledge_graph.json"; const knowledgeGraph: KnowledgeGraphCategory[] = knowledgeGraphData; @@ -32,19 +35,23 @@ const knowledgeGraph: KnowledgeGraphCategory[] = knowledgeGraphData; */ export function App(): ReactElement { return ( - <> - -
-

Knowledge Graph

-

- The Data Commons Knowledge Graph is constructed by synthesizing a - single database from many different data sources. This knowledge - graph can be used both to explore what data is available and to - understand the graph structure. -

-
-
- - + +
+ +
+

Knowledge Graph

+

+ The Data Commons Knowledge Graph is constructed by synthesizing a + single database from many different data sources. This knowledge + graph can be used both to explore what data is available and to + understand the graph structure. +

+
+
+
+
+ +
+
); } diff --git a/static/js/apps/browser_landing/components/knowledge_graph_browser.tsx b/static/js/apps/browser_landing/components/knowledge_graph_browser.tsx new file mode 100644 index 0000000000..2e40e3b861 --- /dev/null +++ b/static/js/apps/browser_landing/components/knowledge_graph_browser.tsx @@ -0,0 +1,136 @@ +/** + * 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. + */ + +/** + * A component to display a category of the knowledge graph + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +import { KnowledgeGraph } from "../../../shared/types/knowledge_graph"; + +interface KnowledgeGraphBrowserProps { + //an object containing the categorized links that make up the knowledge graph + knowledgeGraph: KnowledgeGraph; +} + +export const KnowledgeGraphBrowser = ({ + knowledgeGraph, +}: KnowledgeGraphBrowserProps): ReactElement => { + const theme = useTheme(); + return ( + <> + {knowledgeGraph.map((category) => ( +
+
+

+ {category.title} +

+
+ +
+ ))} + + ); +}; diff --git a/static/js/apps/browser_landing/knowledge_graph.json b/static/js/apps/browser_landing/knowledge_graph.json index f45dc6fd57..b3096e98a6 100644 --- a/static/js/apps/browser_landing/knowledge_graph.json +++ b/static/js/apps/browser_landing/knowledge_graph.json @@ -3,12 +3,12 @@ "title": "Cities", "items": [ { - "endpoint": "geoId/4261000", - "label": "Pittsburgh, PA" + "endpoint": "nuts/DE3", + "label": "Berlin, Germany" }, { - "endpoint": "geoId/0649670", - "label": "Mountain View, CA" + "endpoint": "wikidataId/Q1861", + "label": "Bangkok, Thailand" }, { "endpoint": "geoId/4805000", @@ -17,6 +17,10 @@ { "endpoint": "geoId/0606000", "label": "Berkeley, CA" + }, + { + "endpoint": "geoId/0667000", + "label": "San Francisco, CA" } ], "categoryEndpoint": "City" @@ -26,11 +30,19 @@ "items": [ { "endpoint": "geoId/06085", - "label": "Santa Clara" + "label": "Santa Clara County" }, { "endpoint": "geoId/12086", - "label": "Miami-Dade" + "label": "Miami-Dade County" + }, + { + "endpoint": "geoId/13259", + "label": "Stewart County" + }, + { + "endpoint": "geoId/17031", + "label": "Cook County" } ], "categoryEndpoint": "County" @@ -39,16 +51,24 @@ "title": "States", "items": [ { - "endpoint": "geoId/06", - "label": "Florida" + "endpoint": "wikidataId/Q1164", + "label": "Assam, India" + }, + { + "endpoint": "wikidataId/Q1171", + "label": "Goa, India" }, { - "endpoint": "geoId/56", - "label": "Wyoming" + "endpoint": "wikidataId/Q309478", + "label": "Al Qadarif, Sudan" }, { "endpoint": "geoId/36", - "label": "New York" + "label": "New York, USA" + }, + { + "endpoint": "geoId/48", + "label": "Texas, USA" } ], "categoryEndpoint": "State" @@ -57,12 +77,24 @@ "title": "Countries", "items": [ { - "endpoint": "country/USA", - "label": "United States" + "endpoint": "country/SDN", + "label": "Sudan" }, { "endpoint": "country/CAN", "label": "Canada" + }, + { + "endpoint": "country/IND", + "label": "India" + }, + { + "endpoint": "country/DEU", + "label": "Germany" + }, + { + "endpoint": "country/USA", + "label": "United States" } ], "categoryEndpoint": "Country" @@ -72,86 +104,52 @@ "items": [ { "endpoint": "geoId/0618", - "label": "CA-18" + "label": "District 18 (113th Congress), California" }, { "endpoint": "geoId/3601", - "label": "NY-01" - } - ], - "categoryEndpoint": "CongressionalDistrict" - }, - { - "title": "Elections", - "items": [ - { - "endpoint": "election/2016_P_US00", - "label": "2016 US Presidential Election" + "label": "District 1 (113th Congress), New York" }, { - "endpoint": "election/1990_S_MI00", - "label": "1990 US Senate Elections for Michigan" - } - ], - "categoryEndpoint": "Election" - }, - { - "title": "Census Estimates", - "items": [ + "endpoint": "geoId/0101", + "label": "District 1 (113th Congress), Alabama" + }, { - "endpoint": "geoId/06085?statVar=Count_Person_AsianAlone", - "label": "Asian Population in Santa Clara" + "endpoint": "geoId/1501", + "label": "District 1 (113th Congress), Hawaii" }, { - "endpoint": "geoId/2255000?statVar=Count_Person_EducationalAttainmentMastersDegree", - "label": "Master Degree Holders in New Orleans" + "endpoint": "geoId/4801", + "label": "District 1 (113th Congress), Texas" } - ] + ], + "categoryEndpoint": "CongressionalDistrict" }, { - "title": "Labor Statistics", + "title": "Public Schools and School Districts", "items": [ { - "endpoint": "geoId/2507000?statVar=UnemploymentRate_Person", - "label": "Unemployment in Boston" + "endpoint": "nces/062961004587", + "label": "Henry M. Gunn High" }, { - "endpoint": "geoId/1304000?statVar=Count_CriminalActivities_Robbery", - "label": "Violent Robbery in Atlanta, Georgia" + "endpoint": "nces/360007702877", + "label": "Stuyvesant High School" }, { - "endpoint": "geoId/13?statVar=Count_CriminalActivities_LarcenyTheft", - "label": "Larceny-Theft in Georgia" - } - ] - }, - { - "title": "Health Data", - "items": [ - { - "endpoint": "geoId/1304000?statVar=Percent_Person_WithDiabetes", - "label": "Prevalence of Diabetes in Atlanta" - } - ] - }, - { - "title": "DEA ARCOS", - "items": [ + "endpoint": "geoId/sch3620580", + "label": "New York City Department of Education" + }, { - "endpoint": "geoId/54099?statVar=RetailDrugDistribution_DrugDistribution_Amphetamine", - "label": "Amphetamine distribution in Wayne County, WV" - } - ] - }, - { - "title": "Botanical Specimens", - "items": [ + "endpoint": "nces/010001802104", + "label": "Alabama School Of Fine Arts" + }, { - "endpoint": "dc/bsmvthtq89217", - "label": "Austrobaileya scandens" + "endpoint": "nces/170993006725", + "label": "Englewood Stem High School" } ], - "categoryEndpoint": "BiologicalSpecimen" + "categoryEndpoint": "PublicSchool" }, { "title": "ENCODE (Encyclopedia Of DNA Elements)", @@ -170,22 +168,5 @@ } ], "categoryEndpoint": "EncodeBedFile" - }, - { - "title": "Public Schools and School Districts", - "items": [ - { - "endpoint": "nces/062961004587", - "label": "Henry M. Gunn High" - }, - { - "endpoint": "nces/360007702877", - "label": "Stuyvesant High School" - }, - { - "endpoint": "geoId/sch3620580", - "label": "New York City Department of Education" - } - ] } ] \ No newline at end of file diff --git a/static/js/apps/build/app.tsx b/static/js/apps/build/app.tsx index 2c3a237b54..8ec52a70a6 100644 --- a/static/js/apps/build/app.tsx +++ b/static/js/apps/build/app.tsx @@ -18,16 +18,20 @@ * Main component for the build page */ +import { ThemeProvider } from "@emotion/react"; import React, { ReactElement } from "react"; -import OneDataCommons from "../../build/one_data_commons"; -import Hero from "../../components/content/hero_columns"; -import MediaText from "../../components/content/media_text"; import Partners from "../../components/content/partners"; -import Quote from "../../components/content/quote"; -import SimpleText from "../../components/content/simple_text"; +import { Section } from "../../components/elements/layout/section"; +import { Separator } from "../../components/elements/layout/separator"; import { GA_EVENT_BUILDPAGE_CLICK } from "../../shared/ga_events"; import { Partner } from "../../shared/types/homepage"; +import theme from "../../theme/theme"; +import { BuildHero } from "./components/build_hero"; +import { DataCommonsGlance } from "./components/data_commons_glance"; +import { GetStarted } from "./components/get_started"; +import { OneDataCommons } from "./components/one_data_commons"; +import { OneQuote } from "./components/one_quote"; interface AppProps { //the partners passed from the backend through to the JavaScript via the templates @@ -39,58 +43,34 @@ interface AppProps { */ export function App({ partners }: AppProps): ReactElement { return ( - <> - - - <> -

- {" "} - A custom instance natively joins your data and the base Data Commons - data (from datacommons.org) in a unified fashion. Your users can - visualize and analyze the data seamlessly without the need for - further data preparation. -

-

- You have full control over your own data and computing resources, - with the ability to limit access to specific individuals or open it - to the general public. -

-

- Note that each new Data Commons is deployed using the Google Cloud - Platform (GCP).{" "} -

- -
-
- - - -
- - <> -

Ready to get started?

-

- - Get started - {" "} - building your own Data Commons{" "} -

- -
- + +
+ +
+ +
+ +
+ + + +
+ +
+ +
+ +
+ +
+ +
+ + + +
+ +
+
); } diff --git a/static/js/apps/build/components/build_hero.tsx b/static/js/apps/build/components/build_hero.tsx new file mode 100644 index 0000000000..63ea398ba7 --- /dev/null +++ b/static/js/apps/build/components/build_hero.tsx @@ -0,0 +1,65 @@ +/** + * 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. + */ + +/** + * A component to display the build page hero. + */ + +import React, { ReactElement } from "react"; + +import { HeroColumns } from "../../../components/content/hero_columns"; + +export const BuildHero = (): ReactElement => { + return ( + + +

+ Build your Data Commons, overlay your data with global data, and let + everyone in your organization uncover insights with natural language + questions.{" "} + + Learn how + +

+
+ +
+

Build and deploy your own

+

+ Launch your own Data Commons and customize it with your own data to + better engage your specific audience +

+
+
+

Explore data with natural language

+

+ Ask questions in your own words and get answers directly from your + data +

+
+
+

Gain actionable insights

+

+ Find actionable insights from your data in connection to global data{" "} +

+
+
+
+ ); +}; diff --git a/static/js/apps/build/components/data_commons_glance.tsx b/static/js/apps/build/components/data_commons_glance.tsx new file mode 100644 index 0000000000..2c24a473bb --- /dev/null +++ b/static/js/apps/build/components/data_commons_glance.tsx @@ -0,0 +1,55 @@ +/** + * 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. + */ + +/** + * A component to display the "Data Commons at a Glance" section of the build page + */ + +/** @jsxImportSource @emotion/react */ + +import React, { ReactElement } from "react"; + +import { MediaText } from "../../../components/content/media_text"; + +export const DataCommonsGlance = (): ReactElement => { + return ( + + <> +

+ A custom instance natively joins your data and the base Data Commons + data (from datacommons.org) in a unified fashion. Your users can + visualize and analyze the data seamlessly without the need for further + data preparation. +

+

+ You have full control over your own data and computing resources, with + the ability to limit access to specific individuals or open it to the + general public. +

+

+ Note that each new Data Commons is deployed using the Google Cloud + Platform (GCP). +

+ +
+ ); +}; diff --git a/static/js/apps/build/components/get_started.tsx b/static/js/apps/build/components/get_started.tsx new file mode 100644 index 0000000000..dc40120e66 --- /dev/null +++ b/static/js/apps/build/components/get_started.tsx @@ -0,0 +1,42 @@ +/** + * 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. + */ + +/** + * A component to display the "Ready To Get Started" section on the build page + */ + +import React, { ReactElement } from "react"; + +import SimpleText from "../../../components/content/simple_text"; + +export const GetStarted = (): ReactElement => { + return ( + + <> +

Ready to get started?

+

+ + Get started + {" "} + building your own Data Commons{" "} +

+ +
+ ); +}; diff --git a/static/js/apps/build/components/one_data_commons.tsx b/static/js/apps/build/components/one_data_commons.tsx new file mode 100644 index 0000000000..d0e493137d --- /dev/null +++ b/static/js/apps/build/components/one_data_commons.tsx @@ -0,0 +1,82 @@ +/** + * 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. + */ + +/** + * A component to display the One Data Commons on the build page. + * This is set as a carousel with the anticipation that more items + * will be added. If we do not, it can be simplified. + */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +import { MediaText } from "../../../components/content/media_text"; +import SlideCarousel from "../../../components/elements/slide_carousel"; + +export const OneDataCommons = (): ReactElement => { + const theme = useTheme(); + const createSlides = (): ReactElement[] => { + return [ + +

+ + ONE Data Commons + + , a collaborative platform combining the in-depth data and research + from the ONE Campaign with the vast repository of Google’s Data + Commons, offers unparalleled insights into global issues spanning + economics, climate, health, demographics, and beyond. +

+
, + ]; + }; + + const slides = createSlides(); + + return ( + <> +
+

+ ONE Data Commons +

+
+ + + ); +}; diff --git a/static/js/apps/build/components/one_quote.tsx b/static/js/apps/build/components/one_quote.tsx new file mode 100644 index 0000000000..c09e04b66e --- /dev/null +++ b/static/js/apps/build/components/one_quote.tsx @@ -0,0 +1,36 @@ +/** + * 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. + */ + +/** + * A component to display the one.org quote on the build page + */ + +import React, { ReactElement } from "react"; + +import Quote from "../../../components/content/quote"; + +export const OneQuote = (): ReactElement => { + return ( + + ); +}; diff --git a/static/js/apps/explore_landing/app.tsx b/static/js/apps/explore_landing/app.tsx index c4af574927..e8a1ba2479 100644 --- a/static/js/apps/explore_landing/app.tsx +++ b/static/js/apps/explore_landing/app.tsx @@ -17,9 +17,12 @@ /** * Main component for DC Explore. */ +import { ThemeProvider } from "@emotion/react"; import React, { ReactElement } from "react"; +import { Section } from "../../components/elements/layout/section"; import { TopicConfig, TopicData } from "../../shared/topic_config"; +import theme from "../../theme/theme"; import { ExploreIntro } from "./components/explore_intro"; import { Queries } from "./components/queries"; import { StatVarQueries } from "./components/stat_var_queries"; @@ -57,15 +60,26 @@ export function App(): ReactElement { `/explore#${placeholderQuery.url}` || `/explore#q=${encodeURIComponent(placeholderQuery.title)}&dc=${dc}`;*/ return ( - <> - - - - - + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
); } diff --git a/static/js/apps/explore_landing/components/explore_intro.tsx b/static/js/apps/explore_landing/components/explore_intro.tsx index c731028cee..dabf9a9df8 100644 --- a/static/js/apps/explore_landing/components/explore_intro.tsx +++ b/static/js/apps/explore_landing/components/explore_intro.tsx @@ -31,13 +31,13 @@ interface ExploreIntroProps { export const ExploreIntro = ({ topic }: ExploreIntroProps): ReactElement => { return ( -
+ <>

{topic.title}

Our {topic.title.toLocaleLowerCase()} data spans over{" "} - + {formatNumber(topic.meta.variableCount)} - {" "} + {" "} statistical variables. We collect our{" "} {topic.title.toLocaleLowerCase()} information from sources such as:{" "} {topic.meta.sources.map((s, i) => ( @@ -52,7 +52,7 @@ export const ExploreIntro = ({ topic }: ExploreIntroProps): ReactElement => { ))} {"."}

-
+
); }; diff --git a/static/js/apps/explore_landing/components/stat_var_queries.tsx b/static/js/apps/explore_landing/components/stat_var_queries.tsx index 75224b5134..c4e961f718 100644 --- a/static/js/apps/explore_landing/components/stat_var_queries.tsx +++ b/static/js/apps/explore_landing/components/stat_var_queries.tsx @@ -20,7 +20,8 @@ import React, { ReactElement } from "react"; -import { LinkChip, LinkChips } from "../../../components/content/link_chips"; +import { LinkChips } from "../../../components/content/link_chips"; +import { Link } from "../../../components/elements/link_chip"; import { Query } from "../../../shared/topic_config"; interface StatVarQueriesProps { @@ -28,7 +29,7 @@ interface StatVarQueriesProps { } export function StatVarQueries({ queries }: StatVarQueriesProps): ReactElement { - const statVarLinkChips: LinkChip[] = queries.map((query) => ({ + const statVarLinkChips: Link[] = queries.map((query) => ({ id: query.url, title: query.title, url: query.url, @@ -40,9 +41,10 @@ export function StatVarQueries({ queries }: StatVarQueriesProps): ReactElement { return ( diff --git a/static/js/apps/homepage/app.tsx b/static/js/apps/homepage/app.tsx index 5d0be9640e..b4cb196e4a 100644 --- a/static/js/apps/homepage/app.tsx +++ b/static/js/apps/homepage/app.tsx @@ -15,19 +15,16 @@ */ /** - * Main component for Version 2 of the homepage. + * Main component for the homepage. */ -//TODO: fold this into app.tsx when revamp is complete and final PR is ready. - +import { ThemeProvider } from "@emotion/react"; import React, { ReactElement } from "react"; -import Build from "../../components/content/build_your_own"; -import HeroVideo from "../../components/content/hero_video"; -import { LinkChip, LinkChips } from "../../components/content/link_chips"; import Partners from "../../components/content/partners"; -import SampleQuestions from "../../components/content/sample_questions"; -import Tools from "../../components/content/tools"; +import { Section } from "../../components/elements/layout/section"; +import { Separator } from "../../components/elements/layout/separator"; +import { Link } from "../../components/elements/link_chip"; import { GA_EVENT_HOMEPAGE_CLICK } from "../../shared/ga_events"; import { Routes } from "../../shared/types/base"; import { @@ -35,6 +32,12 @@ import { SampleQuestionCategory, Topic, } from "../../shared/types/homepage"; +import theme from "../../theme/theme"; +import { BuildYourOwn } from "./components/build_your_own"; +import { HomeHero } from "./components/home_hero"; +import { SampleQuestions } from "./components/sample_questions"; +import { Tools } from "./components/tools"; +import { UnitedNations } from "./components/united_nations"; interface AppProps { //the topics passed from the backend through to the JavaScript via the templates @@ -56,24 +59,41 @@ export function App({ sampleQuestions, routes, }: AppProps): ReactElement { - const topicLinkChips: LinkChip[] = topics.map((topic) => ({ + const topicLinkChips: Link[] = topics.map((topic) => ({ id: topic.id, title: topic.title, url: topic.browseUrl, })); return ( - <> - - - - - - - + +
+ +
+ +
+ +
+ +
+ +
+ + + +
+ +
+ +
+ +
+ + + +
+ +
+
); } diff --git a/static/js/apps/homepage/components/build_your_own.tsx b/static/js/apps/homepage/components/build_your_own.tsx new file mode 100644 index 0000000000..f24494a94e --- /dev/null +++ b/static/js/apps/homepage/components/build_your_own.tsx @@ -0,0 +1,67 @@ +/** + * 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. + */ + +/** + * A component to display the build your own section of the homepage + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +import { BigText } from "../../../components/content/big_text"; +import { + GA_EVENT_HOMEPAGE_CLICK, + GA_PARAM_ID, + GA_PARAM_URL, + triggerGAEvent, +} from "../../../shared/ga_events"; +import { Routes } from "../../../shared/types/base"; +import { resolveHref } from "../../base/utilities/utilities"; + +interface BuildYourOwnProps { + //the routes dictionary - this is used to convert routes to resolved urls + routes: Routes; +} + +export const BuildYourOwn = ({ routes }: BuildYourOwnProps): ReactElement => { + const theme = useTheme(); + return ( + + <> + Build your own Data Commons: customize a private instance of the + open-source Data Commons platform. You control the data you include and + who has access. + { + triggerGAEvent(GA_EVENT_HOMEPAGE_CLICK, { + [GA_PARAM_ID]: "build-your-own", + [GA_PARAM_URL]: "{static.build}", + }); + }} + css={css` + color: ${theme.colors.link.primary.light}; + margin-left: ${theme.spacing.sm}px; + `} + > + Learn more + + + + ); +}; diff --git a/static/js/apps/homepage/components/home_hero.tsx b/static/js/apps/homepage/components/home_hero.tsx new file mode 100644 index 0000000000..30df7eeead --- /dev/null +++ b/static/js/apps/homepage/components/home_hero.tsx @@ -0,0 +1,72 @@ +/** + * 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. + */ + +/** + * A component to display the home hero component + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +import { HeroSimple } from "../../../components/content/hero_simple"; +import { LinkChips } from "../../../components/content/link_chips"; +import { Link, LinkChip } from "../../../components/elements/link_chip"; + +interface HomeHeroProps { + //an array of links to be rendered by the component + linkChips: Link[]; +} + +export const HomeHero = ({ linkChips }: HomeHeroProps): ReactElement => { + const theme = useTheme(); + return ( + + <> +

+ Data Commons brings together the world's public data, harmonized + for your exploration +

+ +
+ +
+ +
+ ); +}; diff --git a/static/js/apps/homepage/components/sample_questions.tsx b/static/js/apps/homepage/components/sample_questions.tsx new file mode 100644 index 0000000000..bf983fd539 --- /dev/null +++ b/static/js/apps/homepage/components/sample_questions.tsx @@ -0,0 +1,199 @@ +/** + * 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. + */ + +/** + * A component to display clickable sample questions on a carousel for the homepage + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement, useEffect, useState } from "react"; + +import { Link, LinkBox } from "../../../components/elements/link_box"; +import SlideCarousel from "../../../components/elements/slide_carousel"; +import { GA_EVENT_HOMEPAGE_CLICK } from "../../../shared/ga_events"; +import { BREAKPOINTS } from "../../../shared/hooks/breakpoints"; +import { SampleQuestionCategory } from "../../../shared/types/homepage"; + +interface SampleQuestionsProps { + sampleQuestions: SampleQuestionCategory[]; +} + +const colors: ("green" | "blue" | "red" | "yellow" | "grey")[] = [ + "green", + "blue", + "red", + "yellow", + "grey", +]; + +const MAX_QUESTIONS_PER_CATEGORY = 2; + +const calculateColumnsPerSlide = (): number => { + if (window.innerWidth < BREAKPOINTS.sm) return 1; + if (window.innerWidth < BREAKPOINTS.md) return 2; + return 3; +}; + +const getRandomQuestionsFromCategory = ( + category: SampleQuestionCategory, + count: number +): string[] => { + const shuffled = category.questions.slice().sort(() => 0.5 - Math.random()); + return shuffled.slice(0, Math.min(count, category.questions.length)); +}; + +export const SampleQuestions = ({ + sampleQuestions, +}: SampleQuestionsProps): ReactElement => { + const [columnsPerSlide, setColumnsPerSlide] = useState(() => + calculateColumnsPerSlide() + ); + + const theme = useTheme(); + + useEffect(() => { + const handleResize = (): void => { + setColumnsPerSlide(calculateColumnsPerSlide()); + }; + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + const sampleQuestionToLink = (sampleQuestion: string): Link => ({ + id: sampleQuestion, + title: sampleQuestion, + url: `/explore#q=${encodeURIComponent(sampleQuestion)}`, + }); + + const createSlides = (): ReactElement[] => { + const slides: ReactElement[] = []; + + for (let i = 0; i < sampleQuestions.length; i += columnsPerSlide) { + slides.push( +
+ {sampleQuestions + .slice(i, i + columnsPerSlide) + .map((category, index) => { + const overallIndex = i + index; + const randomQuestions = getRandomQuestionsFromCategory( + category, + MAX_QUESTIONS_PER_CATEGORY + ); + + return ( +
+ {randomQuestions.map((question) => ( + + ))} +
+ ); + })} +
+ ); + } + return slides; + }; + + const createSingleColumnLayout = (): ReactElement => { + return ( +
+
+ {sampleQuestions.map((category, index) => { + const randomQuestions = getRandomQuestionsFromCategory(category, 1); + return randomQuestions.map((question) => ( + + )); + })} +
+
+ ); + }; + + const slides = createSlides(); + return ( + <> +
+

+ Sample questions +

+
+ {columnsPerSlide === 1 ? ( + createSingleColumnLayout() + ) : ( + + )} + + ); +}; diff --git a/static/js/apps/homepage/components/tools.tsx b/static/js/apps/homepage/components/tools.tsx new file mode 100644 index 0000000000..6a387be7c5 --- /dev/null +++ b/static/js/apps/homepage/components/tools.tsx @@ -0,0 +1,125 @@ +/** + * 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. + */ + +/** + * A component that renders the tools section of the home page. + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +import { Download } from "../../../components/elements/icons/download"; +import { IntegrationInstructions } from "../../../components/elements/icons/integration_instructions"; +import { Public } from "../../../components/elements/icons/public"; +import { ScatterPlot } from "../../../components/elements/icons/scatter_plot"; +import { Timeline } from "../../../components/elements/icons/timeline"; +import { LinkIconBox } from "../../../components/elements/link_icon_box"; +import { resolveHref } from "../../base/utilities/utilities"; + +interface ToolsProps { + //the routes dictionary - this is used to convert routes to resolved urls + routes: Record; +} + +export const Tools = ({ routes }: ToolsProps): ReactElement => { + const theme = useTheme(); + return ( + <> +
+

+ Data Commons tools +

+

+ Data Commons addresses offers data exploration tools and cloud-based + APIs to access and integrate cleaned datasets. +

+
+
+ } + link={{ + id: "map", + title: "Map Explorer", + url: resolveHref("{tools.visualization}#visType=map", routes), + }} + /> + } + link={{ + id: "scatter", + title: "Scatter Plot Explorer", + url: resolveHref("{tools.visualization}#visType=scatter", routes), + }} + /> + } + link={{ + id: "timeline", + title: "Timelines Explorer", + url: resolveHref("{tools.visualization}#visType=timeline", routes), + }} + /> + } + link={{ + id: "download", + title: "Data Download Tool", + url: resolveHref("{tools.download}", routes), + }} + /> + } + link={{ + id: "api", + title: "API Access", + url: "https://docs.datacommons.org/api/", + }} + /> +
+ + ); +}; diff --git a/static/js/apps/homepage/components/united_nations.tsx b/static/js/apps/homepage/components/united_nations.tsx new file mode 100644 index 0000000000..5644b63b1e --- /dev/null +++ b/static/js/apps/homepage/components/united_nations.tsx @@ -0,0 +1,66 @@ +/** + * 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. + */ + +/** + * A component to display the build your own section of the homepage + */ + +/** @jsxImportSource @emotion/react */ + +import React, { ReactElement } from "react"; + +import { MediaText } from "../../../components/content/media_text"; +import { + GA_EVENT_HOMEPAGE_CLICK, + GA_PARAM_ID, + GA_PARAM_URL, + triggerGAEvent, +} from "../../../shared/ga_events"; +import { Routes } from "../../../shared/types/base"; +import { resolveHref } from "../../base/utilities/utilities"; + +interface UnitedNationsProps { + //the routes dictionary - this is used to convert routes to resolved urls + routes: Routes; +} + +export const UnitedNations = ({ routes }: UnitedNationsProps): ReactElement => { + return ( + +

+ The United Nations deployed its own Data Commons to track their + Sustainable Development Goals data around the world. With this, the UN + is able to offer a centralized repository, allowing for dynamic + storytelling and targeted analysis related to global progress.{" "} + { + triggerGAEvent(GA_EVENT_HOMEPAGE_CLICK, { + [GA_PARAM_ID]: "build-your-own", + [GA_PARAM_URL]: "{static.build}", + }); + }} + > + Learn more + +

+
+ ); +}; diff --git a/static/js/build/one_data_commons.tsx b/static/js/build/one_data_commons.tsx deleted file mode 100644 index f179e960a6..0000000000 --- a/static/js/build/one_data_commons.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { ReactElement } from "react"; - -import MediaText from "../components/content/media_text"; -import SlideCarousel from "../components/elements/slide_carousel"; - -const OneDataCommons = (): ReactElement => { - const createSlides = (): ReactElement[] => { - return [ - -

- - ONE Data Commons - - , a collaborative platform combining the in-depth data and research - from the ONE Campaign with the vast repository of Google’s Data - Commons, offers unparalleled insights into global issues spanning - economics, climate, health, demographics, and beyond. -

-
, - ]; - }; - - const slides = createSlides(); - - return ( -
-
-
-

ONE Data Commons

-
- -
-
- ); -}; - -export default OneDataCommons; diff --git a/static/js/components/content/big_text.tsx b/static/js/components/content/big_text.tsx new file mode 100644 index 0000000000..bf1a162bfe --- /dev/null +++ b/static/js/components/content/big_text.tsx @@ -0,0 +1,54 @@ +/** + * 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. + */ + +/** + * A component to display a large bold text section + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +interface BigTextProps { + //the content that will be displayed inside the text section + children: ReactElement; +} + +export const BigText = ({ children }: BigTextProps): ReactElement => { + const theme = useTheme(); + return ( +
+

+ {children} +

+
+ ); +}; diff --git a/static/js/components/content/brick_wall.tsx b/static/js/components/content/brick_wall.tsx index 56211bad84..0979ff8e98 100644 --- a/static/js/components/content/brick_wall.tsx +++ b/static/js/components/content/brick_wall.tsx @@ -1,3 +1,22 @@ +/** + * 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. + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; import React, { ReactElement, useMemo } from "react"; interface BrickWallProps { @@ -17,34 +36,37 @@ const patterns = [ ]; export const BrickWall = ({ title, bricks }: BrickWallProps): ReactElement => { - // This function divides the bricks into columns (based on the NUM_COLUMNS above), and tracks the number of rows in - // each column. The number of rows isn't simple to calculate because it varies based on the pattern of the bricks. - const { columns, numRows } = useMemo(() => { - const columns: ReactElement[][] = Array.from( - { length: NUM_COLUMNS }, - () => [] - ); - const numRows: number[] = Array(NUM_COLUMNS).fill(0); - const patternIndices = Array(NUM_COLUMNS).fill(0); + const theme = useTheme(); + // This function spreads the bricks among the columns, effectively generating the mortar pattern based on the + // pattern set given above. Each column is represented by an object containing the bricks, and a patternEntry + // array that gives the corresponding pattern type for the brick at that index (i.e., does the brick take up + // two spaces or one). This is used in the component to correctly style the bricks. + const { columnData } = useMemo(() => { + const columnData: { bricks: ReactElement[]; patternEntries: number[] }[] = [ + { bricks: [], patternEntries: [] }, + { bricks: [], patternEntries: [] }, + ]; + + const patternIndices = [0, 0]; let currentColumn = 0; + let i = 0; - for (let i = 0; i < bricks.length; ) { - const columnPattern = patterns[currentColumn % patterns.length]; - const currentPattern = columnPattern[patternIndices[currentColumn]]; + while (i < bricks.length) { + const pattern = patterns[currentColumn]; + const entry = pattern[patternIndices[currentColumn]]; - for (let j = 0; j < currentPattern && i < bricks.length; j++, i++) { - columns[currentColumn].push(bricks[i]); + for (let j = 0; j < entry && i < bricks.length; j++, i++) { + columnData[currentColumn].bricks.push(bricks[i]); + columnData[currentColumn].patternEntries.push(entry); } - numRows[currentColumn]++; patternIndices[currentColumn] = - (patternIndices[currentColumn] + 1) % columnPattern.length; - + (patternIndices[currentColumn] + 1) % pattern.length; currentColumn = (currentColumn + 1) % NUM_COLUMNS; } - return { columns, numRows }; + return { columnData }; }, [bricks]); if (bricks.length === 0) { @@ -52,22 +74,87 @@ export const BrickWall = ({ title, bricks }: BrickWallProps): ReactElement => { } return ( -
-
- {title &&

{title}

} - {columns.map((column, columnIndex) => ( + <> + {title && ( +
+

+ {title} +

+
+ )} +
+ {columnData.map((column, columnIndex) => (
- {column.map((brick, i) => ( -
- {brick} -
- ))} +
+ {column.bricks.map((brick, i) => { + const isSingleBrickRow = column.patternEntries[i] === 1; + + return ( +
+ {brick} +
+ ); + })} +
))}
-
+ ); }; diff --git a/static/js/components/content/build_your_own.tsx b/static/js/components/content/build_your_own.tsx deleted file mode 100644 index 611fa6732d..0000000000 --- a/static/js/components/content/build_your_own.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/** - * 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. - */ - -/** - * A component to display the build your own Data Commons block - */ - -import React, { ReactElement } from "react"; - -import { resolveHref } from "../../apps/base/utilities/utilities"; -import { - GA_EVENT_HOMEPAGE_CLICK, - GA_PARAM_ID, - GA_PARAM_URL, - triggerGAEvent, -} from "../../shared/ga_events"; - -interface BuildProps { - //the routes dictionary - this is used to convert routes to resolved urls - routes: Record; -} - -const Build = ({ routes }: BuildProps): ReactElement => { - return ( -
-
-
-

Build your own Data Commons

-

- Deploying your own Data Commons lets you create a tailored platform - showcasing relevant data and tools to engage your audience. Any - entity or organization can build their own instance with specific - data, a dedicated website, and specialized tools.{" "} - { - triggerGAEvent(GA_EVENT_HOMEPAGE_CLICK, { - [GA_PARAM_ID]: "build-your-own", - [GA_PARAM_URL]: "{static.build}", - }); - }} - > - Learn more - -

-

United Nations Data Commons for the SDGs

-
-
- -
-
-

- United Nations deployed a Data Commons to amplify the impact of - their Sustainable Development Goals data. With their deployed Data - Commons, the UN created a centralized repository, allowing for - dynamic storytelling and targeted analysis related to global - progress. -

-
-
-
- ); -}; - -export default Build; diff --git a/static/js/components/content/hero_columns.tsx b/static/js/components/content/hero_columns.tsx index af1b82a9e2..0cf89ab964 100644 --- a/static/js/components/content/hero_columns.tsx +++ b/static/js/components/content/hero_columns.tsx @@ -15,54 +15,101 @@ */ /** - * A component to display the columned her component. + * A component to display the columned hero component. */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; import React, { ReactElement } from "react"; -const HeroColumns = (): ReactElement => { +interface HeroColumnsProps { + //the content of the two hero columns, given as slot props: + //...... + children: ReactElement | ReactElement[]; +} + +interface HeroColumnsSlotProps { + //the content that populates either of the two columns + children: ReactElement | ReactElement[] | string; +} + +const HeroColumnsLeft = ({ children }: HeroColumnsSlotProps): ReactElement => { + const theme = useTheme(); + return ( +
+ {children} +
+ ); +}; + +const HeroColumnsRight = ({ children }: HeroColumnsSlotProps): ReactElement => { + const theme = useTheme(); + return ( +
+ {children} +
+ ); +}; + +export const HeroColumns = ({ children }: HeroColumnsProps): ReactElement => { + const theme = useTheme(); return ( -
-
-
-

- Build your Data Commons, overlay your data with global data, and let - everyone in your organization uncover insights with natural language - questions.{" "} - - Learn how - -

-
-
-
-

Build and deploy your own

-

- Launch your own Data Commons and customize it with your own data - to better engage your specific audience -

-
-
-

Explore data with natural language

-

- Ask questions in your own words and get answers directly from your - data -

-
-
-

Gain actionable insights

-

- Find actionable insights from your data in connection to global - data{" "} -

-
-
-
-
+
div, + & > header { + color: ${theme.colors.text.primary.light}; + a { + color: ${theme.colors.link.primary.light}; + } + h3, + h4 { + ${theme.typography.family.text}; + ${theme.typography.text.md}; + font-weight: 900; + } + p { + ${theme.typography.family.text}; + ${theme.typography.text.md}; + } + } + `} + > + {children} +
); }; -export default HeroColumns; +HeroColumns.Left = HeroColumnsLeft; +HeroColumns.Right = HeroColumnsRight; diff --git a/static/js/components/content/hero_simple.tsx b/static/js/components/content/hero_simple.tsx new file mode 100644 index 0000000000..49cc7fea81 --- /dev/null +++ b/static/js/components/content/hero_simple.tsx @@ -0,0 +1,46 @@ +/** + * 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. + */ + +/** + * A component to display the primary video hero component + */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +interface SimpleHeroProps { + //the content to be displayed alongside the video + children: ReactElement; +} + +export const HeroSimple = ({ children }: SimpleHeroProps): ReactElement => { + const theme = useTheme(); + return ( +
+ {children} +
+ ); +}; diff --git a/static/js/components/content/hero_video.tsx b/static/js/components/content/hero_video.tsx index 1eb278eff3..4d12ede638 100644 --- a/static/js/components/content/hero_video.tsx +++ b/static/js/components/content/hero_video.tsx @@ -15,69 +15,35 @@ */ /** - * A component to display the primary video hero component + * A component to display a video hero component */ import React, { ReactElement } from "react"; -import { resolveHref } from "../../apps/base/utilities/utilities"; -import { - GA_EVENT_HOMEPAGE_CLICK, - GA_PARAM_ID, - GA_PARAM_URL, - triggerGAEvent, -} from "../../shared/ga_events"; -import { Routes } from "../../shared/types/base"; - -interface HeroProps { - //the routes dictionary - this is used to convert routes to resolved urls - routes: Routes; +interface HeroVideoProps { + //a link to the video source + videoSource: string; + //a link to the poster image for the video + videoPosterSource: string; + //the content to be displayed alongside the video + children: ReactElement; } -const Hero = ({ routes }: HeroProps): ReactElement => { +export const HeroVideo = ({ + videoSource, + videoPosterSource, + children, +}: HeroVideoProps): ReactElement => { return (
-
-
-

- Data Commons aggregates and harmonizes global, open data, giving - everyone the power to uncover insights with natural language - questions -

-

- Data Commons’ open source foundation allows organizations to - create tailored, private instances, deciding on the openness of - their data contributions.{" "} - { - /* TODO: Pass in GA event so the component can be used on other pages. */ - triggerGAEvent(GA_EVENT_HOMEPAGE_CLICK, { - [GA_PARAM_ID]: `hero-video`, - [GA_PARAM_URL]: "{static.build}", - }); - }} - > - Build yours today - -

-
+
{children}
); }; - -export default Hero; diff --git a/static/js/components/content/intro_text.tsx b/static/js/components/content/intro_text.tsx index 53502e7143..c04c5f2a15 100644 --- a/static/js/components/content/intro_text.tsx +++ b/static/js/components/content/intro_text.tsx @@ -18,6 +18,9 @@ * A component to display a simple text block */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; import React, { ReactElement } from "react"; interface IntroTextProps { @@ -26,9 +29,28 @@ interface IntroTextProps { } export const IntroText = ({ children }: IntroTextProps): ReactElement => { + const theme = useTheme(); return ( -
-
{children}
-
+
+ {children} +
); }; diff --git a/static/js/components/content/link_chips.tsx b/static/js/components/content/link_chips.tsx index 73c37b8d77..a84b84e798 100644 --- a/static/js/components/content/link_chips.tsx +++ b/static/js/components/content/link_chips.tsx @@ -15,69 +15,87 @@ */ /** - * A component that renders a series of chips that function as links with titles + * A component that renders a block of link chips (Material Design-inspired + * chips that act as links */ -import React, { ReactElement } from "react"; +/** @jsxImportSource @emotion/react */ -import { - GA_EVENT_HOMEPAGE_CLICK, - GA_PARAM_ID, - GA_PARAM_URL, - triggerGAEvent, -} from "../../shared/ga_events"; -import { ArrowForward } from "../elements/icons/arrow_forward"; +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; -//an individual LinkChip comprising the title and url attributes of the chip. -export interface LinkChip { - //a unique identifier for the chip (used for map keys) - id: string; - //the title of the chip - this will be the text of the link - title: string; - //the url of the chip link - url: string; -} +import { Link, LinkChip } from "../elements/link_chip"; interface LinkChipsProps { //the variant of the link chip to display: elevated is a raised grey chip and flat is a flat blue chip variant?: "elevated" | "flat"; //the title of the component, displayed as a header above the chips - title?: string; + header?: string; + //the typographical component for the header (defaults to "h3") + headerComponent?: "h3" | "h4" | "p"; //the section gives location of the chip component in order to give context for the GA event section: string; - //the link - linkChips: LinkChip[]; + //an array of links to be rendered by the component + linkChips: Link[]; } export const LinkChips = ({ variant = "elevated", - title, + header, + headerComponent = "h3", section, linkChips, }: LinkChipsProps): ReactElement => { + const theme = useTheme(); return ( -
-
- {title &&

{title}

} - + <> + {header && ( +
h3 { + ${theme.typography.family.heading}; + ${theme.typography.heading.md}; + margin-bottom: ${theme.spacing.lg}px; + } + + & > h4 { + ${theme.typography.family.heading}; + ${theme.typography.heading.xs} + margin-bottom: ${theme.spacing.lg}px; + } + + & > p { + ${theme.typography.text.md} + } + `} + > + {(!headerComponent || headerComponent === "h3") &&

{header}

} + {headerComponent === "h4" &&

{header}

} + {headerComponent === "p" &&

{header}

} +
+ )} +
+ {linkChips.map((linkChip) => ( + + ))}
-
+ ); }; diff --git a/static/js/components/content/media_text.tsx b/static/js/components/content/media_text.tsx index e1aa92d744..7c5151db54 100644 --- a/static/js/components/content/media_text.tsx +++ b/static/js/components/content/media_text.tsx @@ -15,62 +15,158 @@ */ /** - * A component to display a media/text component. This component renders in two columns a piece of media (video or image) and text content + * A component to display a media/text component. This component renders in two columns a piece of + * media (video or image) and text content */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; import React, { ReactElement } from "react"; -interface BaseMediaTextProps { +interface MediaTextProps { //the type of media that will be displayed - either a video or an image, with a video representing a YouTube video mediaType: "video" | "image"; //the source of the media - for video, this will be the video id, and for an image this will be the url mediaSource: string; + //the header of the component (optional) + header?: string; + //the typographical component for the header (defaults to "h4") + headerComponent?: "h3" | "h4" | "p"; + //the alt for the image (optional) + imageAlt?: string; //the text (or other) content, given as the children of the component children: ReactElement; } -//title: the title that renders at the top of the component. If an alt is not provided, the title is required. -//alt: the alt text for the image. If the title is not provided, the alt is required. -type MediaTextProps = BaseMediaTextProps & - ({ title: string; alt?: string } | { title?: string; alt: string }); - -const MediaText = ({ +export const MediaText = ({ mediaType, mediaSource, - title, - alt, + header, + headerComponent, + imageAlt, children, }: MediaTextProps): ReactElement => { + const theme = useTheme(); return ( -
-
- {title && ( -
-

{title}

+
+ {header && ( +
h3 { + ${theme.typography.heading.md} + } + & > h4 { + ${theme.typography.heading.sm} + } + & > p { + ${theme.typography.text.md} + } + `} + > + {(!headerComponent || headerComponent === "h4") &&

{header}

} + {headerComponent === "h3" &&

{header}

} + {headerComponent === "p" &&

{header}

} +
+ )} +
+ {mediaType === "image" ? ( +
+ {imageAlt} +
+ ) : ( +
iframe, + & > video { + margin: 0; + padding: 0; + border: none; + outline: none; + isolation: isolate; + box-sizing: border-box; + display: block; + width: 100%; + height: 100%; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + `} + > +
)} -
- {mediaType === "image" ? ( -
- {alt -
- ) : ( -
- -
- )} -
-
{children}
-
+
h3 { + ${theme.typography.heading.sm} + } + & > h4 { + ${theme.typography.heading.xs} + } + & > p { + ${theme.typography.text.md} + } + `} + > + {children} +
+ ); }; - -export default MediaText; diff --git a/static/js/components/content/partners.tsx b/static/js/components/content/partners.tsx index 2724e6693d..3e1e8b55e5 100644 --- a/static/js/components/content/partners.tsx +++ b/static/js/components/content/partners.tsx @@ -18,6 +18,9 @@ * A component that renders the partners section of the home page. */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; import React, { ReactElement } from "react"; import { @@ -34,41 +37,86 @@ interface PartnersProps { gaEvent: string; } -const Partners = ({ - partners, - gaEvent: ga_event, -}: PartnersProps): ReactElement => { +const Partners = ({ partners, gaEvent }: PartnersProps): ReactElement => { + const theme = useTheme(); return ( -
-
-

Other organizations with a Data Commons

- -
-
+ <> +
+

+ Organizations who have built their own Data Commons +

+
+ + ); }; diff --git a/static/js/components/content/quote.tsx b/static/js/components/content/quote.tsx index 65ab01793e..9faf6479ad 100644 --- a/static/js/components/content/quote.tsx +++ b/static/js/components/content/quote.tsx @@ -18,23 +18,53 @@ * A component to display a splash quote */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; import React, { ReactElement } from "react"; interface QuoteProps { + //a variant that determines the alignment + alignment?: "left" | "center" | "right"; //the quote content quote: string; //the person or organization responsible for the quote byline: string; } -const Quote = ({ quote, byline }: QuoteProps): ReactElement => { +const Quote = ({ + alignment = "center", + quote, + byline, +}: QuoteProps): ReactElement => { + const theme = useTheme(); + return ( -
-
-
“{quote}”
-

- {byline}

-
-
+
+
+ “{quote}” +
+

+ - {byline} +

+
); }; diff --git a/static/js/components/content/sample_questions.tsx b/static/js/components/content/sample_questions.tsx deleted file mode 100644 index df46350bb3..0000000000 --- a/static/js/components/content/sample_questions.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/** - * 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. - */ - -/** - * A component to display clickable sample questions on a carousel for the homepage - */ - -import React, { ReactElement, useEffect, useState } from "react"; - -import { - GA_EVENT_HOMEPAGE_CLICK, - GA_PARAM_ID, - GA_PARAM_QUERY, - triggerGAEvent, -} from "../../shared/ga_events"; -import { BREAKPOINTS } from "../../shared/hooks/breakpoints"; -import { SampleQuestionCategory } from "../../shared/types/homepage"; -import SlideCarousel from "../elements/slide_carousel"; - -interface SampleQuestionsProps { - sampleQuestions: SampleQuestionCategory[]; -} - -const colors = ["green", "blue", "red", "yellow", "gray"]; - -const calculateColumnsPerSlide = (): number => { - if (window.innerWidth < BREAKPOINTS.sm) return 1; - if (window.innerWidth < BREAKPOINTS.md) return 2; - return 3; -}; - -const getRandomQuestionFromCategory = ( - category: SampleQuestionCategory -): string => { - const randomIndex = Math.floor(Math.random() * category.questions.length); - return category.questions[randomIndex]; -}; - -const SampleQuestions = ({ - sampleQuestions, -}: SampleQuestionsProps): ReactElement => { - const [columnsPerSlide, setColumnsPerSlide] = useState(() => - calculateColumnsPerSlide() - ); - - useEffect(() => { - const handleResize = (): void => { - setColumnsPerSlide(calculateColumnsPerSlide()); - }; - window.addEventListener("resize", handleResize); - - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); - - const createSlides = (): ReactElement[] => { - const slides: ReactElement[] = []; - - for (let i = 0; i < sampleQuestions.length; i += columnsPerSlide) { - slides.push( -
- {sampleQuestions - .slice(i, i + columnsPerSlide) - .map((category, index) => { - const overallIndex = i + index; - - return ( - - ); - })} -
- ); - } - return slides; - }; - - const createSingleColumnLayout = (): ReactElement => { - return ( -
-
- {sampleQuestions.map((category, index) => { - const question = getRandomQuestionFromCategory(category); - return ( - - ); - })} -
-
- ); - }; - - const slides = createSlides(); - - return ( -
-
-

Sample questions

- {columnsPerSlide === 1 ? ( - createSingleColumnLayout() - ) : ( - - )} -
-
- ); -}; - -export default SampleQuestions; diff --git a/static/js/components/content/simple_text.tsx b/static/js/components/content/simple_text.tsx index 0749bc816f..34a80704b8 100644 --- a/static/js/components/content/simple_text.tsx +++ b/static/js/components/content/simple_text.tsx @@ -17,7 +17,9 @@ /** * A component to display a simple text block */ +/** @jsxImportSource @emotion/react */ +import { css, useTheme } from "@emotion/react"; import React, { ReactElement } from "react"; interface SimpleTextProps { @@ -26,10 +28,23 @@ interface SimpleTextProps { } const SimpleText = ({ children }: SimpleTextProps): ReactElement => { + const theme = useTheme(); return ( -
-
{children}
-
+
+ {children} +
); }; diff --git a/static/js/components/content/text_columns.tsx b/static/js/components/content/text_columns.tsx new file mode 100644 index 0000000000..475b29c2e4 --- /dev/null +++ b/static/js/components/content/text_columns.tsx @@ -0,0 +1,115 @@ +/** + * 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. + */ + +/** + * A component to display textual content in two columns + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +interface TextColumnsProps { + //an optional header for the column section + header?: string; + //the content of the two columns, given as slot props: + //...... + children: ReactElement | ReactElement[]; +} + +interface TextColumnsSlotProps { + //the content that populates either of the two columns + children: ReactElement | ReactElement[] | string; +} + +const TextColumnsLeft = ({ children }: TextColumnsSlotProps): ReactElement => { + return
{children}
; +}; + +const TextColumnsRight = ({ children }: TextColumnsSlotProps): ReactElement => { + return
{children}
; +}; + +export const TextColumns = ({ + header, + children, +}: TextColumnsProps): ReactElement => { + const theme = useTheme(); + return ( +
div { + h3, + h4 { + ${theme.typography.family.heading}; + ${theme.typography.heading.lg}; + margin-bottom: ${theme.spacing.lg}px; + } + p { + ${theme.typography.family.text}; + ${theme.typography.text.md}; + } + ul { + margin: 0; + padding-left: ${theme.spacing.xl}px; + } + a.btn-primary { + ${theme.radius.full}; + ${theme.typography.family.text}; + ${theme.typography.text.md}; + padding: ${theme.spacing.md}px ${theme.spacing.xl}px; + background-color: ${theme.colors.button.primary.base}; + &:hover { + ${theme.elevation.primary} + } + } + } + `} + > + {header && ( +
+

+ {header} +

+
+ )} + {children} +
+ ); +}; + +TextColumns.Left = TextColumnsLeft; +TextColumns.Right = TextColumnsRight; diff --git a/static/js/components/content/tools.tsx b/static/js/components/content/tools.tsx deleted file mode 100644 index 8cecb90d9f..0000000000 --- a/static/js/components/content/tools.tsx +++ /dev/null @@ -1,138 +0,0 @@ -/** - * 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. - */ - -/** - * A component that renders the tools section of the home page. - */ - -import React, { ReactElement } from "react"; - -import { resolveHref } from "../../apps/base/utilities/utilities"; -import { - GA_EVENT_HOMEPAGE_CLICK, - GA_PARAM_ID, - GA_PARAM_URL, - triggerGAEvent, -} from "../../shared/ga_events"; - -interface ToolsProps { - //the routes dictionary - this is used to convert routes to resolved urls - routes: Record; -} - -const Tools = ({ routes }: ToolsProps): ReactElement => { - return ( -
-
-
-

Data Commons tools

-

- Data forms the foundation of science, policy, and journalism, but - its full potential is often limited. Data Commons addresses this by - offering data exploration tools and cloud-based APIs to access and - integrate cleaned datasets, boosting their usability across - different domains. -

-
- -
-
- ); -}; - -export default Tools; diff --git a/static/js/components/elements/icons/download.tsx b/static/js/components/elements/icons/download.tsx new file mode 100644 index 0000000000..312d51ed8d --- /dev/null +++ b/static/js/components/elements/icons/download.tsx @@ -0,0 +1,40 @@ +/** + * 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. + */ + +/* + * Autogenerated by generate_icon.py + */ + +/* + * Material Icon: Download + * Source: https://github.com/google/material-design-icons + */ + +import React, { ReactElement } from "react"; + +export const Download = ( + props: React.SVGProps +): ReactElement => ( + + + +); diff --git a/static/js/components/elements/icons/integration_instructions.tsx b/static/js/components/elements/icons/integration_instructions.tsx new file mode 100644 index 0000000000..6d6e6d542a --- /dev/null +++ b/static/js/components/elements/icons/integration_instructions.tsx @@ -0,0 +1,40 @@ +/** + * 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. + */ + +/* + * Autogenerated by generate_icon.py + */ + +/* + * Material Icon: Integration Instructions + * Source: https://github.com/google/material-design-icons + */ + +import React, { ReactElement } from "react"; + +export const IntegrationInstructions = ( + props: React.SVGProps +): ReactElement => ( + + + +); diff --git a/static/js/components/elements/icons/public.tsx b/static/js/components/elements/icons/public.tsx new file mode 100644 index 0000000000..bb89c707d9 --- /dev/null +++ b/static/js/components/elements/icons/public.tsx @@ -0,0 +1,38 @@ +/** + * 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. + */ + +/* + * Autogenerated by generate_icon.py + */ + +/* + * Material Icon: Public + * Source: https://github.com/google/material-design-icons + */ + +import React, { ReactElement } from "react"; + +export const Public = (props: React.SVGProps): ReactElement => ( + + + +); diff --git a/static/js/components/elements/icons/scatter_plot.tsx b/static/js/components/elements/icons/scatter_plot.tsx new file mode 100644 index 0000000000..329bd90c9b --- /dev/null +++ b/static/js/components/elements/icons/scatter_plot.tsx @@ -0,0 +1,40 @@ +/** + * 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. + */ + +/* + * Autogenerated by generate_icon.py + */ + +/* + * Material Icon: Scatter Plot + * Source: https://github.com/google/material-design-icons + */ + +import React, { ReactElement } from "react"; + +export const ScatterPlot = ( + props: React.SVGProps +): ReactElement => ( + + + +); diff --git a/static/js/components/elements/icons/timeline.tsx b/static/js/components/elements/icons/timeline.tsx new file mode 100644 index 0000000000..d9e87e3f3c --- /dev/null +++ b/static/js/components/elements/icons/timeline.tsx @@ -0,0 +1,40 @@ +/** + * 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. + */ + +/* + * Autogenerated by generate_icon.py + */ + +/* + * Material Icon: Timeline + * Source: https://github.com/google/material-design-icons + */ + +import React, { ReactElement } from "react"; + +export const Timeline = ( + props: React.SVGProps +): ReactElement => ( + + + +); diff --git a/static/js/components/elements/layout/section.tsx b/static/js/components/elements/layout/section.tsx new file mode 100644 index 0000000000..48401ae836 --- /dev/null +++ b/static/js/components/elements/layout/section.tsx @@ -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. + */ + +/** + * A page section that defines a block of content with standard spacing, + * widths and colors. + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement, ReactNode } from "react"; + +interface SectionProps { + //a variant that determines the vertical (y) padding of the section + variant?: "standard" | "small" | "large" | "compact"; + //a variant that determines the background color of the section + colorVariant?: "base" | "light" | "dark"; + //the content of the section + children?: ReactNode; +} + +export const Section = ({ + colorVariant = "base", + variant = "standard", + children, +}: SectionProps): ReactElement => { + const theme = useTheme(); + const color = theme.colors.background.primary[colorVariant ?? "base"]; + + const padding = + variant === "large" + ? theme.sections.large + : variant === "small" + ? theme.sections.small + : variant === "compact" + ? theme.sections.compact + : theme.sections.standard; + + return ( +
+
+ {children} +
+
+ ); +}; diff --git a/static/js/components/elements/layout/separator.tsx b/static/js/components/elements/layout/separator.tsx new file mode 100644 index 0000000000..5f89301d1c --- /dev/null +++ b/static/js/components/elements/layout/separator.tsx @@ -0,0 +1,70 @@ +/** + * 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. + */ + +/** + * A page separator that adds either space and or border to a page. + * The spacing is in the same proportions as the section components. + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +interface SeparatorProps { + //a variant that determines the vertical (y) padding added to the page + variant?: "standard" | "small" | "large" | "compact"; + //a variant that determines the visibility of the border - defaults to true + border?: boolean; +} + +export const Separator = ({ + variant = "compact", + border = true, +}: SeparatorProps): ReactElement => { + const theme = useTheme(); + + const padding = + variant === "large" + ? theme.sections.large + : variant === "small" + ? theme.sections.small + : variant === "compact" + ? theme.sections.compact + : theme.sections.standard; + + return ( +
+ ); +}; diff --git a/static/js/components/elements/link_box.tsx b/static/js/components/elements/link_box.tsx new file mode 100644 index 0000000000..e3f02e1d7f --- /dev/null +++ b/static/js/components/elements/link_box.tsx @@ -0,0 +1,116 @@ +/** + * 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. + */ + +/** + * A component that renders a link box - a component that + * displays a link inside a Material Design-style rounded box. + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +import { + GA_EVENT_HOMEPAGE_CLICK, + GA_PARAM_ID, + GA_PARAM_QUERY, + triggerGAEvent, +} from "../../shared/ga_events"; + +//an individual Link comprising the title and url. This will be rendered in the LinkBox +export interface Link { + //a unique identifier for the box (used for map keys) + id: string; + //the title of the box - this will be the text of the link + title: string; + //the url of the box link + url: string; +} + +interface LinkBoxProps { + //the content color of the link box + color: "green" | "blue" | "red" | "yellow" | "grey"; + //the link chip to be displayed + link: Link; + //an optional category - if given, this will be displayed under the link title + category?: string; + //the section gives location of the chip component in order to give context for the GA event + section?: string; +} + +export const LinkBox = ({ + color, + link, + category, + section = "", +}: LinkBoxProps): ReactElement => { + const theme = useTheme(); + + const containerStyles = css` + display: block; + list-style: none; + `; + + const linkStyles = css` + ${theme.box.primary}; + ${theme.elevation.primary}; + ${theme.radius.primary}; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: ${theme.spacing.sm}px; + padding: ${theme.spacing.lg}px; + + &:hover { + text-decoration: none; + } + + p { + color: ${theme.colors.box[color].text}; + ${theme.typography.text.xl}; + } + + small { + color: ${theme.colors.box[color].tag}; + background-color: ${theme.colors.box[color].pill}; + ${theme.typography.text.xs}; + ${theme.radius.secondary}; + display: inline-block; + padding: ${theme.spacing.xs}px ${theme.spacing.sm}px; + } + `; + + return ( + + ); +}; diff --git a/static/js/components/elements/link_chip.tsx b/static/js/components/elements/link_chip.tsx new file mode 100644 index 0000000000..1e0577fd27 --- /dev/null +++ b/static/js/components/elements/link_chip.tsx @@ -0,0 +1,120 @@ +/** + * 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. + */ + +/** + * A component that renders a link chip - a commonly-used component that + * displays a link inside a Material Design-style chip. + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement } from "react"; + +import { + GA_EVENT_HOMEPAGE_CLICK, + GA_PARAM_ID, + GA_PARAM_URL, + triggerGAEvent, +} from "../../shared/ga_events"; +import { ArrowForward } from "./icons/arrow_forward"; + +//an individual Link comprising an id, the title and url. This will be rendered in the LinkChip +export interface Link { + //a unique identifier for the chip (used for map keys) + id: string; + //the title of the chip - this will be the text of the link + title: string; + //the url of the chip link + url: string; +} + +interface LinkChipProps { + //the variant of the link chip to display: elevated is a raised grey chip and flat is a flat blue chip + variant?: "elevated" | "flat"; + //the link chip to be displayed + linkChip: Link; + //the section gives location of the chip component in order to give context for the GA event + section?: string; +} + +export const LinkChip = ({ + variant = "elevated", + linkChip, + section = "", +}: LinkChipProps): ReactElement => { + const theme = useTheme(); + + const chipStyles = css` + ${variant === "elevated" ? theme.box.primary : theme.box.secondary}; + ${variant === "elevated" ? theme.elevation.primary : ""}; + ${theme.typography.family.text}; + ${theme.typography.text.md}; + ${theme.radius.primary}; + color: ${variant === "elevated" + ? theme.colors.link.primary.base + : theme.colors.text.primary.base}; + line-height: 1rem; + display: inline-flex; + justify-content: center; + align-items: center; + gap: ${theme.spacing.sm}px; + padding: 10px ${theme.spacing.lg}px 10px ${theme.spacing.md}px; + transition: background-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out; + + &:hover { + text-decoration: none; + color: ${variant === "elevated" + ? theme.colors.link.primary.base + : theme.colors.text.primary.base}; + .icon { + transform: translateX(2px); + } + } + + .icon { + transition: transform 0.1s ease-in-out; + svg { + fill: ${variant === "elevated" + ? theme.colors.link.primary.base + : theme.colors.text.primary.base}; + } + } + `; + + return ( + + ); +}; diff --git a/static/js/components/elements/link_icon_box.tsx b/static/js/components/elements/link_icon_box.tsx new file mode 100644 index 0000000000..9c53bf1559 --- /dev/null +++ b/static/js/components/elements/link_icon_box.tsx @@ -0,0 +1,110 @@ +/** + * 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. + */ + +/** + * A component that renders a link icon box - a component that + * displays a link inside a large Material Design-style rounded box, + * decorated with an icon. + */ + +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; +import React, { ReactElement, SVGProps } from "react"; + +import { + GA_EVENT_HOMEPAGE_CLICK, + GA_PARAM_ID, + GA_PARAM_URL, + triggerGAEvent, +} from "../../shared/ga_events"; + +//an individual Link comprising the title and url. This will be rendered in the LinkBox +export interface Link { + //a unique identifier for the box (used for map keys) + id: string; + //the title of the box - this will be the text of the link + title: string; + //the url of the icon box link + url: string; +} + +interface LinkIconBoxProps { + // the decorator icon + icon: ReactElement>; + //the link chip to be displayed + link: Link; + //the section gives location of the chip component in order to give context for the GA event + section?: string; +} + +export const LinkIconBox = ({ + icon, + link, + section = "", +}: LinkIconBoxProps): ReactElement => { + const theme = useTheme(); + + return ( + + ); +}; diff --git a/static/js/components/elements/slide_carousel.tsx b/static/js/components/elements/slide_carousel.tsx index dfa228a256..abf31e5ea0 100644 --- a/static/js/components/elements/slide_carousel.tsx +++ b/static/js/components/elements/slide_carousel.tsx @@ -18,36 +18,41 @@ * A slide-based carousel that takes arbitrary ReactElements as slides */ +/** @jsxImportSource @emotion/react */ + +import { css, useTheme } from "@emotion/react"; import React, { ReactElement, useEffect, useRef, useState } from "react"; -import { - GA_EVENT_HOMEPAGE_CLICK, - GA_PARAM_ID, - triggerGAEvent, -} from "../../shared/ga_events"; +import { GA_PARAM_ID, triggerGAEvent } from "../../shared/ga_events"; interface SlideCarouselProps { //an array of ReactElements, each of which will be a slide in the carousel slides: ReactElement[]; //an optional autoslide interval - if set, the carousel will slide automatically at that interval until the user interacts with it autoslideInterval?: number | null; + //an optional GA event - if set, the carousel will trigger a GA event on the slide changing. + gaEvent?: string; } const SlideCarousel = ({ slides, autoslideInterval, + gaEvent, }: SlideCarouselProps): ReactElement => { const [activeIndex, setActiveIndex] = useState(0); - const [isInteracting, setIsInteracting] = useState(false); + const [userHasInteracted, setUserHasInteracted] = useState(false); const carouselRef = useRef(null); + const theme = useTheme(); + const goToSlide = (index: number): void => { - setIsInteracting(true); + setUserHasInteracted(true); setActiveIndex(index); - // TODO: Pass in GA event so the component can be used on other pages. - triggerGAEvent(GA_EVENT_HOMEPAGE_CLICK, { - [GA_PARAM_ID]: `carousel ${index}`, - }); + if (gaEvent) { + triggerGAEvent(gaEvent, { + [GA_PARAM_ID]: `carousel ${index}`, + }); + } }; useEffect(() => { @@ -57,13 +62,13 @@ const SlideCarousel = ({ }, [slides.length, activeIndex]); useEffect(() => { - if (autoslideInterval && !isInteracting) { + if (autoslideInterval && !userHasInteracted) { const interval = setInterval(() => { setActiveIndex((prevIndex) => (prevIndex + 1) % slides.length); }, autoslideInterval); return () => clearInterval(interval); } - }, [autoslideInterval, slides.length, activeIndex, isInteracting]); + }, [autoslideInterval, slides.length, activeIndex, userHasInteracted]); useEffect(() => { const handleFocusIn = (event: FocusEvent): void => { @@ -100,25 +105,40 @@ const SlideCarousel = ({ }, [activeIndex]); return ( -
-
+
+
{slides.map((slide, index) => (
{slide}
@@ -126,8 +146,24 @@ const SlideCarousel = ({
{slides.length > 1 && ( -
-
    +
    +
      {slides.map((_, index) => (
    • goToSlide(index)} + css={css` + display: inline-block; + width: 10px; + height: 10px; + background: white; + border: 1px solid ${theme.colors.link.primary.base}; + border-radius: 50%; + cursor: pointer; + &.active { + background: ${theme.colors.link.primary.base}; + } + `} /> ))}
    diff --git a/static/js/components/knowledge_graph/knowledge_graph_browser.tsx b/static/js/components/knowledge_graph/knowledge_graph_browser.tsx deleted file mode 100644 index 4075f1c286..0000000000 --- a/static/js/components/knowledge_graph/knowledge_graph_browser.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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. - */ - -/** - * A component to display a category of the knowledge graph - */ - -import React, { ReactElement } from "react"; - -import { KnowledgeGraph } from "../../shared/types/knowledge_graph"; - -interface KnowledgeGraphBrowserProps { - //an object containing the categorized links that make up the knowledge graph - knowledgeGraph: KnowledgeGraph; -} - -export const KnowledgeGraphBrowser = ({ - knowledgeGraph, -}: KnowledgeGraphBrowserProps): ReactElement => { - return ( -
    -
    - {knowledgeGraph.map((category) => ( - - ))} -
    -
    - ); -}; diff --git a/static/js/theme/emotion.d.ts b/static/js/theme/emotion.d.ts new file mode 100644 index 0000000000..be7079446c --- /dev/null +++ b/static/js/theme/emotion.d.ts @@ -0,0 +1,8 @@ +import "@emotion/react"; + +import { Theme as CustomTheme } from "./types"; + +declare module "@emotion/react" { + // eslint-disable-next-line @typescript-eslint/no-empty-interface + export interface Theme extends CustomTheme {} +} diff --git a/static/js/theme/theme.ts b/static/js/theme/theme.ts new file mode 100644 index 0000000000..437e3276a0 --- /dev/null +++ b/static/js/theme/theme.ts @@ -0,0 +1,298 @@ +/** + * 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. + */ + +/** + * The default Emotion theme for Data Commons + */ + +import { Theme } from "./types"; + +const BREAKPOINTS = { + xs: 0, + sm: 576, + md: 768, + lg: 1068, + xl: 1350, +}; + +const SPACING = { + xs: 4, + sm: 8, + md: 16, + lg: 24, + xl: 32, + xxl: 48, + huge: 64, +}; + +const SECTIONS = { + compact: 0, + small: 32, + standard: 64, + large: 110, +}; + +const WIDTH = { + xs: 0, + sm: 645, + md: 750, + lg: 1040, + xl: 1310, +}; + +const DC_BLACK = "hsl(0, 0%, 18.82%)"; + +const DC_WHITE = "hsl(255, 100%, 100%)"; +const DC_BONE = "hsl(216, 55%, 98%)"; + +const DC_BLUE = "hsl(217, 90%, 43%)"; +const DC_BLUE_PILL_TEXT = "hsl(217, 90%, 15%)"; +const DC_BLUE_PILL_BCKG = "hsl(218, 92%, 95%)"; +const DC_BLUE_PILL_BCKG_HOVER = "hsl(204, 90%, 80%)"; + +const DC_BLUE_DARK = "hsl(217.4, 89.6%, 15.1%)"; +const DC_BLUE_LIGHT = "hsl(218, 57.1%, 62.5%)"; +const DC_BLUE_LIGHTER = "hsl(204, 100%, 88%)"; +const DC_BLUE_WHITE = "hsl(220, 100%, 98.2%)"; + +const DC_GREEN = "hsl(137, 68%, 25%)"; +const DC_GREEN_PILL_TEXT = "hsl(139, 70%, 9%)"; +const DC_GREEN_PILL_BCKG = "hsl(137, 55%, 85%)"; + +const DC_RED = "hsl(3.2, 71.3%, 41%)"; +const DC_RED_PILL_TEXT = "hsl(3.3, 71.1%, 14.9%)"; +const DC_RED_PILL_BCKG = "hsl(4.1, 70.7%, 92%)"; + +const DC_YELLOW = "hsl(35.3, 100%, 29%)"; +const DC_YELLOW_PILL_TEXT = "hsl(3.3, 71.1%, 14.9%)"; +const DC_YELLOW_PILL_BCKG = "hsl(40.4, 100%, 91%)"; + +const DC_GRAY = "hsl(160, 2.2%, 27.3%)"; +const DC_GRAY_PILL_TEXT = DC_YELLOW; +const DC_GRAY_PILL_BCKG = "hsl(240, 20.8%, 90.6%)"; +const DC_GRAY_LIGHT = "hsl(0, 0%, 78%)"; + +const theme: Theme = { + breakpoints: BREAKPOINTS, + spacing: SPACING, + sections: SECTIONS, + width: WIDTH, + colors: { + text: { + primary: { + base: DC_BLACK, + dark: DC_BLUE_DARK, + light: DC_WHITE, + }, + }, + background: { + primary: { + base: DC_WHITE, + dark: DC_BLUE_DARK, + light: DC_BLUE_WHITE, + }, + }, + border: { + primary: { + base: DC_WHITE, + dark: DC_BLUE_DARK, + light: DC_GRAY_LIGHT, + }, + }, + box: { + blue: { + text: DC_BLUE, + tag: DC_BLUE_PILL_TEXT, + pill: DC_BLUE_PILL_BCKG, + }, + green: { + text: DC_GREEN, + tag: DC_GREEN_PILL_TEXT, + pill: DC_GREEN_PILL_BCKG, + }, + red: { + text: DC_RED, + tag: DC_RED_PILL_TEXT, + pill: DC_RED_PILL_BCKG, + }, + yellow: { + text: DC_YELLOW, + tag: DC_YELLOW_PILL_TEXT, + pill: DC_YELLOW_PILL_BCKG, + }, + grey: { + text: DC_GRAY, + tag: DC_GRAY_PILL_TEXT, + pill: DC_GRAY_PILL_BCKG, + }, + }, + link: { + primary: { + base: DC_BLUE, + light: DC_BLUE_LIGHT, + dark: DC_BLUE_DARK, + }, + secondary: { + base: DC_GRAY, + light: DC_GRAY_LIGHT, + dark: DC_BLACK, + }, + }, + button: { + primary: { + base: DC_BLUE, + light: DC_BLUE_LIGHT, + dark: DC_BLUE_DARK, + }, + }, + }, + typography: { + family: { + text: { + fontFamily: "'Google Sans Text', Arial, sans-serif", + fontDisplay: "swap", + fontStyle: "normal", + }, + heading: { + fontFamily: "'Google Sans', Arial, sans-serif", + fontDisplay: "swap", + fontStyle: "normal", + }, + }, + text: { + xs: { + fontSize: "0.75rem", + lineHeight: "0.625rem", + fontWeight: 400, + }, + sm: { + fontSize: "0.875rem", + lineHeight: "1.25rem", + fontWeight: 400, + }, + md: { + fontSize: "1rem", + lineHeight: "1.5rem", + fontWeight: 400, + }, + lg: { + fontSize: "1.375rem", + lineHeight: "1.75rem", + fontWeight: 400, + [`@media (max-width: ${BREAKPOINTS.sm}px)`]: { + fontSize: "1rem", + lineHeight: "1.5rem", + }, + }, + xl: { + fontSize: "1.5rem", + lineHeight: "2rem", + fontWeight: 400, + [`@media (max-width: ${BREAKPOINTS.sm}px)`]: { + fontSize: "1.375rem", + lineHeight: "1.75rem", + }, + }, + }, + heading: { + xs: { + fontSize: "1.375rem", + lineHeight: "1.75rem", + fontWeight: 300, + }, + sm: { + fontSize: "1.75rem", + lineHeight: "2.25rem", + fontWeight: 300, + [`@media (max-width: ${BREAKPOINTS.md}px)`]: { + fontSize: "1.5rem", + lineHeight: "2rem", + }, + }, + md: { + fontSize: "2rem", + lineHeight: "2.5rem", + fontWeight: 300, + [`@media (max-width: ${BREAKPOINTS.md}px)`]: { + fontSize: "1.7rem", + lineHeight: "2.25rem", + }, + }, + lg: { + fontSize: "2.25rem", + lineHeight: "2.75rem", + fontWeight: 300, + [`@media (max-width: ${BREAKPOINTS.md}px)`]: { + fontSize: "2rem", + lineHeight: "2.7rem", + }, + }, + xl: { + fontSize: "3rem", + lineHeight: "2.75rem", + fontWeight: 300, + [`@media (max-width: ${BREAKPOINTS.md}px)`]: { + fontSize: "2.4rem", + lineHeight: "2.75rem", + }, + }, + }, + }, + box: { + primary: { + backgroundColor: DC_BONE, + textDecoration: "none", + ["&:hover"]: { + backgroundColor: DC_BLUE_PILL_BCKG, + }, + }, + secondary: { + backgroundColor: DC_BLUE_LIGHTER, + textDecoration: "none", + ["&:hover"]: { + backgroundColor: DC_BLUE_PILL_BCKG_HOVER, + }, + }, + }, + elevation: { + none: { + boxShadow: "none", + }, + primary: { + boxShadow: + "0px 1px 2px hsla(0, 0%, 0%, 0.3), 0px 1px 3px 1px hsla(0, 0%, 0%, 0.15)", + }, + secondary: { + boxShadow: "0 2px 5px hsla(0,0%,0%,0.1)", + }, + }, + radius: { + none: { + borderRadius: "0", + }, + full: { + borderRadius: "1000px", + }, + primary: { + borderRadius: "32px", + }, + secondary: { + borderRadius: "20px", + }, + }, +}; + +export default theme; diff --git a/static/js/theme/types.ts b/static/js/theme/types.ts new file mode 100644 index 0000000000..4faede11ce --- /dev/null +++ b/static/js/theme/types.ts @@ -0,0 +1,361 @@ +/** + * 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. + */ + +/** + * The interface for the default Emotion theme for Data Commons + */ + +export interface Theme { + breakpoints: { + xs: number; + sm: number; + md: number; + lg: number; + xl: number; + }; + spacing: { + xs: number; + sm: number; + md: number; + lg: number; + xl: number; + xxl: number; + huge: number; + }; + sections: { + compact: number; + small: number; + standard: number; + large: number; + }; + width: { + sm: number; + md: number; + lg: number; + xl: number; + }; + colors: { + text?: { + primary?: { + base?: string; + light?: string; + dark?: string; + }; + secondary?: { + base?: string; + light?: string; + dark?: string; + }; + tertiary?: { + base?: string; + light?: string; + dark?: string; + }; + }; + border?: { + primary?: { + base?: string; + light?: string; + dark?: string; + }; + }; + background?: { + primary?: { + base?: string; + light?: string; + dark?: string; + }; + secondary?: { + base?: string; + light?: string; + dark?: string; + }; + tertiary?: { + base?: string; + light?: string; + dark?: string; + }; + }; + button?: { + primary?: { + base?: string; + light?: string; + dark?: string; + }; + secondary?: { + base?: string; + light?: string; + dark?: string; + }; + tertiary?: { + base?: string; + light?: string; + dark?: string; + }; + }; + box: { + blue: { + text: string; + tag: string; + pill: string; + }; + green: { + text: string; + tag: string; + pill: string; + }; + red: { + text: string; + tag: string; + pill: string; + }; + yellow: { + text: string; + tag: string; + pill: string; + }; + grey: { + text: string; + tag: string; + pill: string; + }; + }; + link?: { + primary?: { + base?: string; + light?: string; + dark?: string; + }; + secondary?: { + base?: string; + light?: string; + dark?: string; + }; + tertiary?: { + base?: string; + light?: string; + dark?: string; + }; + }; + }; + typography: { + family: { + text: { + fontFamily: string; + fontDisplay: string; + fontStyle: string; + }; + heading: { + fontFamily: string; + fontDisplay: string; + fontStyle: string; + }; + extra?: { + fontFamily: string; + fontDisplay: string; + fontStyle: string; + }; + code?: { + fontFamily: string; + fontDisplay: string; + fontStyle: string; + }; + }; + // Text Variants + text: { + xs: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + sm: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + md: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + lg: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + xl: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + }; + // heading Variants + heading: { + xs: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + sm: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + md: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + lg: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + xl: { + fontSize: string; + lineHeight: string; + fontWeight: number; + [key: string]: + | string + | number + | { + fontSize?: string; + lineHeight?: string; + }; + }; + }; + }; + box: { + primary?: { + backgroundColor: string; + textDecoration: string; + [key: string]: + | string + | number + | { + backgroundColor: string; + }; + }; + secondary?: { + backgroundColor: string; + textDecoration: string; + [key: string]: + | string + | number + | { + backgroundColor: string; + }; + }; + tertiary?: { + backgroundColor: string; + textDecoration: string; + [key: string]: + | string + | number + | { + backgroundColor: string; + }; + }; + }; + elevation: { + none: { + boxShadow: string; + }; + primary: { + boxShadow: string; + }; + secondary: { + boxShadow: string; + }; + }; + radius: { + none: { + borderRadius: string; + }; + full: { + borderRadius: string; + }; + primary: { + borderRadius: string; + }; + secondary: { + borderRadius: string; + }; + }; +} diff --git a/static/package-lock.json b/static/package-lock.json index 52636581cd..691df4c375 100644 --- a/static/package-lock.json +++ b/static/package-lock.json @@ -11,6 +11,8 @@ "dependencies": { "@datacommonsorg/client": "file:../packages/client", "@datacommonsorg/web-components": "file:../packages/web-components", + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", "@types/d3": "^5.7.2", "@types/d3-scale": "^4.0.2", "@types/express": "^4.17.17", @@ -201,7 +203,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "dependencies": { "@babel/highlight": "^7.10.4" } @@ -395,7 +396,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, @@ -456,7 +456,6 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -465,7 +464,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -497,7 +495,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -777,7 +774,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", @@ -847,6 +843,80 @@ "node": ">=10.0.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", + "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.2.0", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", + "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/cache/node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", @@ -860,11 +930,125 @@ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, + "node_modules/@emotion/react": { + "version": "11.13.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz", + "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/cache": "^11.13.0", + "@emotion/serialize": "^1.3.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", + "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/serialize/node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz", + "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/styled/node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/styled/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, "node_modules/@emotion/unitless": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", + "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", + "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -5595,6 +5779,12 @@ "optional": true, "peer": true }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, "node_modules/@types/parse5": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", @@ -6653,7 +6843,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -7058,6 +7247,21 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/babel-preset-current-node-syntax": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", @@ -7807,7 +8011,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -7821,7 +8024,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -8271,7 +8473,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -8492,7 +8693,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "dependencies": { "safe-buffer": "~5.1.1" } @@ -8599,6 +8799,22 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -9897,7 +10113,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -9993,7 +10208,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -11237,6 +11451,12 @@ "node": ">=6" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -13471,7 +13691,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -13487,7 +13706,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -13731,8 +13949,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -18268,8 +18485,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/lit": { "version": "2.7.5", @@ -20584,7 +20800,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -20608,7 +20823,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -23446,7 +23660,6 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -24495,7 +24708,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, "engines": { "node": ">=4" } @@ -26393,6 +26605,15 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -26471,7 +26692,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -26617,7 +26837,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, "requires": { "@babel/types": "^7.18.6" } @@ -26665,14 +26884,12 @@ "@babel/helper-string-parser": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.18.6", @@ -26695,7 +26912,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -26915,7 +27131,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, "requires": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", @@ -27025,6 +27240,70 @@ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==" }, + "@emotion/babel-plugin": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", + "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.2.0", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + }, + "dependencies": { + "@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + } + } + }, + "@emotion/cache": { + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", + "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "requires": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + }, + "dependencies": { + "@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + } + } + }, + "@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, "@emotion/is-prop-valid": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", @@ -27038,11 +27317,99 @@ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, + "@emotion/react": { + "version": "11.13.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz", + "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/cache": "^11.13.0", + "@emotion/serialize": "^1.3.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", + "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", + "requires": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.1", + "csstype": "^3.0.2" + }, + "dependencies": { + "@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + } + } + }, + "@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + }, + "@emotion/styled": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz", + "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0" + }, + "dependencies": { + "@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "requires": { + "@emotion/memoize": "^0.9.0" + } + }, + "@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + } + } + }, "@emotion/unitless": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", + "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", + "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==" + }, + "@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -31045,6 +31412,11 @@ "optional": true, "peer": true }, + "@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, "@types/parse5": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", @@ -31892,7 +32264,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -32209,6 +32580,16 @@ "@types/babel__traverse": "^7.0.6" } }, + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + }, "babel-preset-current-node-syntax": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", @@ -32789,7 +33170,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -32800,7 +33180,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -33155,7 +33534,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -33315,7 +33693,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -33396,6 +33773,18 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -34476,7 +34865,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -34550,8 +34938,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "2.0.0", @@ -35494,6 +35881,11 @@ "pkg-dir": "^3.0.0" } }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -37231,7 +37623,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -37240,8 +37631,7 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" } } }, @@ -37443,8 +37833,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-binary-path": { "version": "2.1.0", @@ -40961,8 +41350,7 @@ "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "lit": { "version": "2.7.5", @@ -42665,7 +43053,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" } @@ -42686,7 +43073,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -44854,8 +45240,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "peer": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-js": { "version": "1.0.2", @@ -45657,8 +46042,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-image-data": { "version": "0.0.2", @@ -47037,6 +47421,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, "yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/static/package.json b/static/package.json index 49544600fc..d45e8102f2 100644 --- a/static/package.json +++ b/static/package.json @@ -34,6 +34,8 @@ "@types/react-table": "^7.7.12", "@types/reactstrap": "^8.7.1", "@types/resize-observer-browser": "^0.1.6", + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", "axios": "^1.6.8", "bootstrap": "^4.5.2", "canvas": "2.11.2",