Skip to content

Commit

Permalink
Define colorSelector for PER rating, area, benchmark
Browse files Browse the repository at this point in the history
- Define types for PER rating, area, benchmark
- Add union type in PieChart for colors / colorSelector
  • Loading branch information
tnagorra committed Oct 17, 2024
1 parent 8a8e88c commit 3705667
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 94 deletions.
4 changes: 2 additions & 2 deletions app/src/components/domain/PerAssessmentSummary/i18n.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"namespace": "perAssessmentSummary",
"namespace": "common",
"strings": {
"perArea": "Area {areaId}",
"benchmarksAssessed": "{totalQuestionCount} benchmarks assessed",
"perAssessmentSummaryHeading": "Summary",
"benchmarksAssessedTitle": "benchmarks assessed : {allAnsweredResponses} / {totalQuestionCount}"
"benchmarksAssessedTitle": "Benchmarks assessed : {allAnsweredResponses} / {totalQuestionCount}"
}
}
12 changes: 4 additions & 8 deletions app/src/components/domain/PerAssessmentSummary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ import {
import { PartialForm } from '@togglecorp/toggle-form';

import {
PER_FALLBACK_COLOR,
perAreaColorMap,
perRatingColorMap,
getPerAreaColor,
getPerRatingColor,
} from '#utils/domain/per';
import { type GoApiResponse } from '#utils/restRequest';

Expand Down Expand Up @@ -55,10 +54,7 @@ function numberOfComponentsSelector({ components } : { components: unknown[]}) {
}

function perRatingColorSelector({ ratingValue }: { ratingValue: number | undefined }) {
if (isDefined(ratingValue)) {
return perRatingColorMap[ratingValue];
}
return PER_FALLBACK_COLOR;
return getPerRatingColor(ratingValue);
}

function perRatingLabelSelector({
Expand Down Expand Up @@ -269,7 +265,7 @@ function PerAssessmentSummary(props: Props) {
className={styles.filledBar}
style={{
height: `${getPercentage(rating.rating, averageRatingByAreaList.length)}%`,
backgroundColor: perAreaColorMap[Number(rating.areaId)],
backgroundColor: getPerAreaColor(Number(rating.areaId)),
}}
/>
</div>
Expand Down
106 changes: 59 additions & 47 deletions app/src/utils/domain/per.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,72 +39,84 @@ export function getCurrentPerProcessStep(status: PerProcessStatusResponse | unde

export function getFormattedComponentName(component: PerComponent): string {
const { component_num, component_letter, title } = component;

const prefix = [component_num, component_letter].filter(isDefined).join(' ').trim();

return `${prefix} : ${title}`;
return `${prefix}: ${title}`;
}

export const perRatingColors = [
'var(--go-ui-color-dark-blue-40)',
'var(--go-ui-color-dark-blue-30)',
'var(--go-ui-color-dark-blue-20)',
'var(--go-ui-color-dark-blue-10)',
'var(--go-ui-color-gray-40)',
'var(--go-ui-color-gray-30)',
];

export const perRatingColorMap: {
[key: string]: string;
} = {
5: 'var(--go-ui-color-dark-blue-40)',
4: 'var(--go-ui-color-dark-blue-30)',
3: 'var(--go-ui-color-dark-blue-20)',
2: 'var(--go-ui-color-dark-blue-10)',
1: 'var(--go-ui-color-gray-40)',
0: 'var(--go-ui-color-gray-30)',
};
export const PER_FALLBACK_COLOR = 'var(--go-ui-color-gray-40)';

export const perAreaColorMap: { [key: number]: string } = {
1: 'var(--go-ui-color-purple-per)',
2: 'var(--go-ui-color-orange-per)',
3: 'var(--go-ui-color-blue-per)',
4: 'var(--go-ui-color-teal-per)',
5: 'var(--go-ui-color-red-per)',
export type PerRatingValue = 0 | 1 | 2 | 3 | 4 | 5;
const PER_RATING_VALUE_NOT_REVIEWED = 0 satisfies PerRatingValue;
const PER_RATING_VALUE_DOES_NOT_EXIST = 1 satisfies PerRatingValue;
const PER_RATING_VALUE_PARTIALLY_EXISTS = 2 satisfies PerRatingValue;
const PER_RATING_VALUE_NEEDS_IMPROVEMENT = 3 satisfies PerRatingValue;
const PER_RATING_VALUE_EXISTS_COULD_BE_STRENGTHENED = 4 satisfies PerRatingValue;
const PER_RATING_VALUE_HIGH_PERFORMANCE = 5 satisfies PerRatingValue;

const perRatingColorMap: Record<PerRatingValue, string> = {
[PER_RATING_VALUE_HIGH_PERFORMANCE]: 'var(--go-ui-color-dark-blue-40)',
[PER_RATING_VALUE_EXISTS_COULD_BE_STRENGTHENED]: 'var(--go-ui-color-dark-blue-30)',
[PER_RATING_VALUE_NEEDS_IMPROVEMENT]: 'var(--go-ui-color-dark-blue-20)',
[PER_RATING_VALUE_PARTIALLY_EXISTS]: 'var(--go-ui-color-dark-blue-10)',
[PER_RATING_VALUE_DOES_NOT_EXIST]: 'var(--go-ui-color-gray-40)',
[PER_RATING_VALUE_NOT_REVIEWED]: 'var(--go-ui-color-gray-30)',
};

export const perBenchmarkColorMap: {
[key: string]: string;
} = {
1: 'var(--go-ui-color-dark-blue-40)',
2: 'var(--go-ui-color-dark-blue-30)',
5: 'var(--go-ui-color-dark-blue-10)',
};
export function getPerRatingColor(value: number | undefined) {
if (isDefined(value)) {
return perRatingColorMap[value as PerRatingValue] ?? PER_FALLBACK_COLOR;
}
return PER_FALLBACK_COLOR;
}
export function perRatingColorSelector(item: { value: number | undefined }) {
return getPerRatingColor(item.value);
}

export const PER_FALLBACK_COLOR = 'var(--go-ui-color-gray-40)';
export type PerAreaNumber = 1 | 2 | 3 | 4 | 5;
const PER_AREA_NUMBER_1 = 1 satisfies PerAreaNumber;
const PER_AREA_NUMBER_2 = 2 satisfies PerAreaNumber;
const PER_AREA_NUMBER_3 = 3 satisfies PerAreaNumber;
const PER_AREA_NUMBER_4 = 4 satisfies PerAreaNumber;
const PER_AREA_NUMBER_5 = 5 satisfies PerAreaNumber;

const perAreaColorMap: Record<PerAreaNumber, string> = {
[PER_AREA_NUMBER_1]: 'var(--go-ui-color-purple-per)',
[PER_AREA_NUMBER_2]: 'var(--go-ui-color-orange-per)',
[PER_AREA_NUMBER_3]: 'var(--go-ui-color-blue-per)',
[PER_AREA_NUMBER_4]: 'var(--go-ui-color-teal-per)',
[PER_AREA_NUMBER_5]: 'var(--go-ui-color-red-per)',
};

export function perRatingColorSelector(item: {
value: number | undefined;
}) {
if (isDefined(item.value)) {
return perRatingColorMap[item.value];
export function getPerAreaColor(value: number | undefined) {
if (isDefined(value)) {
return perAreaColorMap[value as PerAreaNumber] ?? PER_FALLBACK_COLOR;
}
return PER_FALLBACK_COLOR;
}

export function perAreaColorSelector(item: {
value: number | undefined;
}) {
if (isDefined(item.value)) {
return perRatingColorMap[item.value];
}
return PER_FALLBACK_COLOR;
return getPerAreaColor(item.value);
}

type PerBenchmarkId = 1 | 2 | 5;
const PER_BENCHMARK_YES = 1 satisfies PerBenchmarkId;
const PER_BENCHMARK_NO = 2 satisfies PerBenchmarkId;
const PER_BENCHMARK_PARTIALLY_EXISTS = 5 satisfies PerBenchmarkId;

const perBenchmarkColorMap: Record<PerBenchmarkId, string> = {
[PER_BENCHMARK_YES]: 'var(--go-ui-color-dark-blue-40)',
[PER_BENCHMARK_NO]: 'var(--go-ui-color-dark-blue-30)',
[PER_BENCHMARK_PARTIALLY_EXISTS]: 'var(--go-ui-color-dark-blue-10)',
};

export function getPerBenchmarkColor(id: number | undefined) {
return perBenchmarkColorMap[id as PerBenchmarkId] ?? PER_FALLBACK_COLOR;
}
export function perBenchmarkColorSelector(item: {
id: number;
label: string;
count: number;
}) {
return perBenchmarkColorMap[item.id];
return getPerBenchmarkColor(item.id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ import WikiLink from '#components/WikiLink';
import useRouting from '#hooks/useRouting';
import {
getFormattedComponentName,
PER_FALLBACK_COLOR,
perAreaColorMap,
getPerAreaColor,
perBenchmarkColorSelector,
perRatingColors,
perRatingColorSelector,
} from '#utils/domain/per';
import {
Expand Down Expand Up @@ -250,9 +248,7 @@ function PrivateCountryPreparedness() {
id: groupedComponentList[0].area.id,
areaNum: groupedComponentList[0].area.area_num,
title: groupedComponentList[0].area.title,
color: isDefined(groupedComponentList[0].area.area_num)
? perAreaColorMap[groupedComponentList[0].area.area_num]
: PER_FALLBACK_COLOR,
color: getPerAreaColor(groupedComponentList[0].area.area_num),
value: getAverage(
groupedComponentList.map(
(component) => (
Expand Down Expand Up @@ -525,7 +521,6 @@ function PrivateCountryPreparedness() {
labelSelector={(item) => item.title ?? '??'}
keySelector={numericIdSelector}
colorSelector={perRatingColorSelector}
colors={perRatingColors}
/>
</Container>
)}
Expand Down Expand Up @@ -604,9 +599,7 @@ function PrivateCountryPreparedness() {
>
{prioritizationStats.componentsToBeStrengthened.map(
(priorityComponent) => {
const progressBarColor = isDefined(priorityComponent.areaNumber)
? perAreaColorMap[priorityComponent.areaNumber]
: PER_FALLBACK_COLOR;
const progressBarColor = getPerAreaColor(priorityComponent.areaNumber);

return (
<div
Expand Down Expand Up @@ -651,7 +644,7 @@ function PrivateCountryPreparedness() {
if (isNotDefined(perFormArea.area_num)) {
return null;
}
const color = perAreaColorMap?.[perFormArea?.area_num];
const color = getPerAreaColor(perFormArea?.area_num);
return (
<LegendItem
key={perFormArea.id}
Expand All @@ -670,9 +663,7 @@ function PrivateCountryPreparedness() {
)}
>
{assessmentStats.topRatedComponents.map((component) => {
const progressBarColor = isDefined(component.area.area_num)
? perAreaColorMap[component.area.area_num]
: PER_FALLBACK_COLOR;
const progressBarColor = getPerAreaColor(component.area.area_num);

return (
<Fragment
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"namespace": "ratingByAreaChart",
"namespace": "countryPreparedness",
"strings": {
"perArea": "Area {areaNumber}: {areaTitle}"
}
Expand Down
15 changes: 4 additions & 11 deletions app/src/views/PerExport/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ import {
import ifrcLogo from '#assets/icons/ifrc-square.png';
import {
getFormattedComponentName,
PER_FALLBACK_COLOR,
perAreaColorMap,
getPerAreaColor,
perBenchmarkColorSelector,
perRatingColors,
perRatingColorSelector,
} from '#utils/domain/per';
import { useRequest } from '#utils/restRequest';
Expand Down Expand Up @@ -247,9 +245,7 @@ export function Component() {
id: groupedComponentList[0].area.id,
areaNum: groupedComponentList[0].area.area_num,
title: groupedComponentList[0].area.title,
color: isDefined(groupedComponentList[0].area.area_num)
? perAreaColorMap[groupedComponentList[0].area.area_num]
: PER_FALLBACK_COLOR,
color: getPerAreaColor(groupedComponentList[0].area.area_num),
value: getAverage(
groupedComponentList.map(
(component) => (
Expand Down Expand Up @@ -428,7 +424,6 @@ export function Component() {
labelSelector={(item) => item.title ?? '??'}
keySelector={numericIdSelector}
colorSelector={perRatingColorSelector}
colors={perRatingColors}
showPercentageInLegend
/>
</Container>
Expand Down Expand Up @@ -507,9 +502,7 @@ export function Component() {
headingLevel={3}
>
{assessmentStats.topRatedComponents.map((component) => {
const progressBarColor = isDefined(component.area.area_num)
? perAreaColorMap[component.area.area_num]
: PER_FALLBACK_COLOR;
const progressBarColor = getPerAreaColor(component.area.area_num);

return (
<Fragment
Expand Down Expand Up @@ -547,7 +540,7 @@ export function Component() {
return null;
}

const color = perAreaColorMap?.[perFormArea.area_num];
const color = getPerAreaColor(perFormArea.area_num);
return (
<LegendItem
key={perFormArea.id}
Expand Down
40 changes: 29 additions & 11 deletions packages/ui/src/components/PieChart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,23 @@ function getPathData(radius: number, startAngle: number, endAngleFromParams: num
return d.join(' ');
}

export interface Props<D> {
export type Props<D> = {
className?: string;
legendClassName?: string;
data: D[] | undefined | null;
valueSelector: (datum: D) => number | undefined | null;
labelSelector: (datum: D) => React.ReactNode;
keySelector: (datum: D) => number | string;
colorSelector?: (datum: D) => string;
colors: string[];
pieRadius?: number;
chartPadding?: number;
showPercentageInLegend?: boolean;
}
} & ({
colorSelector: (datum: D) => string;
colors?: never;
} | {
colors: string[];
colorSelector?: never;
})

function PieChart<D>(props: Props<D>) {
const {
Expand All @@ -103,7 +107,8 @@ function PieChart<D>(props: Props<D>) {
const renderingData = useMemo(
() => {
let endAngle = 0;
return data?.map((datum) => {

const result = data?.map((datum) => {
const value = valueSelector(datum);
if (isNotDefined(value)) {
return undefined;
Expand All @@ -115,15 +120,28 @@ function PieChart<D>(props: Props<D>) {
return {
key: keySelector(datum),
value,
color: isDefined(colorSelector) ? colorSelector(datum) : undefined,
label: labelSelector(datum),
startAngle: endAngle - currentAngle,
percentage: getPercentage(value, totalValueSafe),
endAngle,

datum,
};
}).filter(isDefined) ?? [];

if (colorSelector) {
return result.map(({ datum, ...other }) => ({
...other,
color: colorSelector(datum),
}));
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return result.map(({ datum, ...other }, i) => ({
...other,
color: colors[i % colors.length],
}));
},
[data, keySelector, valueSelector, labelSelector, totalValueSafe, colorSelector],
[data, keySelector, valueSelector, labelSelector, totalValueSafe, colorSelector, colors],
);

return (
Expand All @@ -136,12 +154,12 @@ function PieChart<D>(props: Props<D>) {
}}
>
<g style={{ transform: `translate(${chartPadding / 2}px, ${chartPadding / 2}px)` }}>
{renderingData.map((datum, i) => (
{renderingData.map((datum) => (
<path
key={datum.key}
className={styles.path}
d={getPathData(pieRadius, datum.startAngle, datum.endAngle)}
fill={isDefined(datum.color) ? datum.color : colors[i % colors.length]}
fill={datum.color}
>
<Tooltip
description={(
Expand All @@ -156,7 +174,7 @@ function PieChart<D>(props: Props<D>) {
</g>
</svg>
<div className={_cs(styles.legend, legendClassName)}>
{renderingData.map((datum, i) => (
{renderingData.map((datum) => (
<LegendItem
className={styles.legendItem}
key={datum.key}
Expand All @@ -170,7 +188,7 @@ function PieChart<D>(props: Props<D>) {
withoutLabelColon
/>
) : datum.label}
color={isDefined(datum.color) ? datum.color : colors[i % colors.length]}
color={datum.color}
/>
))}
</div>
Expand Down

0 comments on commit 3705667

Please sign in to comment.