diff --git a/packages/app/src/components/anchor-tile.tsx b/packages/app/src/components/anchor-tile.tsx index 3a952bef1c..110367d79a 100644 --- a/packages/app/src/components/anchor-tile.tsx +++ b/packages/app/src/components/anchor-tile.tsx @@ -3,6 +3,7 @@ import { External as ExternalLinkIcon } from '@corona-dashboard/icons'; import css from '@styled-system/css'; import styled from 'styled-components'; import { Anchor, Heading } from '~/components/typography'; +import { Box } from '~/components/base'; import { asResponsiveArray } from '~/style/utils'; import { Link } from '~/utils/link'; import { ExternalLink } from './external-link'; @@ -31,26 +32,34 @@ export function AnchorTile({ - {!external && ( + {external ? ( + + + + + + {label} + + + ) : ( {label} )} - {external && ( - <> - - - - {label} - - )} ); } +export const IconWrapper = styled.span( + css({ + mr: 2, + svg: { width: 24, height: 11, display: 'block', maxWidth: 'initial' }, + }) +); + const Container = styled.article( css({ display: 'flex', @@ -74,13 +83,6 @@ const StyledAnchor = styled(Anchor)( }) ); -const IconContainer = styled.span( - css({ - mr: 2, - svg: { width: 24, height: 24, display: 'block', maxWidth: 'initial' }, - }) -); - const LinkContainer = styled.div( css({ flexShrink: 1, diff --git a/packages/app/src/components/aside/menu.tsx b/packages/app/src/components/aside/menu.tsx index 9f5ab63021..ab28b4e17f 100644 --- a/packages/app/src/components/aside/menu.tsx +++ b/packages/app/src/components/aside/menu.tsx @@ -6,7 +6,7 @@ import styled from 'styled-components'; import { UrlObject } from 'url'; import chevronUrl from '~/assets/chevron.svg'; import { Box } from '~/components/base'; -import { Anchor, Heading, Text } from '~/components/typography'; +import { Anchor, Heading } from '~/components/typography'; import { ExpandedSidebarMap, Layout } from '~/domain/layout/logic/types'; import { SpaceValue } from '~/style/theme'; import { asResponsiveArray } from '~/style/utils'; @@ -52,33 +52,19 @@ export function Menu({ export function CategoryMenu({ title, - description, children, + icon, }: { children: ReactNode; title?: string; - description?: string; + icon: ReactNode; }) { return ( - {title && ( - - - {title} - - - )} - {description && ( - - - {description} - + {title && icon && ( + + {icon} + {title} )} {children} @@ -93,7 +79,7 @@ interface MenuItemLinkProps { showArrow?: boolean; } -export function MenuItemLink({ href, icon, title }: MenuItemLinkProps) { +export function MenuItemLink({ href, title }: MenuItemLinkProps) { const router = useRouter(); const breakpoints = useBreakpoints(true); @@ -101,7 +87,7 @@ export function MenuItemLink({ href, icon, title }: MenuItemLinkProps) { return (
  • - +
  • ); @@ -116,11 +102,7 @@ export function MenuItemLink({ href, icon, title }: MenuItemLinkProps) { isActive={breakpoints.md && isActive} aria-current={isActive ? 'page' : undefined} > - + @@ -145,26 +127,28 @@ const Unavailable = styled.span( }) ); -const StyledAnchor = styled(Anchor)<{ isActive: boolean }>((x) => +const StyledAnchor = styled(Anchor)<{ isActive: boolean }>((anchorProps) => css({ p: 2, + pl: '3rem', display: 'block', borderRight: '5px solid transparent', - color: x.isActive ? 'blue' : 'black', + color: anchorProps.isActive ? 'blue' : 'black', + fontWeight: anchorProps.isActive ? 'bold' : 'normal', position: 'relative', - bg: x.isActive ? 'lightBlue' : 'transparent', - borderRightColor: x.isActive ? 'sidebarLinkBorder' : 'transparent', - - '&:hover': { - bg: 'offWhite', - }, - - '&:focus': { - bg: '#ebebeb', + bg: anchorProps.isActive ? 'lightBlue' : 'transparent', + borderRightColor: anchorProps.isActive + ? 'sidebarLinkBorder' + : 'transparent', + + '&:hover, &:focus': { + bg: 'blue', + color: 'white', + fontWeight: 'bold', }, '&::after': { - content: x.isActive + content: anchorProps.isActive ? 'none' : asResponsiveArray({ _: 'none', xs: undefined }), backgroundImage: `url('${chevronUrl}')`, @@ -179,3 +163,31 @@ const StyledAnchor = styled(Anchor)<{ isActive: boolean }>((x) => }, }) ); + +const Icon = ({ children }: { children: ReactNode }) => { + return ( + + ); +}; diff --git a/packages/app/src/components/aside/title.tsx b/packages/app/src/components/aside/title.tsx index 6c4d26b808..8c361f542d 100644 --- a/packages/app/src/components/aside/title.tsx +++ b/packages/app/src/components/aside/title.tsx @@ -18,7 +18,7 @@ type TitleProps = { * @param props */ export function AsideTitle(props: TitleProps) { - const { icon, title, subtitle, showArrow } = props; + const { title, subtitle, showArrow } = props; return ( - {icon && {icon}} - - + )} @@ -55,31 +53,3 @@ export function AsideTitle(props: TitleProps) { ); } - -function Icon({ children }: { children: ReactNode }) { - return ( - - ); -} diff --git a/packages/app/src/components/choropleth/components/canvas-choropleth-map.tsx b/packages/app/src/components/choropleth/components/canvas-choropleth-map.tsx index ff53e727d3..3f72515264 100644 --- a/packages/app/src/components/choropleth/components/canvas-choropleth-map.tsx +++ b/packages/app/src/components/choropleth/components/canvas-choropleth-map.tsx @@ -83,7 +83,7 @@ export const CanvasChoroplethMap = (props: CanvasChoroplethMapProps) => { const [hover, setHover] = useState<[number, number][][]>(); const [hoverCode, setHoverCode] = useState(); - const [keyboardActive, setKeyboardActive] = useState(false); + const [isKeyboardActive, setIsKeyboardActive] = useState(false); const [geoInfo, geoInfoLookup] = useProjectedCoordinates( choroplethFeatures.hover, @@ -101,7 +101,7 @@ export const CanvasChoroplethMap = (props: CanvasChoroplethMapProps) => { const selectFeature = useCallback( (code: CodeProp | undefined, isKeyboardAction = false) => { - setKeyboardActive(isKeyboardAction); + setIsKeyboardActive(isKeyboardAction); if (!isDefined(code)) { return; } @@ -132,7 +132,7 @@ export const CanvasChoroplethMap = (props: CanvasChoroplethMapProps) => { const stageRef = useRef(null); useEffect(() => { - if (!keyboardActive) { + if (!isKeyboardActive) { return; } @@ -150,7 +150,7 @@ export const CanvasChoroplethMap = (props: CanvasChoroplethMapProps) => { const y = Math.round(box.y + box.height / 2); tooltipTrigger({ code: hoverCode, x, y }); } - }, [hoverCode, hoveredRef, tooltipTrigger, keyboardActive]); + }, [hoverCode, hoveredRef, tooltipTrigger, isKeyboardActive]); const { getLink } = dataOptions; @@ -209,6 +209,7 @@ export const CanvasChoroplethMap = (props: CanvasChoroplethMapProps) => { hover={hover} hoverCode={hoverCode} featureProps={featureProps} + isKeyboardActive={isKeyboardActive} />
    @@ -261,10 +262,12 @@ type HoveredFeatureProps = { hover: [number, number][][] | undefined; hoverCode: string | undefined; featureProps: FeatureProps; + isKeyboardActive?: boolean; }; const HoveredFeature = memo((props: HoveredFeatureProps) => { - const { hoveredRef, hover, hoverCode, featureProps } = props; + const { hoveredRef, hover, hoverCode, featureProps, isKeyboardActive } = + props; if (!isDefined(hoverCode) || !isDefined(hover)) { return null; @@ -282,7 +285,11 @@ const HoveredFeature = memo((props: HoveredFeatureProps) => { points={x.flat()} strokeWidth={featureProps.hover.strokeWidth(hoverCode, true)} closed - stroke={featureProps.hover.stroke(hoverCode, true)} + stroke={featureProps.hover.stroke( + hoverCode, + true, + isKeyboardActive + )} /> ))} @@ -395,6 +402,11 @@ type AreaMapProps = { width: number; }; +type GeoInfoGroup = { + code: string; + coordinatesList: [number, number][][]; +}; + function AreaMap(props: AreaMapProps) { const { isTabInteractive, @@ -409,6 +421,27 @@ function AreaMap(props: AreaMapProps) { width, } = props; + const geoInfoGroups: GeoInfoGroup[] = geoInfo + .reduce((geoInfoGroups, geoItem) => { + const index = geoInfoGroups.findIndex( + (geoInfoGroup) => geoInfoGroup.code === geoItem.code + ); + if (index === -1) { + geoInfoGroups.push({ + code: geoItem.code, + coordinatesList: [geoItem.coordinates], + }); + } else { + geoInfoGroups[index].coordinatesList.push(geoItem.coordinates); + } + return geoInfoGroups; + }, [] as GeoInfoGroup[]) + .sort((geoInfoGroupA, geoInfoGroupB) => { + return getFeatureName(geoInfoGroupA.code).localeCompare( + getFeatureName(geoInfoGroupB.code) + ); + }); + const isTouch = useIsTouchDevice(); return ( @@ -417,28 +450,34 @@ function AreaMap(props: AreaMapProps) { tabIndex={isTabInteractive ? 0 : -1} onMouseMove={!isTouch || isIOSDevice() ? handleMouseOver : undefined} > - {geoInfo.map((x, i) => ( - { - anchorEventHandlers.onFocus(event); - selectFeature(x.code as CodeProp, true); - }} - onBlur={(event) => { - anchorEventHandlers.onBlur(event); - selectFeature(undefined, true); - }} - /> - ))} + {geoInfoGroups.map((geoInfoGroup) => + geoInfoGroup.coordinatesList.map((geoInfoCoordinates, index) => ( + { + anchorEventHandlers.onFocus(event); + selectFeature(geoInfoGroup.code as CodeProp, true); + }} + onBlur={(event) => { + anchorEventHandlers.onBlur(event); + selectFeature(undefined, true); + }} + /> + )) + )} = (code: string) => T; type GetHoverFeatureProp = ( code: string, - isActivated?: boolean + isActivated?: boolean, + isKeyboardActive?: boolean ) => T; export type FeatureProps = { @@ -86,9 +87,16 @@ export function getFeatureProps( isHover ? dataConfig.hoverFill : 'none', stroke: !dataOptions.highlightSelection || !dataOptions.selectedCode - ? (_code: string, isActivated?: boolean) => { - return isActivated ? dataConfig.hoverStroke : 'none'; - } + ? ( + _code: string, + isActivated?: boolean, + isKeyboardActive?: boolean + ) => + isActivated + ? isKeyboardActive + ? dataConfig.highlightStroke + : dataConfig.hoverStroke + : 'none' : (code: string, isActivated?: boolean) => code === dataOptions.selectedCode ? isActivated @@ -126,8 +134,16 @@ export function getFeatureProps( }, hover: { fill: () => 'none', - stroke: (_code: string, isActivated?: boolean) => - isActivated ? dataConfig.hoverStroke : 'none', + stroke: ( + _code: string, + isActivated?: boolean, + isKeyboardActive?: boolean + ) => + isActivated + ? isKeyboardActive + ? dataConfig.highlightStroke + : dataConfig.hoverStroke + : 'none', strokeWidth: (_code: string, isActivated?: boolean) => isActivated ? dataConfig.hoverStrokeWidth : 0, }, diff --git a/packages/app/src/components/cms/rich-content.tsx b/packages/app/src/components/cms/rich-content.tsx index ddf11f0ee8..264b91c7ed 100644 --- a/packages/app/src/components/cms/rich-content.tsx +++ b/packages/app/src/components/cms/rich-content.tsx @@ -38,6 +38,11 @@ import { InlineChoropleth } from './inline-choropleth'; import { InlineDonutChart } from './inline-donut-chart'; import { InlineKpi } from './inline-kpi'; import { InlineTimeSeriesCharts } from './inline-time-series-charts'; +import { + ChevronRight, + Download, + External as ExternalLinkIcon, +} from '@corona-dashboard/icons'; interface RichContentProps { blocks: PortableTextEntry[]; @@ -56,7 +61,11 @@ interface AgeDemographicConfigNode { title: string; startDate?: string; endDate?: string; - config: AgeDemographicConfiguration, AccessibilityDefinition['key']>; + config: AgeDemographicConfiguration< + DataScopeKey, + MetricKeys, + AccessibilityDefinition['key'] + >; } interface ChoroplethConfigNode { @@ -295,8 +304,12 @@ function InlineAttachmentMark(props: { if (!props.mark.asset) return <>{props.children}; return ( - - {props.children} + + {props.children} ); } @@ -309,10 +322,18 @@ function InlineLinkMark(props: { children: ReactNode; mark: InlineLink }) { if (!mark.href) return <>{children}; return isAbsoluteUrl(mark.href) ? ( - {children} + + {children} + + ) : ( - {children} + + {children} + ); } diff --git a/packages/app/src/components/combo-box/combo-box.tsx b/packages/app/src/components/combo-box/combo-box.tsx index 288d392a79..c7f5798ca6 100644 --- a/packages/app/src/components/combo-box/combo-box.tsx +++ b/packages/app/src/components/combo-box/combo-box.tsx @@ -26,7 +26,6 @@ type TProps