Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow dynamically switching diagram type #144

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion libs/portal-integration-angular/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@
},
"OCX_DIAGRAM": {
"SUM": "Gesamtanzahl",
"NO_DATA": "Es sind keine Daten vorhanden"
"NO_DATA": "Es sind keine Daten vorhanden",
"SWITCH_DIAGRAM_TYPE": {
"PIE": "Zu Tortendiagramm wechseln",
"HORIZONTAL_BAR": "Zu horizontalem Balkendiagramm wechseln",
"VERTICAL_BAR": "Zu vertikalem Balkendiagramm wechseln"
}
},
"OCX_PORTAL_VIEWPORT": {
"SUCCESS": "Erfolg!",
Expand Down
7 changes: 6 additions & 1 deletion libs/portal-integration-angular/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@
},
"OCX_DIAGRAM": {
"SUM": "Total",
"NO_DATA": "There is no data available"
"NO_DATA": "There is no data available",
"SWITCH_DIAGRAM_TYPE": {
"PIE": "Switch to pie chart",
"HORIZONTAL_BAR": "Switch to horizontal bar chart",
"VERTICAL_BAR": "Switch to vertical bar chart"
}
},
"OCX_PORTAL_VIEWPORT": {
"SUCCESS": "Success!",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
<ng-container *ngIf="this.data">
<div class="flex justify-content-center pb-2" *ngIf="shownDiagramTypes.length > 1">
<p-selectButton
[options]="shownDiagramTypes"
[(ngModel)]="selectedDiagramType"
optionLabel="icon"
(onChange)="onDiagramTypeChanged($event)"
name="diagram-type-select-button"
>
<ng-template let-item pTemplate>
<i [class]="item.icon" [title]="item.title || (item.titleKey | translate)"></i>
</ng-template>
</p-selectButton>
</div>
<div class="w-full flex justify-content-center">
<p-chart
id="diagram"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
::ng-deep {
.p-buttonset .p-button {
min-width: auto;
}

.p-buttonset {
display: flex;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { MessageModule } from 'primeng/message'
import { DiagramHarness, TestbedHarnessEnvironment } from '../../../../../testing'
import { MockAuthModule } from '../../../mock-auth/mock-auth.module'
import { DiagramType } from '../../../model/diagram-type'
import { DiagramComponent } from './diagram.component'
import { DiagramComponent, DiagramLayouts } from './diagram.component'
import { PrimeIcons } from 'primeng/api'
import { SelectButtonModule } from 'primeng/selectbutton'
import { FormsModule } from '@angular/forms'

describe('DiagramComponent', () => {
let translateService: TranslateService
Expand Down Expand Up @@ -38,6 +41,8 @@ describe('DiagramComponent', () => {
ChartModule,
MessageModule,
MockAuthModule,
SelectButtonModule,
FormsModule,
TranslateTestingModule.withTranslations({
en: require('./../../../../../assets/i18n/en.json'),
de: require('./../../../../../assets/i18n/de.json'),
Expand Down Expand Up @@ -103,4 +108,110 @@ describe('DiagramComponent', () => {
const chartType = await chartHarness.getType()
expect(chartType).toEqual('bar')
})

it('should not display a diagramType select button by default', async () => {
expect(component.supportedDiagramTypes).toEqual([])
expect(component.shownDiagramTypes).toEqual([])

const diagram = await TestbedHarnessEnvironment.harnessForFixture(fixture, DiagramHarness)
const diagramTypeSelectButton = await diagram.getDiagramTypeSelectButton()

expect(diagramTypeSelectButton).toBe(null)
})

it('should render a diagramType select button if supportedDiagramTypes is specified', async () => {
const expectedDiagramLayouts: DiagramLayouts[] = [
{ icon: PrimeIcons.CHART_PIE, layout: DiagramType.PIE, titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.PIE' },
{
icon: PrimeIcons.BARS,
layout: DiagramType.HORIZONTAL_BAR,
titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.HORIZONTAL_BAR',
},
]

component.supportedDiagramTypes = [DiagramType.PIE, DiagramType.HORIZONTAL_BAR]
const diagram = await TestbedHarnessEnvironment.harnessForFixture(fixture, DiagramHarness)
const diagramTypeSelectButton = await diagram.getDiagramTypeSelectButton()
const diagramTypeSelectButtonOptions = await diagram.getAllSelectionButtons()

expect(component.shownDiagramTypes).toEqual(expectedDiagramLayouts)
expect(diagramTypeSelectButton).toBeTruthy()
expect(diagramTypeSelectButtonOptions.length).toBe(2)
})

it('should change the rendered diagram whenever the select button is used to change the diagramType', async () => {
component.supportedDiagramTypes = [DiagramType.PIE, DiagramType.HORIZONTAL_BAR]

const diagram = await TestbedHarnessEnvironment.harnessForFixture(fixture, DiagramHarness)
const diagramTypeSelectButton = await diagram.getDiagramTypeSelectButton()
const diagramTypeSelectButtonOptions = await diagram.getAllSelectionButtons()

let diagramTypeChangedEvent: DiagramType | undefined
component.diagramTypeChanged.subscribe((event) => (diagramTypeChangedEvent = event))

expect(diagramTypeSelectButton).toBeTruthy()
expect(component.diagramType).toBe(DiagramType.PIE)
let chartHarness = await diagram.getChart()
let chartType = await chartHarness.getType()
expect(chartType).toEqual('pie')

await diagramTypeSelectButtonOptions[1].click()
expect(component.diagramType).toBe(DiagramType.HORIZONTAL_BAR)
chartHarness = await diagram.getChart()
chartType = await chartHarness.getType()
expect(chartType).toEqual('bar')
expect(diagramTypeChangedEvent).toBe(DiagramType.HORIZONTAL_BAR)

await diagramTypeSelectButtonOptions[0].click()
expect(component.diagramType).toBe(DiagramType.PIE)
chartHarness = await diagram.getChart()
chartType = await chartHarness.getType()
expect(chartType).toEqual('pie')
expect(diagramTypeChangedEvent).toBe(DiagramType.PIE)
})

it('should dynamically add/remove options to/from the diagramType select button', async () => {
const allDiagramLayouts: DiagramLayouts[] = [
{ icon: PrimeIcons.CHART_PIE, layout: DiagramType.PIE, titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.PIE' },
{
icon: PrimeIcons.BARS,
layout: DiagramType.HORIZONTAL_BAR,
titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.HORIZONTAL_BAR',
},
{
icon: PrimeIcons.CHART_BAR,
layout: DiagramType.VERTICAL_BAR,
titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.VERTICAL_BAR',
},
]

expect(component.shownDiagramTypes).toEqual([])

component.supportedDiagramTypes = [DiagramType.PIE, DiagramType.HORIZONTAL_BAR]
const diagram = await TestbedHarnessEnvironment.harnessForFixture(fixture, DiagramHarness)
const diagramTypeSelectButton = await diagram.getDiagramTypeSelectButton()

expect(diagramTypeSelectButton).toBeTruthy()
expect(component.shownDiagramTypes).toEqual(allDiagramLayouts.slice(0, 2))
const diagramTypeSelectButtonOptions = await diagram.getAllSelectionButtons()
expect(diagramTypeSelectButtonOptions.length).toBe(2)

component.supportedDiagramTypes = [DiagramType.PIE, DiagramType.HORIZONTAL_BAR, DiagramType.VERTICAL_BAR]
const diagramTypeSelectButtonAfterUpdate = await diagram.getDiagramTypeSelectButton()
const diagramTypeSelectButtonOptionsAfterUpdate = await diagram.getAllSelectionButtons()
expect(diagramTypeSelectButtonAfterUpdate).toBeTruthy()
expect(component.shownDiagramTypes).toEqual(allDiagramLayouts)
expect(diagramTypeSelectButtonOptionsAfterUpdate.length).toBe(3)
})

it('should automatically select the button for the currently displayed diagram', async () => {
component.supportedDiagramTypes = [DiagramType.PIE, DiagramType.HORIZONTAL_BAR]
component.diagramType = DiagramType.HORIZONTAL_BAR

const diagram = await TestbedHarnessEnvironment.harnessForFixture(fixture, DiagramHarness)
const diagramTypeSelectButtonOptions = await diagram.getAllSelectionButtons()

expect(await diagramTypeSelectButtonOptions[0].hasClass('p-highlight')).toBe(false)
expect(await diagramTypeSelectButtonOptions[1].hasClass('p-highlight')).toBe(true)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { importProvidersFrom } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { Meta, StoryFn, applicationConfig, moduleMetadata } from '@storybook/angular'
import { BreadcrumbModule } from 'primeng/breadcrumb'
import { ButtonModule } from 'primeng/button'
import { MenuModule } from 'primeng/menu'
import { SkeletonModule } from 'primeng/skeleton'
import { DynamicPipe } from '../../pipes/dynamic.pipe'
import { StorybookTranslateModule } from '../../storybook-translate.module'
import { DiagramComponent } from './diagram.component'
import { DiagramType } from '../../../model/diagram-type'
import { DiagramData } from '../../../model/diagram-data'
import { ChartModule } from 'primeng/chart'
import { SelectButtonModule } from 'primeng/selectbutton'
import { FormsModule } from '@angular/forms'

export default {
title: 'DiagramComponent',
component: DiagramComponent,
argTypes: {
diagramType: {
options: [DiagramType.HORIZONTAL_BAR, DiagramType.VERTICAL_BAR, DiagramType.PIE],
control: { type: 'select' },
},
},
decorators: [
applicationConfig({
providers: [importProvidersFrom(BrowserModule), importProvidersFrom(BrowserAnimationsModule)],
}),
moduleMetadata({
declarations: [DiagramComponent, DynamicPipe],
imports: [MenuModule, BreadcrumbModule, ButtonModule, SkeletonModule, StorybookTranslateModule, ChartModule, SelectButtonModule, FormsModule],
}),
],
} as Meta<DiagramComponent>

const Template: StoryFn<DiagramComponent> = (args: DiagramComponent) => ({
props: args,
})

const mockData: DiagramData[] = [
{
label: 'Apples',
value: 10,
},
{
label: 'Bananas',
value: 7,
},
{
label: 'Oranges',
value: 3,
},
]

export const PieChart = {
render: Template,

args: {
diagramType: DiagramType.PIE,
data: mockData,
},
}

export const HorizontalBarChart = {
render: Template,

args: {
diagramType: DiagramType.HORIZONTAL_BAR,
data: mockData,
},
}

export const VerticalBarChart = {
render: Template,

args: {
diagramType: DiagramType.VERTICAL_BAR,
data: mockData,
},
}

export const WithDiagramTypeSelection = {
render: Template,
args: {
diagramType: DiagramType.PIE,
data: mockData,
supportedDiagramTypes: [DiagramType.PIE, DiagramType.HORIZONTAL_BAR, DiagramType.VERTICAL_BAR]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,59 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angu
import { TranslateService } from '@ngx-translate/core'
import { ChartData, ChartOptions } from 'chart.js'
import * as d3 from 'd3-scale-chromatic'
import { ColorUtils } from '../../utils/colorutils'
import { PrimeIcons } from 'primeng/api'
import { DiagramData } from '../../../model/diagram-data'
import { DiagramType } from '../../../model/diagram-type'
import { ColorUtils } from '../../utils/colorutils'

export interface DiagramLayouts {
SchettlerKoehler marked this conversation as resolved.
Show resolved Hide resolved
icon: string
layout: DiagramType
title?: string
titleKey: string
}

const allDiagramTypes: DiagramLayouts[] = [
{ icon: PrimeIcons.CHART_PIE, layout: DiagramType.PIE, titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.PIE' },
{ icon: PrimeIcons.BARS, layout: DiagramType.HORIZONTAL_BAR, titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.HORIZONTAL_BAR' },
{ icon: PrimeIcons.CHART_BAR, layout: DiagramType.VERTICAL_BAR, titleKey: 'OCX_DIAGRAM.SWITCH_DIAGRAM_TYPE.VERTICAL_BAR' },
]

@Component({
selector: 'ocx-diagram',
templateUrl: './diagram.component.html',
styleUrls: ['./diagram.component.scss']
})
export class DiagramComponent implements OnInit, OnChanges {
@Input() data: DiagramData[] | undefined
@Input() sumKey = 'OCX_DIAGRAM.SUM'
private _diagramType: DiagramType = DiagramType.PIE
selectedDiagramType: DiagramLayouts | undefined
public chartType = 'pie'
@Input()
get diagramType(): DiagramType {
return this._diagramType
}
set diagramType(value: DiagramType) {
this._diagramType = value
this.selectedDiagramType = allDiagramTypes.find((v) => v.layout === value)
this.chartType = this.diagramTypeToChartType(value)
}
private _supportedDiagramTypes: DiagramType[] = []
@Input()
get supportedDiagramTypes(): DiagramType[] {
return this._supportedDiagramTypes
}
set supportedDiagramTypes(value: DiagramType[]) {
this._supportedDiagramTypes = value
this.shownDiagramTypes = allDiagramTypes.filter((vl) => this.supportedDiagramTypes.includes(vl.layout))
}
@Output() dataSelected: EventEmitter<any> = new EventEmitter()
@Output() diagramTypeChanged: EventEmitter<DiagramType> = new EventEmitter()
chartOptions: ChartOptions | undefined
chartData: ChartData | undefined
amountOfData: number | undefined | null
shownDiagramTypes: DiagramLayouts[] = []
// Changing the colorRangeInfo, will change the range of the color palette of the diagram.
private colorRangeInfo = {
colorStart: 0,
Expand Down Expand Up @@ -90,6 +118,12 @@ export class DiagramComponent implements OnInit, OnChanges {
dataClicked(event: []) {
this.dataSelected.emit(event.length)
}

onDiagramTypeChanged(event: any) {
this.diagramType = event.value.layout
this.generateChart(this.colorScale, this.colorRangeInfo)
this.diagramTypeChanged.emit(event.value.layout)
}
}
function interpolateColors(amountOfData: number, colorScale: any, colorRangeInfo: any) {
return ColorUtils.interpolateColors(amountOfData, colorScale, colorRangeInfo)
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
<ocx-diagram [data]="(diagramData$ | async) || []" [sumKey]="sumKey" [diagramType]="type" (onDataSelect)="dataClicked($event)"></ocx-diagram>
<ocx-diagram
[data]="(diagramData$ | async) || []"
[sumKey]="sumKey"
[diagramType]="diagramType"
(onDataSelect)="dataClicked($event)"
[supportedDiagramTypes]="supportedDiagramTypes"
(diagramTypeChanged)="onDiagramTypeChanged($event)"
></ocx-diagram>
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { MockAuthModule } from '../../../mock-auth/mock-auth.module'
import { ColumnType } from '../../../model/column-type.model'
import { DiagramComponent } from '../diagram/diagram.component'
import { GroupByCountDiagramComponent } from './group-by-count-diagram.component'
import { DiagramType } from '../../../model/diagram-type'

describe('GroupByCountDiagramComponent', () => {
let translateService: TranslateService
Expand Down Expand Up @@ -195,4 +196,22 @@ describe('GroupByCountDiagramComponent', () => {
const definedSumKeyTranslation = translateService.instant(definedSumKey)
expect(displayedText).toEqual(definedSumKeyTranslation)
})

it('should not display a selectButton on the diagram by default', async () => {
expect(component.supportedDiagramTypes).toEqual([])

const diagram = await loader.getHarness(DiagramHarness)
const diagramTypeSelectButton = await diagram.getDiagramTypeSelectButton()

expect(diagramTypeSelectButton).toBe(null)
})

it('should display a selectButton on the diagram if supportedDiagramTypes is specified', async () => {
component.supportedDiagramTypes = [DiagramType.PIE, DiagramType.HORIZONTAL_BAR]

const diagram = await loader.getHarness(DiagramHarness)
const diagramTypeSelectButton = await diagram.getDiagramTypeSelectButton()

expect(diagramTypeSelectButton).toBeTruthy()
})
})
Loading
Loading