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

Commit

Permalink
fix(radial-chart): Display of rounded percentage with precision
Browse files Browse the repository at this point in the history
fix(radial-chart): Moving sort out of the returning chain
  • Loading branch information
tonidalmases authored and tomheller committed Feb 15, 2021
1 parent 2255e2e commit d08b808
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 19 deletions.
16 changes: 8 additions & 8 deletions libs/barista-components/radial-chart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ You can specify the position of the radial chart by adjusting the value of the

## DtRadialChartSeries inputs

| Name | Type | Default | Description |
| --------------- | --------- | ----------------------------------- | ---------------------------------------------------------------- |
| `value` | `number` | - | The series value (required). |
| `name` | `string` | - | The series name (required). |
| `color` | `string` | `DT_CHART_COLOR_PALETTE_ORDERED[i]` | The color in which the series is displayed within the chart. |
| `valueRelative` | `number` | - | Numeric percentage value based on this node vs sum of top level. |
| `selected` | `boolean` | false | Marks series as selected. |
| `active` | `boolean` | true | Marks series as activated through legend. |
| Name | Type | Default | Description |
| ----------------- | --------- | ----------------------------------- | ---------------------------------------------------------------- |
| `value` | `number` | - | The series value (required). |
| `name` | `string` | - | The series name (required). |
| `color` | `string` | `DT_CHART_COLOR_PALETTE_ORDERED[i]` | The color in which the series is displayed within the chart. |
| `valuePercentage` | `number` | - | Numeric percentage value based on this node vs sum of top level. |
| `selected` | `boolean` | false | Marks series as selected. |
| `active` | `boolean` | true | Marks series as activated through legend. |

#### Outputs

Expand Down
2 changes: 1 addition & 1 deletion libs/barista-components/radial-chart/src/radial-chart.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<strong>{{
valueDisplayMode === 'absolute'
? series.value
: (series.valueRelative * 100 | dtPercent)
: (series.valuePercentage | dtPercent: precision)
}}</strong>
{{ series.name }}
</dt-legend-item>
Expand Down
28 changes: 20 additions & 8 deletions libs/barista-components/radial-chart/src/radial-chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { DtRadialChartRenderData } from './utils/radial-chart-interfaces';
import {
generatePathData,
generatePieArcData,
getPercentages,
getSum,
} from './utils/radial-chart-utils';

Expand Down Expand Up @@ -124,6 +125,9 @@ export class DtRadialChart implements AfterContentInit, OnDestroy {
/** Sets the display mode for the radial-chart values to either 'percent' or 'absolute'. */
@Input() valueDisplayMode: 'absolute' | 'percent' = 'absolute';

/** Sets the decimal precision for the percentage values */
@Input() precision: number = 1;

/** Sets the display mode for the radial-chart values to either 'percent' or 'absolute'. */
@Input()
get selectable(): boolean {
Expand Down Expand Up @@ -276,13 +280,25 @@ export class DtRadialChart implements AfterContentInit, OnDestroy {
const arcs = generatePieArcData(seriesValues, this.maxValue);
this._totalSeriesValue = getSum(seriesValues);

const max =
this.maxValue && this.maxValue >= this._totalSeriesValue
? this.maxValue
: this._totalSeriesValue;

const seriesPercentages = getPercentages(
seriesValues,
max,
this.precision,
);

this._renderData = this._radialChartSeries.map((series, i) => {
const colorIdx = i % DT_CHART_COLOR_PALETTE_ORDERED.length;
return this._getSeriesRenderData(
series,
arcs[i],
DT_CHART_COLOR_PALETTE_ORDERED[colorIdx],
this._totalSeriesValue,
max,
seriesPercentages[i],
);
});

Expand All @@ -304,7 +320,8 @@ export class DtRadialChart implements AfterContentInit, OnDestroy {
series: DtRadialChartSeries,
arcData: PieArcDatum<number>,
chartColor: string,
totalSeriesValue: number,
max: number,
valuePercentage: number,
): DtRadialChartRenderData {
const path =
generatePathData(
Expand Down Expand Up @@ -333,11 +350,6 @@ export class DtRadialChart implements AfterContentInit, OnDestroy {
// The series' color overrides the given color from the chart color palette.
const color = series.color ? series.color : chartColor;

const max =
this.maxValue && this.maxValue >= totalSeriesValue
? this.maxValue
: totalSeriesValue;

// The path's aria label consists of the series' name, value and the chart's max-value
const ariaLabel = `${series.name}: ${series.value} of ${max}`;

Expand All @@ -349,7 +361,7 @@ export class DtRadialChart implements AfterContentInit, OnDestroy {
ariaLabel,
name: series.name,
value: series.value,
valueRelative: series.value / max,
valuePercentage,
origin: series,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface DtRadialChartRenderData {
selectionPath: string;
color: string;
value: number;
valueRelative: number;
valuePercentage: number;
ariaLabel: string;
origin: DtRadialChartSeries;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
*/

// tslint:disable: no-magic-numbers
import { getSum, getEndAngle, generatePieArcData } from './radial-chart-utils';
import {
getSum,
getEndAngle,
generatePieArcData,
getPercentages,
getRoundedPercentages,
} from './radial-chart-utils';

const FULL_CIRCLE = Math.PI * 2;

Expand Down Expand Up @@ -92,6 +98,30 @@ describe('DtRadialChart util functions', () => {
expect(arcData[2].value).toBe(35);
});
});

describe('getPercentages', () => {
it('should return percentages with precision 2 when total is higher than the sum of values', () => {
const values = [10, 21, 8];
const total = 75;
const percentages = getPercentages(values, total, 2);
expect(percentages).toEqual([13.33, 28, 10.67]);
});

it('should return percentages with precision 2 when total is equal to sum of values', () => {
const values = [10, 21, 8];
const total = 39;
const percentages = getPercentages(values, total, 2);
expect(percentages).toEqual([25.64, 53.85, 20.51]);
});
});

describe('getRoundedPercentages', () => {
it('should return rounded percentages with precision 0', () => {
const values = [10, 21, 8];
const percentages = getRoundedPercentages(values, 0);
expect(percentages).toEqual([26, 54, 20]);
});
});
});

// tslint:enable: no-magic-numbers
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,63 @@ export function generatePathData(
endAngle,
});
}

/**
* Returns an array of percentages with decimal precision.
*/
export function getPercentages(
values: number[],
total: number,
precision: number,
): number[] {
const sumAllValues = getSum(values);
if (total > sumAllValues) {
return values.map((value) => +((value / total) * 100).toFixed(precision));
}

return getRoundedPercentages(values, precision);
}

/**
* Returns an array of rounded percentages so that the sum of
* all values is equal to 100.
*/
export function getRoundedPercentages(
values: number[],
precision: number,
): number[] {
const sumAllValues = getSum(values);
const precisionPower = Math.pow(10, precision);

// Temp object to keep the percentage with precision decimals and initial position.
const tempPercentages = values.map((value, i) => ({
percentage: (value / sumAllValues) * 100 * precisionPower,
position: i,
}));

// Difference between the total and the sum of the rounded values.
const diff =
Math.pow(10, precision + 2) -
tempPercentages.reduce((sum, x) => sum + Math.floor(x.percentage), 0);

// Sorting the items in decimal decreasing order.
tempPercentages.sort(
(x, y) =>
Math.floor(x.percentage) -
x.percentage -
(Math.floor(y.percentage) - y.percentage),
);

// Distributing the difference to the first items.
const distributedPercentages = tempPercentages.map((x, i) => ({
percentage:
i < diff ? Math.floor(x.percentage) + 1 : Math.floor(x.percentage),
position: x.position,
}));

// Sorting the items in the initial position.
distributedPercentages.sort((x, y) => x.position - y.position);

// Returning the array of percentages taking into account the precision.
return distributedPercentages.map((x) => x.percentage / precisionPower);
}

0 comments on commit d08b808

Please sign in to comment.