Skip to content

Commit

Permalink
🎉 early entity exclusion for all chart types
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Feb 20, 2025
1 parent f8b9d8c commit 0f0f725
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 384 deletions.
15 changes: 15 additions & 0 deletions adminSiteClient/AbstractChartEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,25 @@ export abstract class AbstractChartEditor<
return difference(focusedSeriesNames, availableSeriesNames)
}

@computed get invalidSelectedEntityNames(): SeriesName[] {
const { grapher } = this

// find invalid selected entities
const availableEntityNames = grapher.availableEntities.map(
(e) => e.entityName
)
const selectedEntityNames = grapher.selection.selectedEntityNames
return difference(selectedEntityNames, availableEntityNames)
}

@action.bound removeInvalidFocusedSeriesNames(): void {
this.grapher.focusArray.remove(...this.invalidFocusedSeriesNames)
}

@action.bound removeInvalidSelectedEntityNames(): void {
this.grapher.selection.deselectEntities(this.invalidSelectedEntityNames)
}

abstract get isNewGrapher(): boolean
abstract get availableTabs(): EditorTab[]

Expand Down
177 changes: 177 additions & 0 deletions adminSiteClient/EditorDataTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import { ColorBox, SelectField, Section, FieldsRow } from "./Forms.js"
import {
faArrowsAltV,
faLink,
faMinus,
faTimes,
faTrash,
faUnlink,
} from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js"
Expand Down Expand Up @@ -407,6 +409,180 @@ class MissingDataSection<
}
}

@observer
class EntityFilterSection<
Editor extends AbstractChartEditor,
> extends React.Component<{ editor: Editor }> {
@computed private get editor(): Editor {
return this.props.editor
}

@computed private get grapher(): Grapher {
return this.editor.grapher
}

@computed private get includedEntityNames(): EntityName[] {
return this.grapher.includedEntityNames ?? []
}

@computed private get excludedEntityNames(): EntityName[] {
return this.grapher.excludedEntityNames ?? []
}

@computed private get includedEntityChoices() {
const { inputTable, includedEntityNames = [] } = this.grapher
return inputTable.availableEntityNames
.filter((entityName) => !includedEntityNames.includes(entityName))
.sort()
}

@computed private get excludedEntityChoices() {
const { inputTable, excludedEntityNames = [] } = this.grapher
return inputTable.availableEntityNames
.filter((entityName) => !excludedEntityNames.includes(entityName))
.sort()
}

@action.bound validateSelectionAndFocus() {
this.editor.removeInvalidSelectedEntityNames()
this.editor.removeInvalidFocusedSeriesNames()
}

@action.bound onExcludeEntity(entityName: string) {
const { grapher } = this
if (grapher.excludedEntityNames === undefined) {
grapher.excludedEntityNames = []
}

if (!grapher.excludedEntityNames.includes(entityName))
grapher.excludedEntityNames.push(entityName)

this.validateSelectionAndFocus()
}

@action.bound onUnexcludeEntity(entityName: string) {
const { grapher } = this
if (!grapher.excludedEntityNames) return
grapher.excludedEntityNames = grapher.excludedEntityNames.filter(
(e) => e !== entityName
)

this.validateSelectionAndFocus()
}

@action.bound onIncludeEntity(entityName: string) {
const { grapher } = this
if (grapher.includedEntityNames === undefined) {
grapher.includedEntityNames = []
}

if (!grapher.includedEntityNames.includes(entityName))
grapher.includedEntityNames.push(entityName)

this.validateSelectionAndFocus()
}

@action.bound onUnincludeEntity(entityName: string) {
const { grapher } = this
if (!grapher.includedEntityNames) return
grapher.includedEntityNames = grapher.includedEntityNames.filter(
(e) => e !== entityName
)

this.validateSelectionAndFocus()
}

@action.bound onClearExcludedEntities() {
const { grapher } = this
grapher.excludedEntityNames = []
this.validateSelectionAndFocus()
}

@action.bound onClearIncludedEntities() {
const { grapher } = this
grapher.includedEntityNames = []
this.validateSelectionAndFocus()
}

render() {
const { includedEntityChoices, excludedEntityChoices } = this
return (
<Section name="Manual entity selection">
<SelectField
label={
"Explicit start selection (leave empty to show all entities)"
}
placeholder={"Select an entity to include"}
value={undefined}
onValue={(v) => v && this.onIncludeEntity(v)}
options={includedEntityChoices.map((entry) => ({
value: entry,
}))}
/>
{this.includedEntityNames && (
<ul className="includedEntities">
{this.includedEntityNames.map((entity) => (
<li key={entity}>
<div
className="clickable"
onClick={() =>
this.onUnincludeEntity(entity)
}
>
<FontAwesomeIcon icon={faMinus} />
</div>
{entity}
</li>
))}
</ul>
)}
{this.includedEntityNames && (
<button
className="btn btn-light btn-clear-selection"
onClick={this.onClearIncludedEntities}
>
<FontAwesomeIcon icon={faTrash} /> Clear start selection
</button>
)}
<SelectField
label="Exclude individual entities"
placeholder="Select an entity to exclude"
value={undefined}
onValue={(v) => v && this.onExcludeEntity(v)}
options={excludedEntityChoices.map((entry) => ({
value: entry,
}))}
/>
{this.excludedEntityNames && (
<ul className="excludedEntities">
{this.excludedEntityNames.map((entity) => (
<li key={entity}>
<div
className="clickable"
onClick={() =>
this.onUnexcludeEntity(entity)
}
>
<FontAwesomeIcon icon={faMinus} />
</div>
{entity}
</li>
))}
</ul>
)}
{this.excludedEntityNames && (
<button
className="btn btn-light btn-clear-selection"
onClick={this.onClearExcludedEntities}
>
<FontAwesomeIcon icon={faTrash} /> Clear exclude list
</button>
)}
</Section>
)
}
}

@observer
export class EditorDataTab<
Editor extends AbstractChartEditor,
Expand Down Expand Up @@ -480,6 +656,7 @@ export class EditorDataTab<
{features.canHighlightSeries && (
<FocusSection editor={editor} />
)}
<EntityFilterSection editor={editor} />
{features.canSpecifyMissingDataStrategy && (
<MissingDataSection editor={this.props.editor} />
)}
Expand Down
Loading

0 comments on commit 0f0f725

Please sign in to comment.