From 10cdbf0a24053a1f0fca0dac6fa7597c9ad1cf40 Mon Sep 17 00:00:00 2001
From: Hallie Swan <26949006+hallieswan@users.noreply.github.com>
Date: Fri, 16 Jun 2023 14:51:43 -0700
Subject: [PATCH 1/5] refactor median-chart to remove dc.js dependency, move
styles shared between median-barchart and score-barchart into global
stylesheet
---
src/app/features/charts/charts.module.ts | 3 +
src/app/features/charts/components/index.ts | 1 +
.../components/median-barchart/index.ts | 1 +
.../median-barchart.component.html | 12 +
.../median-barchart.component.scss | 26 ++
.../median-barchart.component.spec.ts | 222 +++++++++++++++
.../median-barchart.component.ts | 260 ++++++++++++++++++
.../score-barchart.component.html | 4 +-
.../score-barchart.component.scss | 76 +----
.../gene-evidence-rna.component.html | 4 +-
src/styles/_variables.scss | 1 +
src/styles/components/_chart_d3.scss | 94 +++++++
src/styles/styles.scss | 1 +
13 files changed, 626 insertions(+), 79 deletions(-)
create mode 100644 src/app/features/charts/components/median-barchart/index.ts
create mode 100644 src/app/features/charts/components/median-barchart/median-barchart.component.html
create mode 100644 src/app/features/charts/components/median-barchart/median-barchart.component.scss
create mode 100644 src/app/features/charts/components/median-barchart/median-barchart.component.spec.ts
create mode 100644 src/app/features/charts/components/median-barchart/median-barchart.component.ts
create mode 100644 src/styles/components/_chart_d3.scss
diff --git a/src/app/features/charts/charts.module.ts b/src/app/features/charts/charts.module.ts
index 1f1b2b028..c00cb82de 100644
--- a/src/app/features/charts/charts.module.ts
+++ b/src/app/features/charts/charts.module.ts
@@ -7,6 +7,7 @@ import {
BoxPlotComponent,
CandlestickChartComponent,
MedianChartComponent,
+ MedianBarChartComponent,
RowChartComponent,
ScoreChartComponent,
ScoreBarChartComponent,
@@ -19,6 +20,7 @@ import {
BoxPlotComponent,
CandlestickChartComponent,
MedianChartComponent,
+ MedianBarChartComponent,
RowChartComponent,
ScoreChartComponent,
ScoreBarChartComponent,
@@ -30,6 +32,7 @@ import {
BoxPlotComponent,
CandlestickChartComponent,
MedianChartComponent,
+ MedianBarChartComponent,
RowChartComponent,
ScoreChartComponent,
ScoreBarChartComponent,
diff --git a/src/app/features/charts/components/index.ts b/src/app/features/charts/components/index.ts
index 189d12e46..9d00708b7 100644
--- a/src/app/features/charts/components/index.ts
+++ b/src/app/features/charts/components/index.ts
@@ -1,6 +1,7 @@
export * from './box-plot-chart';
export * from './candlestick-chart';
export * from './median-chart';
+export * from './median-barchart';
export * from './row-chart';
export * from './score-chart';
export * from './score-barchart';
diff --git a/src/app/features/charts/components/median-barchart/index.ts b/src/app/features/charts/components/median-barchart/index.ts
new file mode 100644
index 000000000..a587af3d3
--- /dev/null
+++ b/src/app/features/charts/components/median-barchart/index.ts
@@ -0,0 +1 @@
+export * from './median-barchart.component';
diff --git a/src/app/features/charts/components/median-barchart/median-barchart.component.html b/src/app/features/charts/components/median-barchart/median-barchart.component.html
new file mode 100644
index 000000000..b8797ac54
--- /dev/null
+++ b/src/app/features/charts/components/median-barchart/median-barchart.component.html
@@ -0,0 +1,12 @@
+
diff --git a/src/app/features/charts/components/median-barchart/median-barchart.component.scss b/src/app/features/charts/components/median-barchart/median-barchart.component.scss
new file mode 100644
index 000000000..530405ea6
--- /dev/null
+++ b/src/app/features/charts/components/median-barchart/median-barchart.component.scss
@@ -0,0 +1,26 @@
+#median-barchart {
+ .x-axis-label, .y-axis-label {
+ font-size: var(--font-size-lg);
+ font-weight: 700;
+ color: var(--color-chart-axis-label);
+ fill: var(--color-chart-axis-label);
+ }
+
+ .y-axis .tick {
+ text {
+ font-size: var(--font-size-sm);
+ }
+ }
+
+ .x-axis .tick {
+ text {
+ font-size: var(--font-size-md);
+ font-weight: 700;
+ }
+ }
+
+ .bar-labels {
+ font-size: var(--font-size-sm);
+ text-anchor: middle;
+ }
+}
\ No newline at end of file
diff --git a/src/app/features/charts/components/median-barchart/median-barchart.component.spec.ts b/src/app/features/charts/components/median-barchart/median-barchart.component.spec.ts
new file mode 100644
index 000000000..c3a745699
--- /dev/null
+++ b/src/app/features/charts/components/median-barchart/median-barchart.component.spec.ts
@@ -0,0 +1,222 @@
+// -------------------------------------------------------------------------- //
+// External
+// -------------------------------------------------------------------------- //
+import { TestBed, ComponentFixture, waitForAsync } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+// -------------------------------------------------------------------------- //
+// Internal
+// -------------------------------------------------------------------------- //
+import { MedianBarChartComponent } from './';
+import { HelperService } from '../../../../core/services';
+import { geneMock1 } from '../../../../testing';
+import { MedianExpression } from '../../../../models/genes';
+
+// -------------------------------------------------------------------------- //
+// Tests
+// -------------------------------------------------------------------------- //
+const XAXIS_LABEL = 'BRAIN REGION';
+const YAXIS_LABEL = 'LOG2CPM';
+const FULL_MOCK_DATA = geneMock1.medianexpression;
+const TISSUES = ['TCX', 'PHG', 'STG'];
+const SMALL_MOCK_DATA = [
+ {
+ ensembl_gene_id: 'MOCK_GENE',
+ minimumlogcpm: -2.54173091337051,
+ quartile1logcpm: -1.03358030635935,
+ medianlogcpm: 0.483801733963266,
+ meanlogcpm: -0.517398199667356,
+ quartile3logcpm: -0.0800759845652829,
+ maximumlogcpm: 2.32290808871289,
+ tissue: TISSUES[0],
+ },
+ {
+ ensembl_gene_id: 'MOCK_GENE',
+ minimumlogcpm: -2.44077907413711,
+ quartile1logcpm: -0.592671867557559,
+ medianlogcpm: 0.013739530502129,
+ meanlogcpm: -0.0324143336865,
+ quartile3logcpm: 0.49577213202412,
+ maximumlogcpm: 2.23019575245731,
+ tissue: TISSUES[1],
+ },
+ {
+ ensembl_gene_id: 'MOCK_GENE',
+ minimumlogcpm: -5.03189866356294,
+ quartile1logcpm: -1.02644563959975,
+ medianlogcpm: 0.176348063122062,
+ meanlogcpm: -0.323038107200895,
+ quartile3logcpm: 0.391874711168331,
+ maximumlogcpm: 1.9113258251877,
+ tissue: TISSUES[2],
+ },
+];
+
+describe('Component: BarChart - Median', () => {
+ let fixture: ComponentFixture;
+ let component: MedianBarChartComponent;
+ let element: HTMLElement;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [MedianBarChartComponent],
+ imports: [RouterTestingModule],
+ providers: [HelperService],
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MedianBarChartComponent);
+ component = fixture.componentInstance;
+ });
+
+ const setUp = (
+ data: MedianExpression[] = FULL_MOCK_DATA,
+ xAxisLabel: string = XAXIS_LABEL,
+ yAxisLabel: string = YAXIS_LABEL
+ ) => {
+ component.data = data;
+ component.xAxisLabel = xAxisLabel;
+ component.yAxisLabel = yAxisLabel;
+
+ fixture.detectChanges();
+ element = fixture.nativeElement;
+ const chart = element.querySelector('svg g');
+ return { chart };
+ };
+
+ it('should create', () => {
+ setUp();
+ expect(component).toBeTruthy();
+ });
+
+ it('should not render the chart if there is no data', () => {
+ const { chart } = setUp([]);
+
+ expect(component.data?.length).toEqual(0);
+ expect(element.querySelector('.chart-no-data')).toBeTruthy();
+
+ expect(chart).toBeFalsy();
+ });
+
+ it('should not render the chart if all values are negative', () => {
+ const { chart } = setUp(
+ SMALL_MOCK_DATA.map((obj) => {
+ return { ...obj, medianlogcpm: -1 * obj.medianlogcpm };
+ })
+ );
+
+ expect(element.querySelector('.chart-no-data')).toBeTruthy();
+
+ expect(chart).toBeFalsy();
+ });
+
+ it('should render the chart if there is positive data', () => {
+ const createChartSpy = spyOn(component, 'createChart').and.callThrough();
+ const { chart } = setUp();
+
+ expect(component.data?.length).toEqual(FULL_MOCK_DATA.length);
+ expect(createChartSpy).toHaveBeenCalled();
+ expect(chart).toBeTruthy();
+
+ expect(element.querySelector('.chart-no-data')).toBeFalsy();
+ });
+
+ it('should render the chart axes', () => {
+ const { chart } = setUp();
+
+ expect(chart?.querySelector('.x-axis')).toBeTruthy();
+ expect(chart?.querySelector('.y-axis')).toBeTruthy();
+ });
+
+ it('should render the correct number of bars', () => {
+ const { chart } = setUp();
+
+ expect(chart?.querySelectorAll('rect').length).toEqual(
+ FULL_MOCK_DATA.length
+ );
+ });
+
+ it('should not render bars for negative values', () => {
+ const { chart } = setUp([
+ { ...SMALL_MOCK_DATA[0], medianlogcpm: -0.01 },
+ ...SMALL_MOCK_DATA.slice(1),
+ ]);
+
+ expect(chart?.querySelectorAll('rect').length).toEqual(
+ SMALL_MOCK_DATA.length - 1
+ );
+ });
+
+ it('should render the bar labels', () => {
+ const { chart } = setUp();
+
+ expect(chart?.querySelectorAll('text.bar-labels').length).toEqual(
+ FULL_MOCK_DATA.length
+ );
+ });
+
+ it('should render the labels for both axes', () => {
+ const { chart } = setUp();
+
+ expect(chart?.querySelector('.x-axis-label')?.textContent).toEqual(
+ XAXIS_LABEL
+ );
+ expect(chart?.querySelector('.y-axis-label')?.textContent).toEqual(
+ YAXIS_LABEL
+ );
+ });
+
+ it('should render the meaningful expression threshold', () => {
+ const { chart } = setUp();
+
+ expect(
+ chart?.querySelector('.meaningful-expression-threshold-line')
+ ).toBeTruthy();
+ });
+
+ it('should render the meaningful expression threshold even when all values are small', () => {
+ const { chart } = setUp(SMALL_MOCK_DATA);
+
+ expect(
+ chart?.querySelector('.meaningful-expression-threshold-line')
+ ).toBeTruthy();
+ });
+
+ it('should alphabetize the x-axis values', () => {
+ setUp(SMALL_MOCK_DATA);
+
+ const sortedTissues = TISSUES.sort();
+ const xAxisTicks = element
+ .querySelector('svg g .x-axis')
+ ?.querySelectorAll('.tick');
+ xAxisTicks?.forEach((val, index) => {
+ expect(val.textContent).toEqual(sortedTissues[index]);
+ });
+ });
+
+ it('should show and hide tooltip', () => {
+ setUp();
+
+ const tooltip = element.querySelector('#tooltip');
+ expect(tooltip?.textContent).toBeFalsy();
+
+ const xAxisTick = element.querySelector('svg g .x-axis .tick');
+ const mouseEnterEvent = new MouseEvent('mouseenter', {
+ bubbles: true,
+ cancelable: true,
+ });
+ xAxisTick?.dispatchEvent(mouseEnterEvent);
+
+ expect(tooltip?.style.display).toEqual('block');
+ expect(tooltip?.textContent).toBeTruthy();
+
+ const mouseLeaveEvent = new MouseEvent('mouseleave', {
+ bubbles: true,
+ cancelable: true,
+ });
+ xAxisTick?.dispatchEvent(mouseLeaveEvent);
+
+ expect(tooltip?.style.display).toEqual('none');
+ });
+});
diff --git a/src/app/features/charts/components/median-barchart/median-barchart.component.ts b/src/app/features/charts/components/median-barchart/median-barchart.component.ts
new file mode 100644
index 000000000..efd50bf7c
--- /dev/null
+++ b/src/app/features/charts/components/median-barchart/median-barchart.component.ts
@@ -0,0 +1,260 @@
+// -------------------------------------------------------------------------- //
+// External
+// -------------------------------------------------------------------------- //
+import {
+ AfterViewInit,
+ Component,
+ ElementRef,
+ HostListener,
+ Input,
+ OnChanges,
+ OnDestroy,
+ SimpleChanges,
+ ViewChild,
+ ViewEncapsulation,
+} from '@angular/core';
+import * as d3 from 'd3';
+
+// -------------------------------------------------------------------------- //
+// Internal
+// -------------------------------------------------------------------------- //
+import { MedianExpression } from '../../../../models';
+import { HelperService } from '../../../../core/services';
+
+// -------------------------------------------------------------------------- //
+// Component
+// -------------------------------------------------------------------------- //
+@Component({
+ selector: 'median-barchart',
+ templateUrl: './median-barchart.component.html',
+ styleUrls: ['./median-barchart.component.scss'],
+ encapsulation: ViewEncapsulation.None,
+})
+export class MedianBarChartComponent
+ implements OnChanges, AfterViewInit, OnDestroy
+{
+ private chartInitialized = false;
+ private tooltipInitialized = false;
+ private _data: MedianExpression[] = [];
+ private chart!: d3.Selection;
+ private tooltip!: d3.Selection;
+ private MEANINGFUL_EXPRESSION_THRESHOLD = Math.log2(5);
+ private maxValueY = -1;
+
+ get data() {
+ return this._data;
+ }
+ @Input() set data(data: MedianExpression[]) {
+ this._data = data
+ .filter((el) => el.medianlogcpm && el.medianlogcpm > 0)
+ .sort((a, b) => a.tissue.localeCompare(b.tissue));
+ this.maxValueY = Math.max(
+ this.MEANINGFUL_EXPRESSION_THRESHOLD,
+ d3.max(this._data, (d) => d.medianlogcpm) || 0
+ );
+ }
+
+ @Input() xAxisLabel = '';
+ @Input() yAxisLabel = 'LOG2 CPM';
+
+ @ViewChild('chart') chartRef: ElementRef = {} as ElementRef;
+ @ViewChild('tooltip') tooltipRef: ElementRef = {} as ElementRef;
+
+ dimension: any;
+ group: any;
+
+ constructor(private helperService: HelperService) {}
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (
+ (changes._data && !changes._data.firstChange) ||
+ (changes.xAxisLabel && !changes.xAxisLabel.firstChange) ||
+ (changes.yAxisLabel && !changes.yAxisLabel.firstChange)
+ ) {
+ if (this._data.length === 0) {
+ this.clearChart();
+ this.hideChart();
+ } else {
+ this.clearChart();
+ this.showChart();
+ this.createChart();
+ }
+ }
+ }
+
+ ngAfterViewInit(): void {
+ if (this._data.length === 0) this.hideChart();
+ else this.createChart();
+ }
+
+ ngOnDestroy(): void {
+ this.destroyChart();
+ }
+
+ clearChart() {
+ const svg = d3.select(this.chartRef.nativeElement);
+ svg.selectAll('*').remove();
+ }
+
+ hideChart() {
+ const svg = d3.select(this.chartRef.nativeElement);
+ svg.style('display', 'none');
+ }
+
+ showChart() {
+ const svg = d3.select(this.chartRef.nativeElement);
+ svg.style('display', 'block');
+ }
+
+ destroyChart() {
+ if (this.chartInitialized) this.chart.remove();
+ if (this.tooltipInitialized) this.tooltip.remove();
+ }
+
+ showTooltip(text: string, x: number, y: number): void {
+ this.tooltip = d3
+ .select(this.tooltipRef.nativeElement)
+ .style('left', `${x}px`)
+ .style('top', `${y}px`)
+ .style('display', 'block')
+ .html(text);
+ this.tooltipInitialized = true;
+ }
+
+ hideTooltip() {
+ if (this.tooltipInitialized) {
+ this.tooltip.style('display', 'none');
+ }
+ }
+
+ getBarCenterX(tissue: string, xScale: d3.ScaleBand): number {
+ return (xScale(tissue) || 0) + xScale.bandwidth() / 2;
+ }
+
+ // get the current width allotted to this chart or default
+ getChartBoundingWidth(): number {
+ return (
+ d3.select(this.chartRef.nativeElement).node()?.getBoundingClientRect()
+ .width || 500
+ );
+ }
+
+ createChart() {
+ if (this._data.length > 0) {
+ const barColor = this.helperService.getColor('secondary');
+ const width = this.getChartBoundingWidth();
+ const height = 350;
+ const margin = { top: 20, right: 20, bottom: 65, left: 65 };
+ const innerWidth = width - margin.left - margin.right;
+ const innerHeight = height - margin.top - margin.bottom;
+
+ this.chart = d3
+ .select(this.chartRef.nativeElement)
+ .attr('width', width)
+ .attr('height', height)
+ .append('g')
+ .attr('transform', `translate(${margin.left}, ${margin.top})`);
+
+ // SCALES
+ const xScale = d3
+ .scaleBand()
+ .domain(this._data.map((d) => d.tissue))
+ .range([0, innerWidth])
+ .padding(0.2);
+
+ const yScale = d3
+ .scaleLinear()
+ .domain([0, this.maxValueY])
+ .nice()
+ .range([innerHeight, 0]);
+
+ // BARS
+ this.chart
+ .selectAll('.medianbars')
+ .data(this._data)
+ .enter()
+ .append('rect')
+ .attr('class', 'medianbars')
+ .attr('x', (d) => xScale(d.tissue) as number)
+ .attr('y', (d) => yScale(d.medianlogcpm || 0))
+ .attr('width', xScale.bandwidth())
+ .attr('height', (d) => innerHeight - yScale(d.medianlogcpm || 0))
+ .attr('fill', barColor);
+
+ // SCORE LABELS
+ this.chart
+ .selectAll('.bar-labels')
+ .data(this._data)
+ .enter()
+ .append('text')
+ .attr('class', 'bar-labels')
+ .attr('x', (d) => this.getBarCenterX(d.tissue, xScale))
+ .attr('y', (d) => yScale(d.medianlogcpm || 0) - 5)
+ .text((d) => this.helperService.roundNumber(d.medianlogcpm || 0, 2));
+
+ // X-AXIS
+ const xAxis = d3.axisBottom(xScale);
+ this.chart
+ .append('g')
+ .attr('class', 'x-axis')
+ .attr('transform', `translate(0, ${innerHeight})`)
+ .call(xAxis.tickSizeOuter(0))
+ .selectAll('.tick')
+ .on('mouseenter', (_, tissue) => {
+ const tooltipText = this.helperService.getGCTColumnTooltipText(
+ tissue as string
+ );
+ this.showTooltip(
+ tooltipText,
+ this.getBarCenterX(tissue as string, xScale) + margin.left,
+ height - margin.top
+ );
+ })
+ .on('mouseleave', () => {
+ this.hideTooltip();
+ });
+
+ // Y-AXIS
+ const yAxis = d3.axisLeft(yScale);
+ this.chart.append('g').attr('class', 'y-axis').call(yAxis);
+
+ // X-AXIS LABEL
+ this.chart
+ .append('text')
+ .attr('class', 'x-axis-label')
+ .attr('x', innerWidth / 2)
+ .attr('y', innerHeight + margin.bottom)
+ .attr('text-anchor', 'middle')
+ .text(this.xAxisLabel);
+
+ // Y-AXIS LABEL
+ this.chart
+ .append('text')
+ .attr('class', 'y-axis-label')
+ .attr('x', -innerHeight / 2)
+ .attr('y', -margin.left)
+ .attr('dy', '1em')
+ .attr('text-anchor', 'middle')
+ .attr('transform', 'rotate(-90)')
+ .text(this.yAxisLabel);
+
+ // THRESHOLD LINE
+ this.chart
+ .append('line')
+ .attr('class', 'meaningful-expression-threshold-line')
+ .attr('x1', 0)
+ .attr('x2', innerWidth)
+ .attr('y1', yScale(this.MEANINGFUL_EXPRESSION_THRESHOLD))
+ .attr('y2', yScale(this.MEANINGFUL_EXPRESSION_THRESHOLD))
+ .attr('stroke', 'red');
+
+ this.chartInitialized = true;
+ }
+ }
+
+ @HostListener('window:resize', ['$event'])
+ onResize() {
+ this.clearChart();
+ this.createChart();
+ }
+}
diff --git a/src/app/features/charts/components/score-barchart/score-barchart.component.html b/src/app/features/charts/components/score-barchart/score-barchart.component.html
index 39d8fadfe..6d1a768e0 100644
--- a/src/app/features/charts/components/score-barchart/score-barchart.component.html
+++ b/src/app/features/charts/components/score-barchart/score-barchart.component.html
@@ -1,5 +1,5 @@
-
-
+
+
No data is currently available.
diff --git a/src/app/features/charts/components/score-barchart/score-barchart.component.scss b/src/app/features/charts/components/score-barchart/score-barchart.component.scss
index 8430a57ac..82720761e 100644
--- a/src/app/features/charts/components/score-barchart/score-barchart.component.scss
+++ b/src/app/features/charts/components/score-barchart/score-barchart.component.scss
@@ -1,49 +1,4 @@
-$tooltip-color: #63676C;
-
#score-barchart {
- position: relative;
- height: 350px;
-
- svg {
- width: 100%;
- }
-
- #score-barchart-tooltip {
- position: absolute;
- text-align: center;
- padding: 5px;
- font-size: 14px;
- background-color: $tooltip-color;
- color: white;
- display: none;
- z-index: 200;
- opacity: 0.9;
- width: 200px;
- cursor: pointer;
- border-radius: 5px;
- pointer-events: none;
- }
-
- .tooltip-arrow {
- &::before {
- content: '';
- position: absolute;
- left: 50%;
- border: 10px solid transparent;
- transform: translateX(-50%);
- }
-
- &.arrow-below {
- transform: translate(calc(-50%), calc(-100% - 20px));
-
- &::before {
- bottom: -9px;
- border-bottom: 0;
- border-top-color: $tooltip-color;
- }
- }
- }
-
.negative-bars {
&:hover {
fill: transparent;
@@ -51,38 +6,9 @@ $tooltip-color: #63676C;
}
}
- .scorebars {
- &:hover {
- cursor: pointer;
- }
- }
-
- .bar-labels {
+ .scorebars, .bar-labels {
&:hover {
cursor: pointer;
}
}
-
- .x-axis .tick {
- cursor: default;
- }
-
- .y-axis .tick {
- cursor: default;
- }
-
- text.x-axis-label {
- cursor: default;
- }
-
- text.y-axis-label {
- cursor: default;
- }
-
- .chart-no-data {
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100%;
- }
}
\ No newline at end of file
diff --git a/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.html b/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.html
index 9d91365f5..290b38485 100644
--- a/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.html
+++ b/src/app/features/genes/components/gene-evidence-rna/gene-evidence-rna.component.html
@@ -40,10 +40,10 @@
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
index f668e58ce..711d0ab45 100644
--- a/src/styles/_variables.scss
+++ b/src/styles/_variables.scss
@@ -80,6 +80,7 @@ $extra-colors: (
border: map.get($main-colors, 'gray-300'),
separator: map.get($main-colors, 'gray-300'),
shadow: #c7c5c5,
+ tooltip: #63676C,
);
:root {
diff --git a/src/styles/components/_chart_d3.scss b/src/styles/components/_chart_d3.scss
new file mode 100644
index 000000000..e04684689
--- /dev/null
+++ b/src/styles/components/_chart_d3.scss
@@ -0,0 +1,94 @@
+.chart-d3 {
+ position: relative;
+ height: 350px;
+
+ svg {
+ width: 100%;
+ }
+
+ .bar-labels {
+ cursor: default;
+
+ &:hover {
+ cursor: default;
+ }
+ }
+
+ // -------------------------------------------------------------------------- //
+ // EMPTY CHART
+ // -------------------------------------------------------------------------- //
+ .chart-no-data {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+
+ .chart-no-data-text {
+ font-size: var(--font-size-lg);
+ font-style: italic;
+ color: var(--color-gray-600);
+ }
+ }
+
+ // -------------------------------------------------------------------------- //
+ // AXES
+ // -------------------------------------------------------------------------- //
+ .y-axis-label,
+ .x-axis-label {
+ text-transform: uppercase;
+ cursor: default;
+ }
+
+ .x-axis .tick, .y-axis .tick {
+ cursor: default;
+ }
+
+ // -------------------------------------------------------------------------- //
+ // TOOLTIP
+ // -------------------------------------------------------------------------- //
+ #tooltip {
+ position: absolute;
+ text-align: center;
+ padding: 5px;
+ font-size: 14px;
+ background-color: var(--color-tooltip);
+ color: white;
+ display: none;
+ z-index: 200;
+ opacity: 0.9;
+ width: 200px;
+ cursor: pointer;
+ border-radius: 5px;
+ pointer-events: none;
+ }
+
+ .tooltip-arrow {
+ &::before {
+ content: '';
+ position: absolute;
+ left: 50%;
+ border: 10px solid transparent;
+ transform: translateX(-50%);
+ }
+
+ &.arrow-above {
+ transform: translate(calc(-50%), calc(50%));
+
+ &::before {
+ top: -9px;
+ border-top: 0;
+ border-bottom-color: var(--color-tooltip);
+ }
+ }
+
+ &.arrow-below {
+ transform: translate(calc(-50%), calc(-100% - 20px));
+
+ &::before {
+ bottom: -9px;
+ border-bottom: 0;
+ border-top-color: var(--color-tooltip);
+ }
+ }
+ }
+}
diff --git a/src/styles/styles.scss b/src/styles/styles.scss
index 8479b2e65..a6aa2e512 100644
--- a/src/styles/styles.scss
+++ b/src/styles/styles.scss
@@ -22,6 +22,7 @@
@import 'components/tooltip';
@import 'components/table';
@import 'components/chart';
+@import 'components/chart_d3';
@import 'components/icon';
@import 'components/shame';
From 2096feb70ee9f24ce5c58ea1af5dff0c66383939 Mon Sep 17 00:00:00 2001
From: Hallie Swan <26949006+hallieswan@users.noreply.github.com>
Date: Fri, 16 Jun 2023 16:17:10 -0700
Subject: [PATCH 2/5] fix lint error
---
.../components/median-barchart/median-barchart.component.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/app/features/charts/components/median-barchart/median-barchart.component.ts b/src/app/features/charts/components/median-barchart/median-barchart.component.ts
index efd50bf7c..63689211b 100644
--- a/src/app/features/charts/components/median-barchart/median-barchart.component.ts
+++ b/src/app/features/charts/components/median-barchart/median-barchart.component.ts
@@ -30,8 +30,7 @@ import { HelperService } from '../../../../core/services';
styleUrls: ['./median-barchart.component.scss'],
encapsulation: ViewEncapsulation.None,
})
-export class MedianBarChartComponent
- implements OnChanges, AfterViewInit, OnDestroy
+export class MedianBarChartComponent implements OnChanges, AfterViewInit, OnDestroy
{
private chartInitialized = false;
private tooltipInitialized = false;
From 832c530f417b6bab568dbb2b217d37a6dbdb4826 Mon Sep 17 00:00:00 2001
From: Hallie Swan <26949006+hallieswan@users.noreply.github.com>
Date: Fri, 7 Jul 2023 14:26:14 -0700
Subject: [PATCH 3/5] updates per PR and add transition on resize
---
.../median-barchart.component.html | 4 +-
.../median-barchart.component.ts | 114 +++++++++++++-----
src/styles/components/_chart_d3.scss | 2 +-
3 files changed, 88 insertions(+), 32 deletions(-)
diff --git a/src/app/features/charts/components/median-barchart/median-barchart.component.html b/src/app/features/charts/components/median-barchart/median-barchart.component.html
index b8797ac54..cdccce2d9 100644
--- a/src/app/features/charts/components/median-barchart/median-barchart.component.html
+++ b/src/app/features/charts/components/median-barchart/median-barchart.component.html
@@ -1,11 +1,11 @@
-