diff --git a/README.md b/README.md index f941cf8c99..e5271cf53a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ give a broad perspective on the subject. ## Contact -If you want to contact the dashboard team, feel free to open an issue for +If you want to contact the dashboard team, feel free to open an issue for technical questions, bug reports or security findings. If you have a generic question or remark about the corona policy of the Dutch government, please consult the [frequently asked questions](https://coronadashboard.rijksoverheid.nl/veelgestelde-vragen) or [contact page](https://coronadashboard.rijksoverheid.nl/contact) on the dashboard. @@ -31,7 +31,7 @@ multiple packages. - `common`: Commonly shared code that multiple packages are using, like types and utils. - `e2e`: End-to-end tests using Cypress -- `icons`: A React icon component library, used by the CMS and by app +- `icons`: A React icon component library, used by the CMS and by app. [README](/packages/icons/README.md) ## Getting started (quickly) diff --git a/packages/app/schema/topical/__index.json b/packages/app/schema/topical/__index.json new file mode 100644 index 0000000000..f82c3183b1 --- /dev/null +++ b/packages/app/schema/topical/__index.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "topical", + "additionalProperties": false, + "required": ["version", "title", "dynamicDescription", "themes", "measures"], + "properties": { + "version": { + "type": "string" + }, + "title": { + "$ref": "multilanguage_string.json" + }, + "dynamicDescription": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "dynamic_description.json" + } + }, + "themes": { + "type": "array", + "minItems": 1, + "maxItems": 2, + "items": { + "$ref": "theme.json" + } + }, + "measures": { + "$ref": "measures.json" + } + } +} diff --git a/packages/app/schema/topical/dynamic_description.json b/packages/app/schema/topical/dynamic_description.json new file mode 100644 index 0000000000..3725e287d3 --- /dev/null +++ b/packages/app/schema/topical/dynamic_description.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "topical_dynamic_description", + "type": "object", + "additionalProperties": false, + "required": ["index", "content"], + "properties": { + "index": { + "type": "number" + }, + "content": { + "$ref": "multilanguage_string.json" + } + } +} diff --git a/packages/app/schema/topical/icon.json b/packages/app/schema/topical/icon.json new file mode 100644 index 0000000000..52fe670437 --- /dev/null +++ b/packages/app/schema/topical/icon.json @@ -0,0 +1,135 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "topical_icon", + "type": "string", + "enum": [ + "AfstandSporten", + "AlcoholVerkoop", + "Arrow", + "Arts", + "Avondklok", + "BarChart", + "BasisregelsAfstand", + "BasisregelsDrukte", + "BasisregelsElleboog", + "BasisregelsGeenBezoek", + "BasisregelsHandenwassen", + "BasisregelsMondkapje", + "BasisregelsTesten", + "Bevolking", + "Bezoek", + "Bibliotheek", + "BinnenMetZitplaats", + "BinnenZonderZitplaats", + "Binnensporten", + "Binnensportlocaties", + "Boosterprik", + "Calendar", + "Check", + "Checked", + "ChevronDown", + "ChevronRight", + "Clock", + "Close", + "CloseThick", + "ContactBeroepen", + "Coronavirus", + "Cross", + "Database", + "DoorstroomEvenementen", + "Dot", + "Down", + "Download", + "EenPersoonDoorgestreept", + "Elderly", + "Expand", + "Experimenteel", + "External", + "Eye", + "FrisseLucht", + "GedeeltelijkOpenRugzak", + "GeenEntertainment", + "GeenMaxAantalBezoekers", + "GeenWedstrijden", + "Gehandicaptenzorg", + "GeorganiseerdeKunstEnCultuurbeoefening", + "GgdTesten", + "Groepen", + "HealthCare", + "HomeAndVisits", + "HorecaEnEvenementenBestellen", + "HorecaEnEvenementenEtendrinken", + "HorecaEnEvenementenEvenementen", + "HorecaEvenementen", + "Hospitality", + "Information", + "IntensiveCareOpnames", + "Klachten", + "Klok210001", + "KunstCultuur", + "KunstcultuurMusea", + "Line", + "Locaties", + "Location", + "Lopend", + "Maatregelen", + "MaxAantalBezoekers", + "MaxVisitors", + "MedischeScreening", + "MeerInformatie", + "MeerdaagseEvenementen", + "Menu", + "Mondkapje", + "Nederland", + "Notification", + "OnderwijsEnKinderopvangNoodopvang", + "OnderwijsEnKinderopvangOpAfstand", + "OntmoetingenBezoek", + "OpenbaarVervoer", + "Openingstijden", + "Other", + "Overige", + "Phone", + "Recreatie", + "Reizen", + "ReproductieGraf", + "Reproductiegetal", + "Rioolvirus", + "SearchIcon", + "SearchIconBold", + "SportBuiten", + "SportMetZweetband", + "SportWedstrijden", + "Stap1HorecaMax", + "Stap1HorecaPertafel", + "Stap1HorecaVerplaatsen", + "Stap1OnderwijsBibliotheek", + "Stap1OnderwijsOpen", + "Stap1Theorie", + "Stap1Thuisbezoek", + "Stap1Uitvaarten", + "Stap1WinkelsAlleen", + "Stap1WinkelsMarkten", + "Stap1WinkelsOpen", + "Stopwatch", + "Taxi", + "Testbewijs", + "Toegangsbewijzen", + "Travel", + "Unchecked", + "Up", + "Vaccinaties", + "Varianten", + "Verpleeghuis", + "VervoerEnReizenBuitenland", + "VervoerEnReizenOv", + "Vliegen", + "Warn", + "Warning", + "WinkelenEnBoodschappenAlcohol", + "WinkelenEnBoodschappenOpen", + "Work", + "Ziekenhuis", + "Ziektegolf" + ] +} diff --git a/packages/app/schema/topical/link.json b/packages/app/schema/topical/link.json new file mode 100644 index 0000000000..a91796a1d7 --- /dev/null +++ b/packages/app/schema/topical/link.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "topical_theme_link", + "type": "object", + "additionalProperties": false, + "required": ["index", "label", "href"], + "properties": { + "index": { + "type": "number" + }, + "label": { + "$ref": "multilanguage_string.json" + }, + "href": { + "$ref": "multilanguage_string.json" + } + } +} diff --git a/packages/app/schema/topical/measure_tile.json b/packages/app/schema/topical/measure_tile.json new file mode 100644 index 0000000000..c0ceaa1b5d --- /dev/null +++ b/packages/app/schema/topical/measure_tile.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "topical_measures_tile", + "type": "object", + "additionalProperties": false, + "required": ["index", "title", "icon"], + "properties": { + "index": { + "type": "number" + }, + "title": { + "$ref": "multilanguage_string.json" + }, + "icon": { + "$ref": "icon.json" + } + } +} diff --git a/packages/app/schema/topical/measures.json b/packages/app/schema/topical/measures.json new file mode 100644 index 0000000000..7480fdc015 --- /dev/null +++ b/packages/app/schema/topical/measures.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "topical_measures", + "additionalProperties": false, + "required": ["title", "dynamicSubtitle", "icon", "measureTiles"], + "properties": { + "title": { + "$ref": "multilanguage_string.json" + }, + "dynamicSubtitle": { + "$ref": "multilanguage_string.json" + }, + "icon": { + "$ref": "icon.json" + }, + "measureTiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "measure_tile.json" + } + } + } +} diff --git a/packages/app/schema/topical/multilanguage_string.json b/packages/app/schema/topical/multilanguage_string.json new file mode 100644 index 0000000000..bf6db3b7c2 --- /dev/null +++ b/packages/app/schema/topical/multilanguage_string.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "multilanguage_string", + "additionalProperties": false, + "required": ["NL", "EN"], + "properties": { + "NL": { + "type": "string" + }, + "EN": { + "type": "string" + } + } +} diff --git a/packages/app/schema/topical/theme.json b/packages/app/schema/topical/theme.json new file mode 100644 index 0000000000..aa1a823c0e --- /dev/null +++ b/packages/app/schema/topical/theme.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "topical_theme", + "type": "object", + "additionalProperties": false, + "required": [ + "index", + "title", + "dynamicSubtitle", + "icon", + "themeTiles", + "moreLinks" + ], + "properties": { + "index": { + "type": "number" + }, + "title": { + "$ref": "multilanguage_string.json" + }, + "dynamicSubtitle": { + "$ref": "multilanguage_string.json" + }, + "icon": { + "$ref": "icon.json" + }, + "themeTiles": { + "type": "array", + "minItems": 1, + "maxItems": 3, + "items": { + "$ref": "theme_tile.json" + } + }, + "moreLinks": { + "type": "object", + "additionalProperties": false, + "required": ["label", "links"], + "properties": { + "label": { + "type": "object", + "additionalProperties": false, + "required": ["DESKTOP", "MOBILE"], + "properties": { + "DESKTOP": { + "$ref": "multilanguage_string.json" + }, + "MOBILE": { + "$ref": "multilanguage_string.json" + } + } + }, + "links": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "link.json" + } + } + } + } + } +} diff --git a/packages/app/schema/topical/theme_tile.json b/packages/app/schema/topical/theme_tile.json new file mode 100644 index 0000000000..23f39c3b4d --- /dev/null +++ b/packages/app/schema/topical/theme_tile.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "topical_theme_tile", + "type": "object", + "additionalProperties": false, + "required": [ + "index", + "title", + "dynamicDescription", + "trendIcon", + "tileIcon", + "cta" + ], + "properties": { + "index": { + "type": "number" + }, + "title": { + "$ref": "multilanguage_string.json" + }, + "dynamicDescription": { + "$ref": "multilanguage_string.json" + }, + "trendIcon": { + "type": ["object", "null"], + "required": ["direction", "color"], + "additionalProperties": false, + "properties": { + "direction": { + "type": "string", + "enum": ["UP", "DOWN"] + }, + "color": { + "type": "string", + "enum": ["GREEN", "RED"] + } + } + }, + "tileIcon": { + "$ref": "icon.json" + }, + "cta": { + "type": ["object", "null"], + "required": ["label", "href"], + "additionalProperties": false, + "properties": { + "label": { + "$ref": "multilanguage_string.json" + }, + "href": { + "$ref": "multilanguage_string.json" + } + } + } + } +} diff --git a/packages/app/src/components/get-icon-by-name.tsx b/packages/app/src/components/get-icon-by-name.tsx new file mode 100644 index 0000000000..cd5801502d --- /dev/null +++ b/packages/app/src/components/get-icon-by-name.tsx @@ -0,0 +1,38 @@ +import * as allIcons from '@corona-dashboard/icons'; +import { iconName2filename, IconProps } from '@corona-dashboard/icons'; +import React from 'react'; +import { isDefined } from 'ts-is-present'; +import { assert } from '~/utils/assert'; + +type IconName = keyof typeof iconName2filename; +type IconCollection = Record; + +interface DynamicIconProps extends IconProps { + name: IconName; +} + +/** + * Returns an icon component by the name of the icon + * Throws when requested component was not found + */ +function getIconByName(name: IconName) { + const icons: IconCollection = allIcons; + const DynamicIcon = icons[name]; + + assert( + isDefined(DynamicIcon), + `[${getIconByName.name}] Icon with name "${name}" does not exist` + ); + + return DynamicIcon; +} + +/** + * Renders an icon component by the name of the icon + */ +function DynamicIcon({ name, ...otherProps }: DynamicIconProps) { + const Icon = getIconByName(name); + return ; +} + +export default DynamicIcon; diff --git a/packages/app/src/components/layout/app-footer.tsx b/packages/app/src/components/layout/app-footer.tsx index caac0bc9ba..3d2ad56112 100644 --- a/packages/app/src/components/layout/app-footer.tsx +++ b/packages/app/src/components/layout/app-footer.tsx @@ -40,7 +40,7 @@ export function AppFooter() {