Skip to content

Commit

Permalink
187994058 v3 DI horizontalScrollOffset (#1369)
Browse files Browse the repository at this point in the history
* Handle horizontalScrollOffset in create and update component caseTable API requests.
  • Loading branch information
tealefristoe authored Jul 24, 2024
1 parent 4d94688 commit 383f1b5
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 20 deletions.
16 changes: 13 additions & 3 deletions v3/src/components/case-table/case-table-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,20 @@ export const CaseTableModel = TileContentModel
type: types.optional(types.literal(kCaseTableTileType), kCaseTableTileType),
// key is attribute id; value is width
columnWidths: types.map(types.number),
// Only used for serialization; volatile property used during run time
horizontalScrollOffset: 0
})
.volatile(self => ({
// entire hierarchical table scrolls as a unit horizontally
scrollLeft: 0
_horizontalScrollOffset: 0
}))
.actions(self => ({
afterCreate() {
self._horizontalScrollOffset = self.horizontalScrollOffset
},
prepareSnapshot() {
self.horizontalScrollOffset = self._horizontalScrollOffset
}
}))
.views(self => ({
get data() {
Expand Down Expand Up @@ -53,8 +63,8 @@ export const CaseTableModel = TileContentModel
updateAfterSharedModelChanges(sharedModel?: ISharedModel) {
// TODO
},
setScrollLeft(scrollLeft: number) {
self.scrollLeft = scrollLeft
setHorizontalScrollOffset(horizontalScrollOffset: number) {
self._horizontalScrollOffset = horizontalScrollOffset
}
}))
export interface ICaseTableModel extends Instance<typeof CaseTableModel> {}
Expand Down
36 changes: 23 additions & 13 deletions v3/src/components/case-table/case-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import { useSyncScrolling } from "./use-sync-scrolling"
import { CollectionContext, ParentCollectionContext } from "../../hooks/use-collection-context"
import { useDataSetContext } from "../../hooks/use-data-set-context"
import { useInstanceIdContext } from "../../hooks/use-instance-id-context"
import { useTileModelContext } from "../../hooks/use-tile-model-context"
import { ICollectionModel } from "../../models/data/collection"
import { IDataSet } from "../../models/data/data-set"
import { createCollectionNotification, deleteCollectionNotification } from "../../models/data/data-set-notifications"
import { INotification } from "../../models/history/apply-model-change"
import { mstReaction } from "../../utilities/mst-reaction"
import { prf } from "../../utilities/profiler"
import { t } from "../../utilities/translation/translate"

Expand All @@ -27,8 +27,6 @@ export const CaseTable = observer(function CaseTable({ setNodeRef }: IProps) {
const instanceId = useInstanceIdContext() || "case-table"
const data = useDataSetContext()
const tableModel = useCaseTableModel()
const { isTileSelected } = useTileModelContext()
const isFocused = isTileSelected()
const contentRef = useRef<HTMLDivElement | null>(null)
const lastNewCollectionDrop = useRef<{ newCollectionId: string, beforeCollectionId: string } | undefined>()

Expand All @@ -37,16 +35,28 @@ export const CaseTable = observer(function CaseTable({ setNodeRef }: IProps) {
setNodeRef(elt)
}

useEffect(function syncScrollLeft() {
// There is a bug, seemingly in React, in which the scrollLeft property gets reset
// to 0 when the order of tiles is changed (which happens on selecting/focusing tiles
// in the free tile layout), even though the CaseTable component is not re-rendered
// or unmounted/mounted. Therefore, we reset the scrollLeft property from our saved
// cache on focus change.
if (isFocused && contentRef.current) {
contentRef.current.scrollLeft = tableModel?.scrollLeft ?? 0
useEffect(() => {
const updateScroll = (horizontalScrollOffset?: number) => {
if (horizontalScrollOffset != null && contentRef.current &&
contentRef.current.scrollLeft !== horizontalScrollOffset
) {
contentRef.current.scrollLeft = horizontalScrollOffset
}
}
}, [isFocused, tableModel])

// Initial scroll is delayed a frame to let RDG do its thing
setTimeout(() => updateScroll(tableModel?._horizontalScrollOffset))

// Reaction handles changes to the model, such as via the API
return tableModel && mstReaction(
() => tableModel?._horizontalScrollOffset,
_horizontalScrollOffset => {
updateScroll(_horizontalScrollOffset)
},
{ name: "CaseTable.updateHorizontalScroll" },
tableModel
)
}, [tableModel])

const { handleTableScroll, syncTableScroll } = useSyncScrolling()

Expand Down Expand Up @@ -100,7 +110,7 @@ export const CaseTable = observer(function CaseTable({ setNodeRef }: IProps) {

const collections = data.collections
const handleHorizontalScroll: React.UIEventHandler<HTMLDivElement> = () => {
tableModel?.setScrollLeft(contentRef.current?.scrollLeft ?? 0)
tableModel.setHorizontalScrollOffset(contentRef.current?.scrollLeft ?? 0)
}

return (
Expand Down
18 changes: 18 additions & 0 deletions v3/src/data-interactive/handlers/component-handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { getSnapshot } from "mobx-state-tree"
import { ICaseCardModel, isCaseCardModel } from "../../components/case-card/case-card-model"
import { kCaseCardIdPrefix } from "../../components/case-card/case-card-registration"
import { kCaseTableTileType } from "../../components/case-table/case-table-defs"
import { ICaseTableModel, isCaseTableModel } from "../../components/case-table/case-table-model"
import { kCaseTableIdPrefix } from "../../components/case-table/case-table-registration"
import { createOrShowTableOrCardForDataset } from "../../components/case-table-card-common/case-table-card-utils"
import { appState } from "../../models/app-state"
import { getSharedDataSets } from "../../models/shared/shared-data-utils"
import { toV3Id } from "../../utilities/codap-utils"
import { V2CaseCard } from "../data-interactive-component-types"
import { DIComponentInfo } from "../data-interactive-types"
Expand Down Expand Up @@ -70,4 +73,19 @@ describe("DataInteractive ComponentHandler", () => {
expect(isCaseCardModel(card2Tile.content)).toBe(true)
expect((card2Tile.content as ICaseCardModel).data?.id).toBe(dataset2.id)
})

it("update caseTable works", () => {
const { dataset } = setupTestDataset()
documentContent.createDataSet(getSnapshot(dataset))
const sharedDataSet = getSharedDataSets(documentContent)[0]
const component = createOrShowTableOrCardForDataset(sharedDataSet, kCaseTableTileType)!
const tableContent = component.content as ICaseTableModel

expect(handler.update?.({}, { horizontalScrollOffset: 100 }).success).toBe(false)
expect(handler.update?.({ component }).success).toBe(false)

expect(tableContent._horizontalScrollOffset).toBe(0)
expect(handler.update?.({ component }, { horizontalScrollOffset: 100 }).success).toBe(true)
expect(tableContent._horizontalScrollOffset).toBe(100)
})
})
27 changes: 23 additions & 4 deletions v3/src/data-interactive/handlers/component-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SetRequired } from "type-fest"
import { kCaseCardTileType } from "../../components/case-card/case-card-defs"
import { createOrShowTableOrCardForDataset } from "../../components/case-table-card-common/case-table-card-utils"
import { kCaseTableTileType } from "../../components/case-table/case-table-defs"
import { isCaseTableModel } from "../../components/case-table/case-table-model"
import { attrRoleToGraphPlace, GraphAttrRole } from "../../components/data-display/data-display-types"
import {
AttributeDescriptionsMapSnapshot, IAttributeDescriptionSnapshot, kDataConfigurationType
Expand Down Expand Up @@ -79,7 +80,7 @@ export const diComponentHandler: DIHandler = {
return document.applyModelChange(() => {
// Special case for caseCard and caseTable, which require a dataset
if ([kV2CaseCardType, kV2CaseTableType].includes(type)) {
const { dataContext } = values as V2CaseTable
const { dataContext, horizontalScrollOffset } = values as V2CaseTable
if (!dataContext) return dataContextRequiredResult
const sharedDataSet = getSharedDataSet(dataContext)
if (!sharedDataSet) return dataContextNotFoundResult
Expand All @@ -92,13 +93,15 @@ export const diComponentHandler: DIHandler = {
}

const title = _title ?? name
const options = { cannotClose, ...dimensions, title }
const content = type === kV2CaseTableType && horizontalScrollOffset != null
? { horizontalScrollOffset, type: kCaseTableTileType } : undefined
const options = { cannotClose, content, ...dimensions, title }

const tileType = type === kV2CaseCardType ? kCaseCardTileType : kCaseTableTileType
const tile = createOrShowTableOrCardForDataset(sharedDataSet, tileType, options)
if (!tile) return componentNotCreatedResult

// TODO Handle horizontalScrollOffset and isIndexHidden
// TODO Handle isIndexHidden
return {
success: true,
values: {
Expand Down Expand Up @@ -409,7 +412,23 @@ export const diComponentHandler: DIHandler = {
return { success: true }
},

update: diNotImplementedYet
update(resources: DIResources, values?: DIValues) {
const { component } = resources
if (!component) return componentNotFoundResult
const { content } = component

if (!values) return valuesRequiredResult

if (isCaseTableModel(content)) {
// TODO Handle isIndexHidden
const { horizontalScrollOffset } = values as V2CaseTable
if (horizontalScrollOffset != null) content.setHorizontalScrollOffset(horizontalScrollOffset)

return { success: true }
}

return errorResult(t("V3.DI.Error.unsupportedComponent", { vars: [content.type] }))
}
}

registerDIHandler("component", diComponentHandler)

0 comments on commit 383f1b5

Please sign in to comment.