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

🔨 drop manager pattern for vertical color legend / TAS-799 #4368

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,11 @@ describe("colors & legend", () => {
})

it("legend contains every continent for which there is data (before timeline filter)", () => {
expect(chart.legendItems.map((item) => item.label).sort()).toEqual([
expect(
chart.verticalColorLegendBins
.map((item) => item.type === "categorical" && item.label)
.sort()
).toEqual([
"Africa",
"Europe",
"North America",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ import {
} from "./ConnectedScatterLegend"
import {
VerticalColorLegend,
VerticalColorLegendManager,
VerticalColorLegendBin,
} from "../verticalColorLegend/VerticalColorLegend"
import { VerticalColorLegendComponent } from "../verticalColorLegend/VerticalColorLegendComponent"
import { DualAxisComponent } from "../axis/AxisViews"
import { DualAxis, HorizontalAxis, VerticalAxis } from "../axis/Axis"

Expand Down Expand Up @@ -97,7 +98,7 @@ import {
ColorScaleConfigDefaults,
} from "../color/ColorScaleConfig"
import { SelectionArray } from "../selection/SelectionArray"
import { ColorScaleBin } from "../color/ColorScaleBin"
import { CategoricalBin } from "../color/ColorScaleBin"
import {
ScatterSizeLegend,
ScatterSizeLegendManager,
Expand Down Expand Up @@ -127,7 +128,6 @@ export class ScatterPlotChart
ConnectedScatterLegendManager,
ScatterSizeLegendManager,
ChartInterface,
VerticalColorLegendManager,
ColorScaleManager
{
// currently hovered legend color
Expand Down Expand Up @@ -510,12 +510,13 @@ export class ScatterPlotChart
return this.tooltipState.target?.series
}

@computed private get legendDimensions(): VerticalColorLegend {
return new VerticalColorLegend({ manager: this })
}

@computed get maxLegendWidth(): number {
return this.sidebarMaxWidth
@computed private get verticalColorLegend(): VerticalColorLegend {
return new VerticalColorLegend({
bins: this.verticalColorLegendBins,
maxWidth: this.sidebarMaxWidth,
legendTitle: this.colorScale.legendDescription,
fontSize: this.fontSize,
})
}

@computed private get sidebarMinWidth(): number {
Expand All @@ -527,10 +528,10 @@ export class ScatterPlotChart
}

@computed.struct get sidebarWidth(): number {
const { legendDimensions, sidebarMinWidth, sidebarMaxWidth } = this
const { verticalColorLegend, sidebarMinWidth, sidebarMaxWidth } = this

return Math.max(
Math.min(legendDimensions.width, sidebarMaxWidth),
Math.min(verticalColorLegend.width, sidebarMaxWidth),
sidebarMinWidth
)
}
Expand Down Expand Up @@ -685,16 +686,27 @@ export class ScatterPlotChart
return this.transformedTable.get(this.colorColumnSlug)
}

@computed get legendItems(): ColorScaleBin[] {
return this.colorScale.legendBins.filter(
@computed get verticalColorLegendBins(): VerticalColorLegendBin[] {
const bins = this.colorScale.legendBins.filter(
(bin) =>
this.colorsInUse.includes(bin.color) &&
bin.label !== NO_DATA_LABEL
)
}

@computed get legendTitle(): string | undefined {
return this.colorScale.legendDescription
return bins.map((bin) =>
bin instanceof CategoricalBin
? {
type: "categorical",
color: bin.color,
label: bin.label ?? "",
}
: {
type: "numeric",
color: bin.color,
minLabel: bin.minText,
maxLabel: bin.maxText,
}
)
}

@computed get sizeScale(): ScaleLinear<number, number> {
Expand Down Expand Up @@ -762,12 +774,12 @@ export class ScatterPlotChart
sizeLegend,
sidebarWidth,
comparisonLines,
legendDimensions,
verticalColorLegend,
} = this

const hasLegendItems = this.legendItems.length > 0
const hasLegendItems = this.verticalColorLegendBins.length > 0
const verticalLegendHeight = hasLegendItems
? legendDimensions.height
? verticalColorLegend.height
: 0
const sizeLegendHeight = sizeLegend?.height ?? 0
const arrowLegendHeight = arrowLegend?.height ?? 0
Expand All @@ -787,7 +799,7 @@ export class ScatterPlotChart
(arrowLegendHeight > 0 ? legendPadding : 0)

const noDataSectionBounds = new Bounds(
this.legendX,
this.verticalColorLegendX,
yNoDataSection,
sidebarWidth,
bounds.height - yNoDataSection
Expand All @@ -796,7 +808,7 @@ export class ScatterPlotChart
const separatorLine = (y: number): React.ReactElement | null =>
y > bounds.top ? (
<line
x1={this.legendX}
x1={this.verticalColorLegendX}
y1={y - 0.5 * legendPadding}
x2={bounds.right}
y2={y - 0.5 * legendPadding}
Expand Down Expand Up @@ -828,11 +840,23 @@ export class ScatterPlotChart
/>
))}
{this.points}
<VerticalColorLegend manager={this} />
<VerticalColorLegendComponent
legend={this.verticalColorLegend}
x={this.verticalColorLegendX}
y={this.verticalColorLegendY}
activeColors={this.activeColors}
focusColors={this.focusColors}
onMouseOver={this.onLegendMouseOver}
onMouseLeave={this.onLegendMouseLeave}
onClick={this.onLegendClick}
/>
{sizeLegend && (
<>
{separatorLine(ySizeLegend)}
{sizeLegend.render(this.legendX, ySizeLegend)}
{sizeLegend.render(
this.verticalColorLegendX,
ySizeLegend
)}
</>
)}
{arrowLegend && (
Expand All @@ -842,7 +866,10 @@ export class ScatterPlotChart
className="clickable"
onClick={this.onToggleEndpoints}
>
{arrowLegend.render(this.legendX, yArrowLegend)}
{arrowLegend.render(
this.verticalColorLegendX,
yArrowLegend
)}
</g>
</>
)}
Expand Down Expand Up @@ -988,11 +1015,11 @@ export class ScatterPlotChart
)
}

@computed get legendY(): number {
@computed get verticalColorLegendY(): number {
return this.bounds.top
}

@computed get legendX(): number {
@computed get verticalColorLegendX(): number {
return this.bounds.right - this.sidebarWidth
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import { DualAxisComponent } from "../axis/AxisViews"
import { NoDataModal } from "../noDataModal/NoDataModal"
import {
VerticalColorLegend,
VerticalColorLegendManager,
LegendItem,
VerticalColorLegendCategoricalBin,
} from "../verticalColorLegend/VerticalColorLegend"
import { VerticalColorLegendComponent } from "../verticalColorLegend/VerticalColorLegendComponent"
import { TooltipFooterIcon } from "../tooltip/TooltipProps.js"
import {
Tooltip,
Expand Down Expand Up @@ -144,7 +144,7 @@ class StackedBarSegment extends React.Component<StackedBarSegmentProps> {
@observer
export class StackedBarChart
extends AbstractStackedChart
implements VerticalColorLegendManager, ColorScaleManager
implements ColorScaleManager
{
readonly minBarSpacing = 4

Expand Down Expand Up @@ -259,12 +259,12 @@ export class StackedBarChart
)
}

// used by <VerticalColorLegend />
@computed get legendItems(): (LegendItem &
Required<Pick<LegendItem, "label">>)[] {
@computed
get verticalColorLegendBins(): VerticalColorLegendCategoricalBin[] {
return this.series
.map((series) => {
return {
type: "categorical" as const,
label: series.seriesName,
color: series.color,
}
Expand All @@ -274,7 +274,7 @@ export class StackedBarChart

// used by <HorizontalCategoricalColorLegend />
@computed get categoricalLegendData(): CategoricalBin[] {
return this.legendItems.map(
return this.verticalColorLegendBins.map(
(legendItem, index) =>
new CategoricalBin({
index,
Expand Down Expand Up @@ -319,7 +319,13 @@ export class StackedBarChart
}

@computed private get verticalColorLegend(): VerticalColorLegend {
return new VerticalColorLegend({ manager: this })
return new VerticalColorLegend({
bins: this.verticalColorLegendBins,
maxWidth: this.showHorizontalLegend
? this.bounds.width
: this.sidebarMaxWidth,
fontSize: this.fontSize,
})
}

@computed
Expand Down Expand Up @@ -467,16 +473,28 @@ export class StackedBarChart

renderLegend(): React.ReactElement | void {
const {
manager: { showLegend },
manager: { showLegend, isStatic },
showHorizontalLegend,
} = this

if (!showLegend) return

const x = this.showHorizontalLegend
? this.bounds.left
: this.bounds.right - this.sidebarWidth
const y = this.bounds.top

return showHorizontalLegend ? (
<HorizontalCategoricalColorLegend manager={this} />
) : (
<VerticalColorLegend manager={this} />
<VerticalColorLegendComponent
legend={this.verticalColorLegend}
x={x}
y={y}
activeColors={this.activeColors}
onMouseOver={!isStatic ? this.onLegendMouseOver : undefined}
onMouseLeave={!isStatic ? this.onLegendMouseLeave : undefined}
/>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
import React from "react"
import {
VerticalColorLegend,
VerticalColorLegendManager,
VerticalColorLegendProps,
} from "./VerticalColorLegend"
import { VerticalColorLegendComponent } from "./VerticalColorLegendComponent"

export default {
title: "VerticalColorLegend",
component: VerticalColorLegend,
}

const manager: VerticalColorLegendManager = {
maxLegendWidth: 500,
const props: VerticalColorLegendProps = {
maxWidth: 500,
legendTitle: "Legend Title",
legendItems: [
bins: [
{
type: "categorical",
label: "Canada",
color: "red",
},
{
type: "categorical",
label: "Mexico",
color: "green",
},
],
activeColors: ["red", "green"],
}

export const CategoricalBins = (): React.ReactElement => {
const verticalColorLegend = new VerticalColorLegend(props)
return (
<svg width={600} height={400}>
<VerticalColorLegend manager={manager} />
<VerticalColorLegendComponent
legend={verticalColorLegend}
activeColors={["red", "green"]}
/>
</svg>
)
}
Loading
Loading