Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

188651851 v3 DI Multiple Y Attributes #1683

Merged
merged 8 commits into from
Dec 13, 2024
78 changes: 70 additions & 8 deletions v3/src/components/graph/component-handler-graph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,40 @@ describe("DataInteractive ComponentHandler Graph", () => {
handler.delete!({ component: tileIds })
expect(documentContent.tileMap.size).toBe(0)

// Create a graph with multiple y attributes using ids
const resultYIDs = handler.create!({}, {
type: "graph", dataContext: "data", yAttributeIDs: [toV2Id(a3.id), toV2Id(a4.id)]
})
expect(resultYIDs.success).toBe(true)
const resultYIDsValues = resultYIDs.values as DIComponentInfo
const tileYIDs = documentContent.tileMap.get(toV3Id(kGraphIdPrefix, resultYIDsValues.id!))!
expect(tileYIDs).toBeDefined()
expect(isGraphContentModel(tileYIDs.content)).toBe(true)
const tileContentYIDs = tileYIDs.content as IGraphContentModel
expect(tileContentYIDs.dataConfiguration._yAttributeDescriptions.length).toBe(2)
expect(tileContentYIDs.dataConfiguration._yAttributeDescriptions[0].attributeID).toBe(a3.id)
expect(tileContentYIDs.dataConfiguration._yAttributeDescriptions[1].attributeID).toBe(a4.id)
// Delete the graph when we're finished
handler.delete!({ component: tileYIDs })
expect(documentContent.tileMap.size).toBe(0)

// Create a graph with multiple y attributes using names
const resultYNames = handler.create!({}, {
type: "graph", dataContext: "data", yAttributeNames: [a3.name, a4.name]
})
expect(resultYNames.success).toBe(true)
const resultYNamesValues = resultYNames.values as DIComponentInfo
const tileYNames = documentContent.tileMap.get(toV3Id(kGraphIdPrefix, resultYNamesValues.id!))!
expect(tileYNames).toBeDefined()
expect(isGraphContentModel(tileYNames.content)).toBe(true)
const tileContentYNames = tileYNames.content as IGraphContentModel
expect(tileContentYNames.dataConfiguration._yAttributeDescriptions.length).toBe(2)
expect(tileContentYNames.dataConfiguration._yAttributeDescriptions[0].attributeID).toBe(a3.id)
expect(tileContentYNames.dataConfiguration._yAttributeDescriptions[1].attributeID).toBe(a4.id)
// Delete the graph when we're finished
handler.delete!({ component: tileYNames })
expect(documentContent.tileMap.size).toBe(0)

// Create a graph with options
const result = handler.create!({}, {
type: "graph", cannotClose: true, dataContext: "data", xAttributeName: "a3", yAttributeName: "a4",
Expand Down Expand Up @@ -188,13 +222,37 @@ describe("DataInteractive ComponentHandler Graph", () => {
})
expect(dataConfig.attributeDescriptionForRole("rightNumeric")?.attributeID).toBe(a3.id)

// Update to remove y attributes
const updateResultRemoveYs = handler.update!({ component: tile }, {
yAttributeNames: [] as string[]
} as V2Graph)
expect(updateResultRemoveYs.success).toBe(true)
expect(dataConfig._yAttributeDescriptions.length).toBe(0)

// Update to set multiple y attributes with names
const updateResultYNames = handler.update!({ component: tile }, {
yAttributeNames: [a4.name, a3.name]
} as V2Graph)
expect(updateResultYNames.success).toBe(true)
expect(dataConfig._yAttributeDescriptions.length).toBe(2)
expect(dataConfig._yAttributeDescriptions[0].attributeID).toBe(a4.id)
expect(dataConfig._yAttributeDescriptions[1].attributeID).toBe(a3.id)

// Update to set y attributes with ids
const updateResultYIDs = handler.update!({ component: tile }, {
yAttributeIDs: [toV2Id(a3.id)]
} as V2Graph)
expect(updateResultYIDs.success).toBe(true)
expect(dataConfig._yAttributeDescriptions.length).toBe(1)
expect(dataConfig._yAttributeDescriptions[0].attributeID).toBe(a3.id)

// Get graph
testGetComponent(tile, handler, (graphTile, values) => {
const {
dataContext, enableNumberToggle, numberToggleLastMode, captionAttributeID, captionAttributeName,
legendAttributeID, legendAttributeName, rightSplitAttributeID, rightSplitAttributeName,
topSplitAttributeID, topSplitAttributeName, xAttributeID, xAttributeName, xLowerBound, xUpperBound,
yAttributeID, yAttributeName, yLowerBound, yUpperBound,
yAttributeID, yAttributeIDs, yAttributeName, yAttributeNames, yLowerBound, yUpperBound,
y2AttributeID, y2AttributeName, y2LowerBound, y2UpperBound
} = values as V2GetGraph
const content = graphTile.content as IGraphContentModel
Expand All @@ -205,35 +263,39 @@ describe("DataInteractive ComponentHandler Graph", () => {
expect(numberToggleLastMode).toBe(content.showOnlyLastCase)

const captionAttributeId = dataConfiguration.attributeDescriptionForRole("caption")!.attributeID
expect(captionAttributeID).toBe(captionAttributeId)
expect(captionAttributeID).toBe(toV2Id(captionAttributeId))
expect(captionAttributeName).toBe(graphDataset.getAttribute(captionAttributeId)?.name)

const legendAttributeId = dataConfiguration.attributeDescriptionForRole("legend")!.attributeID
expect(legendAttributeID).toBe(legendAttributeId)
expect(legendAttributeID).toBe(toV2Id(legendAttributeId))
expect(legendAttributeName).toBe(graphDataset.getAttribute(legendAttributeId)?.name)

const rightSplitId = dataConfiguration.attributeDescriptionForRole("rightSplit")!.attributeID
expect(rightSplitAttributeID).toBe(rightSplitId)
expect(rightSplitAttributeID).toBe(toV2Id(rightSplitId))
expect(rightSplitAttributeName).toBe(graphDataset.getAttribute(rightSplitId)?.name)

const topSplitId = dataConfiguration.attributeDescriptionForRole("topSplit")!.attributeID
expect(topSplitAttributeID).toBe(topSplitId)
expect(topSplitAttributeID).toBe(toV2Id(topSplitId))
expect(topSplitAttributeName).toBe(graphDataset.getAttribute(topSplitId)?.name)

const xAttributeId = dataConfiguration.attributeDescriptionForRole("x")!.attributeID
expect(xAttributeID).toBe(xAttributeId)
expect(xAttributeID).toBe(toV2Id(xAttributeId))
expect(xAttributeName).toBe(graphDataset.getAttribute(xAttributeId)?.name)
expect(xLowerBound).toBe(xAxis.min)
expect(xUpperBound).toBe(xAxis.max)

const yAttributeId = dataConfiguration.attributeDescriptionForRole("y")!.attributeID
expect(yAttributeID).toBe(yAttributeId)
expect(yAttributeID).toBe(toV2Id(yAttributeId))
expect(yAttributeIDs?.length).toBe(1)
expect(yAttributeIDs?.[0]).toBe(toV2Id(a3.id))
expect(yAttributeName).toBe(graphDataset.getAttribute(yAttributeId)?.name)
expect(yAttributeNames?.length).toBe(1)
expect(yAttributeNames?.[0]).toBe(a3.name)
expect(yLowerBound).toBe(yAxis.min)
expect(yUpperBound).toBe(yAxis.max)

const y2AttributeId = dataConfiguration.attributeDescriptionForRole("rightNumeric")!.attributeID
expect(y2AttributeID).toBe(y2AttributeId)
expect(y2AttributeID).toBe(toV2Id(y2AttributeId))
expect(y2AttributeName).toBe(graphDataset.getAttribute(y2AttributeId)?.name)
expect(y2LowerBound).toBe(y2Axis.min)
expect(y2UpperBound).toBe(y2Axis.max)
Expand Down
2 changes: 1 addition & 1 deletion v3/src/components/graph/components/graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@
let disposer: Maybe<IDisposer>
if (graphModel) {
disposer = onAnyAction(graphModel, action => {
const dataConfigActions = ["setAttribute", "replaceYAttribute", "removeYAttributeAtIndex"]
const dataConfigActions = ["replaceYAttribute", "replaceYAttributes", "removeYAttributeAtIndex", "setAttribute"]

Check warning on line 239 in v3/src/components/graph/components/graph.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/graph/components/graph.tsx#L239

Added line #L239 was not covered by tests
if (dataConfigActions.includes(action.name) || isSetAttributeIDAction(action)) {
startAnimation()
graphController?.handleAttributeAssignment()
Expand Down
99 changes: 68 additions & 31 deletions v3/src/components/graph/graph-component-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getSnapshot } from "mobx-state-tree"
import { destroy, getSnapshot } from "mobx-state-tree"
import { V2GetGraph, V2Graph } from "../../data-interactive/data-interactive-component-types"
import { DIValues } from "../../data-interactive/data-interactive-types"
import { DIComponentHandler } from "../../data-interactive/handlers/component-handler"
Expand All @@ -9,7 +9,7 @@ import { IDataSet } from "../../models/data/data-set"
import { ISharedCaseMetadata } from "../../models/shared/shared-case-metadata"
import { getSharedCaseMetadataFromDataset, getSharedDataSets } from "../../models/shared/shared-data-utils"
import { ITileContentModel, ITileContentSnapshotWithType } from "../../models/tiles/tile-content"
import { toV3AttrId, toV3DataSetId } from "../../utilities/codap-utils"
import { maybeToV2Id, toV2Id, toV3AttrId, toV3DataSetId } from "../../utilities/codap-utils"
import { t } from "../../utilities/translation/translate"
import { AxisPlace } from "../axis/axis-types"
import { isNumericAxisModel } from "../axis/models/axis-model"
Expand Down Expand Up @@ -72,7 +72,7 @@ export const graphComponentHandler: DIComponentHandler = {
create({ values }) {
const {
dataContext: _dataContext, enableNumberToggle: showParentToggles, numberToggleLastMode: showOnlyLastCase,
yAttributeID, yAttributeName,
yAttributeID, yAttributeIDs, yAttributeName, yAttributeNames
} = values as V2Graph
const attributeInfo = getAttributeInfo(values)

Expand Down Expand Up @@ -100,15 +100,27 @@ export const graphComponentHandler: DIComponentHandler = {
}
}

let yAttribute: Maybe<IAttribute>
if (yAttributeID != null) {
yAttribute = dataset.getAttribute(toV3AttrId(yAttributeID))
}
if (!yAttribute && yAttributeName != null) {
yAttribute = dataset.getAttributeByName(yAttributeName)
}
if (yAttribute) {
_yAttributeDescriptions.push({ attributeID: yAttribute.id, type: yAttribute.type })
if (yAttributeIDs) {
yAttributeIDs.forEach(id => {
const attribute = dataset.getAttribute(toV3AttrId(id))
if (attribute) _yAttributeDescriptions.push({ attributeID: attribute.id })
})
} else if (yAttributeNames) {
yAttributeNames.forEach(name => {
const attribute = dataset.getAttributeByName(name)
if (attribute) _yAttributeDescriptions.push({ attributeID: attribute.id })
})
} else {
let yAttribute: Maybe<IAttribute>
if (yAttributeID != null) {
yAttribute = dataset.getAttribute(toV3AttrId(yAttributeID))
}
if (!yAttribute && yAttributeName != null) {
yAttribute = dataset.getAttributeByName(yAttributeName)
}
if (yAttribute) {
_yAttributeDescriptions.push({ attributeID: yAttribute.id, type: yAttribute.type })
}
}

if (showOnlyLastCase) {
Expand Down Expand Up @@ -185,7 +197,10 @@ export const graphComponentHandler: DIComponentHandler = {
}
})
}
return { content: { ...getSnapshot(graphModel), layers: finalLayers } as ITileContentSnapshotWithType }
const result = { content: { ...getSnapshot(graphModel), layers: finalLayers } as ITileContentSnapshotWithType }
// After we get the snapshot, destroy the model to stop all reactions
destroy(graphModel)
return result
},

get(content: ITileContentModel) {
Expand All @@ -195,48 +210,61 @@ export const graphComponentHandler: DIComponentHandler = {
const { dataConfiguration } = content.graphPointLayerModel
const { showParentToggles: enableNumberToggle, showOnlyLastCase: numberToggleLastMode } = content

const captionAttributeID = dataConfiguration.attributeDescriptionForRole("caption")?.attributeID
const captionAttributeName = captionAttributeID ? dataset?.getAttribute(captionAttributeID)?.name : undefined
const _captionAttributeID = dataConfiguration.attributeDescriptionForRole("caption")?.attributeID
const captionAttributeID = maybeToV2Id(_captionAttributeID)
const captionAttributeName = _captionAttributeID ? dataset?.getAttribute(_captionAttributeID)?.name : undefined

const legendAttributeID = dataConfiguration.attributeDescriptionForRole("legend")?.attributeID
const legendAttributeName = legendAttributeID ? dataset?.getAttribute(legendAttributeID)?.name : undefined
const _legendAttributeID = dataConfiguration.attributeDescriptionForRole("legend")?.attributeID
const legendAttributeID = maybeToV2Id(_legendAttributeID)
const legendAttributeName = _legendAttributeID ? dataset?.getAttribute(_legendAttributeID)?.name : undefined

const rightSplitAttributeID = dataConfiguration.attributeDescriptionForRole("rightSplit")?.attributeID
const rightSplitAttributeName = rightSplitAttributeID
? dataset?.getAttribute(rightSplitAttributeID)?.name
const _rightSplitAttributeID = dataConfiguration.attributeDescriptionForRole("rightSplit")?.attributeID
const rightSplitAttributeID = maybeToV2Id(_rightSplitAttributeID)
const rightSplitAttributeName = _rightSplitAttributeID
? dataset?.getAttribute(_rightSplitAttributeID)?.name
: undefined

const topSplitAttributeID = dataConfiguration.attributeDescriptionForRole("topSplit")?.attributeID
const topSplitAttributeName = topSplitAttributeID ? dataset?.getAttribute(topSplitAttributeID)?.name : undefined
const _topSplitAttributeID = dataConfiguration.attributeDescriptionForRole("topSplit")?.attributeID
const topSplitAttributeID = maybeToV2Id(_topSplitAttributeID)
const topSplitAttributeName = _topSplitAttributeID ? dataset?.getAttribute(_topSplitAttributeID)?.name : undefined

const xAttributeID = dataConfiguration.attributeDescriptionForRole("x")?.attributeID
const xAttributeName = xAttributeID ? dataset?.getAttribute(xAttributeID)?.name : undefined
const _xAttributeID = dataConfiguration.attributeDescriptionForRole("x")?.attributeID
const xAttributeID = maybeToV2Id(_xAttributeID)
const xAttributeName = _xAttributeID ? dataset?.getAttribute(_xAttributeID)?.name : undefined
const xAxis = content.getAxis("bottom")
const xNumericAxis = isNumericAxisModel(xAxis) ? xAxis : undefined
const xLowerBound = xNumericAxis?.min
const xUpperBound = xNumericAxis?.max

const yAttributeID = dataConfiguration.attributeDescriptionForRole("y")?.attributeID
const yAttributeName = yAttributeID ? dataset?.getAttribute(yAttributeID)?.name : undefined
const _yAttributeID = dataConfiguration.attributeDescriptionForRole("y")?.attributeID
const yAttributeID = maybeToV2Id(_yAttributeID)
const yAttributeName = _yAttributeID ? dataset?.getAttribute(_yAttributeID)?.name : undefined
const yAxis = content.getAxis("left")
const yNumericAxis = isNumericAxisModel(yAxis) ? yAxis : undefined
const yLowerBound = yNumericAxis?.min
const yUpperBound = yNumericAxis?.max

const y2AttributeID = dataConfiguration.attributeDescriptionForRole("rightNumeric")?.attributeID
const y2AttributeName = y2AttributeID ? dataset?.getAttribute(y2AttributeID)?.name : undefined
const _y2AttributeID = dataConfiguration.attributeDescriptionForRole("rightNumeric")?.attributeID
const y2AttributeID = maybeToV2Id(_y2AttributeID)
const y2AttributeName = _y2AttributeID ? dataset?.getAttribute(_y2AttributeID)?.name : undefined
const y2Axis = content.getAxis("rightNumeric")
const y2NumericAxis = isNumericAxisModel(y2Axis) ? y2Axis : undefined
const y2LowerBound = y2NumericAxis?.min
const y2UpperBound = y2NumericAxis?.max

const yAttributeIDs = dataConfiguration._yAttributeDescriptions
.map(description => toV2Id(description.attributeID))
const yAttributeNames = dataConfiguration._yAttributeDescriptions
.map(description => dataset?.getAttribute(description.attributeID)?.name).filter(name => name != null)

return {
dataContext, enableNumberToggle, numberToggleLastMode,
captionAttributeID, captionAttributeName, legendAttributeID, legendAttributeName,
rightSplitAttributeID, rightSplitAttributeName, topSplitAttributeID, topSplitAttributeName,
xAttributeID, xAttributeName, xLowerBound, xUpperBound,
yAttributeID, yAttributeName, yLowerBound, yUpperBound,
y2AttributeID, y2AttributeName, y2LowerBound, y2UpperBound
y2AttributeID, y2AttributeName, y2LowerBound, y2UpperBound,
yAttributeIDs, yAttributeNames
}
}
},
Expand All @@ -246,7 +274,8 @@ export const graphComponentHandler: DIComponentHandler = {

const {
dataContext: _dataContext, enableNumberToggle: showParentToggles, numberToggleLastMode: showOnlyLastCase,
xLowerBound, xUpperBound, yAttributeID, yAttributeName, yLowerBound, yUpperBound, y2LowerBound, y2UpperBound
xLowerBound, xUpperBound, yAttributeID, yAttributeIDs, yAttributeName, yAttributeNames,
yLowerBound, yUpperBound, y2LowerBound, y2UpperBound
} = values as V2GetGraph
const attributeInfo = getAttributeInfo(values)

Expand Down Expand Up @@ -310,7 +339,15 @@ export const graphComponentHandler: DIComponentHandler = {

// Any attribute can be put on the y axis, so we don't check to make sure the attribute is legal first
// We don't use dataConfiguration.setAttribute() to make the change because that clears additional y attributes
if (yAttributeID !== undefined) {
if (yAttributeIDs != null) {
content.dataConfiguration.replaceYAttributes(yAttributeIDs.map(id => ({ attributeID: toV3AttrId(id) })))
} else if (yAttributeNames != null) {
const descriptions = yAttributeNames.map(name => {
const attribute = dataSet?.getAttributeByName(name)
if (attribute) return { attributeID: attribute.id }
}).filter(desc => !!desc)
content.dataConfiguration.replaceYAttributes(descriptions)
} else if (yAttributeID !== undefined) {
if (yAttributeID) {
content.dataConfiguration.replaceYAttribute({ attributeID: toV3AttrId(yAttributeID) }, 0)
} else {
Expand Down
Loading
Loading