diff --git a/js_modules/dagster-ui/packages/ui-core/src/partitions/AssetJobPartitionsView.tsx b/js_modules/dagster-ui/packages/ui-core/src/partitions/AssetJobPartitionsView.tsx index f19cbaea5a75d..a5b9b4dc99e37 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/partitions/AssetJobPartitionsView.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/partitions/AssetJobPartitionsView.tsx @@ -1,5 +1,5 @@ import {Box, Button, Subheading, useViewport} from '@dagster-io/ui-components'; -import {useEffect, useMemo, useState} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import {JobBackfillsTable} from './JobBackfillsTable'; import {CountBox, usePartitionDurations} from './OpJobPartitionsView'; @@ -24,237 +24,247 @@ import {DagsterTag} from '../runs/RunTag'; import {repoAddressToSelector} from '../workspace/repoAddressToSelector'; import {RepoAddress} from '../workspace/types'; -export const AssetJobPartitionsView = ({ - partitionSetName, - repoAddress, - pipelineName, -}: { - pipelineName: string; - partitionSetName: string; - repoAddress: RepoAddress; -}) => { - const {viewport, containerProps} = useViewport(); - const repositorySelector = repoAddressToSelector(repoAddress); - - const assetGraph = useAssetGraphData('*', { - pipelineSelector: { - pipelineName, - repositoryName: repoAddress.name, - repositoryLocationName: repoAddress.location, - }, - }); +export const AssetJobPartitionsView = React.memo( + ({ + partitionSetName, + repoAddress, + pipelineName, + }: { + pipelineName: string; + partitionSetName: string; + repoAddress: RepoAddress; + }) => { + const {viewport, containerProps} = useViewport(); + const repositorySelector = repoAddressToSelector(repoAddress); - const assetKeysWithPartitions = useMemo(() => { - return assetGraph.graphAssetKeys.filter((key) => { - return assetGraph.assetGraphData?.nodes[toGraphId(key)]?.definition.isPartitioned; + const assetGraph = useAssetGraphData('*', { + pipelineSelector: { + pipelineName, + repositoryName: repoAddress.name, + repositoryLocationName: repoAddress.location, + }, }); - }, [assetGraph]); - const assetHealth = usePartitionHealthData( - assetKeysWithPartitions.length - ? assetKeysWithPartitions - : assetGraph.graphAssetKeys[0] - ? [assetGraph.graphAssetKeys[0]] - : [], - ); + const assetKeysWithPartitions = useMemo(() => { + return assetGraph.graphAssetKeys.filter((key) => { + return assetGraph.assetGraphData?.nodes[toGraphId(key)]?.definition.isPartitioned; + }); + }, [assetGraph]); - const {total, missing, merged} = useMemo(() => { - const merged = mergedAssetHealth(assetHealth.filter((h) => h.dimensions.length > 0)); - const selection = merged.dimensions.map((d) => ({ - selectedKeys: d.partitionKeys, - selectedRanges: [allPartitionsRange(d)], - dimension: d, - })); - const missing = explodePartitionKeysInSelectionMatching(selection, (dIdxs) => - merged.stateForKeyIdx(dIdxs).includes(AssetPartitionStatus.MISSING), + const assetHealth = usePartitionHealthData( + assetKeysWithPartitions.length + ? assetKeysWithPartitions + : assetGraph.graphAssetKeys[0] + ? [assetGraph.graphAssetKeys[0]] + : [], ); - return { - merged, - total: keyCountInSelections(selection), - missing: missing.length, - }; - }, [assetHealth]); + const {total, missing, merged} = useMemo(() => { + const merged = mergedAssetHealth(assetHealth.filter((h) => h.dimensions.length > 0)); + const selection = merged.dimensions.map((d) => ({ + selectedKeys: d.partitionKeys, + selectedRanges: [allPartitionsRange(d)], + dimension: d, + })); + const missing = explodePartitionKeysInSelectionMatching(selection, (dIdxs) => + merged.stateForKeyIdx(dIdxs).includes(AssetPartitionStatus.MISSING), + ); - const [pageSize, setPageSize] = useState(60); - const [offset, setOffset] = useState(0); - const [showAssets, setShowAssets] = useState(false); + return { + merged, + total: keyCountInSelections(selection), + missing: missing.length, + }; + }, [assetHealth]); - useEffect(() => { - if (viewport.width) { - // magical numbers to approximate the size of the window, which is calculated in the step - // status component. This approximation is to make sure that the window does not jump as - // the pageSize gets recalculated - const approxPageSize = getVisibleItemCount(viewport.width - GRID_FLOATING_CONTAINER_WIDTH); - setPageSize(approxPageSize); - } - }, [viewport.width, setPageSize]); + const [pageSize, setPageSize] = useState(60); + const [offset, setOffset] = useState(0); + const [showAssets, setShowAssets] = useState(false); - let dimensionIdx = merged.dimensions.findIndex(isTimeseriesDimension); - if (dimensionIdx === -1) { - dimensionIdx = 0; // may as well show something - } + useEffect(() => { + if (viewport.width) { + // magical numbers to approximate the size of the window, which is calculated in the step + // status component. This approximation is to make sure that the window does not jump as + // the pageSize gets recalculated + const approxPageSize = getVisibleItemCount(viewport.width - GRID_FLOATING_CONTAINER_WIDTH); + setPageSize(approxPageSize); + } + }, [viewport.width, setPageSize]); - const dimension = merged.dimensions[dimensionIdx] ? merged.dimensions[dimensionIdx] : null; - const dimensionKeys = dimension?.partitionKeys || []; + const {selectedDimensionKeys, dimensionIdx, dimension, dimensionKeys} = useMemo(() => { + let dimensionIdx = merged.dimensions.findIndex(isTimeseriesDimension); + if (dimensionIdx === -1) { + dimensionIdx = 0; // may as well show something + } + const dimension = merged.dimensions[dimensionIdx] ? merged.dimensions[dimensionIdx] : null; + const dimensionKeys = dimension?.partitionKeys || []; + return { + selectedDimensionKeys: dimensionKeys.slice( + Math.max(0, dimensionKeys.length - 1 - offset - pageSize), + dimensionKeys.length - offset, + ), + dimensionIdx, + dimension, + dimensionKeys, + }; + }, [merged.dimensions, offset, pageSize]); - const selectedDimensionKeys = dimensionKeys.slice( - Math.max(0, dimensionKeys.length - 1 - offset - pageSize), - dimensionKeys.length - offset, - ); - return ( -
- - Status - - - g.node), skipAllTerm: true}} - preferredJobName={pipelineName} - /> + return ( +
+ + Status + + + g.node), skipAllTerm: true}} + preferredJobName={pipelineName} + /> + - - - - - - -
- + + + + +
+ { + const maxIdx = dimensionKeys.length - 1; + const selectedIdx = dimensionKeys.indexOf(partitionName); + const nextOffset = Math.min( + maxIdx, + Math.max(0, maxIdx - selectedIdx - 0.5 * pageSize), + ); + setOffset(nextOffset); + }} + /> +
+ {showAssets && dimension && ( + + + + )} +
+ {showAssets && ( + 1} + dimensionName={dimension ? dimension.name : null} + dimensionKeys={dimensionKeys} selected={selectedDimensionKeys} - selectionWindowSize={pageSize} - tooltipMessage="Click to view per-asset status" - onClick={(partitionName) => { - const maxIdx = dimensionKeys.length - 1; - const selectedIdx = dimensionKeys.indexOf(partitionName); - const nextOffset = Math.min( - maxIdx, - Math.max(0, maxIdx - selectedIdx - 0.5 * pageSize), - ); - setOffset(nextOffset); - }} + offset={offset} + pageSize={pageSize} /> -
- {showAssets && dimension && ( - - - )} -
- {showAssets && ( - 1} - dimensionName={dimension ? dimension.name : null} - dimensionKeys={dimensionKeys} - selected={selectedDimensionKeys} - offset={offset} - pageSize={pageSize} - /> - )} - - Backfill history - - - - -
- ); -}; + + Backfill history + + + + +
+ ); + }, +); -const AssetJobPartitionGraphs = ({ - repositorySelector, - dimensionKeys, - dimensionName, - selected, - pageSize, - partitionSetName, - multidimensional, - pipelineName, - offset, -}: { - repositorySelector: RepositorySelector; - pipelineName: string; - partitionSetName: string; - multidimensional: boolean; - dimensionName: string | null; - dimensionKeys: string[]; - selected: string[]; - pageSize: number; - offset: number; -}) => { - const partitions = usePartitionStepQuery({ - partitionSetName, - partitionTagName: multidimensional - ? `${DagsterTag.Partition}/${dimensionName}` - : DagsterTag.Partition, - partitionNames: dimensionKeys, +const AssetJobPartitionGraphs = React.memo( + ({ repositorySelector, + dimensionKeys, + dimensionName, + selected, pageSize, - runsFilter: [], - jobName: pipelineName, + partitionSetName, + multidimensional, + pipelineName, offset, - skipQuery: !dimensionName, - }); + }: { + repositorySelector: RepositorySelector; + pipelineName: string; + partitionSetName: string; + multidimensional: boolean; + dimensionName: string | null; + dimensionKeys: string[]; + selected: string[]; + pageSize: number; + offset: number; + }) => { + const partitions = usePartitionStepQuery({ + partitionSetName, + partitionTagName: multidimensional + ? `${DagsterTag.Partition}/${dimensionName}` + : DagsterTag.Partition, + partitionNames: dimensionKeys, + repositorySelector, + pageSize, + runsFilter: [], + jobName: pipelineName, + offset, + skipQuery: !dimensionName, + }); - const {stepDurationData, runDurationData} = usePartitionDurations(partitions); + const {stepDurationData, runDurationData} = usePartitionDurations(partitions); - return ( - <> - - Run duration - + return ( + <> + + Run duration + - - - - - Step durations - - - - - - ); -}; + + + + + Step durations + + + + + + ); + }, +);