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

Commit

Permalink
feat(sunburst-chart): Improving view for small screens.
Browse files Browse the repository at this point in the history
  • Loading branch information
PedroMosquera authored and tomheller committed Jan 21, 2021
1 parent 8baf9ce commit 7087662
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 23 deletions.
18 changes: 18 additions & 0 deletions libs/barista-components/sunburst-chart/src/pipes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from './sunburst-chart-pipes.module';
export * from './truncate.pipe';
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TruncatePipe } from './truncate.pipe';

@NgModule({
imports: [CommonModule],
declarations: [TruncatePipe],
exports: [TruncatePipe],
})
export class SunburstChartPipesModule {}
35 changes: 35 additions & 0 deletions libs/barista-components/sunburst-chart/src/pipes/truncate.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Pipe, PipeTransform } from '@angular/core';

/**
* Truncate string and an ellipsis at the end
* @param value - Value to be truncated
* @param limit - Limit of characters before being truncated
*/
@Pipe({ name: 'truncate' })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit: number = 10): string {
if (!value) {
return value;
}

return limit > 0 && value.length > limit
? value.substr(0, limit) + '…'
: value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
}}
</text>
<text class="dt-sunburst-chart-label dt-sunburst-chart-slice-label">
{{ slice.data.label }}
{{ slice.data.label | truncate: truncateLabelBy }}
</text>
</svg:g>
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export class DtSunburstChartSegment
*/
@Input() valueAsAbsolute: boolean;

/** @internal Defines the maxlength for the nodes labels. */
@Input() truncateLabelBy: number;

/**
* @internal
* The template ref for the overlay template.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
[overlayTemplate]="_overlay"
[slice]="slice"
[valueAsAbsolute]="_valueAsAbsolute"
[truncateLabelBy]="truncateLabelBy"
[attr.style]="
_sanitizeCSS('--dt-sunburst-chart-slice-color', slice.data.color)
"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ import { DtSunburstChartOverlay } from './sunburst-chart.directive';
import { OverlayModule } from '@angular/cdk/overlay';
import { DtFormattersModule } from '@dynatrace/barista-components/formatters';
import { DtSunburstChartSegment } from './sunburst-chart-segment';
import { SunburstChartPipesModule } from './pipes/sunburst-chart-pipes.module';

@NgModule({
imports: [CommonModule, OverlayModule, DtFormattersModule],
imports: [
CommonModule,
OverlayModule,
SunburstChartPipesModule,
DtFormattersModule,
],
exports: [DtSunburstChart, DtSunburstChartOverlay],
declarations: [
DtSunburstChart,
Expand Down
10 changes: 9 additions & 1 deletion libs/barista-components/sunburst-chart/src/sunburst-chart.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@

.dt-sunburst-chart-selected-info {
justify-self: center;
align-self: center;
align-self: flex-end;
text-align: center;
font-weight: bold;
max-width: 30%;
}

@media (min-width: 440px) {
.dt-sunburst-chart-selected-info {
font-weight: normal;
align-self: center;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('DtSunburstChart', () => {
expect(slices.length).toEqual(2);

expect(slices[0].nativeElement.getAttribute('d')).toBe(
'M8.817456953860943e-15,-144A144,144,0,1,1,8.817456953860943e-15,144L6.858022075225178e-15,112A112,112,0,1,0,6.858022075225178e-15,-112Z',
'M5.75583995599256e-15,-94A94,94,0,1,1,5.75583995599256e-15,94L4.0413344371862654e-15,66A66,66,0,1,0,4.0413344371862654e-15,-66Z',
);
});
});
Expand Down Expand Up @@ -242,7 +242,7 @@ describe('DtSunburstChart', () => {
);

expect(selectedSlice.nativeElement.getAttribute('d')).toBe(
'M9.797174393178826e-15,-160A160,160,0,1,1,9.797174393178826e-15,160L6.858022075225178e-15,112A112,112,0,1,0,6.858022075225178e-15,-112Z',
'M6.490628035480972e-15,-106A106,106,0,1,1,6.490628035480972e-15,106L4.0413344371862654e-15,66A66,66,0,1,0,4.0413344371862654e-15,-66Z',
);
});

Expand Down
17 changes: 12 additions & 5 deletions libs/barista-components/sunburst-chart/src/sunburst-chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import {
} from './sunburst-chart.util';

/** Minimum width of the chart */
const MIN_WIDTH = 480;
const MIN_WIDTH = 340;

/**
* Sunburst chart is a donut chart with multiple levels that get unfolded on click and show an overlay on hover
Expand Down Expand Up @@ -101,6 +101,12 @@ export class DtSunburstChart implements AfterContentInit, OnDestroy {
}
private _selected: DtSunburstChartTooltipData[];

/**
* Defines the maxlength for the nodes labels.
* If it's set to 0, no truncation is applied.
*/
@Input() truncateLabelBy: number = 10;

/** Defines the default label displayed in the center of the sunburst-chart, if no nodes are selected. */
@Input() noSelectionLabel: string = 'All';
/** Sets the display mode for the sunburst-chart values to either 'percent' or 'absolute'. */
Expand All @@ -122,9 +128,8 @@ export class DtSunburstChart implements AfterContentInit, OnDestroy {
@ViewChild('svg') _svgEl;

/** @internal Viewchildren selection for the slices */
@ViewChildren(DtSunburstChartSegment) private _segments: QueryList<
DtSunburstChartSegment
>;
@ViewChildren(DtSunburstChartSegment)
private _segments: QueryList<DtSunburstChartSegment>;

/** Slices to be painted. Exposed so users can open overlays */
get slices(): DtSunburstChartNodeSlice[] {
Expand Down Expand Up @@ -278,11 +283,13 @@ export class DtSunburstChart implements AfterContentInit, OnDestroy {

/** Calculates visible slices based on their state */
private _render(): void {
const containerWidth = this._elementRef.nativeElement.getBoundingClientRect()
.width;
const nodesWithState = getNodesWithState(
this._filledSeries,
getSelectedId(this._filledSeries, this._selected),
);
this._slices = getSlices(nodesWithState, this._radius);
this._slices = getSlices(nodesWithState, this._radius, containerWidth);

if (this._selected && this._selected.length) {
this._selectedLabel = this._selected.slice(-1)[0].label ?? '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('SunburstChart util', () => {
const palette = DT_CHART_COLOR_PALETTE_ORDERED;
const disabledColor = DtColors.GRAY_300;
const disabledColorHover = `${disabledColor}99`;
const containerWidth = 800;

describe('fillNodes', () => {
it('should fill and sort the values and children', () => {
Expand Down Expand Up @@ -549,7 +550,11 @@ describe('SunburstChart util', () => {
value: 4,
},
];
const actual = getSlices(getNodesWithState(filledNodes), radius);
const actual = getSlices(
getNodesWithState(filledNodes),
radius,
containerWidth,
);

expect(actual).toEqual(expected);
});
Expand Down Expand Up @@ -664,7 +669,11 @@ describe('SunburstChart util', () => {
value: 3,
},
];
const actual = getSlices(getNodesWithState(filledNodes, '1.0'), radius);
const actual = getSlices(
getNodesWithState(filledNodes, '1.0'),
radius,
containerWidth,
);

expect(actual).toEqual(expected);
});
Expand Down
43 changes: 32 additions & 11 deletions libs/barista-components/sunburst-chart/src/sunburst-chart.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,27 @@ export interface DtSunburstChartNodeSlice {
const SVG_SETTINGS = {
ringWidthRadius: 32,
borderWidthRadius: 16,
// innerRadius: 64,
// radius for the centroid of label
// Value for narrowing labels to the chart's centroid
labelTighten: 0,

// Margin from the chart's external end until the content-box end
labelOffsetRadius: 32,
// radius for the centroid of tooltip so it does not overlap the slice causing a possible flickering
tooltipOffsetRadius: 64,
// Minimum angle for the label should be 15°, needs to be converted into radians for further processing.
minAngleForLabel: (15 / 360) * 2 * Math.PI,
};
const SVG_SETTINGS_SMALL_VIEW = {
...SVG_SETTINGS,
ringWidthRadius: 28,
borderWidthRadius: 12,
// Value for narrowing labels to the chart's centroid
labelTighten: 10,

// Margen desde el final del exterior chart hasta el límite de la caja componente
labelOffsetRadius: 24,
};
const MALL_VIEW_WIDTH = 440;
const IS_SEPARATOR = '.';
const MAX_LEVELS = 3;

Expand Down Expand Up @@ -345,7 +358,10 @@ export const getSelectedId = (
export const getSlices = (
nodes: DtSunburstChartTooltipData[],
radius: number,
containerWidth: number,
): DtSunburstChartNodeSlice[] => {
const settings =
containerWidth > MALL_VIEW_WIDTH ? SVG_SETTINGS : SVG_SETTINGS_SMALL_VIEW;
const numLevels = nodes.reduce(
(maxLevel, node) => Math.max(maxLevel, node.depth),
0,
Expand All @@ -355,8 +371,9 @@ export const getSlices = (
nodes,
radius -
Math.min(numLevels, MAX_LEVELS) *
(SVG_SETTINGS.ringWidthRadius + SVG_SETTINGS.borderWidthRadius) -
SVG_SETTINGS.labelOffsetRadius,
(settings.ringWidthRadius + settings.borderWidthRadius) -
settings.labelOffsetRadius,
settings,
);
};

Expand All @@ -372,6 +389,7 @@ export const getSlices = (
const getSlicesByParent = (
nodes: DtSunburstChartTooltipData[],
innerRadius: number,
svgSettings = SVG_SETTINGS,
startAngle: number = 0,
endAngle: number = 2 * Math.PI,
): DtSunburstChartNodeSlice[] => {
Expand All @@ -389,8 +407,8 @@ const getSlicesByParent = (
innerRadius,
outerRadius:
innerRadius +
SVG_SETTINGS.ringWidthRadius +
(segment.data.active ? SVG_SETTINGS.borderWidthRadius : 0),
svgSettings.ringWidthRadius +
(segment.data.active ? svgSettings.borderWidthRadius : 0),
};
return [
...paths,
Expand All @@ -400,24 +418,27 @@ const getSlicesByParent = (
labelPosition: arc().centroid({
...sliceBoundaries,
innerRadius:
sliceBoundaries.outerRadius + SVG_SETTINGS.labelOffsetRadius,
sliceBoundaries.outerRadius +
svgSettings.labelOffsetRadius -
svgSettings.labelTighten,
outerRadius:
sliceBoundaries.outerRadius + SVG_SETTINGS.labelOffsetRadius,
sliceBoundaries.outerRadius + svgSettings.labelOffsetRadius,
}),
tooltipPosition: arc().centroid({
...sliceBoundaries,
innerRadius:
sliceBoundaries.outerRadius + SVG_SETTINGS.tooltipOffsetRadius,
sliceBoundaries.outerRadius + svgSettings.tooltipOffsetRadius,
outerRadius:
sliceBoundaries.outerRadius + SVG_SETTINGS.tooltipOffsetRadius,
sliceBoundaries.outerRadius + svgSettings.tooltipOffsetRadius,
}),
showLabel:
segment.endAngle - segment.startAngle >
SVG_SETTINGS.minAngleForLabel,
svgSettings.minAngleForLabel,
},
...getSlicesByParent(
segment.data.children ?? [],
sliceBoundaries.outerRadius,
svgSettings,
segment.startAngle,
segment.endAngle,
),
Expand Down

1 comment on commit 7087662

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for design-tokens-ui ready!

✅ Preview
https://design-tokens-ui-q4lser51v.vercel.app

Built with commit 7087662.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.