diff --git a/src/__test__/components/plots/styling/SelectData.test.jsx b/src/__test__/components/plots/styling/SelectData.test.jsx index baf26befac..fb670cb5d9 100644 --- a/src/__test__/components/plots/styling/SelectData.test.jsx +++ b/src/__test__/components/plots/styling/SelectData.test.jsx @@ -38,11 +38,11 @@ describe('Select Data', () => { }); // It has the first dropdown - expect(screen.getByText(/Select the Cell sets or Metadata that cells are grouped by \(determines the y-axis\)/)).toBeInTheDocument(); + expect(screen.getByText(/Select the cell sets or metadata that cells are grouped by/i)).toBeInTheDocument(); expect(screen.getByRole('combobox', { name: 'selectCellSets' })); // It has the second dropdown - expect(screen.getByText(/Select the Cell sets or Metadata to be shown as data:/)).toBeInTheDocument(); + expect(screen.getByText(/Select the cell sets or metadata to be shown as data/i)).toBeInTheDocument(); expect(screen.getByRole('combobox', { name: 'selectPoints' })); }); @@ -79,22 +79,19 @@ describe('Select Data', () => { expect(screen.getByText(/Error loading cell set/i)).toBeInTheDocument(); }); - it('Shows x or y axis properly according to config ', async () => { - await act(async () => { - render(selectDataFactory()); - }); - - // It shows y-axis by default - expect(screen.getByText(/determines the y-axis/)).toBeInTheDocument(); + it('Renders custom texts properly', async () => { + const firstSelectionText = 'First selection text'; + const secondSelectionText = 'Second selection text'; await act(async () => { - render( - selectDataFactory({ axisName: 'x' }), - ); + render(selectDataFactory({ + firstSelectionText, + secondSelectionText, + })); }); - // It shows x-axis if configured - expect(screen.getByText(/determines the x-axis/)).toBeInTheDocument(); + expect(screen.getByText(firstSelectionText)).toBeInTheDocument(); + expect(screen.getByText(secondSelectionText)).toBeInTheDocument(); }); it('Changing the first option triggers onUpdate ', async () => { diff --git a/src/__test__/data/cell_sets_with_scratchpad.json b/src/__test__/data/cell_sets_with_scratchpad.json index 5fe1f4eea7..c2d4887b55 100644 --- a/src/__test__/data/cell_sets_with_scratchpad.json +++ b/src/__test__/data/cell_sets_with_scratchpad.json @@ -84,6 +84,18 @@ 4, 5 ] + }, + { + "key": "d273c823-ffa0-4f10-823d-a9ee6edb793f", + "name": "Copied KO", + "color": "#8c564b", + "cellIds": [ + 6, + 7, + 8, + 9, + 10 + ] } ], "type": "cellSets" diff --git a/src/__test__/pages/experiments/[experimentId]/plots-and-tables/heatmap/index.test.jsx b/src/__test__/pages/experiments/[experimentId]/plots-and-tables/heatmap/index.test.jsx index 8e3c3a667b..fad606fc23 100644 --- a/src/__test__/pages/experiments/[experimentId]/plots-and-tables/heatmap/index.test.jsx +++ b/src/__test__/pages/experiments/[experimentId]/plots-and-tables/heatmap/index.test.jsx @@ -6,12 +6,15 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import { act } from 'react-dom/test-utils'; import { Provider } from 'react-redux'; +import { seekFromS3 } from 'utils/work/seekWorkResponse'; +import expressionDataFAKEGENE from '__test__/data/gene_expression_FAKEGENE.json'; +import cellSetsWithScratchpad from '__test__/data/cell_sets_with_scratchpad.json'; + import { loadBackendStatus } from 'redux/actions/backendStatus'; -import { loadPlotConfig, updatePlotConfig } from 'redux/actions/componentConfig'; +import { loadGeneExpression } from 'redux/actions/genes'; +import { updatePlotConfig } from 'redux/actions/componentConfig'; import { makeStore } from 'redux/store'; -import cellSetsWithScratchpad from '__test__/data/cell_sets_with_scratchpad.json'; - import preloadAll from 'jest-next-dynamic'; import fake from '__test__/test-utils/constants'; @@ -45,9 +48,34 @@ jest.mock('redux/actions/componentConfig', () => { }; }); +// Mock hash so we can control the ETag that is produced by hash.MD5 when fetching work requests +// EtagParams is the object that's passed to the function which generates ETag in fetchWork +jest.mock('object-hash', () => { + const objectHash = jest.requireActual('object-hash'); + const mockWorkResultETag = jest.requireActual('__test__/test-utils/mockWorkResultETag').default; + + const mockWorkRequestETag = (ETagParams) => { + if (ETagParams.body.name === 'ListGenes') return 'ListGenes'; + return `${ETagParams.body.nGenes}-marker-genes`; + }; + + const mockGeneExpressionETag = (ETagParams) => `${ETagParams.missingGenesBody.genes.join('-')}-expression`; + + return mockWorkResultETag(objectHash, mockWorkRequestETag, mockGeneExpressionETag); +}); + +jest.mock('utils/work/seekWorkResponse', () => ({ + __esModule: true, + dispatchWorkRequest: jest.fn(() => true), + seekFromS3: jest.fn(), +})); + +const mockWorkerResponses = { + 'FAKEGENE-expression': expressionDataFAKEGENE, +}; + const experimentId = fake.EXPERIMENT_ID; const plotUuid = 'heatmapPlotMain'; -const plotType = 'heatmap'; let storeState = null; const customAPIResponses = { @@ -93,7 +121,6 @@ describe('Heatmap plot', () => { // Set up state for backend status await storeState.dispatch(loadBackendStatus(experimentId)); - await storeState.dispatch(loadPlotConfig(experimentId, plotUuid, plotType)); }); it('Loads controls and elements', async () => { @@ -115,7 +142,7 @@ describe('Heatmap plot', () => { expect(screen.getByText(/Add some genes to this heatmap to get started/i)).toBeInTheDocument(); }); - it('Changing clusters updates the plot data', async () => { + it('It shows an informative text if there are cell sets to show', async () => { await renderHeatmapPage(storeState); // Open the Select Data panel @@ -126,9 +153,23 @@ describe('Heatmap plot', () => { userEvent.click(screen.getByText(/Custom cell sets/i), null, { skipPointerEventsCheck: true }); expect(updatePlotConfig).toHaveBeenCalled(); + expect(screen.getByText(/There is no data to show/i)).toBeInTheDocument(); + expect(screen.getByText(/Select another option from the 'Select data' menu/i)).toBeInTheDocument(); }); - it('It shows an informative text if there are cell sets to show', async () => { + it('Shows the plot if there are custom clusters to show', async () => { + const withScratchpadResponse = _.merge( + generateDefaultMockAPIResponses(experimentId), + customAPIResponses, + { + [`experiments/${experimentId}/cellSets`]: () => promiseResponse( + JSON.stringify(cellSetsWithScratchpad), + ), + }, + ); + + fetchMock.mockIf(/.*/, mockAPI(withScratchpadResponse)); + await renderHeatmapPage(storeState); // Open the Select Data panel @@ -139,10 +180,10 @@ describe('Heatmap plot', () => { userEvent.click(screen.getByText(/Custom cell sets/i), null, { skipPointerEventsCheck: true }); expect(updatePlotConfig).toHaveBeenCalled(); - expect(screen.getByText(/There are no custom cell sets to show/i)).toBeInTheDocument(); + expect(screen.queryByText(/There is no data to show/i)).toBeNull(); }); - it('Shows the plot if there are custom clusters to show', async () => { + it('Changing chosen cluster updates the plot data', async () => { const withScratchpadResponse = _.merge( generateDefaultMockAPIResponses(experimentId), customAPIResponses, @@ -154,18 +195,32 @@ describe('Heatmap plot', () => { ); fetchMock.mockIf(/.*/, mockAPI(withScratchpadResponse)); - await storeState.dispatch(loadPlotConfig(experimentId, plotUuid, plotType)); + + seekFromS3 + .mockReset() + .mockImplementationOnce(() => null) + .mockImplementationOnce((Etag) => mockWorkerResponses[Etag]); await renderHeatmapPage(storeState); + const genesToLoad = ['FAKEGENE']; + + await act(async () => { + await storeState.dispatch(loadGeneExpression(experimentId, genesToLoad, plotUuid)); + }); + // Open the Select Data panel userEvent.click(screen.getByText(/Select data/i)); - // Change from Louvain to Custom cell sets - userEvent.click(screen.getByText(/Louvain/i)); - userEvent.click(screen.getByText(/Custom cell sets/i), null, { skipPointerEventsCheck: true }); + // Change to display another cell set + userEvent.click(screen.getByText(/All/i)); + await act(async () => { + userEvent.click(screen.getByText(/Copied KO/i), null, { skipPointerEventsCheck: true }); + }); - expect(updatePlotConfig).toHaveBeenCalled(); - expect(screen.queryByText(/There are no custom cell sets to show/i)).toBeNull(); + // 2 calls: + // 1 for the selected gene (loadGeneExpression) + // 1 for changing the cell set + expect(updatePlotConfig).toHaveBeenCalledTimes(2); }); }); diff --git a/src/__test__/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.test.jsx b/src/__test__/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.test.jsx index 1ff85727e9..9a3db3fdb7 100644 --- a/src/__test__/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.test.jsx +++ b/src/__test__/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.test.jsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; import { mount } from 'enzyme'; -import { within, fireEvent } from '@testing-library/dom'; +import { within } from '@testing-library/dom'; import userEvent from '@testing-library/user-event'; import fetchMock, { enableFetchMocks } from 'jest-fetch-mock'; import _ from 'lodash'; @@ -106,7 +106,7 @@ const getCurrentGeneOrder = (component) => { }); newOrder.splice(0, 1); return newOrder; -} +}; const renderHeatmapPage = async (store) => { await act(async () => ( @@ -167,7 +167,7 @@ describe('Marker heatmap plot', () => { expect(screen.getByText(/Colours/i)).toBeInTheDocument(); expect(screen.getByText(/Legend/i)).toBeInTheDocument(); }); - + it('Loads the plot', async () => { await renderHeatmapPage(storeState); @@ -261,6 +261,24 @@ describe('Marker heatmap plot', () => { expect(_.isEqual(displayedGenesList, genesToLoad)).toEqual(false); }); + it('Shows an information text if a selected cell set does not contain enough number of samples', async () => { + await renderHeatmapPage(storeState); + + // Open the toggle + userEvent.click(screen.getByText(/Select data/i)); + + // Click custom cell sets + userEvent.click(screen.getByText(/louvain clusters/i)); + userEvent.click(screen.getByText(/Custom cell sets/i), null, { skipPointerEventsCheck: true }); + + // It shouldn't show the plot + expect(screen.queryByRole('graphics-document', { name: 'Marker heatmap' })).toBeNull(); + + // There is an error message + expect(screen.getByText(/There is no data to show/i)).toBeInTheDocument(); + expect(screen.getByText(/Select another option from the 'Select data' menu/i)).toBeInTheDocument(); + }); + it('Shows an error message if gene expression fails to load', async () => { seekFromS3 .mockReset() diff --git a/src/components/data-exploration/heatmap/HeatmapPlot.jsx b/src/components/data-exploration/heatmap/HeatmapPlot.jsx index 0164ba4f7f..f49b6268f5 100644 --- a/src/components/data-exploration/heatmap/HeatmapPlot.jsx +++ b/src/components/data-exploration/heatmap/HeatmapPlot.jsx @@ -24,6 +24,7 @@ import HeatmapTracksCellInfo from 'components/data-exploration/heatmap/HeatmapTr import getContainingCellSetsProperties from 'utils/cellSets/getContainingCellSetsProperties'; import useConditionalEffect from 'utils/customHooks/useConditionalEffect'; +import generateVitessceData from 'components/plots/helpers/heatmap/vitessce/generateVitessceData'; const COMPONENT_TYPE = 'interactiveHeatmap'; @@ -134,10 +135,10 @@ const HeatmapPlot = (props) => { return; } - const data = populateHeatmapData( - cellSets, heatmapSettings, expressionData, selectedGenes, true, true, - ); + const cellOrder = populateHeatmapData(cellSets, heatmapSettings, true); + heatmapSettings.selectedGenes = selectedGenes; + const data = generateVitessceData(cellOrder, heatmapSettings, expressionData, cellSets); setHeatmapData(data); }, [ selectedGenes, diff --git a/src/components/plots/helpers/heatmap/populateHeatmapData.js b/src/components/plots/helpers/heatmap/populateHeatmapData.js index 5a28f6c912..e39869f4e7 100644 --- a/src/components/plots/helpers/heatmap/populateHeatmapData.js +++ b/src/components/plots/helpers/heatmap/populateHeatmapData.js @@ -1,18 +1,16 @@ import _ from 'lodash'; -import generateVegaData from 'components/plots/helpers/heatmap/vega/generateVegaData'; -import generateVitessceData from 'components/plots/helpers/heatmap/vitessce/generateVitessceData'; - import SetOperations from 'utils/setOperations'; import { union } from 'utils/cellSetOperations'; -import { reversed } from 'utils/arrayUtils'; const populateHeatmapData = ( - cellSets, heatmapSettings, expression, - selectedGenes, downsampling = false, vitessce = false, + cellSets, heatmapSettings, + downsampling = false, ) => { const { hierarchy, properties, hidden } = cellSets; - const { selectedTracks, groupedTracks } = heatmapSettings; + const { + groupedTracks, selectedCellSet, selectedPoints, + } = heatmapSettings; const maxCells = 1000; const getCellsInSet = (cellSetName) => properties[cellSetName].cellIds; @@ -80,18 +78,21 @@ const populateHeatmapData = ( }; const getAllEnabledCellIds = () => { - // we want to avoid displaying elements which are not in a louvain cluster - // so initially consider as enabled only cells in louvain clusters - // See: https://biomage.atlassian.net/browse/BIOMAGE-809 - const selectedCellSet = heatmapSettings?.selectedCellSet ? heatmapSettings.selectedCellSet : 'louvain'; - const selectedClusters = hierarchy.find( - (clusters) => clusters.key === selectedCellSet, - ); - const cellIsInLouvainCluster = getCellsSetInGroup(selectedClusters); + let cellIds; + + if (selectedPoints === 'All') { + const selectedClusters = hierarchy.find( + (clusters) => clusters.key === selectedCellSet, + ); + cellIds = getCellsSetInGroup(selectedClusters); + } else { + const cellSetKey = selectedPoints.split('/')[1]; + cellIds = getCellsInSet(cellSetKey); + } // Remove cells from groups marked as hidden by the user in the UI. const hiddenCellIds = union(Array.from(hidden), properties); - const enabledCellIds = new Set([...cellIsInLouvainCluster] + const enabledCellIds = new Set([...cellIds] .filter((cellId) => !hiddenCellIds.has(cellId))); return enabledCellIds; @@ -141,23 +142,22 @@ const populateHeatmapData = ( return cellIds; }; - // For now, this is statically defined. In the future, these values are - // controlled from the settings panel in the heatmap. - - // Do downsampling and return cellIds with their order by groupings. - const cellOrder = generateCellOrder(groupedTracks); - const geneOrder = selectedGenes; - - if (!vitessce) { - return generateVegaData( - cellOrder, geneOrder, reversed(selectedTracks), - expression, heatmapSettings, cellSets, - ); - } - - return generateVitessceData( - cellOrder, geneOrder, selectedTracks, - expression, heatmapSettings, cellSets, + + let firstOptionCellIds = hierarchy.find( + (cellSet) => cellSet.key === selectedCellSet, + ).children.reduce( + (acc, cellSet) => [ + ...acc, + ...properties[cellSet.key].cellIds, + ], [], ); + + firstOptionCellIds = new Set(firstOptionCellIds); + + const filteredCellOrder = generateCellOrder(groupedTracks) + .filter((cellId) => firstOptionCellIds.has(cellId)); + + return filteredCellOrder; }; + export default populateHeatmapData; diff --git a/src/components/plots/helpers/heatmap/vega/generateVegaData.js b/src/components/plots/helpers/heatmap/vega/generateVegaData.js index 947a5fa002..1633d7e45d 100644 --- a/src/components/plots/helpers/heatmap/vega/generateVegaData.js +++ b/src/components/plots/helpers/heatmap/vega/generateVegaData.js @@ -1,31 +1,34 @@ import generateVegaGeneExpressionsData from 'components/plots/helpers/heatmap/vega/utils/generateVegaGeneExpressionsData'; import generateVegaHeatmapTracksData from 'components/plots/helpers/heatmap/vega/utils/generateVegaHeatmapTracksData'; +import { reversed } from 'utils/arrayUtils'; const generateVegaData = ( - cellOrder, geneOrder, trackOrder, - expression, heatmapSettings, cellSets, + cellOrder, expression, heatmapSettings, cellSets, ) => { + const { selectedGenes, selectedTracks, guardlines } = heatmapSettings; + const trackOrder = reversed(selectedTracks); + const data = { cellOrder, - geneOrder, + geneOrder: selectedGenes, trackOrder, geneExpressionsData: [], trackPositionData: [], trackGroupData: [], }; - const cells = new Set(cellOrder); - data.geneExpressionsData = generateVegaGeneExpressionsData( - cellOrder, geneOrder, expression, heatmapSettings, + cellOrder, selectedGenes, expression, heatmapSettings, ); - const trackData = trackOrder.map((rootNode) => generateVegaHeatmapTracksData( - cells, - rootNode, - cellSets, - heatmapSettings, - )); + const trackData = trackOrder.map( + (rootNode) => generateVegaHeatmapTracksData( + cellOrder, + rootNode, + cellSets, + guardlines, + ), + ); data.trackColorData = trackData.map((datum) => datum.trackColorData).flat(); data.trackGroupData = trackData.map((datum) => datum.groupData).flat(); diff --git a/src/components/plots/helpers/heatmap/vega/utils/generateVegaHeatmapTracksData.js b/src/components/plots/helpers/heatmap/vega/utils/generateVegaHeatmapTracksData.js index b6cf9b70a1..5e2c5991c7 100644 --- a/src/components/plots/helpers/heatmap/vega/utils/generateVegaHeatmapTracksData.js +++ b/src/components/plots/helpers/heatmap/vega/utils/generateVegaHeatmapTracksData.js @@ -1,4 +1,4 @@ -const generateVegaHeatmapTracksData = (cells, track, cellSets, heatmapSettings) => { +const generateVegaHeatmapTracksData = (cellOrder, track, cellSets, showGuardlines) => { const { hierarchy, properties } = cellSets; const getCellClusterFromCellId = (clusters, cellId) => { @@ -26,9 +26,9 @@ const generateVegaHeatmapTracksData = (cells, track, cellSets, heatmapSettings) // Iterate over each child node. const clusterSeparationLines = []; - if (heatmapSettings.guardLines) { - let currentCluster = getCellClusterFromCellId(childrenCellSets, cells[0]); - cells.forEach((cell) => { + if (showGuardlines) { + let currentCluster = getCellClusterFromCellId(childrenCellSets, cellOrder[0]); + cellOrder.forEach((cell) => { const isTheSameCluster = properties[currentCluster]?.cellIds?.has(cell); if (!isTheSameCluster) { currentCluster = getCellClusterFromCellId(childrenCellSets, cell); @@ -36,6 +36,7 @@ const generateVegaHeatmapTracksData = (cells, track, cellSets, heatmapSettings) } }); } + childrenCellSets.forEach(({ key }) => { const { cellIds, name, color } = properties[key]; @@ -47,9 +48,8 @@ const generateVegaHeatmapTracksData = (cells, track, cellSets, heatmapSettings) trackName: properties[track].name, }); - const intersectionSet = [cellIds, cells].reduce( - (acc, curr) => new Set([...acc].filter((x) => curr.has(x))), - ); + const cellOrderSet = new Set(cellOrder); + const intersectionSet = [...cellIds].filter((x) => cellOrderSet.has(x)); intersectionSet.forEach((cellId) => trackColorData.push({ cellId, diff --git a/src/components/plots/helpers/heatmap/vitessce/generateVitessceData.js b/src/components/plots/helpers/heatmap/vitessce/generateVitessceData.js index a4cc1f38f6..8530054f18 100644 --- a/src/components/plots/helpers/heatmap/vitessce/generateVitessceData.js +++ b/src/components/plots/helpers/heatmap/vitessce/generateVitessceData.js @@ -2,29 +2,29 @@ import generateVitessceHeatmapExpressionsMatrix from 'components/plots/helpers/h import generateVitessceHeatmapTracksData from 'components/plots/helpers/heatmap/vitessce/utils/generateVitessceHeatmapTracksData'; const generateVitessceData = ( - cellOrder, geneOrder, trackOrder, - expression, heatmapSettings, cellSets, + cellOrder, heatmapSettings, + expression, cellSets, ) => { - const cells = new Set(cellOrder); + const { selectedGenes, selectedTracks } = heatmapSettings; const trackColorData = generateVitessceHeatmapTracksData( - trackOrder, cellSets, cells, + selectedTracks, cellSets, cellOrder, ); // Expression matrix is an array // with shape [cell_1 gene_1, ..., cell_1 gene_n, cell_2 gene_1, ... ] const expressionMatrix = generateVitessceHeatmapExpressionsMatrix( cellOrder, - geneOrder, + selectedGenes, expression, ); - const metadataTracksLabels = heatmapSettings.selectedTracks + const metadataTracksLabels = selectedTracks .map((cellClassKey) => cellSets.properties[cellClassKey].name); return { expressionMatrix: { - cols: geneOrder, + cols: selectedGenes, rows: cellOrder.map((x) => `${x}`), matrix: Uint8Array.from(expressionMatrix), }, diff --git a/src/components/plots/styling/SelectData.jsx b/src/components/plots/styling/SelectData.jsx index b7d1aeef7a..fe6f7492c6 100644 --- a/src/components/plots/styling/SelectData.jsx +++ b/src/components/plots/styling/SelectData.jsx @@ -17,7 +17,7 @@ const { Option, OptGroup } = Select; const SelectData = (props) => { const { - onUpdate, config, cellSets, axisName, + onUpdate, config, cellSets, firstSelectionText, secondSelectionText, } = props; const { @@ -82,10 +82,9 @@ const SelectData = (props) => { return ( <> -
- {`Select the Cell sets or Metadata that cells are grouped by (determines the ${axisName}-axis)`} - : -
+

+ {firstSelectionText} +

-
- Select the Cell sets or Metadata to be shown as data: -
+

+ {secondSelectionText} +

- + @@ -194,6 +182,24 @@ const HeatmapPlot = ({ experimentId }) => { ); const renderPlot = () => { + if (isCellSetEmpty(config.selectedCellSet)) { + return ( +
+ +

+ There is no data to show. +

+

+ Select another option from the 'Select data' menu. +

+ + )} + /> +
+ ); + } + if (!config || loading.length > 0 || cellSets.loading) { return ( @@ -210,19 +216,6 @@ const HeatmapPlot = ({ experimentId }) => { ); } - if (isCellSetEmpty(config.selectedCellSet)) { - return ( - - There are no custom cell sets to show -
- Create some custom cell sets in Data Exploration - - )} - /> - ); - } - if (selectedGenes.length === 0) { return ( diff --git a/src/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.jsx b/src/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.jsx index da7d713bf6..66fdf5ce67 100644 --- a/src/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.jsx +++ b/src/pages/experiments/[experimentId]/plots-and-tables/marker-heatmap/index.jsx @@ -4,7 +4,6 @@ import { Collapse, Skeleton, Empty, - Select, Radio, Tabs, } from 'antd'; @@ -16,7 +15,6 @@ import pushNotificationMessage from 'utils/pushNotificationMessage'; import endUserMessages from 'utils/endUserMessages'; import { getCellSets, getCellSetsHierarchyByKeys, getCellSetsHierarchyByType } from 'redux/selectors'; -import getSelectOptions from 'utils/plots/getSelectOptions'; import HeatmapGroupBySettings from 'components/data-exploration/heatmap/HeatmapGroupBySettings'; import HeatmapMetadataTracksSettings from 'components/data-exploration/heatmap/HeatmapMetadataTrackSettings'; @@ -31,7 +29,9 @@ import { loadGeneExpression, loadMarkerGenes, loadPaginatedGeneProperties } from import { loadCellSets } from 'redux/actions/cellSets'; import PlatformError from 'components/PlatformError'; import Loader from 'components/Loader'; +import SelectData from 'components/plots/styling/SelectData'; import populateHeatmapData from 'components/plots/helpers/heatmap/populateHeatmapData'; +import generateVegaData from 'components/plots/helpers/heatmap/vega/generateVegaData'; import { plotNames } from 'utils/constants'; import GeneReorderTool from 'components/plots/GeneReorderTool'; @@ -56,8 +56,6 @@ const MarkerHeatmap = ({ experimentId }) => { const cellSets = useSelector(getCellSets()); const { hierarchy, properties } = cellSets; - const cellOptions = useSelector(getCellSetsHierarchyByType('cellSets')); - const selectedCellSetClassAvailable = useSelector( getCellSetsHierarchyByKeys([config?.selectedCellSet]), ).length; @@ -209,7 +207,9 @@ const MarkerHeatmap = ({ experimentId }) => { return; } - const data = populateHeatmapData(cellSets, config, expressionData, config.selectedGenes, true); + const cellOrder = populateHeatmapData(cellSets, config, true); + const data = generateVegaData(cellOrder, expressionData, config, cellSets); + const spec = generateSpec(config, 'Cluster ID', data, true); spec.description = 'Marker heatmap'; @@ -313,13 +313,6 @@ const MarkerHeatmap = ({ experimentId }) => { return (); } - const clustersForSelect = getSelectOptions(cellOptions); - - const changeClusters = (option) => { - const newValue = option.value.toLowerCase(); - updatePlotWithChanges({ selectedCellSet: newValue }); - }; - const renderExtraPanels = () => ( <> @@ -361,19 +354,13 @@ const MarkerHeatmap = ({ experimentId }) => { - -

Select cell sets to show in the heatmap:

-