Skip to content

Commit

Permalink
Update main (#61)
Browse files Browse the repository at this point in the history
* Client/feature/adapt story 1 (#53)

* Add bubble chart default options

* Fix story content size

* add production link to images in next config

* Fix no map error

* Update legend logic (#54)

* Update legend logic

* Fix outro step disclaimer (#55)

* Fix disclaimer

* Update styles and legends (#56)

* Update styles and legends to story 2

* Client/feature/south sudan (#57)

* Update timeline legend componenmt and add header to legends

* Add top stories to cms and update client (#58)

* Remove top stories number

* Fix last step logos width (#59)

* Add richtext to map step card (#60)

* Add richtext to map step card

* Fix globe position

* feat: Turning on CDN for space/bucket with map data

---------

Co-authored-by: martintomas <[email protected]>
  • Loading branch information
barbara-chaves and martintomas authored Jun 17, 2024
1 parent fc36858 commit 8a28ac6
Show file tree
Hide file tree
Showing 52 changed files with 4,589 additions and 1,785 deletions.
2 changes: 1 addition & 1 deletion client/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const nextConfig = {
'esa-gda-comms-staging-cms.fra1.digitaloceanspaces.com',
'fra1.digitaloceanspaces.com',
'esa-gda-comms-staging-mfafc.ondigitalocean.app',
'impact-sphere-gda.esa.int',
'https://impact-sphere-gda.esa.int',
],
},
env: {
Expand Down
6 changes: 5 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@hookform/resolvers": "^3.1.1",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.6",
Expand Down Expand Up @@ -62,7 +63,10 @@
"react-dom": "18.2.0",
"react-hook-form": "^7.45.0",
"react-map-gl": "7.1.5",
"react-markdown": "8.0.7",
"react-markdown": "^9.0.1",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.0",
"remark-unwrap-images": "^4.0.0",
"rooks": "7.14.1",
"tailwind-merge": "^1.13.2",
"tailwindcss": "3.3.2",
Expand Down
23 changes: 14 additions & 9 deletions client/src/components/chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import {
Filler,
registerables,
} from 'chart.js';
import { ChartJSOrUndefined } from 'react-chartjs-2/dist/types';
import { ChartJSOrUndefined, ChartProps } from 'react-chartjs-2/dist/types';

import { WidgetWidgetComponent } from '@/types/generated/strapi.schemas';

import { getChartDefaultData, getChartDefaultOptions } from './utils';
import { bubbleDefaultOptions, getChartDefaultData, getChartDefaultOptions } from './utils';

type ChartJsProps = {
widget: WidgetWidgetComponent;
Expand Down Expand Up @@ -49,19 +49,24 @@ const ChartJs = ({ widget, ...props }: ChartJsProps) => {
() => getChartDefaultData(data, chartRef),
[data, chartRef.current]
);
const optionsWithDefaults = useMemo(() => getChartDefaultOptions(options), [options]);

const chartType = type as ChartType;

const optionsWithDefaults: Partial<ChartProps['options']> = getChartDefaultOptions(options);

const OPTIONS = {
...optionsWithDefaults,
datasets: {
...(chartType === 'bubble'
? { bubble: { ...bubbleDefaultOptions, ...optionsWithDefaults?.datasets?.bubble } }
: {}),
},
};

return (
<div {...props}>
{!chartTypes.includes(chartType) || !data || !(data as ChartData).datasets ? null : (
<Chart
ref={chartRef}
type={chartType}
options={optionsWithDefaults}
data={dataWithDefaults}
/>
<Chart ref={chartRef} type={chartType} options={OPTIONS} data={dataWithDefaults} />
)}
</div>
);
Expand Down
117 changes: 88 additions & 29 deletions client/src/components/chart/utils.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,85 @@
import { MutableRefObject } from 'react';

import { BubbleDataPoint, ChartTypeRegistry, Point } from 'chart.js';
import { BubbleDataPoint, ChartTypeRegistry, Point, ScriptableContext } from 'chart.js';
import { ChartJSOrUndefined } from 'react-chartjs-2/dist/types';

export const getChartDefaultOptions = (options: any) => {
export const PluginCallbacks = {
PredictedTravelDemandDhakaTooltopTitle: (context: any) => {
return `${context[0].raw.y} in ${context[0].raw.x}`;
},
PredictedTravelDemandDhakaTooltopLabel: (context: any) => {
return `${context.raw.value?.toLocaleString()} ${context.raw.scale}`;
},
};

export const bubbleDefaultOptions = {
radius: (context: ScriptableContext<'bubble'>) => {
const maxRadius = 20;
const minRadius = 10;
const max = Math.max(...context.dataset.data.map((d: any) => d.value || 0));
const min = Math.min(...context.dataset.data.map((d: any) => d.value || 0));
const value = (context.dataset.data[context.dataIndex] as any).value || 0;
const normalized = minRadius + ((value - min) * (maxRadius - minRadius)) / (max - min);
context.dataset.data[context.dataIndex].r = normalized;
return normalized;
},
};

const extractPluginCallbackFunction = (options: Record<string, any>) => {
const plugins = options?.plugins || {};
if (!plugins) return options;
for (const key in plugins) {
const plugin = plugins[key];
if (plugin?.callbacks) {
for (const callback in plugin.callbacks) {
const pluginCallback = plugin.callbacks[callback];
if (typeof pluginCallback === 'string' && pluginCallback in PluginCallbacks) {
plugins[key].callbacks[callback] =
PluginCallbacks[pluginCallback as keyof typeof PluginCallbacks];
}
}
}
}
return {
...options,
plugins,
};
};

export const getChartDefaultOptions = (a: unknown) => {
const options = !!a && typeof a === 'object' ? extractPluginCallbackFunction(a) : {};
return {
borderColor: '#fff',
interaction: {
intersect: false,
mode: 'index',
...options.interaction,
mode: 'point',
...options?.interaction,
},
scales: {
x: {
grid: {
display: false,
color: '#fff',
...options.scales?.x?.grid,
...options?.scales?.x?.grid,
},
ticks: {
maxTicksLimit: 5,
color: '#fff',
padding: 10,
...options.scales?.x?.ticks,
...options?.scales?.x?.ticks,
},
...options.scales?.x,
...options?.scales?.x,
},
y: {
border: {
dash: [4, 4],
display: false,
...options.scales?.y?.border,
...(options?.scales?.y as any)?.border,
},
grid: {
color: '#fff',
drawTicks: false,
...options.scales?.y?.grid,
...options?.scales?.y?.grid,
},
ticks: {
padding: 0,
Expand All @@ -47,18 +91,34 @@ export const getChartDefaultOptions = (options: any) => {
return label;
},
maxTicksLimit: 5,
...options.scales?.y?.ticks,
...options?.scales?.y?.ticks,
},
...options.scales?.y,
...options?.scales?.y,
},
...options.scales,
...options?.scales,
},
plugins: {
legend: {
display: false,
...options.plugins?.legend,
...options?.plugins?.legend,
},
tooltip: {
mode: 'point',
bodyFont: {
size: 14,
weight: 'bold',
},
bodyColor: '#003247',
titleFont: {
size: 14,
weight: 'bold',
},
titleColor: '#9AABB5',
displayColors: false,
backgroundColor: '#fff',
...options?.plugins?.tooltip,
},
...options.plugins,
...options?.plugins,
},
...options,
};
Expand All @@ -74,19 +134,18 @@ export const getChartDefaultData = (
) => {
if (!chartRef.current) return data;

const gradient = chartRef.current.ctx.createLinearGradient(0, 0, 400, 200);
gradient.addColorStop(0, 'rgba(0, 174, 157, 1)');
gradient.addColorStop(1, 'rgba(0, 174, 157, 0)');
return {
...data,
datasets: data.datasets.map((dataset: any) => ({
...dataset,
backgroundColor: gradient,
borderColor: 'rgb(0, 174, 157)',
borderWidth: 2,
pointRadius: 0,
pointHoverRadius: 0,
pointHoverBorderWidth: 0,
})),
};
if (data.datasets?.some((dataset: any) => dataset.backgroundColor === 'GRADIENT')) {
const gradient = chartRef.current.ctx.createLinearGradient(0, 0, 400, 200);
gradient.addColorStop(0, 'rgba(0, 174, 157, 1)');
gradient.addColorStop(1, 'rgba(0, 174, 157, 0)');
return {
...data,
datasets: data.datasets.map((dataset: any) => ({
...dataset,
backgroundColor: dataset.backgroundColor === 'GRADIENT' && gradient,
})),
};
}

return data;
};
45 changes: 29 additions & 16 deletions client/src/components/map/legend/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React, { useMemo, Children, isValidElement } from 'react';

import { ChevronDown } from 'lucide-react';

import { cn } from '@/lib/classnames';

import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';

import SortableList from './sortable/list';
import { LegendProps } from './types';

Expand All @@ -16,27 +20,36 @@ export const Legend: React.FC<LegendProps> = ({
}, [children]);

return (
<div
className={cn({
'relative flex-col overflow-hidden': true,
hidden: !isChildren,
[className]: !!className,
})}
>
{isChildren && (
<div className="relative flex h-full flex-col overflow-hidden">
<div className="flex items-end gap-4 overflow-y-auto overflow-x-hidden">
{!!sortable.enabled && !!onChangeOrder && (
isChildren && (
<div
className={cn({
'bg-card-map relative flex-col space-y-2 rounded-lg p-2 px-3 backdrop-blur-sm': true,
hidden: !isChildren,
[className]: !!className,
})}
>
{isChildren && (
<div className="flex flex-col gap-4 overflow-x-hidden">
{!!sortable?.enabled && !!onChangeOrder ? (
<SortableList sortable={sortable} onChangeOrder={onChangeOrder}>
{children}
</SortableList>
) : Array.isArray(children) && children.length > 1 ? (
<Collapsible defaultOpen className="space-y-2">
<CollapsibleTrigger className="font-open-sans group flex w-full items-center justify-between gap-2 text-sm font-semibold text-white">
Legends <ChevronDown className="w-5 group-data-[state=closed]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="flex flex-col gap-2 overflow-x-hidden">
{children}
</CollapsibleContent>
</Collapsible>
) : (
children
)}

{children}
</div>
</div>
)}
</div>
)}
</div>
)
);
};

Expand Down
16 changes: 10 additions & 6 deletions client/src/components/map/legend/item-types/basic/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,31 @@ import React from 'react';
import { cn } from '@/lib/classnames';

import { LegendTypeProps } from '../../types';
import LegendHeader from '../header';

export const LegendTypeBasic: React.FC<LegendTypeProps> = ({ className = '', items = [] }) => {
export const LegendTypeBasic: React.FC<LegendTypeProps> = ({
className = '',
items = [],
title,
info,
}) => {
return (
<div
className={cn({
[className]: !!className,
})}
>
<LegendHeader title={title} info={info} />
<ul className="flex w-full flex-wrap items-center gap-3">
{items.map(({ value, color }) => (
<li
key={`${value}`}
className="font-notes flex items-center gap-x-1 font-bold text-white"
>
<li key={`${value}`} className="flex items-center gap-x-1 text-white">
<div
className="shadow-xs h-4 w-4 flex-shrink-0 rounded-sm"
style={{
backgroundColor: color,
}}
/>
<div>{value}</div>
<div className="font-open-sans text-sm">{value}</div>
</li>
))}
</ul>
Expand Down
13 changes: 10 additions & 3 deletions client/src/components/map/legend/item-types/choropleth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ import React from 'react';
import { cn } from '@/lib/classnames';

import { LegendTypeProps } from '../../types';
import LegendHeader from '../header';

export const LegendTypeChoropleth: React.FC<LegendTypeProps> = ({ className = '', items }) => {
export const LegendTypeChoropleth: React.FC<LegendTypeProps> = ({
className = '',
items,
title,
info,
}) => {
return (
<div
className={cn({
className={cn('font-open', {
[className]: !!className,
})}
>
<LegendHeader title={title} info={info} />
<ul className="flex w-full">
{items.map(({ color, value }) => (
<li
Expand All @@ -28,7 +35,7 @@ export const LegendTypeChoropleth: React.FC<LegendTypeProps> = ({ className = ''
{items.map(({ color, value }) => (
<li
key={`${color}-${value}`}
className="flex-shrink-0 text-center text-xs"
className="font-open-sans flex-shrink-0 text-center text-sm text-white"
style={{
width: `${100 / items.length}%`,
}}
Expand Down
Loading

0 comments on commit 8a28ac6

Please sign in to comment.