Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Commit

Permalink
bugfix(choropleth): added onClick event to selected image map areas t…
Browse files Browse the repository at this point in the history
…o properly show tooltips for Android devices; fixed tooltip notification styling issues introduced by COR-694; (#4532)

Co-authored-by: VWSCoronaDashboard26 <[email protected]>
  • Loading branch information
VWSCoronaDashboard26 and VWSCoronaDashboard26 authored Dec 15, 2022
1 parent 18d8b65 commit cbd9b01
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { colors } from '@corona-dashboard/common';
import type { GeoProjection } from 'd3-geo';
import Konva from 'konva';
import { memo, MouseEvent, MutableRefObject, RefObject, useCallback, useEffect, useRef, useState } from 'react';
import { memo, FocusEvent, MouseEvent, MutableRefObject, RefObject, useCallback, useEffect, useRef, useState } from 'react';
import { Group, Layer, Line, Stage } from 'react-konva';
import { isDefined, isPresent } from 'ts-is-present';
import { isIOSDevice } from '~/utils/is-ios-device';
Expand Down Expand Up @@ -404,6 +404,11 @@ function AreaMap(props: AreaMapProps) {

const isTouch = useIsTouchDevice();

const handleAreaInteraction = (event: FocusEvent<HTMLElement> | MouseEvent<HTMLElement>, geoInfoGroup: GeoInfoGroup) => {
anchorEventHandlers.onFocus(event);
selectFeature(geoInfoGroup.code as CodeProp, true);
};

return (
<map name={id} tabIndex={isTabInteractive ? 0 : -1} onMouseMove={!isTouch || isIOSDevice() ? handleMouseOver : undefined}>
{geoInfoGroups.map((geoInfoGroup) =>
Expand All @@ -420,10 +425,8 @@ function AreaMap(props: AreaMapProps) {
shape="poly"
coords={geoInfoCoordinates.flat().join(',')}
href={!isTouch && isDefined(getLink) ? getLink(geoInfoGroup.code) : undefined}
onFocus={(event) => {
anchorEventHandlers.onFocus(event);
selectFeature(geoInfoGroup.code as CodeProp, true);
}}
onClick={(event) => (isTouch ? handleAreaInteraction(event, geoInfoGroup) : undefined)}
onFocus={(event) => handleAreaInteraction(event, geoInfoGroup)}
onBlur={(event) => {
anchorEventHandlers.onBlur(event);
selectFeature(undefined, true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { geoConicConformal, geoMercator } from 'd3-geo';
import { FocusEvent, memo, useMemo } from 'react';
import { FocusEvent, MouseEvent, memo, useMemo } from 'react';
import { isDefined } from 'ts-is-present';
import { Box } from '~/components/base';
import { useAccessibilityAnnotations } from '~/utils/use-accessibility-annotations';
Expand All @@ -21,22 +21,17 @@ import { TooltipSettings } from '../tooltips/types';
import { CanvasChoroplethMap } from './canvas-choropleth-map';

export type AnchorEventHandler = {
onFocus: (evt: FocusEvent<HTMLElement>) => void;
onFocus: (evt: FocusEvent<HTMLElement> | MouseEvent<HTMLElement>) => void;
onBlur: (evt: FocusEvent<HTMLElement>) => void;
};

type ChoroplethMapProps<T extends ChoroplethDataItem> = Omit<
ChoroplethProps<T>,
'formatTooltip' | 'tooltipPlacement' | 'dataFormatters'
> & {
type ChoroplethMapProps<T extends ChoroplethDataItem> = Omit<ChoroplethProps<T>, 'formatTooltip' | 'tooltipPlacement' | 'dataFormatters'> & {
setTooltip: (tooltip: TooltipSettings<T> | undefined) => void;
isTabInteractive: boolean;
anchorEventHandlers: AnchorEventHandler;
};

export const ChoroplethMap: <T extends ChoroplethDataItem>(
props: ChoroplethMapProps<T>
) => JSX.Element | null = memo((props) => {
export const ChoroplethMap: <T extends ChoroplethDataItem>(props: ChoroplethMapProps<T>) => JSX.Element | null = memo((props) => {
const {
data: originalData,
dataConfig: partialDataConfig,
Expand All @@ -54,48 +49,22 @@ export const ChoroplethMap: <T extends ChoroplethDataItem>(

const dataConfig = createDataConfig(partialDataConfig);

const mapProjection = isDefined(dataOptions.projection)
? geoConicConformal
: geoMercator;
const mapProjection = isDefined(dataOptions.projection) ? geoConicConformal : geoMercator;

const annotations = useAccessibilityAnnotations(accessibility);
const data = useChoroplethData(originalData, map, dataOptions.selectedCode);

const [containerRef, { width = 0, height = minHeight }] =
useResizeObserver<HTMLDivElement>();
const [containerRef, { width = 0, height = minHeight }] = useResizeObserver<HTMLDivElement>();

const [mapHeight, padding] = useResponsiveSize(
width,
height,
boundingBoxPadding,
responsiveSizeConfiguration
);
const [mapHeight, padding] = useResponsiveSize(width, height, boundingBoxPadding, responsiveSizeConfiguration);

const choroplethFeatures = useChoroplethFeatures(
map,
data,
dataOptions.selectedCode
);
const choroplethFeatures = useChoroplethFeatures(map, data, dataOptions.selectedCode);

const getFillColor = useFillColor(data, map, dataConfig, thresholdMap);

const featureProps = useFeatureProps(
map,
getFillColor,
dataOptions,
dataConfig
);
const featureProps = useFeatureProps(map, getFillColor, dataOptions, dataConfig);

const [featureOverHandler, featureOutHandler, tooltipTrigger] =
useChoroplethTooltip(
map,
data,
dataConfig,
dataOptions,
isTabInteractive,
setTooltip,
containerRef
);
const [featureOverHandler, featureOutHandler, tooltipTrigger] = useChoroplethTooltip(map, data, dataConfig, dataOptions, isTabInteractive, setTooltip, containerRef);

const getFeatureName = useFeatureName(map, dataOptions.getFeatureName);

Expand All @@ -107,26 +76,15 @@ export const ChoroplethMap: <T extends ChoroplethDataItem>(
],
choroplethFeatures?.boundingBox,
],
[
width,
mapHeight,
padding.left,
padding.right,
padding.top,
padding.bottom,
choroplethFeatures?.boundingBox,
]
[width, mapHeight, padding.left, padding.right, padding.top, padding.bottom, choroplethFeatures?.boundingBox]
);

const correctedHeight = Math.max(height, minHeight);

return (
<Box ref={containerRef} width="100%" height="100%">
{!isDefined(choroplethFeatures) ? (
<img
src={`/api/choropleth/${map}/${dataConfig.metricName.toString()}/${dataConfig.metricProperty.toString()}/${minHeight}`}
loading="lazy"
/>
<img src={`/api/choropleth/${map}/${dataConfig.metricName.toString()}/${dataConfig.metricProperty.toString()}/${minHeight}`} loading="lazy" />
) : (
<>
{annotations.descriptionElement}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { colors } from '@corona-dashboard/common';
import { VisuallyHidden } from '~/components';
import { space } from '~/style/theme';
import { radii, space } from '~/style/theme';
import styled from 'styled-components';

interface TooltipNotificationProps {
Expand All @@ -23,6 +23,7 @@ export const TooltipNotification = (props: TooltipNotificationProps) => {
// Negative margin is used her to offset the padding from the parent component and to not alter styles for tooltips without a notification.
const StyledTooltipNotification = styled.div`
background-color: ${colors.yellow1};
border-radius: 0 0 ${radii[1]}px ${radii[1]}px;
margin: ${space[2]} -${space[3]} -${space[2]};
padding: ${space[2]} ${space[3]};
`;
2 changes: 1 addition & 1 deletion packages/app/src/style/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const mediaQueries = {
xl: `screen and (min-width: ${breakpoints[4]})`,
} as const;

const radii = [0, 5, 10];
export const radii = [0, 5, 10];

export const shadows = {
tile: '0px 4px 8px rgba(0, 0, 0, 0.1)',
Expand Down
5 changes: 1 addition & 4 deletions packages/app/src/utils/is-touch-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,5 @@
* @returns boolean
*/
export function isTouchDevice() {
return (
typeof window !== 'undefined' &&
window.matchMedia('(pointer: coarse)').matches
);
return typeof window !== 'undefined' && ('ontouchstart' in window || window.navigator.maxTouchPoints > 0 || window.matchMedia('(pointer: coarse)').matches);
}

0 comments on commit cbd9b01

Please sign in to comment.