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

Commit

Permalink
Merge pull request #4489 from minvws/release/2.61.0
Browse files Browse the repository at this point in the history
Release/2.61.0
  • Loading branch information
hasan-ozaynaci authored Nov 10, 2022
2 parents 9ad326c + 85f7e88 commit c70796e
Show file tree
Hide file tree
Showing 53 changed files with 2,051 additions and 1,561 deletions.
127 changes: 28 additions & 99 deletions packages/app/src/components/cms/rich-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@ import { useIntl } from '~/intl';
import { getFileSrc, PortableText } from '~/lib/sanity';
import { nestedHtml } from '~/style/preset';
import { asResponsiveArray } from '~/style/utils';
import {
ImageBlock,
InlineAttachment,
InlineCollapsibleList,
InlineLink,
RichContentImageBlock,
} from '~/types/cms';
import { ImageBlock, InlineAttachment, InlineCollapsibleList, InlineLink, RichContentImageBlock } from '~/types/cms';
import { assert } from '~/utils/assert';
import { isAbsoluteUrl } from '~/utils/is-absolute-url';
import { Link } from '~/utils/link';
Expand All @@ -38,16 +32,15 @@ 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';
import { ChevronRight, Download, External as ExternalLinkIcon } from '@corona-dashboard/icons';

type ElementAlignment = 'start' | 'center' | 'end' | 'stretch';

interface RichContentProps {
blocks: PortableTextEntry[];
contentWrapper?: FunctionComponent;
imageSizes?: number[][];
elementAlignment?: ElementAlignment;
}

interface ChartConfigNode {
Expand All @@ -61,11 +54,7 @@ interface AgeDemographicConfigNode {
title: string;
startDate?: string;
endDate?: string;
config: AgeDemographicConfiguration<
DataScopeKey,
MetricKeys<DataScope>,
AccessibilityDefinition['key']
>;
config: AgeDemographicConfiguration<DataScopeKey, MetricKeys<DataScope>, AccessibilityDefinition['key']>;
}

interface ChoroplethConfigNode {
Expand All @@ -90,43 +79,19 @@ interface KpisConfigNode {
kpis: KpiConfigNode[];
}

export function RichContent({
contentWrapper,
blocks,
imageSizes,
}: RichContentProps) {
export function RichContent({ contentWrapper, blocks, imageSizes, elementAlignment }: RichContentProps) {
const ContentWrapper = contentWrapper ?? Fragment;
const serializers = {
types: {
inlineBlock: (props: unknown) => {
assert(
PortableText.defaultSerializers.types?.inlineBlock,
`[${RichContent.name}] PortableText needs to provide a serializer for inlineBlock content`
);
return (
<ContentWrapper>
{PortableText.defaultSerializers.types.inlineBlock(props)}
</ContentWrapper>
);
assert(PortableText.defaultSerializers.types?.inlineBlock, `[${RichContent.name}] PortableText needs to provide a serializer for inlineBlock content`);
return <ContentWrapper>{PortableText.defaultSerializers.types.inlineBlock(props)}</ContentWrapper>;
},
block: (props: unknown) => {
assert(
PortableText.defaultSerializers.types?.block,
`[${RichContent.name}] PortableText needs to provide a serializer for block content`
);
return (
<ContentWrapper>
{PortableText.defaultSerializers.types.block(props)}
</ContentWrapper>
);
assert(PortableText.defaultSerializers.types?.block, `[${RichContent.name}] PortableText needs to provide a serializer for block content`);
return <ContentWrapper>{PortableText.defaultSerializers.types.block(props)}</ContentWrapper>;
},
image: (props: { node: ImageBlock | RichContentImageBlock }) => (
<ContentImage
contentWrapper={contentWrapper}
sizes={imageSizes}
{...props}
/>
),
image: (props: { node: ImageBlock | RichContentImageBlock }) => <ContentImage contentWrapper={contentWrapper} sizes={imageSizes} {...props} />,

inlineCollapsible: (props: { node: InlineCollapsibleList }) => {
if (!props.node.content.inlineBlockContent) return null;
Expand Down Expand Up @@ -172,17 +137,11 @@ export function RichContent({
{node.title}
</Heading>
</Box>
<InlineTimeSeriesCharts
configuration={node.config}
startDate={node.startDate}
endDate={node.endDate}
/>
<InlineTimeSeriesCharts configuration={node.config} startDate={node.startDate} endDate={node.endDate} />
</Box>
);
},
dashboardAgeDemographicChart: (props: {
node: AgeDemographicConfigNode;
}) => {
dashboardAgeDemographicChart: (props: { node: AgeDemographicConfigNode }) => {
const node = props.node;

return (
Expand All @@ -200,11 +159,7 @@ export function RichContent({
{node.title}
</Heading>
</Box>
<InlineAgeDemographic
configuration={node.config}
startDate={node.startDate}
endDate={node.endDate}
/>
<InlineAgeDemographic configuration={node.config} startDate={node.startDate} endDate={node.endDate} />
</Box>
</Box>
);
Expand All @@ -221,10 +176,7 @@ export function RichContent({
</Heading>
</Box>

<InlineChoropleth
configuration={node.config}
title={node.title}
/>
<InlineChoropleth configuration={node.config} title={node.title} />
</Box>
</ContentWrapper>
);
Expand All @@ -241,11 +193,7 @@ export function RichContent({
</Heading>
</Box>

<InlineDonutChart
configuration={node.config}
startDate={node.startDate}
endDate={node.endDate}
/>
<InlineDonutChart configuration={node.config} startDate={node.startDate} endDate={node.endDate} />
</Box>
</ContentWrapper>
);
Expand All @@ -265,20 +213,9 @@ export function RichContent({

return (
<ContentWrapper>
<Box
spacing={{ _: 4, md: 2 }}
display="flex"
py={3}
flexDirection={{ _: 'column', md: 'row' }}
>
<InlineKpi
configuration={kpiLeft.config}
date={kpiLeft.endDate}
/>
<InlineKpi
configuration={kpiRight.config}
date={kpiRight.endDate}
/>
<Box spacing={{ _: 4, md: 2 }} display="flex" py={3} flexDirection={{ _: 'column', md: 'row' }}>
<InlineKpi configuration={kpiLeft.config} date={kpiLeft.endDate} />
<InlineKpi configuration={kpiRight.config} date={kpiRight.endDate} />
</Box>
</ContentWrapper>
);
Expand All @@ -292,23 +229,16 @@ export function RichContent({

return (
<ErrorBoundary>
<StyledPortableText blocks={blocks} serializers={serializers} />
<StyledPortableText blocks={blocks} serializers={serializers} elementAlignment={elementAlignment} />
</ErrorBoundary>
);
}

function InlineAttachmentMark(props: {
children: ReactNode;
mark: InlineAttachment;
}) {
function InlineAttachmentMark(props: { children: ReactNode; mark: InlineAttachment }) {
if (!props.mark.asset) return <>{props.children}</>;

return (
<a
css={css({ textDecoration: 'underline' })}
download
href={getFileSrc(props.mark.asset)}
>
<a css={css({ textDecoration: 'underline' })} download href={getFileSrc(props.mark.asset)}>
{props.children} <Download width={15} height={11} />
</a>
);
Expand All @@ -322,10 +252,7 @@ function InlineLinkMark(props: { children: ReactNode; mark: InlineLink }) {
if (!mark.href) return <>{children}</>;

return isAbsoluteUrl(mark.href) ? (
<ExternalLink
href={mark.href}
underline
>
<ExternalLink href={mark.href} underline>
{children}
<ExternalLinkIcon width={20} height={11} />
</ExternalLink>
Expand All @@ -338,13 +265,15 @@ function InlineLinkMark(props: { children: ReactNode; mark: InlineLink }) {
);
}

const StyledPortableText = styled(PortableText)(
const StyledPortableText = styled(PortableText)<{
elementAlignment?: ElementAlignment;
}>(({ elementAlignment }) =>
css({
...nestedHtml,
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
alignItems: elementAlignment ? elementAlignment : 'center',

'& > ul': {
display: 'flex',
Expand Down
7 changes: 2 additions & 5 deletions packages/app/src/components/get-icon-by-name.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react';
import { isDefined } from 'ts-is-present';
import { assert } from '~/utils/assert';

type IconName = keyof typeof iconName2filename;
export type IconName = keyof typeof iconName2filename;
type IconCollection = Record<IconName, React.ComponentType>;

interface DynamicIconProps extends IconProps {
Expand All @@ -19,10 +19,7 @@ function getIconByName(name: IconName) {
const icons: IconCollection = allIcons;
const DynamicIcon = icons[name];

assert(
isDefined(DynamicIcon),
`[${getIconByName.name}] Icon with name "${name}" does not exist`
);
assert(isDefined(DynamicIcon), `[${getIconByName.name}] Icon with name "${name}" does not exist`);

return DynamicIcon;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { space } from '~/style/theme';
import { getSeverityColor } from '../logic/get-severity-color';
import { getSeverityHeight } from '../logic/get-severity-height';
import { SeverityLevel, SeverityLevels } from '../types';
import { SEVERITY_LEVELS_LIST } from '../constants';

interface SeverityIndicatorProps {
level: SeverityLevel;
Expand All @@ -17,18 +18,16 @@ const SeverityIndicatorArrow = () => (
);

export const SeverityIndicator = ({ level }: SeverityIndicatorProps) => {
const severityLevels = Object.entries(SeverityLevels);

return (
<Box alignItems="flex-end" css={css({ gap: `0 ${space[1]}` })} display="flex" height={space[4]} mb={space[4]} mt={space[3]}>
{severityLevels.map(([key, value]) => (
{SEVERITY_LEVELS_LIST.map((value, index) => (
<Box
key={key}
backgroundColor={Number(level) >= Number(value) ? getSeverityColor(level as SeverityLevels) : colors.gray3}
key={index}
backgroundColor={level >= value ? getSeverityColor(level as SeverityLevels) : colors.gray3}
borderRadius={4}
height={`${getSeverityHeight(value)}%`}
height={`${getSeverityHeight(value as SeverityLevel)}%`}
position="relative"
width={`${100 / severityLevels.length}%`}
width={`${100 / SEVERITY_LEVELS_LIST.length}%`}
>
{level === value && <SeverityIndicatorArrow />}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const TimelineTooltipContent = ({ config, hasMultipleEvents, onNext, onPr
gridTemplateColumns={`${space[4]} auto`}
gridTemplateRows="auto"
>
<SeverityIndicatorLevel level={config.level.toString() as SeverityLevels}>{config.level}</SeverityIndicatorLevel>
<SeverityIndicatorLevel level={config.level as SeverityLevels}>{config.level}</SeverityIndicatorLevel>
<BoldText>{config.title}</BoldText>
</Box>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createDateFromUnixTimestamp } from '~/utils/create-date-from-unix-timestamp';
import { middleOfDayInSeconds } from '@corona-dashboard/common';

// Returns the difference between two dates in days.
const getDifferenceInDays = (start: Date, end: Date): number => {
return Math.floor((end.getTime() - start.getTime()) / (1000 * 3600 * 24));
};

// Returns the date representing the middle of the day for the passed in dates.
const getMiddleOfDayDates = (today: Date, startDate: number, endDate: number): Record<string, Date> => {
const todayMiddleOfDaySeconds = middleOfDayInSeconds(today.getTime() / 1000);

return {
startOfIntervalDate: createDateFromUnixTimestamp(middleOfDayInSeconds(startDate)),
endOfIntervalDate: createDateFromUnixTimestamp(middleOfDayInSeconds(endDate)),
todayMiddleOfDayDate: createDateFromUnixTimestamp(todayMiddleOfDaySeconds),
};
};

// Determines where to position the 'Vandaag' label on the timeline.
export const getTimelineBarArrowOffset = (today: Date, startDate: number, endDate: number): number => {
const { startOfIntervalDate, endOfIntervalDate, todayMiddleOfDayDate } = getMiddleOfDayDates(today, startDate, endDate);
const numberOfDaysInInterval = getDifferenceInDays(startOfIntervalDate, endOfIntervalDate);
const numberOfDaysFromStartToToday = getDifferenceInDays(startOfIntervalDate, todayMiddleOfDayDate);
const arrowOffset = (numberOfDaysFromStartToToday / numberOfDaysInInterval) * 100;

return arrowOffset;
};
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { createDateFromUnixTimestamp } from '~/utils/create-date-from-unix-timestamp';
import { SeverityIndicatorTimelineEventConfig } from '../timeline';

export const getTimelineRangeDates = (timelineEvents: SeverityIndicatorTimelineEventConfig[] | undefined) => {
if (!timelineEvents) {
return {
startDate: null,
endDate: null,
};
}

export const getTimelineRangeDates = (timelineEvents: SeverityIndicatorTimelineEventConfig[]) => {
const timelineEventDates = timelineEvents
.flatMap((timelineEvent: SeverityIndicatorTimelineEventConfig) => [timelineEvent.start, timelineEvent.end])
.map((timelineEventDate: number) => {
Expand Down
Loading

0 comments on commit c70796e

Please sign in to comment.