Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a3be401
feat: toggle able legend for cartesian chart
itssharmasandeep Nov 18, 2021
1fe78fe
fix: add test cases
itssharmasandeep Nov 18, 2021
1fbd6c9
Merge remote-tracking branch 'origin/main' into cartesian-toggle-legend
itssharmasandeep Nov 19, 2021
b53fe40
fix: addressing review comments
itssharmasandeep Nov 19, 2021
747556e
fix: addressing review comments
itssharmasandeep Nov 22, 2021
09513b3
fix: addressing review comments
itssharmasandeep Nov 22, 2021
1781dee
Merge remote-tracking branch 'origin/main' into cartesian-toggle-legend
itssharmasandeep Nov 25, 2021
a33b271
feat: grouped cartesian legend
itssharmasandeep Nov 26, 2021
805d7e1
fix: some style changes
itssharmasandeep Nov 26, 2021
7da7be2
Merge branch cartesian-toggle-legend into grouped-legend
itssharmasandeep Nov 26, 2021
0244802
fix: code refactoring and lint fixes
itssharmasandeep Nov 26, 2021
458fa52
fix: test cases
itssharmasandeep Nov 26, 2021
43c206e
fix: adding tests
itssharmasandeep Nov 26, 2021
0d2b82e
fix: adding test for coverage
itssharmasandeep Nov 26, 2021
c9df216
Merge remote-tracking branch 'origin/main' into cartesian-toggle-legend
itssharmasandeep Nov 30, 2021
9f5f84b
Merge remote-tracking branch 'origin/main' into cartesian-toggle-legend
itssharmasandeep Nov 30, 2021
408b490
Merge branch 'cartesian-toggle-legend' into grouped-legend
itssharmasandeep Nov 30, 2021
af35f00
fix: addressing review comments
itssharmasandeep Nov 30, 2021
967346e
fix: test cases
itssharmasandeep Nov 30, 2021
d9be399
fix: addressing review comments
itssharmasandeep Dec 1, 2021
53e46bd
Merge branch cartesian-toggle-legend into grouped-legend
itssharmasandeep Dec 1, 2021
72d2af7
fix: addressing review comments
itssharmasandeep Dec 1, 2021
863d65d
fix: test cases
itssharmasandeep Dec 1, 2021
902c0d8
fix: test cases
itssharmasandeep Dec 1, 2021
a9b166b
fix: suggested changes
itssharmasandeep Dec 1, 2021
c78844b
fix: lint fixes
itssharmasandeep Dec 1, 2021
c2f177b
fix: addressing review comments
itssharmasandeep Dec 2, 2021
2669bb7
Merge branch cartesian-toggle-legend into grouped-legend
itssharmasandeep Dec 2, 2021
8ef7d2e
fix: legend text name
itssharmasandeep Dec 2, 2021
316f563
Merge remote-tracking branch origin/main into grouped-legend
itssharmasandeep Dec 6, 2021
6b39400
Merge remote-tracking branch 'origin/main' into grouped-legend
itssharmasandeep Dec 13, 2021
5c57a97
Merge remote-tracking branch 'origin/main' into grouped-legend
itssharmasandeep Dec 14, 2021
5f5aafe
fix: addressing review comments
itssharmasandeep Dec 14, 2021
3572db1
fix: some test cases
itssharmasandeep Dec 14, 2021
a469833
fix: bug
itssharmasandeep Dec 14, 2021
3387b08
fix: test cases
itssharmasandeep Dec 14, 2021
37efef0
fix: remove unwanted code
itssharmasandeep Dec 14, 2021
f8b591a
Merge remote-tracking branch 'origin/main' into grouped-legend
itssharmasandeep Dec 17, 2021
f243622
fix: addressing review comments
itssharmasandeep Dec 17, 2021
3d585ba
fix: test cases
itssharmasandeep Dec 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,56 @@
}
}

&.grouped {
flex-direction: column;
gap: 12px;

.legend-entries {
flex-direction: row;
display: flex;
gap: 20px;
border: 1px solid $gray-2;
border-radius: 8px;
padding: 8px 20px;

&.active {
border-color: $blue-5;
}

.legend-entries-title {
@include body-1-regular($gray-5);
width: 200px;
cursor: pointer;

&.active {
color: $blue-5;
}
}

.legend-entry-values {
flex: 1;
display: flex;
flex-wrap: wrap;
}
}
}

.reset {
@include font-title($blue-4);
cursor: pointer;
position: absolute;
bottom: 0;
right: 0;

&.hidden {
display: none;
}

&:hover {
color: $blue-6;
}
}

.interval-control {
padding: 0 8px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,18 @@ describe('Cartesian Chart component', () => {
series: [
{
data: [[1, 2]],
name: 'test series 1',
name: 'first',
color: 'blue',
type: CartesianSeriesVisualizationType.Column,
groupName: 'test series',
stacking: true
},
{
data: [[1, 6]],
name: 'test series 2',
name: 'second',
color: 'red',
type: CartesianSeriesVisualizationType.Column,
groupName: 'test series',
stacking: true
}
],
Expand All @@ -202,14 +204,28 @@ describe('Cartesian Chart component', () => {
expect(chart.queryAll('.legend-entry').length).toBe(2);
expect(chart.query('.reset.hidden')).toExist();

const legendEntriesTitleElement = chart.query('.legend-entries-title') as Element;
chart.click(legendEntriesTitleElement);
tick();
expect(chart.queryAll('.legend-text.active').length).toBe(2);

chart.click(legendEntriesTitleElement);
tick();
expect(chart.queryAll('.legend-text.active').length).toBe(0);

const legendEntryTexts = chart.queryAll('.legend-text');
chart.click(legendEntryTexts[0]);
tick();
expect(chart.queryAll('.legend-text.active').length).toBe(1);
expect(chart.query('.reset.hidden')).not.toExist();

chart.click(chart.query('.reset') as Element);
tick();
expect(chart.query('.reset.hidden')).toExist();

chart.click(legendEntryTexts[0]);
tick();
expect(chart.queryAll('.legend-text.active').length).toBe(1);
}));

test('should render column chart', fakeAsync(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface Series<TInterval> {
// Override the default color string using a method that takes data point as input
getColor?(datum?: TInterval): string;
name: string;
groupName?: string;
symbol?: SeriesSymbol;
type: CartesianSeriesVisualizationType;
stacking?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentRef, Injector } from '@angular/core';
import { Color, DynamicComponentService } from '@hypertrace/common';
import { Color, Dictionary, DynamicComponentService } from '@hypertrace/common';
import { ContainerElement, EnterElement, select, Selection } from 'd3-selection';
import { groupBy } from 'lodash-es';
import { Observable, Subject } from 'rxjs';
import { startWith } from 'rxjs/operators';
import { LegendPosition } from '../../../legend/legend.component';
Expand All @@ -24,7 +25,9 @@ export class CartesianLegend<TData> {
public readonly activeSeries$: Observable<Series<TData>[]>;
private readonly activeSeriesSubject: Subject<Series<TData>[]> = new Subject();
private readonly initialSeries: Series<TData>[];
private readonly groupedSeries: Dictionary<Series<TData>[]>;

private readonly isGrouped: boolean = true;
private isSelectionModeOn: boolean = false;
private legendElement?: HTMLDivElement;
private activeSeries: Series<TData>[];
Expand All @@ -37,6 +40,10 @@ export class CartesianLegend<TData> {
private readonly intervalData?: CartesianIntervalData,
private readonly summaries: Summary[] = []
) {
this.isGrouped =
this.series.length > 0 && this.series.every(seriesEntry => seriesEntry.groupName !== seriesEntry.name);
this.groupedSeries = this.isGrouped ? groupBy(this.series, seriesEntry => seriesEntry.groupName) : {};

this.activeSeries = [...this.series];
this.initialSeries = [...this.series];
this.activeSeries$ = this.activeSeriesSubject.asObservable().pipe(startWith(this.series));
Expand Down Expand Up @@ -83,11 +90,42 @@ export class CartesianLegend<TData> {
}

private drawLegendEntries(container: ContainerElement): void {
select(container)
const containerSelection = select(container);
if (!this.isGrouped) {
containerSelection
.append('div')
.classed('legend-entries', true)
.selectAll('.legend-entry')
.data(this.series.filter(series => !series.hide))
.enter()
.each((_, index, elements) => this.drawLegendEntry(elements[index]));
} else {
containerSelection
.selectAll('.legend-entries')
.data(Object.values(this.groupedSeries))
.enter()
.append('div')
.classed('legend-entries', true)
.each((seriesGroup, index, elements) => this.drawLegendEntriesTitleAndValues(seriesGroup, elements[index]));
}
}

private drawLegendEntriesTitleAndValues(seriesGroup: Series<TData>[], element: HTMLDivElement): void {
const legendEntriesSelection = select(element);
legendEntriesSelection
.selectAll('.legend-entries-title')
.data([seriesGroup])
.enter()
.append('div')
.classed('legend-entries-title', true)
.text(group => `${group[0].groupName}:`)
.on('click', () => this.updateActiveSeriesGroup(seriesGroup));

legendEntriesSelection
.append('div')
.classed('legend-entries', true)
.classed('legend-entry-values', true)
.selectAll('.legend-entry')
.data(this.series.filter(series => !series.hide))
.data(seriesGroup)
.enter()
.each((_, index, elements) => this.drawLegendEntry(elements[index]));
}
Expand All @@ -107,7 +145,8 @@ export class CartesianLegend<TData> {
return select(hostElement)
.append('div')
.classed(CartesianLegend.CSS_CLASS, true)
.classed(`position-${legendPosition}`, true);
.classed(`position-${legendPosition}`, true)
.classed('grouped', this.isGrouped);
}

private drawLegendEntry(element: EnterElement): Selection<HTMLDivElement, Series<TData>, null, undefined> {
Expand All @@ -128,6 +167,21 @@ export class CartesianLegend<TData> {

private updateLegendClassesAndStyle(): void {
const legendElementSelection = select(this.legendElement!);
if (this.isGrouped) {
// Legend entries
select(this.legendElement!)
.selectAll('.legend-entries')
.classed(CartesianLegend.ACTIVE_CSS_CLASS, seriesGroup =>
this.isThisLegendSeriesGroupActive(seriesGroup as Series<TData>[])
);

// Legend entry title
select(this.legendElement!)
.selectAll('.legend-entries-title')
.classed(CartesianLegend.ACTIVE_CSS_CLASS, seriesGroup =>
this.isThisLegendSeriesGroupActive(seriesGroup as Series<TData>[])
);
}

// Legend entry symbol
legendElementSelection
Expand Down Expand Up @@ -201,14 +255,29 @@ export class CartesianLegend<TData> {
this.activeSeriesSubject.next(this.activeSeries);
}

private updateActiveSeries(seriesEntry: Series<TData>): void {
private updateActiveSeriesGroup(seriesGroup: Series<TData>[]): void {
if (!this.isSelectionModeOn) {
this.activeSeries = [seriesEntry];
this.activeSeries = [...seriesGroup];
this.isSelectionModeOn = true;
} else if (this.isThisLegendEntryActive(seriesEntry)) {
this.activeSeries = this.activeSeries.filter(series => series !== seriesEntry);
} else if (!this.isThisLegendSeriesGroupActive(seriesGroup)) {
this.activeSeries = this.activeSeries.filter(series => !seriesGroup.includes(series));
this.activeSeries.push(...seriesGroup);
} else {
this.activeSeries.push(seriesEntry);
this.activeSeries = this.activeSeries.filter(series => !seriesGroup.includes(series));
}
this.updateLegendClassesAndStyle();
this.updateResetElementVisibility(!this.isSelectionModeOn);
this.activeSeriesSubject.next(this.activeSeries);
}

private updateActiveSeries(series: Series<TData>): void {
if (!this.isSelectionModeOn) {
this.activeSeries = [series];
this.isSelectionModeOn = true;
} else if (this.isThisLegendEntryActive(series)) {
this.activeSeries = this.activeSeries.filter(seriesEntry => series !== seriesEntry);
} else {
this.activeSeries.push(series);
}
this.updateLegendClassesAndStyle();
this.updateResetElementVisibility(!this.isSelectionModeOn);
Expand All @@ -218,4 +287,8 @@ export class CartesianLegend<TData> {
private isThisLegendEntryActive(seriesEntry: Series<TData>): boolean {
return this.activeSeries.includes(seriesEntry);
}

private isThisLegendSeriesGroupActive(seriesGroup: Series<TData>[]): boolean {
return !this.isSelectionModeOn ? false : seriesGroup.every(series => this.activeSeries.includes(series));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ describe('Explore cartesian data source model', () => {
timestamp: secondIntervalTime,
value: 15
}
]
],
groupName: 'sum(foo)'
}
],
bands: []
Expand Down Expand Up @@ -198,7 +199,8 @@ describe('Explore cartesian data source model', () => {
data: [
['first', 10],
['second', 15]
]
],
groupName: 'sum(foo)'
}
],
bands: []
Expand Down Expand Up @@ -280,7 +282,7 @@ describe('Explore cartesian data source model', () => {
series: [
{
color: 'first color',
name: 'sum(foo): first',
name: 'first',
type: CartesianSeriesVisualizationType.Area,
data: [
{
Expand All @@ -295,11 +297,12 @@ describe('Explore cartesian data source model', () => {
timestamp: secondIntervalTime,
value: 15
}
]
],
groupName: 'sum(foo)'
},
{
color: 'second color',
name: 'sum(foo): second',
name: 'second',
type: CartesianSeriesVisualizationType.Area,
data: [
{
Expand All @@ -314,7 +317,8 @@ describe('Explore cartesian data source model', () => {
timestamp: secondIntervalTime,
value: 25
}
]
],
groupName: 'sum(foo)'
}
],
bands: []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,9 @@ export abstract class ExploreCartesianDataSourceModel extends GraphQlDataSourceM
data: result.data,
units: obj.attribute.units !== '' ? obj.attribute.units : undefined,
type: request.series.find(series => series.specification === result.spec)!.visualizationOptions.type,
name: isEmpty(result.groupName)
? obj.specDisplayName
: request.useGroupName
? result.groupName!
: `${obj.specDisplayName}: ${result.groupName}`,
name: !isEmpty(result.groupName) ? result.groupName! : obj.specDisplayName,
groupName:
!isEmpty(result.groupName) && (request.useGroupName ?? false) ? result.groupName! : obj.specDisplayName,
color: color
}))
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ describe('Explorer Visualization cartesian data source model', () => {
timestamp: secondIntervalTime,
value: 15
}
]
],
groupName: 'sum(foo)'
}
],
bands: []
Expand Down Expand Up @@ -218,7 +219,8 @@ describe('Explorer Visualization cartesian data source model', () => {
data: [
['first', 10],
['second', 15]
]
],
groupName: 'sum(foo)'
}
],
bands: []
Expand Down Expand Up @@ -302,7 +304,7 @@ describe('Explorer Visualization cartesian data source model', () => {
series: [
{
color: 'first color',
name: 'sum(foo): first',
name: 'first',
type: CartesianSeriesVisualizationType.Area,
data: [
{
Expand All @@ -317,11 +319,12 @@ describe('Explorer Visualization cartesian data source model', () => {
timestamp: secondIntervalTime,
value: 15
}
]
],
groupName: 'sum(foo)'
},
{
color: 'second color',
name: 'sum(foo): second',
name: 'second',
type: CartesianSeriesVisualizationType.Area,
data: [
{
Expand All @@ -336,7 +339,8 @@ describe('Explorer Visualization cartesian data source model', () => {
timestamp: secondIntervalTime,
value: 25
}
]
],
groupName: 'sum(foo)'
}
],
bands: []
Expand Down