Skip to content

Commit

Permalink
wip refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed May 2, 2024
1 parent f5d08d4 commit 354fcd8
Showing 1 changed file with 111 additions and 151 deletions.
262 changes: 111 additions & 151 deletions packages/@ourworldindata/grapher/src/entitySelector/EntitySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ export interface EntitySelectorState {
sortConfig: SortConfig
localEntityNames?: string[]
mostRecentlySelectedEntityName?: string
populationColumn?: CoreColumn
gdpPerCapitaColumn?: CoreColumn
externalSortColumnsByIndicatorId?: Record<number, CoreColumn>
isLoadingExternalSortColumn?: boolean
}

Expand Down Expand Up @@ -91,13 +90,33 @@ interface DropdownOption {
label: string
}

const DEFAULT_POPULATION_INDICATOR_ID =
POPULATION_INDICATOR_ID_USED_IN_ENTITY_SELECTOR
const DEFAULT_POPULATION_LABEL = "Population"
const EXTERNAL_SORT_INDICATORS = [
{
key: "population",
label: "Population",
indicatorId: POPULATION_INDICATOR_ID_USED_IN_ENTITY_SELECTOR,
isMatch: (column: CoreColumn): boolean =>
isPopulationVariableId(column.slug),
},
{
key: "gdpPerCapita",
label: "GDP per capita",
indicatorId: GDP_PER_CAPITA_INDICATOR_ID_USED_IN_ENTITY_SELECTOR,
isMatch: (column: CoreColumn): boolean => {
const label = makeColumnLabel(column)

// matches "gdp per capita" and content within parentheses
const potentialMatches =
label.match(/\(.*?\)|(\bgdp per capita\b)/gi) ?? []
// filter for "gdp per capita" matches that are not within parentheses
const matches = potentialMatches.filter(
(match) => !match.includes("(")
)

const DEFAULT_GDP_PER_CAPITA_INDICATOR_ID =
GDP_PER_CAPITA_INDICATOR_ID_USED_IN_ENTITY_SELECTOR
const DEFAULT_GDP_PER_CAPITA_LABEL = "GDP per capita"
return matches.length > 0
},
},
] as const

@observer
export class EntitySelector extends React.Component<{
Expand Down Expand Up @@ -235,12 +254,18 @@ export class EntitySelector extends React.Component<{
return this.manager.entitySelectorState.localEntityNames
}

@computed private get populationColumn(): CoreColumn | undefined {
return this.manager.entitySelectorState.populationColumn
@computed private get externalSortColumnsByIndicatorId(): Record<
number,
CoreColumn
> {
return (
this.manager.entitySelectorState.externalSortColumnsByIndicatorId ??
{}
)
}

@computed private get gdpPerCapitaColumn(): CoreColumn | undefined {
return this.manager.entitySelectorState.gdpPerCapitaColumn
@computed private get externalSortColumns(): CoreColumn[] {
return Object.values(this.externalSortColumnsByIndicatorId)
}

@computed private get isLoadingExternalSortColumn(): boolean {
Expand Down Expand Up @@ -273,93 +298,53 @@ export class EntitySelector extends React.Component<{
}

@computed
private get numericalChartColumnsWithoutPopulationAndGdpPerCapita(): CoreColumn[] {
private get numericalChartColumnsWithoutExternalSortColumns(): CoreColumn[] {
return this.numericalChartColumns.filter(
(column) =>
column.slug !== this.populationSlug &&
column.slug !== this.gdpPerCapitaSlug
(column) => !this.externalSortIndicatorSlugs.includes(column.slug)
)
}

@computed private get sortColumns(): CoreColumn[] {
return excludeUndefined([
this.table.entityNameColumn,
this.populationColumn,
this.gdpPerCapitaColumn,
...this.numericalChartColumnsWithoutPopulationAndGdpPerCapita,
...this.externalSortColumns,
...this.numericalChartColumnsWithoutExternalSortColumns,
])
}

@computed private get populationColumnUsedInChart():
| CoreColumn
| undefined {
const activePopulationColumn = this.numericalChartColumns.find(
(column) => isPopulationVariableId(column.slug)
)
return activePopulationColumn
}

/**
* Either the ID of a population indicator used in the chart,
* or the default population indicator ID (or undefined if we
* don't want to sort by population)
*/
@computed private get populationIndicatorId(): number | undefined {
if (this.populationColumnUsedInChart)
return +this.populationColumnUsedInChart.slug

if (this.hasCountryOrRegionEntities)
return DEFAULT_POPULATION_INDICATOR_ID

return undefined
}

@computed private get populationSlug(): ColumnSlug | undefined {
return this.populationIndicatorId !== undefined
? this.populationIndicatorId.toString()
: undefined
}
@computed private get externalSortIndicators(): {
key: string
defaultLabel: string
indicatorId: number | undefined
slug: string | undefined
chartColumn?: CoreColumn
}[] {
return EXTERNAL_SORT_INDICATORS.map((externalSortIndicator) => {
const chartColumn = this.numericalChartColumns.find((column) =>
externalSortIndicator.isMatch(column)
)

@computed private get gdpPerCapitaColumnUsedInChart():
| CoreColumn
| undefined {
const activeGdpPerCapitaColumn = this.numericalChartColumns.find(
(column) => {
const label = makeColumnLabel(column)

// matches "gdp per capita" and content within parentheses
const potentialMatches =
label.match(/\(.*?\)|(\bgdp per capita\b)/gi) ?? []
// filter for "gdp per capita" matches that are not within parentheses
const matches = potentialMatches.filter(
(match) => !match.includes("(")
)

return matches.length > 0
let indicatorId: number | undefined
if (chartColumn) {
indicatorId = +chartColumn.slug
} else if (this.hasCountryOrRegionEntities) {
indicatorId = externalSortIndicator.indicatorId
}
)
return activeGdpPerCapitaColumn
}

/**
* Either the ID of a GDP per capita indicator used in the chart,
* or the default GDP per capita indicator ID (or undefined if we
* don't want to sort by GDP per capita)
*/
@computed private get gdpPerCapitaIndicatorId(): number | undefined {
if (this.gdpPerCapitaColumnUsedInChart)
return +this.gdpPerCapitaColumnUsedInChart.slug

if (this.hasCountryOrRegionEntities)
return DEFAULT_GDP_PER_CAPITA_INDICATOR_ID

return undefined
return {
key: externalSortIndicator.key,
defaultLabel: externalSortIndicator.label,
indicatorId,
slug: indicatorId?.toString(),
chartColumn,
}
})
}

@computed private get gdpPerCapitaSlug(): ColumnSlug | undefined {
return this.gdpPerCapitaIndicatorId !== undefined
? this.gdpPerCapitaIndicatorId.toString()
: undefined
@computed private get externalSortIndicatorSlugs(): string[] {
return this.externalSortIndicators
.map(({ slug }) => slug)
.filter((slug): slug is string => slug !== undefined)
}

@computed private get sortColumnsBySlug(): Record<ColumnSlug, CoreColumn> {
Expand All @@ -375,29 +360,21 @@ export class EntitySelector extends React.Component<{
label: "Name",
})

// add population to the dropdown if applicable
if (this.populationSlug) {
options.push({
value: this.populationSlug,
label: this.populationColumnUsedInChart
? makeColumnLabel(this.populationColumnUsedInChart)
: DEFAULT_POPULATION_LABEL,
})
}

// add GDP per capita to the dropdown if applicable
if (this.gdpPerCapitaSlug) {
options.push({
value: this.gdpPerCapitaSlug,
label: this.gdpPerCapitaColumnUsedInChart
? makeColumnLabel(this.gdpPerCapitaColumnUsedInChart)
: DEFAULT_GDP_PER_CAPITA_LABEL,
})
}
// add external indicators to the dropdown if applicable
this.externalSortIndicators.forEach((external) => {
if (external.slug) {
options.push({
value: external.slug,
label: external.chartColumn
? makeColumnLabel(external.chartColumn)
: external.defaultLabel,
})
}
})

// add chart columns to the dropdown
const chartColumns =
this.numericalChartColumnsWithoutPopulationAndGdpPerCapita
this.numericalChartColumnsWithoutExternalSortColumns
options.push(
...chartColumns.map((column) => {
return {
Expand Down Expand Up @@ -602,67 +579,51 @@ export class EntitySelector extends React.Component<{
}
}

@action.bound async loadPopulationColumn(): Promise<void> {
if (this.populationColumn) return
@action.bound async loadExternalSortColumn(
indicatorId: number
): Promise<void> {
if (this.externalSortColumnsByIndicatorId[indicatorId]) return

if (this.populationColumnUsedInChart) {
this.set({ populationColumn: this.populationColumnUsedInChart })
return
}

if (this.populationIndicatorId === undefined) return

this.set({ isLoadingExternalSortColumn: true })

try {
const variable = await loadVariableDataAndMetadata(
this.populationIndicatorId,
this.manager.dataApiUrl
)

const variableTable = buildVariableTable(variable)
if (variableTable) {
this.set({
populationColumn: variableTable.get(this.populationSlug),
})
}
} catch {
console.error(
`Failed to load variable with id ${this.populationIndicatorId}`
)
}

this.set({ isLoadingExternalSortColumn: false })
}
const externalSortIndicator = this.externalSortIndicators.find(
(external) => external.indicatorId === indicatorId
)

@action.bound async loadGdpPerCapitaColumn(): Promise<void> {
if (this.gdpPerCapitaColumn) return
if (!externalSortIndicator) return undefined

if (this.gdpPerCapitaColumnUsedInChart) {
this.set({ gdpPerCapitaColumn: this.gdpPerCapitaColumnUsedInChart })
if (externalSortIndicator.chartColumn) {
this.set({
externalSortColumnsByIndicatorId: {
...this.externalSortColumnsByIndicatorId,
[externalSortIndicator.key]:
externalSortIndicator.chartColumn,
},
})
return
}

if (this.gdpPerCapitaIndicatorId === undefined) return
if (externalSortIndicator.indicatorId === undefined) return

this.set({ isLoadingExternalSortColumn: true })

try {
const variable = await loadVariableDataAndMetadata(
this.gdpPerCapitaIndicatorId,
externalSortIndicator.indicatorId,
this.manager.dataApiUrl
)
const variableTable = buildVariableTable(variable)
if (variableTable) {
this.set({
gdpPerCapitaColumn: variableTable.get(
this.gdpPerCapitaSlug
),
externalSortColumnsByIndicatorId: {
...this.externalSortColumnsByIndicatorId,
[externalSortIndicator.indicatorId]: variableTable.get(
externalSortIndicator.slug
),
},
})
}
} catch {
console.error(
`Failed to load variable with id ${this.gdpPerCapitaIndicatorId}`
`Failed to load variable with id ${externalSortIndicator.indicatorId}`
)
}

Expand All @@ -673,12 +634,11 @@ export class EntitySelector extends React.Component<{
if (selected) {
const { value } = selected as DropdownOption

// if population or GDP per capita is selected, load the column
// (this is a no-op if the column is already loaded)
if (value === this.populationSlug) {
await this.loadPopulationColumn()
} else if (value === this.gdpPerCapitaSlug) {
await this.loadGdpPerCapitaColumn()
const isExternalIndicator =
this.externalSortIndicatorSlugs.includes(value)

if (isExternalIndicator) {
await this.loadExternalSortColumn(+value)
}

this.updateSortSlug(value)
Expand Down

0 comments on commit 354fcd8

Please sign in to comment.