Skip to content

Commit

Permalink
✨ (grapher) prevent controls from overlapping
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Jun 10, 2024
1 parent 98d7b42 commit 756ec13
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@ import { LoadingIndicator } from "../loadingIndicator/LoadingIndicator"
import { FacetChart } from "../facetChart/FacetChart"
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js"
import {
EntitySelectionToggle,
EntitySelectionManager,
} from "../controls/EntitySelectionToggle"
import {
MapProjectionMenu,
MapProjectionMenuManager,
} from "../controls/MapProjectionMenu"
import { SettingsMenu, SettingsMenuManager } from "../controls/SettingsMenu"
import { FooterManager } from "../footer/FooterManager"
import { HeaderManager } from "../header/HeaderManager"
import { SelectionArray } from "../selection/SelectionArray"
Expand All @@ -47,26 +38,23 @@ import {
RelatedQuestionsConfig,
} from "@ourworldindata/types"
import { DataTable, DataTableManager } from "../dataTable/DataTable"
import {
ContentSwitchers,
ContentSwitchersManager,
} from "../controls/ContentSwitchers"
import {
TimelineComponent,
TIMELINE_HEIGHT,
} from "../timeline/TimelineComponent"
import { TimelineController } from "../timeline/TimelineController"
import {
ControlsRow,
ControlsRowManager,
} from "../controls/controlsRow/ControlsRow"

export interface CaptionedChartManager
extends ChartManager,
MapChartManager,
FooterManager,
HeaderManager,
DataTableManager,
ContentSwitchersManager,
EntitySelectionManager,
MapProjectionMenuManager,
SettingsMenuManager {
ControlsRowManager {
containerElement?: HTMLDivElement
bakedGrapherURL?: string
isReady?: boolean
Expand Down Expand Up @@ -207,22 +195,6 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {
return !this.manager.isOnMapTab && hasStrategy
}

@computed get showContentSwitchers(): boolean {
return (this.manager.availableTabs?.length ?? 0) > 1
}

@computed get showControls(): boolean {
return (
SettingsMenu.shouldShow(this.manager) ||
EntitySelectionToggle.shouldShow(this.manager) ||
MapProjectionMenu.shouldShow(this.manager)
)
}

@computed get showControlsRow(): boolean {
return this.showContentSwitchers || this.showControls
}

@computed get chartTypeName(): ChartTypeName {
const { manager } = this
return this.manager.isOnMapTab
Expand Down Expand Up @@ -274,44 +246,23 @@ export class CaptionedChart extends React.Component<CaptionedChartProps> {
return this.manager.isMedium ? 24 : 28
}

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

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

<SettingsMenu
manager={this.manager}
top={
this.framePaddingVertical +
this.header.height +
this.verticalPadding +
CONTROLS_ROW_HEIGHT +
4 // margin between button and menu
}
bottom={this.framePaddingVertical}
right={
this.sidePanelWidth + this.framePaddingHorizontal
}
/>
<MapProjectionMenu manager={this.manager} />
</div>
</nav>
<ControlsRow
manager={this.manager}
maxWidth={this.maxWidth}
settingsMenuTop={
this.framePaddingVertical +
this.header.height +
this.verticalPadding +
CONTROLS_ROW_HEIGHT +
4 // margin between button and menu
}
/>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
.ContentSwitchers {
// keep in sync with variables in ContentSwitchers.tsx
$font-size: 13px;
$icon-width: 13px;
$icon-padding: 6px;
--outer-padding: 16px;

$light-stroke: #e7e7e7;

$hover-fill: #f2f2f2;
Expand Down Expand Up @@ -30,12 +36,12 @@
display: block;
text-transform: capitalize;
color: $light-text;
font-size: 13px;
font-size: $font-size;
font-weight: 500;
height: $height;
line-height: $height;
border-radius: $border-radius - $visual-gap;
padding: 0 16px;
padding: 0 var(--outer-padding);
cursor: default;
letter-spacing: 0.01em;
white-space: nowrap;
Expand All @@ -47,14 +53,14 @@
}

.label {
margin-left: 6px;
margin-left: $icon-padding;
}

svg {
color: $info-icon;

&.custom-icon {
--size: 13px;
--size: $icon-width;

display: inline-block;
height: var(--size);
Expand Down Expand Up @@ -106,6 +112,6 @@

&.GrapherComponentMedium {
.ContentSwitchers:not(.iconOnly) li > button {
padding: 0 8px;
--outer-padding: 8px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,36 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js"
import { faTable, faEarthAmericas } from "@fortawesome/free-solid-svg-icons"
import { ChartTypeName, GrapherTabOption } from "@ourworldindata/types"
import { chartIcons } from "./ChartIcons"
import { Bounds, capitalize } from "@ourworldindata/utils"

export interface ContentSwitchersManager {
availableTabs?: GrapherTabOption[]
tab?: GrapherTabOption
isNarrow?: boolean
type?: ChartTypeName
isMedium?: boolean
type: ChartTypeName
isLineChartThatTurnedIntoDiscreteBar?: boolean
}

// keep in sync with ContentSwitcher.scss
const TAB_FONT_SIZE = 13
const ICON_WIDTH = 13
const ICON_PADDING = 6

@observer
export class ContentSwitchers extends React.Component<{
manager: ContentSwitchersManager
}> {
static shouldShow(manager: ContentSwitchersManager): boolean {
const test = new ContentSwitchers({ manager })
return test.showTabs
}

static width(manager: ContentSwitchersManager): number {
const test = new ContentSwitchers({ manager })
return test.width
}

@computed private get manager(): ContentSwitchersManager {
return this.props.manager
}
Expand All @@ -27,6 +44,10 @@ export class ContentSwitchers extends React.Component<{
return this.manager.availableTabs || []
}

@computed private get showTabs(): boolean {
return this.availableTabs.length > 1
}

@computed private get showTabLabels(): boolean {
return !this.manager.isNarrow
}
Expand All @@ -35,6 +56,25 @@ export class ContentSwitchers extends React.Component<{
return this.manager.type ?? ChartTypeName.LineChart
}

@computed get width(): number {
return this.availableTabs.reduce((totalWidth, tab) => {
// keep in sync with ContentSwitcher.scss
const outerPadding =
this.showTabLabels && this.manager.isMedium ? 8 : 16

let tabWidth = 2 * outerPadding + ICON_WIDTH

if (this.showTabLabels) {
const labelWidth = Bounds.forText(capitalize(tab), {
fontSize: TAB_FONT_SIZE,
}).width
tabWidth += labelWidth + ICON_PADDING
}

return totalWidth + tabWidth
}, 0)
}

private tabIcon(tab: GrapherTabOption): React.ReactElement {
const { manager } = this
switch (tab) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ $lato: $sans-serif-font-stack;
.settings-menu-contents {
// on/off switch with label written to the right
.labeled-switch {
// keep in sync with TableFilterToggle.tsx
// where the width of a labeled switch is calculated

display: flex;
color: $light-text;
font: $medium 13px/16px $lato;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
.map-projection-menu {
.control {
width: 150px;

.menu {
min-width: 150px;
right: 0;
}

.menu:before {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MapConfig } from "../mapCharts/MapConfig"
import { MapProjectionName } from "@ourworldindata/types"
import { MapProjectionLabels } from "../mapCharts/MapProjections"
import { Dropdown } from "./Dropdown"
import { DEFAULT_BOUNDS } from "@ourworldindata/utils"

export { AbsRelToggle } from "./settings/AbsRelToggle"
export { FacetStrategySelector } from "./settings/FacetStrategySelector"
Expand All @@ -27,6 +28,7 @@ interface MapProjectionMenuItem {
@observer
export class MapProjectionMenu extends React.Component<{
manager: MapProjectionMenuManager
maxWidth?: number
}> {
static shouldShow(manager: MapProjectionMenuManager): boolean {
const menu = new MapProjectionMenu({ manager })
Expand All @@ -40,6 +42,10 @@ export class MapProjectionMenu extends React.Component<{
return !hideMapProjectionMenu && !!(isOnMapTab && projection)
}

@computed private get maxWidth(): number {
return this.props.maxWidth ?? DEFAULT_BOUNDS.width
}

@action.bound onChange(selected: unknown): void {
const { mapConfig } = this.props.manager
if (selected && mapConfig)
Expand All @@ -62,7 +68,10 @@ export class MapProjectionMenu extends React.Component<{

render(): React.ReactElement | null {
return this.showMenu ? (
<div className="map-projection-menu">
<div
className="map-projection-menu"
style={{ maxWidth: this.maxWidth }}
>
<Dropdown
options={this.options}
onChange={this.onChange}
Expand Down
Loading

0 comments on commit 756ec13

Please sign in to comment.