Skip to content

Commit

Permalink
187485557 v3 DI Select Case and Update DataContext Nofications (#1231)
Browse files Browse the repository at this point in the history
* Set up notifications for select case actions.

* Only broadcast selectCases notifications when selection has actually changed.

* Broadcast updateDataContext notifications.

* Use utility functions for most selection actions.
  • Loading branch information
tealefristoe authored May 8, 2024
1 parent d57e840 commit abaca04
Show file tree
Hide file tree
Showing 20 changed files with 252 additions and 140 deletions.
27 changes: 27 additions & 0 deletions v3/cypress/e2e/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,33 @@ context("codap plugins", () => {
openAPITester()
webView.toggleAPITesterFilter()

cy.log("Broadcast select cases notifications")
table.getCell(2, 2).click()
webView.confirmAPITesterResponseContains(/"operation":\s"selectCases/)
webView.confirmAPITesterResponseContains(/"extend":\sfalse/)
webView.clearAPITesterResponses()
table.getCell(2, 3).click({ metaKey: true })
webView.confirmAPITesterResponseContains(/"operation":\s"selectCases/)
webView.confirmAPITesterResponseContains(/"extend":\strue/)
webView.clearAPITesterResponses()
table.getCell(2, 3).click({ metaKey: true })
webView.confirmAPITesterResponseContains(/"operation":\s"selectCases/)
webView.confirmAPITesterResponseContains(/"extend":\strue/)
webView.confirmAPITesterResponseContains(/"removedCases":/)
webView.clearAPITesterResponses()
// TODO There are many more ways to select cases that should be covered with tests.

cy.log("Broadcast update dataContext notifications")
c.selectTile("table")
table.getDatasetInfoButton().click()
table.getDatasetDescriptionTextArea().type("test")
table.submitDatasetInfo()
webView.confirmAPITesterResponseContains(/"operation":\s"updateDataContext/)
webView.clearAPITesterResponses()
c.changeComponentTitle("table", "Mammals2")
webView.confirmAPITesterResponseContains(/"operation":\s"updateDataContext/)
webView.clearAPITesterResponses()

cy.log("Broadcast attribute notifications")

cy.log("Broadcast hideAttributes notifications")
Expand Down
6 changes: 6 additions & 0 deletions v3/cypress/support/elements/table-tile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ export const TableTileElements = {
getDatasetInfoButton() {
return c.getInspectorPanel().find("[data-testid=dataset-info-button]")
},
getDatasetDescriptionTextArea() {
return cy.get("[data-testid=dataset-description-input")
},
submitDatasetInfo() {
cy.get("[data-testid=Apply-button]").click()
},
getResizeButton() {
return c.getInspectorPanel().find("[data-testid=resize-table-button]")
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { observer } from "mobx-react-lite"
import CardIcon from "../../assets/icons/icon-case-card.svg"
import TableIcon from "../../assets/icons/icon-table.svg"
import { useDocumentContent } from "../../hooks/use-document-content"
import { updateDataContextNotification } from "../../models/data/data-set-notifications"
import { getTileDataSet } from "../../models/shared/shared-data-utils"
import { t } from "../../utilities/translation/translate"
import { kCaseCardTileType } from "../case-card/case-card-defs"
Expand Down Expand Up @@ -81,7 +82,13 @@ export const CaseTableCardTitleBar =
const handleChangeTitle = (newTitle?: string) => {
if (newTitle) {
// case table title reflects DataSet title
data?.setTitle(newTitle)
data?.applyModelChange(() => {
data.setTitle(newTitle)
}, {
notifications: () => updateDataContextNotification(data),
undoStringKey: "DG.Undo.component.componentTitleChange",
redoStringKey: "DG.Redo.component.componentTitleChange"
})
}
}

Expand Down
3 changes: 2 additions & 1 deletion v3/src/components/case-table/cell-text-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useRef } from "react"
import { textEditorClassname } from "react-data-grid"
import { useDataSetContext } from "../../hooks/use-data-set-context"
import { selectAllCases } from "../../models/data/data-set-utils"
import { TRenderEditCellProps } from "./case-table-types"

/*
Expand All @@ -27,7 +28,7 @@ export default function CellTextEditor({ row, column, onRowChange, onClose }: TR
const valueRef = useRef(initialValueRef.current)

useEffect(()=>{
data?.setSelectedCases([])
selectAllCases(data, false)
}, [data])

const handleChange = (value: string) => {
Expand Down
3 changes: 2 additions & 1 deletion v3/src/components/case-table/color-cell-text-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
import React, { ChangeEvent, useCallback, useEffect, useRef, useState } from "react"
import { textEditorClassname } from "react-data-grid"
import { useDataSetContext } from "../../hooks/use-data-set-context"
import { selectAllCases } from "../../models/data/data-set-utils"
import { parseColor, parseColorToHex } from "../../utilities/color-utils"
import { t } from "../../utilities/translation/translate"
import { TRenderEditCellProps } from "./case-table-types"
Expand Down Expand Up @@ -56,7 +57,7 @@ export default function ColorCellTextEditor({ row, column, onRowChange, onClose
const showColorSwatch = useRef(!!hexColor)

useEffect(() => {
data?.setSelectedCases([])
selectAllCases(data, false)
}, [data])

// commits the change and closes the editor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from "react"
import { Button, FormControl, FormLabel, Input, ModalBody, ModalCloseButton, ModalFooter, ModalHeader, Textarea,
Tooltip } from "@chakra-ui/react"
import { useDataSetContext } from "../../../hooks/use-data-set-context"
import { updateDataContextNotification } from "../../../models/data/data-set-notifications"
import { t } from "../../../utilities/translation/translate"
import { CodapModal } from "../../codap-modal"

Expand All @@ -20,11 +21,15 @@ export const DatasetInfoModal = ({showInfoModal, setShowInfoModal}: IProps) => {
const [description, setDescription] = useState(data?.description || "")

const handleCloseInfoModal = () => {
data?.setName(datasetName)
data?.setSourceName(sourceName)
data?.setImportDate(importDate)
data?.setDescription(description)
setShowInfoModal(false)
data?.applyModelChange(() => {
data.setName(datasetName)
data.setSourceName(sourceName)
data.setImportDate(importDate)
data.setDescription(description)
setShowInfoModal(false)
}, {
notifications: () => updateDataContextNotification(data)
})
}

const buttons=[{ label: t("DG.AttrFormView.cancelBtnTitle"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { MenuItem, MenuList } from "@chakra-ui/react"
import React from "react"
import { useDataSetContext } from "../../../hooks/use-data-set-context"
import { selectAllCases } from "../../../models/data/data-set-utils"
import { t } from "../../../utilities/translation/translate"

export const TrashMenuList = () => {
const data = useDataSetContext()

const handleSelectAllCases = () => {
data?.setSelectedCases(data.cases.map(c => c.__id__))
selectAllCases(data)
}

const handleDeleteSelectedCases = () => {
Expand Down
17 changes: 9 additions & 8 deletions v3/src/components/case-table/use-selected-rows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { useCallback, useEffect, useRef, useState, MouseEvent } from "react"
import { DataGridHandle } from "react-data-grid"
import { appState } from "../../models/app-state"
import { isPartialSelectionAction, isSelectionAction } from "../../models/data/data-set-actions"
import { collectionCaseIdFromIndex, collectionCaseIndexFromId } from "../../models/data/data-set-utils"
import { OnScrollClosestRowIntoViewFn, TCellClickArgs } from "./case-table-types"
import { useCollectionTableModel } from "./use-collection-table-model"
import {
collectionCaseIdFromIndex, collectionCaseIndexFromId, selectCases, setSelectedCases
} from "../../models/data/data-set-utils"
import { useCollectionContext } from "../../hooks/use-collection-context"
import { useDataSetContext } from "../../hooks/use-data-set-context"
import { onAnyAction } from "../../utilities/mst-utils"
import { prf } from "../../utilities/profiler"
import { OnScrollClosestRowIntoViewFn, TCellClickArgs } from "./case-table-types"
import { useCollectionTableModel } from "./use-collection-table-model"

interface UseSelectedRows {
gridRef: React.RefObject<DataGridHandle | null>
Expand All @@ -27,7 +29,7 @@ export const useSelectedRows = ({ gridRef, onScrollClosestRowIntoView }: UseSele
const setSelectedRows = useCallback((rowSet: ReadonlySet<string>) => {
const rows = Array.from(rowSet)
++syncCount.current
data?.setSelectedCases(rows)
setSelectedCases(rows, data)
--syncCount.current
_setSelectedRows(rowSet)
}, [data])
Expand Down Expand Up @@ -119,17 +121,16 @@ export const useSelectedRows = ({ gridRef, onScrollClosestRowIntoView }: UseSele
for (let i = start; i <= end; ++i) {
const id = collectionCaseIdFromIndex(i, data, collectionId)
id && casesToSelect.push(id)
data?.selectCases(casesToSelect, true)
selectCases(casesToSelect, data)
}
}
anchorCase.current = caseId
}
else if (isExtending) {
data?.selectCases([caseId], !isCaseSelected)
anchorCase.current = !isCaseSelected ? caseId : null
selectCases([caseId], data, !isCaseSelected)
}
else if (!isCaseSelected) {
data?.setSelectedCases([caseId])
setSelectedCases([caseId], data)
anchorCase.current = caseId
}
}, [collectionId, data])
Expand Down
20 changes: 12 additions & 8 deletions v3/src/components/data-display/components/background.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import {useMemo} from "use-memo-one"
import {select, color, range} from "d3"
import RTreeLib from 'rtree'
import * as PIXI from "pixi.js"
import {appState} from "../../../models/app-state"
import {IDataSet} from "../../../models/data/data-set"
import { /*selectAllCases,*/ selectAndDeselectCases } from "../../../models/data/data-set-utils"
import {defaultBackgroundColor} from "../../../utilities/color-utils"
import {rTreeRect} from "../data-display-types"
import {rectangleSubtract, rectNormalize} from "../data-display-utils"
import {useDataDisplayLayout} from "../hooks/use-data-display-layout"
import {useDataDisplayModelContext} from "../hooks/use-data-display-model"
import {MarqueeState} from "../models/marquee-state"
import {IPixiPointMetadata, IPixiPointsArrayRef, PixiBackgroundPassThroughEvent, PixiPoints}
from "../pixi/pixi-points"
import {IDataSet} from "../../../models/data/data-set"
import {MarqueeState} from "../models/marquee-state"
import {appState} from "../../../models/app-state"
import {useDataDisplayModelContext} from "../hooks/use-data-display-model"
import {useDataDisplayLayout} from "../hooks/use-data-display-layout"

interface IProps {
marqueeState: MarqueeState
Expand Down Expand Up @@ -102,7 +103,11 @@ export const Background = forwardRef<SVGGElement | HTMLDivElement, IProps>((prop
width.current = 0
height.current = 0
if (!event.shiftKey) {
datasetsArray.forEach(dataset => dataset.setSelectedCases([]))
datasetsArray.forEach(dataset => {
// This is breaking the graph-legend cypress test
// selectAllCases(dataset, false)
dataset.setSelectedCases([])
})
}
marqueeState.setMarqueeRect({x: startX.current, y: startY.current, width: 0, height: 0})
}, [datasetsArray, marqueeState, pixiPointsArrayRef]),
Expand Down Expand Up @@ -136,8 +141,7 @@ export const Background = forwardRef<SVGGElement | HTMLDivElement, IProps>((prop
// Apply the selections and de-selections for each dataset
Object.values(datasetsMap).forEach((selectionSpec) => {
const {dataset, caseIDsToSelect, caseIDsToDeselect} = selectionSpec
dataset.selectCases(caseIDsToSelect, true)
dataset.selectCases(caseIDsToDeselect, false)
selectAndDeselectCases(caseIDsToSelect, caseIDsToDeselect, dataset)
})
}
clearDatasetsMapArrays()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {mstReaction} from "../../../../utilities/mst-reaction"
import {comparer, reaction} from "mobx"
import {drag, range, select} from "d3"
import React, {useCallback, useEffect, useMemo, useRef} from "react"
// import { setOrExtendSelection } from "../../../../models/data/data-set-utils"
import {isSelectionAction} from "../../../../models/data/data-set-actions"
import {missingColor} from "../../../../utilities/color-utils"
import {onAnyAction} from "../../../../utilities/mst-utils"
Expand Down Expand Up @@ -243,16 +244,21 @@ export const CategoricalLegend = observer(
const sel = select<SVGGElement, number>(this),
size = sel.selectAll<SVGRectElement, number>('rect').size()
if (size === 0) {
const handleClick = (event: any, i: number) => {
const caseIds = dataConfiguration?.getCasesForLegendValue(categoryData.current[i].category)
if (caseIds) {
// This is breaking the graph-legend cypress test
// setOrExtendSelection(caseIds, dataConfiguration?.dataset, event.shiftKey)
if (event.shiftKey) dataConfiguration?.dataset?.selectCases(caseIds)
else dataConfiguration?.dataset?.setSelectedCases(caseIds)
}
}
sel.append('rect')
.attr('width', keySize)
.attr('height', keySize)
.on('click', (event, i: number) => {
dataConfiguration?.selectCasesForLegendValue(categoryData.current[i].category, event.shiftKey)
})
.on('click', handleClick)
sel.append('text')
.on('click', (event, i: number) => {
dataConfiguration?.selectCasesForLegendValue(categoryData.current[i].category, event.shiftKey)
})
.on('click', handleClick)
}
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {ScaleQuantile, scaleQuantile, schemeBlues} from "d3"
import {reaction} from "mobx"
import {observer} from "mobx-react-lite"
import React, {useCallback, useEffect, useRef, useState} from "react"
import {useDataConfigurationContext} from "../../hooks/use-data-configuration-context"
import {useDataDisplayLayout} from "../../hooks/use-data-display-layout"
import {getStringBounds} from "../../../axis/axis-utils"
import {ScaleQuantile, scaleQuantile, schemeBlues} from "d3"
import {isSelectionAction} from "../../../../models/data/data-set-actions"
import { setOrExtendSelection } from "../../../../models/data/data-set-utils"
import {axisGap} from "../../../axis/axis-types"
import {getStringBounds} from "../../../axis/axis-utils"
import {kChoroplethHeight} from "../../data-display-types"
import {useDataConfigurationContext} from "../../hooks/use-data-configuration-context"
import {useDataDisplayLayout} from "../../hooks/use-data-display-layout"
import {choroplethLegend} from "./choropleth-legend/choropleth-legend"
import {axisGap} from "../../../axis/axis-types"

import vars from "../../../vars.scss"

Expand Down Expand Up @@ -51,7 +52,11 @@ export const NumericLegend =
width: tileWidth,
marginLeft: 6, marginTop: labelHeight, marginRight: 6, ticks: 5,
clickHandler: (quantile: number, extend: boolean) => {
dataConfiguration?.selectCasesForLegendQuantile(quantile, extend)
const dataset = dataConfiguration?.dataset
const quantileCases = dataConfiguration?.getCasesForLegendQuantile(quantile)
if (quantileCases) {
setOrExtendSelection(quantileCases, dataset, extend)
}
},
casesInQuantileSelectedHandler: (quantile: number) => {
return !!dataConfiguration?.casesInQuantileAreSelected(quantile)
Expand Down
24 changes: 11 additions & 13 deletions v3/src/components/data-display/data-display-utils.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import {measureText} from "../../hooks/use-measure-text"
import {between} from "../../utilities/math-utils"
import {IDataSet} from "../../models/data/data-set"
import { selectCases, setOrExtendSelection } from "../../models/data/data-set-utils"
import {
defaultSelectedColor, defaultSelectedStroke, defaultSelectedStrokeOpacity, defaultSelectedStrokeWidth,
defaultStrokeOpacity, defaultStrokeWidth
} from "../../utilities/color-utils"
import {IDataSet} from "../../models/data/data-set"
import {IDataConfigurationModel } from "./models/data-configuration-model"
import {between} from "../../utilities/math-utils"
import { IBarCover } from "../graph/graphing-types"
import {ISetPointSelection} from "../graph/utilities/graph-utils"
import {
hoverRadiusFactor, kDataDisplayFont, Point, PointDisplayType, pointRadiusLogBase, pointRadiusMax, pointRadiusMin,
pointRadiusSelectionAddend, Rect, rTreeRect
} from "./data-display-types"
import {ISetPointSelection} from "../graph/utilities/graph-utils"
import {IDataConfigurationModel } from "./models/data-configuration-model"
import {IPixiPointStyle, PixiPoints} from "./pixi/pixi-points"
import { IBarCover } from "../graph/graphing-types"

export const maxWidthOfStringsD3 = (strings: Iterable<string>) => {
let maxWidth = 0
Expand Down Expand Up @@ -52,13 +53,9 @@ export function handleClickOnCase(event: PointerEvent, caseID: string, dataset?:
caseIsSelected = dataset?.isCaseSelected(caseID)

if (!caseIsSelected) {
if (extendSelection) { // case is not selected and Shift key is down => add case to selection
dataset?.selectCases([caseID])
} else { // case is not selected and Shift key is up => only this case should be selected
dataset?.setSelectedCases([caseID])
}
setOrExtendSelection([caseID], dataset, extendSelection)
} else if (extendSelection) { // case is selected and Shift key is down => deselect case
dataset?.selectCases([caseID], false)
selectCases([caseID], dataset, false)
}
}

Expand All @@ -73,9 +70,10 @@ export const handleClickOnBar = ({ event, dataConfig, primaryAttrRole, barCover
const { primeSplitCat, secSplitCat, legendCat, primeCat, secCat } = barCover
const extendSelection = event.shiftKey
if (primeCat) {
dataConfig?.selectCasesForCategoryValues(
primaryAttrRole, primeCat, secCat, primeSplitCat, secSplitCat, legendCat, extendSelection
const caseIDs = dataConfig.getCasesForCategoryValues(
primaryAttrRole, primeCat, secCat, primeSplitCat, secSplitCat, legendCat
)
setOrExtendSelection(caseIDs, dataConfig.dataset, extendSelection)
}
}

Expand Down
7 changes: 4 additions & 3 deletions v3/src/components/data-display/hooks/use-connecting-lines.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { curveLinear, line, select } from "d3"
import { tip as d3tip } from "d3-v6-tip"
import { useCallback } from "react"
import { useDataConfigurationContext } from "./use-data-configuration-context"
import { PixiBackgroundPassThroughEvent, PixiPoints } from "../pixi/pixi-points"
import { setSelectedCases } from "../../../models/data/data-set-utils"
import { t } from "../../../utilities/translation/translate"
import { IConnectingLineDescription, transitionDuration } from "../data-display-types"
import { PixiBackgroundPassThroughEvent, PixiPoints } from "../pixi/pixi-points"
import { useDataConfigurationContext } from "./use-data-configuration-context"

interface IMouseOverProps {
caseIDs: string[]
Expand Down Expand Up @@ -80,7 +81,7 @@ export const useConnectingLines = (props: IProps) => {
newSelection = caseIDs
}
}
dataset?.setSelectedCases(newSelection)
setSelectedCases(newSelection, dataset)
}
}, [dataset, onConnectingLinesClick])

Expand Down
Loading

0 comments on commit abaca04

Please sign in to comment.