diff --git a/src/containers/MainPlotContainer/selectors.ts b/src/containers/MainPlotContainer/selectors.ts index 00098c04..97304a4c 100644 --- a/src/containers/MainPlotContainer/selectors.ts +++ b/src/containers/MainPlotContainer/selectors.ts @@ -13,7 +13,11 @@ import { SELECTIONS_PLOT_NAME, THUMBNAIL_PATH, } from "../../constants"; -import { getCategoricalFeatureKeys, getMeasuredFeaturesDefs } from "../../state/metadata/selectors"; +import { + getCategoricalFeatureKeys, + getMeasuredFeaturesDefs, + getPerCellDataForPlot, +} from "../../state/metadata/selectors"; import { DataForPlot, FileInfo, @@ -46,6 +50,7 @@ import { getLineMovingAverageWindow, getConnectByFeature, getConnectByCategory, + getFiltersToExclude, } from "../../state/selection/selectors"; import { ColorForPlot, @@ -288,15 +293,25 @@ export function calculateLinePlotData( } const getAnnotationData = createSelector( - [getFilteredCellData, getClickedCellsFileInfo, getPlotByOnX, getPlotByOnY, getHoveredCardId], + [ + getPerCellDataForPlot, + getClickedCellsFileInfo, + getFiltersToExclude, + getPlotByOnX, + getPlotByOnY, + getGroupingCategoryNamesAsArray, + getHoveredCardId, + ], ( - filteredCellData: DataForPlot, + allCellData: DataForPlot, clickedCellsFileInfo: FileInfo[], + filtersToExclude: string[], xAxis, yAxis, + groupByCategoryNames, currentHoveredCellId ): AnnotationData[] => { - if (isEmpty(filteredCellData.values) || isEmpty(filteredCellData.labels)) { + if (isEmpty(allCellData.values) || isEmpty(allCellData.labels)) { return []; } const initAcc: AnnotationData[] = []; @@ -305,15 +320,23 @@ const getAnnotationData = createSelector( const fovID = data[FOV_ID_KEY] || ""; const thumbnailPath = data[THUMBNAIL_PATH] || ""; - const cellIds = filteredCellData.labels[ARRAY_OF_CELL_IDS_KEY]; - // FileInfo is typed with `index` as optional because it gets added to the - // data from the database. However, at this point, index will always be defined, but since - // typescript doesn't know that, we still have this backup to find it in the - // id array but that code should never be executed. + const cellIds = allCellData.labels[ARRAY_OF_CELL_IDS_KEY]; + // `FileInfo.index` is typed as optional because it is added from + // the database, but it will always be defined here. Note that + // `index` is the position in the full array of cell IDs, and does + // not account for filtering, so filtering needs to be done + // manually. const pointIndex = data.index !== undefined ? data.index : findIndex(cellIds, (id) => id === cellID); - const x = filteredCellData.values[xAxis][pointIndex]; - const y = filteredCellData.values[yAxis][pointIndex]; + const x = allCellData.values[xAxis][pointIndex] ?? null; + const y = allCellData.values[yAxis][pointIndex] ?? null; + + // Apply filtering + const groupName = groupByCategoryNames[pointIndex] ?? null; + if (filtersToExclude.includes(groupName)) { + return acc; + } + if (pointIndex >= 0 && x !== null && y !== null) { acc.push({ cellID, diff --git a/src/containers/MainPlotContainer/test/selectors.test.ts b/src/containers/MainPlotContainer/test/selectors.test.ts index 6f9d4aed..7dcf2299 100644 --- a/src/containers/MainPlotContainer/test/selectors.test.ts +++ b/src/containers/MainPlotContainer/test/selectors.test.ts @@ -51,8 +51,7 @@ describe("MainPlotContainer selectors", () => { const result: PlotlyAnnotation[] = getAnnotations(state); expect(result).to.have.lengthOf(2); }); - }); - describe("getAnnotations selector", () => { + it("it filters out non visible points", () => { const state: State = { ...newMockState, @@ -66,8 +65,26 @@ describe("MainPlotContainer selectors", () => { const result: PlotlyAnnotation[] = getAnnotations(state); expect(result).to.have.lengthOf(0); }); - }); - describe("getAnnotations selector", () => { + + it("it excludes filtered groups from annotations", () => { + const state: State = { + ...newMockState, + selection: { + ...newMockState.selection, + plotByOnX: "apical-proximity", + plotByOnY: "apical-proximity", + // 2 cells are selected, but the first (cell ID 1) will be + // excluded by filtering + selectedPoints: selectedCellFileInfo, + filterExclude: ["Paxillin"], + }, + }; + const result: PlotlyAnnotation[] = getAnnotations(state); + expect(result).to.have.lengthOf(1); + expect(result[0].pointIndex).to.equal(1); + expect(result[0].cellID).to.equal("2"); + }); + it("it still shows points whose values are zero", () => { const state: State = { ...newMockState, @@ -82,6 +99,7 @@ describe("MainPlotContainer selectors", () => { expect(result).to.have.lengthOf(2); }); }); + describe("getXDisplayName", () => { it("returns the displayName for a continuous feature", () => { const state: State = {