Skip to content

Commit

Permalink
✨ (entity selector) render into panel, drawer or modal
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Apr 15, 2024
1 parent 72b12ee commit 2e584a7
Show file tree
Hide file tree
Showing 38 changed files with 1,127 additions and 632 deletions.
2 changes: 1 addition & 1 deletion adminSiteClient/ChartEditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ export class ChartEditorPage
@computed private get bounds(): Bounds {
return this.isMobilePreview
? new Bounds(0, 0, 380, 525)
: this.grapher.idealBounds
: this.grapher.defaultBounds
}

@computed private get staticFormat(): GrapherStaticFormat {
Expand Down
4 changes: 2 additions & 2 deletions baker/GrapherImageBaker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function bakeGraphersToPngs(
.then(() => console.log(`${outPath}.svg`)),
sharp(Buffer.from(grapher.staticSVG), { density: 144 })
.png()
.resize(grapher.idealBounds.width, grapher.idealBounds.height)
.resize(grapher.defaultBounds.width, grapher.defaultBounds.height)
.flatten({ background: "#ffffff" })
.toFile(`${outPath}.png`),
])
Expand Down Expand Up @@ -102,7 +102,7 @@ export async function bakeGrapherToSvg(
verbose = true
) {
const grapher = initGrapherForSvgExport(jsonConfig, queryStr)
const { width, height } = grapher.idealBounds
const { width, height } = grapher.defaultBounds
const outPath = buildSvgOutFilepath(
outDir,
{
Expand Down
2 changes: 1 addition & 1 deletion devTools/svgTester/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ export async function renderSvg(
},
queryStr
)
const { width, height } = grapher.idealBounds
const { width, height } = grapher.defaultBounds
const outFilename = buildSvgOutFilename(
{
slug: configAndData.config.slug!,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rules:
"@typescript-eslint/explicit-function-return-type": "warn"
"@typescript-eslint/explicit-module-boundary-types": "warn"
32 changes: 32 additions & 0 deletions packages/@ourworldindata/grapher/src/bodyPortal/BodyPortal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react"
import ReactDOM from "react-dom"

interface BodyPortalProps {
id?: string
tagName?: string // default: "div"
children: React.ReactNode
}

// Render a component on the Body instead of inside the current Tree.
// https://reactjs.org/docs/portals.html
export class BodyPortal extends React.Component<BodyPortalProps> {
el: HTMLElement

constructor(props: BodyPortalProps) {
super(props)
this.el = document.createElement(props.tagName || "div")
if (props.id) this.el.id = props.id
}

componentDidMount(): void {
document.body.appendChild(this.el)
}

componentWillUnmount(): void {
document.body.removeChild(this.el)
}

render(): any {
return ReactDOM.createPortal(this.props.children, this.el)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// keep in sync with constant values in CaptionedChart.tsx
$controlRowHeight: 32px; // keep in sync with CONTROLS_ROW_HEIGHT

.CaptionedChart {
width: 100%;
}

.HeaderHTML,
.SourcesFooterHTML {
font-family: $sans-serif-font-stack;
Expand All @@ -22,7 +26,6 @@ $controlRowHeight: 32px; // keep in sync with CONTROLS_ROW_HEIGHT
align-items: center;
border-top: 1px solid $frame-color;
position: absolute;
width: 100%;
bottom: 0;
color: $dark-text;
font-weight: 700;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default {
const table = SynthesizeGDPTable({ entityCount: 5 })

const manager: CaptionedChartManager = {
tabBounds: DEFAULT_BOUNDS,
captionedChartBounds: DEFAULT_BOUNDS,
table,
selection: table.availableEntityNames,
currentTitle: "This is the Title",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import {
GrapherTabOption,
RelatedQuestionsConfig,
} from "@ourworldindata/types"
import { AxisConfig } from "../axis/AxisConfig"
import { DataTable, DataTableManager } from "../dataTable/DataTable"
import {
ContentSwitchers,
Expand All @@ -68,33 +67,42 @@ export interface CaptionedChartManager
MapProjectionMenuManager,
SettingsMenuManager {
containerElement?: HTMLDivElement
tabBounds?: Bounds
bakedGrapherURL?: string
isReady?: boolean
whatAreWeWaitingFor?: string

// bounds
captionedChartBounds?: Bounds
sidePanelBounds?: Bounds
staticBounds?: Bounds
staticBoundsWithDetails?: Bounds

// layout
isSmall?: boolean
isMedium?: boolean
framePaddingHorizontal?: number
framePaddingVertical?: number
fontSize?: number
bakedGrapherURL?: string

// state
tab?: GrapherTabOption
type: ChartTypeName
yAxis: AxisConfig
xAxis: AxisConfig
typeExceptWhenLineChartAndSingleTimeThenWillBeBarChart?: ChartTypeName
isReady?: boolean
whatAreWeWaitingFor?: string
entityType?: string
entityTypePlural?: string
shouldIncludeDetailsInStaticExport?: boolean
detailRenderers: MarkdownTextWrap[]
isOnMapTab?: boolean
isOnTableTab?: boolean
type: ChartTypeName
typeExceptWhenLineChartAndSingleTimeThenWillBeBarChart?: ChartTypeName
showEntitySelectionToggle?: boolean

// timeline
hasTimeline?: boolean
timelineController?: TimelineController
hasRelatedQuestion?: boolean
isRelatedQuestionTargetDifferentFromCurrentPage?: boolean

// details on demand
shouldIncludeDetailsInStaticExport?: boolean
detailRenderers: MarkdownTextWrap[]

// related question
relatedQuestions?: RelatedQuestionsConfig[]
isSmall?: boolean
isMedium?: boolean
framePaddingHorizontal?: number
framePaddingVertical?: number
showRelatedQuestion?: boolean
}

interface CaptionedChartProps {
Expand Down Expand Up @@ -144,10 +152,6 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {
return this.manager.isMedium ? 8 : 16
}

@computed protected get relatedQuestionHeight(): number {
return this.manager.isMedium ? 24 : 28
}

@computed protected get header(): Header {
return new Header({
manager: this.manager,
Expand Down Expand Up @@ -181,7 +185,9 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {

@computed protected get bounds(): Bounds {
const bounds =
this.props.bounds ?? this.manager.tabBounds ?? DEFAULT_BOUNDS
this.props.bounds ??
this.manager.captionedChartBounds ??
DEFAULT_BOUNDS
// the padding ensures grapher's frame is not cut off
return bounds.padRight(2).padBottom(2)
}
Expand Down Expand Up @@ -258,28 +264,36 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {
return this.manager.selection
}

@computed get showRelatedQuestion(): boolean {
return (
!!this.manager.relatedQuestions &&
!!this.manager.hasRelatedQuestion &&
!!this.manager.isRelatedQuestionTargetDifferentFromCurrentPage
)
@computed private get showRelatedQuestion(): boolean {
return !!this.manager.showRelatedQuestion
}

@computed get relatedQuestionHeight(): number {
if (!this.showRelatedQuestion) return 0
return this.manager.isMedium ? 24 : 28
}

@computed private get sidePanelWidth(): number {
return this.manager.sidePanelBounds?.width ?? 0
}

private renderControlsRow(): JSX.Element {
const { showContentSwitchers } = this
const { showEntitySelectionToggle } = this.manager
return (
<nav
className="controlsRow"
style={{ padding: `0 ${this.framePaddingHorizontal}px` }}
>
<div>
{showContentSwitchers && (
{this.showContentSwitchers && (
<ContentSwitchers manager={this.manager} />
)}
</div>
<div className="chart-controls">
<EntitySelectionToggle manager={this.manager} />
{showEntitySelectionToggle && (
<EntitySelectionToggle manager={this.manager} />
)}

<SettingsMenu
manager={this.manager}
top={
Expand All @@ -290,6 +304,9 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {
4 // margin between button and menu
}
bottom={this.framePaddingVertical}
right={
this.sidePanelWidth + this.framePaddingHorizontal
}
/>
<MapProjectionMenu manager={this.manager} />
</div>
Expand All @@ -303,6 +320,7 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {
<div
className="relatedQuestion"
style={{
width: this.bounds.width,
height: this.relatedQuestionHeight,
padding: `0 ${this.framePaddingHorizontal}px`,
}}
Expand Down Expand Up @@ -433,7 +451,7 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {
// #5 Footer
// #6 [Related question]
return (
<>
<div className="CaptionedChart">
{/* #1 Header */}
<Header manager={this.manager} maxWidth={this.maxWidth} />
<VerticalSpace height={this.verticalPadding} />
Expand Down Expand Up @@ -461,7 +479,7 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {

{/* #6 [Related question] */}
{this.showRelatedQuestion && this.renderRelatedQuestion()}
</>
</div>
)
}

Expand Down
2 changes: 0 additions & 2 deletions packages/@ourworldindata/grapher/src/chart/ChartManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ export interface ChartManager {
table: OwidTable
transformedTable?: OwidTable

isSelectingData?: boolean
startSelectingWhenLineClicked?: boolean // used by lineLabels
isExportingToSvgOrPng?: boolean
isRelativeMode?: boolean
comparisonLines?: ComparisonLineConfig[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import {
import classnames from "classnames"

export interface EntitySelectionManager {
showSelectEntitiesButton?: boolean
showChangeEntityButton?: boolean
showAddEntityButton?: boolean
canHighlightEntities?: boolean
canChangeEntity?: boolean
canAddEntities?: boolean
entityType?: string
entityTypePlural?: string
isSelectingData?: boolean
isEntitySelectorModalOrDrawerOpen?: boolean
isOnChartTab?: boolean
}

Expand Down Expand Up @@ -43,42 +43,43 @@ export class EntitySelectionToggle extends React.Component<{
const {
entityType = "",
entityTypePlural = "",
showSelectEntitiesButton,
showChangeEntityButton,
showAddEntityButton,
canHighlightEntities,
canChangeEntity,
canAddEntities,
} = this.props.manager

return showSelectEntitiesButton
return canHighlightEntities
? {
action: "Select",
entity: entityTypePlural,
icon: <FontAwesomeIcon icon={faEye} />,
}
: showChangeEntityButton
? {
action: "Change",
entity: entityType,
icon: <FontAwesomeIcon icon={faRightLeft} />,
}
: showAddEntityButton
? {
action: "Edit",
entity: entityTypePlural,
icon: <FontAwesomeIcon icon={faPencilAlt} />,
}
: null
: canChangeEntity
? {
action: "Change",
entity: entityType,
icon: <FontAwesomeIcon icon={faRightLeft} />,
}
: canAddEntities
? {
action: "Edit",
entity: entityTypePlural,
icon: <FontAwesomeIcon icon={faPencilAlt} />,
}
: null
}

render(): JSX.Element | null {
const { showToggle, label } = this
const { isSelectingData: active } = this.props.manager
const { isEntitySelectorModalOrDrawerOpen: active } = this.props.manager

return showToggle && label ? (
<div className="entity-selection-menu">
<button
className={classnames("menu-toggle", { active })}
onClick={(e): void => {
this.props.manager.isSelectingData = !active
this.props.manager.isEntitySelectorModalOrDrawerOpen =
!active
e.stopPropagation()
}}
type="button"
Expand Down
Loading

0 comments on commit 2e584a7

Please sign in to comment.