Skip to content

Commit

Permalink
🐛 (scatter) feedback for selected data without data
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed May 30, 2024
1 parent 28d89e0 commit f13a47e
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 84 deletions.
1 change: 1 addition & 0 deletions packages/@ourworldindata/grapher/src/chart/ChartManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export interface ChartManager {
missingDataStrategy?: MissingDataStrategy

isNarrow?: boolean
isStatic?: boolean
isStaticAndSmall?: boolean
secondaryColorInStaticCharts?: string

Expand Down
2 changes: 1 addition & 1 deletion packages/@ourworldindata/grapher/src/core/Grapher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ export class Grapher
@observable.ref isDownloadModalOpen = false
@observable.ref isEmbedModalOpen = false

@computed private get isStatic(): boolean {
@computed get isStatic(): boolean {
return this.renderToStatic || this.isExportingToSvgOrPng
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react"
import { Bounds } from "@ourworldindata/utils"
import {
GRAPHER_DARK_TEXT,
GRAPHER_FONT_SCALE_11_2,
} from "../core/GrapherConstants"

export function NoDataSection({
entityNames,
bounds,
baseFontSize = 16,
}: {
entityNames: string[]
bounds: Bounds
baseFontSize?: number
}): JSX.Element {
{
const displayedEntities = entityNames.slice(0, 5)
const numRemainingEntities = Math.max(
0,
entityNames.length - displayedEntities.length
)

return (
<foreignObject
{...bounds.toProps()}
style={{
color: GRAPHER_DARK_TEXT,
fontSize: GRAPHER_FONT_SCALE_11_2 * baseFontSize,
}}
>
<div
style={{
textTransform: "uppercase",
fontWeight: 700,
marginBottom: 2,
lineHeight: 1.15,
}}
>
No data
</div>
<ul>
{displayedEntities.map((entityName) => (
<li
key={entityName}
style={{
fontStyle: "italic",
lineHeight: 1.15,
marginBottom: 2,
}}
>
{entityName}
</li>
))}
</ul>
{numRemainingEntities > 0 && (
<div>
&{" "}
{numRemainingEntities === 1
? "one"
: numRemainingEntities}{" "}
more
</div>
)}
</foreignObject>
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
DEFAULT_BOUNDS,
isTouchDevice,
round,
difference,
} from "@ourworldindata/utils"
import { observer } from "mobx-react"
import { NoDataModal } from "../noDataModal/NoDataModal"
Expand Down Expand Up @@ -104,6 +105,7 @@ import {
ScatterSizeLegendManager,
} from "./ScatterSizeLegend"
import { Tooltip, TooltipState, TooltipValueRange } from "../tooltip/Tooltip"
import { NoDataSection } from "./NoDataSection"

@observer
export class ScatterPlotChart
Expand Down Expand Up @@ -741,6 +743,14 @@ export class ScatterPlotChart
return new ScatterSizeLegend(this)
}

@computed
private get selectedEntitiesWithoutData(): string[] {
return difference(
this.selectedEntityNames,
this.series.map((s) => s.seriesName)
)
}

componentDidMount(): void {
exposeInstanceOnWindow(this)
}
Expand All @@ -766,13 +776,44 @@ export class ScatterPlotChart
legendDimensions,
} = this

let sizeLegendY = bounds.top
if (this.legendItems.length > 0) {
sizeLegendY = bounds.top + legendDimensions.height + 16
}
const arrowLegendY = sizeLegend
? sizeLegendY + sizeLegend.height + 15
: sizeLegendY
const hasLegendItems = this.legendItems.length > 0
const verticalLegendHeight = hasLegendItems
? legendDimensions.height
: 0
const sizeLegendHeight = sizeLegend?.height ?? 0
const arrowLegendHeight = arrowLegend?.height ?? 0

const legendPadding = 16
const ySizeLegend =
bounds.top +
verticalLegendHeight +
(verticalLegendHeight > 0 ? legendPadding : 0)
const yArrowLegend =
ySizeLegend +
sizeLegendHeight +
(sizeLegendHeight > 0 ? legendPadding : 0)
const yNoDataSection =
yArrowLegend +
arrowLegendHeight +
(arrowLegendHeight > 0 ? legendPadding : 0)

const noDataSectionBounds = new Bounds(
this.legendX,
yNoDataSection,
sidebarWidth,
bounds.height - yNoDataSection
)

const separatorLine = (y: number): JSX.Element | null =>
y > bounds.top ? (
<line
x1={this.legendX}
y1={y - 0.5 * legendPadding}
x2={bounds.right}
y2={y - 0.5 * legendPadding}
stroke="#e7e7e7"
/>
) : null

return (
<g className="ScatterPlot" onMouseMove={this.onScatterMouseMove}>
Expand Down Expand Up @@ -800,38 +841,32 @@ export class ScatterPlotChart
<VerticalColorLegend manager={this} />
{sizeLegend && (
<>
{this.legendItems.length > 0 && (
<line
x1={bounds.right - sidebarWidth}
y1={sizeLegendY - 14}
x2={bounds.right - 5}
y2={sizeLegendY - 14}
stroke="#ccc"
/>
)}
{sizeLegend.render(this.legendX, sizeLegendY)}
{separatorLine(ySizeLegend)}
{sizeLegend.render(this.legendX, ySizeLegend)}
</>
)}
{arrowLegend && (
<>
<line
x1={bounds.right - sidebarWidth}
y1={arrowLegendY - 7}
x2={bounds.right - 5}
y2={arrowLegendY - 7}
stroke="#ccc"
/>
{separatorLine(yArrowLegend)}
<g
className="clickable"
onClick={this.onToggleEndpoints}
>
{arrowLegend.render(
bounds.right - sidebarWidth,
arrowLegendY
)}
{arrowLegend.render(this.legendX, yArrowLegend)}
</g>
</>
)}
{this.selectedEntitiesWithoutData.length > 0 && (
<>
{!this.manager.isStatic &&
separatorLine(noDataSectionBounds.top)}
<NoDataSection
entityNames={this.selectedEntitiesWithoutData}
bounds={noDataSectionBounds}
baseFontSize={this.fontSize}
/>
</>
)}
{this.tooltip}
</g>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ export class ScatterPointsWithLabels extends React.Component<ScatterPointsWithLa
@computed private get isLayerMode(): boolean {
return (
this.focusedSeriesNames.length > 0 ||
this.hoveredSeriesNames.length > 0
this.hoveredSeriesNames.length > 0 ||
// if the user has selected entities that are not in the chart,
// we want to move all entities into the background
(this.props.focusedSeriesNames.length > 0 &&
this.focusedSeriesNames.length === 0)
)
}

Expand Down
62 changes: 8 additions & 54 deletions packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
GRAPHER_DARK_TEXT,
GRAPHER_FONT_SCALE_9_6,
GRAPHER_FONT_SCALE_10_5,
GRAPHER_FONT_SCALE_11_2,
GRAPHER_TIMELINE_CLASS,
GRAPHER_SIDE_PANEL_CLASS,
} from "../core/GrapherConstants"
Expand Down Expand Up @@ -66,6 +65,7 @@ import {
HorizontalColorLegendManager,
} from "../horizontalColorLegend/HorizontalColorLegends"
import { CategoricalBin, ColorScaleBin } from "../color/ColorScaleBin"
import { NoDataSection } from "../scatterCharts/NoDataSection"

export interface SlopeChartManager extends ChartManager {
isModalOpen?: boolean
Expand Down Expand Up @@ -343,64 +343,18 @@ export class SlopeChart
}

@computed private get noDataSection(): JSX.Element {
const { selectedEntitiesWithoutData } = this

const displayedEntities = selectedEntitiesWithoutData.slice(0, 5)
const numRemainingEntities = Math.max(
0,
selectedEntitiesWithoutData.length - displayedEntities.length
)

const bounds = new Bounds(
this.legendX,
this.legendY + this.legendHeight,
this.legendY + this.legendHeight + 12,
this.sidebarWidth,
this.bounds.height - this.legendHeight
this.bounds.height - this.legendHeight - 12
)

return (
<foreignObject
{...bounds.toProps()}
style={{
color: GRAPHER_DARK_TEXT,
fontSize: GRAPHER_FONT_SCALE_11_2 * this.fontSize,
}}
>
<div
style={{
textTransform: "uppercase",
fontWeight: 700,
marginTop: 12,
marginBottom: 2,
lineHeight: 1.15,
}}
>
No data
</div>
<ul>
{displayedEntities.map((entityName) => (
<li
key={entityName}
style={{
fontStyle: "italic",
lineHeight: 1.15,
marginBottom: 2,
}}
>
{entityName}
</li>
))}
</ul>
{numRemainingEntities > 0 && (
<div>
&{" "}
{numRemainingEntities === 1
? "one"
: numRemainingEntities}{" "}
more
</div>
)}
</foreignObject>
<NoDataSection
entityNames={this.selectedEntitiesWithoutData}
bounds={bounds}
baseFontSize={this.fontSize}
/>
)
}

Expand Down

0 comments on commit f13a47e

Please sign in to comment.