diff --git a/end-to-end-test/local/screenshots/reference/shows_ascn_columns_for_study_where_some_samples_have_ascn_data_and_some_do_not_element_chrome_1600x1000.png b/end-to-end-test/local/screenshots/reference/shows_ascn_columns_for_study_where_some_samples_have_ascn_data_and_some_do_not_element_chrome_1600x1000.png index e736a5449b3..5876b5ff8cc 100644 Binary files a/end-to-end-test/local/screenshots/reference/shows_ascn_columns_for_study_where_some_samples_have_ascn_data_and_some_do_not_element_chrome_1600x1000.png and b/end-to-end-test/local/screenshots/reference/shows_ascn_columns_for_study_where_some_samples_have_ascn_data_and_some_do_not_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/download_tab_-_msk_impact_2017_with_alk_and_sos1_-_sos1_should_be_not_sequenced_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/download_tab_-_msk_impact_2017_with_alk_and_sos1_-_sos1_should_be_not_sequenced_element_chrome_1600x1000.png index 120dee6297d..8b3a0ee40d9 100644 Binary files a/end-to-end-test/remote/screenshots/reference/download_tab_-_msk_impact_2017_with_alk_and_sos1_-_sos1_should_be_not_sequenced_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/download_tab_-_msk_impact_2017_with_alk_and_sos1_-_sos1_should_be_not_sequenced_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_for_query_egfr:_mut=t790m_amp_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_for_query_egfr:_mut=t790m_amp_element_chrome_1600x1000.png index b6bb129a45f..dabdf50111a 100644 Binary files a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_for_query_egfr:_mut=t790m_amp_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_for_query_egfr:_mut=t790m_amp_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_cdkn2a_mdm2_and_merged_track_mdm4_tp53_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_cdkn2a_mdm2_and_merged_track_mdm4_tp53_element_chrome_1600x1000.png index c8373875715..3ec246bf8c2 100644 Binary files a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_cdkn2a_mdm2_and_merged_track_mdm4_tp53_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_cdkn2a_mdm2_and_merged_track_mdm4_tp53_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_overlapping_tp53_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_overlapping_tp53_element_chrome_1600x1000.png index 7ab74a79f8d..80e8482e00d 100644 Binary files a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_overlapping_tp53_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_overlapping_tp53_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_tp53_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_tp53_element_chrome_1600x1000.png index 9ef994928b2..6fb2a71d66f 100644 Binary files a/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_tp53_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/download_tab_-_nsclc_tcga_broad_2016_with_tp53_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_comparison_tab_clinical_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_comparison_tab_clinical_element_chrome_1600x1000.png index 90af9a71ae6..dcdf82430ff 100644 Binary files a/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_comparison_tab_clinical_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_comparison_tab_clinical_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutation_tab_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutation_tab_element_chrome_1600x1000.png index cb87f36038f..3b6ae93a59f 100644 Binary files a/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutation_tab_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutation_tab_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutex_tab_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutex_tab_element_chrome_1600x1000.png index dd1e97c6389..84b2c2726d9 100644 Binary files a/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutex_tab_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/excluding_unprofiled_samples_mutex_tab_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_several_groups_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_several_groups_element_chrome_1600x1000.png index 9167fcbd796..ec1829e8035 100644 Binary files a/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_several_groups_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_several_groups_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_two_groups_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_two_groups_element_chrome_1600x1000.png index 944d463da82..838ea58ed3e 100644 Binary files a/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_two_groups_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/group_comparison_page_microbiome_signature_tab_two_groups_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_several_groups_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_several_groups_element_chrome_1600x1000.png index b12ddd1d1f3..8049174dbec 100644 Binary files a/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_several_groups_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_several_groups_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_two_groups_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_two_groups_element_chrome_1600x1000.png index fe01f649843..ea034a2b438 100644 Binary files a/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_two_groups_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/group_comparison_page_mrna_enrichments_tab_two_groups_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/no_session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/no_session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png index 76015bf9bc9..5a921a0485a 100644 Binary files a/end-to-end-test/remote/screenshots/reference/no_session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/no_session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/no_session_mutation_tab_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/no_session_mutation_tab_element_chrome_1600x1000.png index 82286f297af..dd7448cf1ff 100644 Binary files a/end-to-end-test/remote/screenshots/reference/no_session_mutation_tab_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/no_session_mutation_tab_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/no_session_mutex_tab_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/no_session_mutex_tab_element_chrome_1600x1000.png index 15d970517e2..3e30f695a4d 100644 Binary files a/end-to-end-test/remote/screenshots/reference/no_session_mutex_tab_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/no_session_mutex_tab_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/patient_view_lgg_ucsf_2014_p04_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/patient_view_lgg_ucsf_2014_p04_element_chrome_1600x1000.png index e205967b4f1..72dd937a165 100644 Binary files a/end-to-end-test/remote/screenshots/reference/patient_view_lgg_ucsf_2014_p04_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/patient_view_lgg_ucsf_2014_p04_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/patient_view_with_0_mutations_msk_impact_2017_p-0000053-t01-im3_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/patient_view_with_0_mutations_msk_impact_2017_p-0000053-t01-im3_element_chrome_1600x1000.png index 81614da57bb..5e8b46efcb5 100644 Binary files a/end-to-end-test/remote/screenshots/reference/patient_view_with_0_mutations_msk_impact_2017_p-0000053-t01-im3_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/patient_view_with_0_mutations_msk_impact_2017_p-0000053-t01-im3_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/pvge_heatmap_with_two_mutations_selected_from_before_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/pvge_heatmap_with_two_mutations_selected_from_before_element_chrome_1600x1000.png index 5991cefe966..5a80dba68a4 100644 Binary files a/end-to-end-test/remote/screenshots/reference/pvge_heatmap_with_two_mutations_selected_from_before_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/pvge_heatmap_with_two_mutations_selected_from_before_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/pvge_hover_a_mutation_with_heatmap_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/pvge_hover_a_mutation_with_heatmap_element_chrome_1600x1000.png index c838fec07ee..129cfdfd36a 100644 Binary files a/end-to-end-test/remote/screenshots/reference/pvge_hover_a_mutation_with_heatmap_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/pvge_hover_a_mutation_with_heatmap_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/pvge_initial_view_with_line_chart_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/pvge_initial_view_with_line_chart_element_chrome_1600x1000.png index cff9a55a53f..f334b927b97 100644 Binary files a/end-to-end-test/remote/screenshots/reference/pvge_initial_view_with_line_chart_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/pvge_initial_view_with_line_chart_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_heatmap_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_heatmap_element_chrome_1600x1000.png index e5b797302dd..eacd77d40c0 100644 Binary files a/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_heatmap_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_heatmap_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_line_chart_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_line_chart_element_chrome_1600x1000.png index 719e4799cc1..007435d7a71 100644 Binary files a/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_line_chart_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/pvge_one_mutation_selected_with_line_chart_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/pvge_show_timeline_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/pvge_show_timeline_element_chrome_1600x1000.png index 7a052003482..87d77cba047 100644 Binary files a/end-to-end-test/remote/screenshots/reference/pvge_show_timeline_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/pvge_show_timeline_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_bar_chart_chi_squared_test_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_bar_chart_chi_squared_test_element_chrome_1600x1000.png index e9a288101c4..66d613ef4d5 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_bar_chart_chi_squared_test_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_bar_chart_chi_squared_test_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_log_scale__kruskal_wallis_test_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_log_scale__kruskal_wallis_test_element_chrome_1600x1000.png index cb618c998df..b4e6641c412 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_log_scale__kruskal_wallis_test_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_log_scale__kruskal_wallis_test_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_percentage_stacked_bar_chart_exclude_overlapping_samples_chi_squared_test_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_percentage_stacked_bar_chart_exclude_overlapping_samples_chi_squared_test_element_chrome_1600x1000.png index 60fdf63145f..2d36e1d7eec 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_percentage_stacked_bar_chart_exclude_overlapping_samples_chi_squared_test_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_percentage_stacked_bar_chart_exclude_overlapping_samples_chi_squared_test_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_chi_squared_test_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_chi_squared_test_element_chrome_1600x1000.png index 23970415d57..07c3d5f3669 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_chi_squared_test_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_chi_squared_test_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_horizontal_bars_chi_squared_test_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_horizontal_bars_chi_squared_test_element_chrome_1600x1000.png index bb4d22a5da4..2b4d0046bf4 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_horizontal_bars_chi_squared_test_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_horizontal_bars_chi_squared_test_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_swaped_axes_chi_squared_test_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_swaped_axes_chi_squared_test_element_chrome_1600x1000.png index 3007c33d7be..0c63d545373 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_swaped_axes_chi_squared_test_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_stacked_bar_chart_swaped_axes_chi_squared_test_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_swaped_axes_kruskal_wallis_test_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_swaped_axes_kruskal_wallis_test_element_chrome_1600x1000.png index 62d5dee88dc..e8254165d13 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_swaped_axes_kruskal_wallis_test_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_clinical_tab_swaped_axes_kruskal_wallis_test_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_protein_enrichments_tab_two_groups_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_protein_enrichments_tab_two_groups_element_chrome_1600x1000.png index a27c164597e..38252a624aa 100644 Binary files a/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_protein_enrichments_tab_two_groups_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/results_view_comparison_tab_protein_enrichments_tab_two_groups_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png index 76015bf9bc9..5a921a0485a 100644 Binary files a/end-to-end-test/remote/screenshots/reference/session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/session_comparison_tab_mutation_enrichments_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/session_mutation_tab_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/session_mutation_tab_element_chrome_1600x1000.png index 82286f297af..dd7448cf1ff 100644 Binary files a/end-to-end-test/remote/screenshots/reference/session_mutation_tab_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/session_mutation_tab_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/session_mutex_tab_element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/session_mutex_tab_element_chrome_1600x1000.png index 15d970517e2..3e30f695a4d 100644 Binary files a/end-to-end-test/remote/screenshots/reference/session_mutex_tab_element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/session_mutex_tab_element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/screenshots/reference/when_quickly_adding_charts,_each_chart_should_get_proper_data._element_chrome_1600x1000.png b/end-to-end-test/remote/screenshots/reference/when_quickly_adding_charts,_each_chart_should_get_proper_data._element_chrome_1600x1000.png index f4f9a2772be..2fde0c43fe4 100644 Binary files a/end-to-end-test/remote/screenshots/reference/when_quickly_adding_charts,_each_chart_should_get_proper_data._element_chrome_1600x1000.png and b/end-to-end-test/remote/screenshots/reference/when_quickly_adding_charts,_each_chart_should_get_proper_data._element_chrome_1600x1000.png differ diff --git a/end-to-end-test/remote/specs/core/mutationTable.spec.js b/end-to-end-test/remote/specs/core/mutationTable.spec.js index 7fa93adeed2..d7592f639c6 100644 --- a/end-to-end-test/remote/specs/core/mutationTable.spec.js +++ b/end-to-end-test/remote/specs/core/mutationTable.spec.js @@ -36,8 +36,12 @@ describe('Mutation Table', function() { // click on column button browser.click('button*=Columns'); - // scroll down to activated "exon" selection - browser.scroll(1000, 1000); + // scroll down to activated "Exon" selection + browser.execute( + 'document.getElementsByClassName("ReactVirtualized__Grid")[0].scroll(1000, 1000)' + ); + // wait for exon checkbox to appear + browser.pause(2000); // click "exon" browser.click('//*[text()="Exon"]'); // check if three exact matches for 6 appear @@ -114,13 +118,15 @@ describe('Mutation Table', function() { 60000 ); // show the gnomad column - browser.scroll(1000, 0); + browser.scroll(0, 1000); // click on column button browser.click('button*=Columns'); // scroll down to activated "GNOMAD" selection - browser.scroll(1000, 1000); - // wait for gnomad checkbox appear - browser.waitForVisible('[data-id=gnomAD]', 60000); + browser.execute( + 'document.getElementsByClassName("ReactVirtualized__Grid")[0].scroll(1000, 1000)' + ); + // wait for gnomad checkbox to appear + browser.pause(2000); // click "GNOMAD" browser.click('//*[text()="gnomAD"]'); // find frequency @@ -172,7 +178,11 @@ describe('Mutation Table', function() { // click on column button browser.click('button*=Columns'); // scroll down to activated "ClinVar" selection - browser.scroll(1000, 1000); + browser.execute( + 'document.getElementsByClassName("ReactVirtualized__Grid")[0].scroll(1000, 1000)' + ); + // wait for clinvar checkbox to appear + browser.pause(2000); // click "clinvar" browser.click('//*[text()="ClinVar"]'); let res; @@ -206,7 +216,11 @@ describe('Mutation Table', function() { // click on column button browser.click('button*=Columns'); // scroll down to activated "dbSNP" selection - browser.scroll(1000, 1000); + browser.execute( + 'document.getElementsByClassName("ReactVirtualized__Grid")[0].scroll(1000, 1000)' + ); + // wait for dbSNP checkbox to appear + browser.pause(2000); // click "dbSNP" browser.click('//*[text()="dbSNP"]'); let res; diff --git a/src/pages/resultsView/ResultsViewPageStore.ts b/src/pages/resultsView/ResultsViewPageStore.ts index e9cc7c89f09..f145e42e029 100644 --- a/src/pages/resultsView/ResultsViewPageStore.ts +++ b/src/pages/resultsView/ResultsViewPageStore.ts @@ -69,6 +69,7 @@ import GenomeNexusCache from 'shared/cache/GenomeNexusCache'; import GenomeNexusMutationAssessorCache from 'shared/cache/GenomeNexusMutationAssessorCache'; import CancerTypeCache from 'shared/cache/CancerTypeCache'; import MutationCountCache from 'shared/cache/MutationCountCache'; +import ClinicalAttributeCache from 'shared/cache/ClinicalAttributeCache'; import DiscreteCNACache from 'shared/cache/DiscreteCNACache'; import PdbHeaderCache from 'shared/cache/PdbHeaderCache'; import { @@ -1143,6 +1144,26 @@ export class ResultsViewPageStore { }, }); + // TODO: Should include all clinical attributes, not just server attributes + readonly mutationsTabClinicalAttributes = remoteData({ + await: () => [this.studyIds], + invoke: async () => { + const clinicalAttributes = await client.fetchClinicalAttributesUsingPOST( + { + studyIds: this.studyIds.result!, + } + ); + const excludeList = ['CANCER_TYPE_DETAILED', 'MUTATION_COUNT']; + + return _.uniqBy( + clinicalAttributes.filter( + x => !excludeList.includes(x.clinicalAttributeId) + ), + x => x.clinicalAttributeId + ); + }, + }); + readonly clinicalAttributeIdToClinicalAttribute = remoteData({ await: () => [this.clinicalAttributes], invoke: () => @@ -1255,6 +1276,22 @@ export class ResultsViewPageStore { }, }); + readonly clinicalAttributeIdToAvailableFrequency = remoteData({ + await: () => [ + this.clinicalAttributeIdToAvailableSampleCount, + this.samples, + ], + invoke: () => { + const numSamples = this.samples.result!.length; + return Promise.resolve( + _.mapValues( + this.clinicalAttributeIdToAvailableSampleCount.result!, + count => (100 * count) / numSamples + ) + ); + }, + }); + readonly cnSegments = remoteData( { await: () => [this.filteredSamples], @@ -3428,6 +3465,7 @@ export class ResultsViewPageStore { this.oncoKbCancerGenes, () => this.mutationsByGene.result![gene.hugoGeneSymbol] || [], () => this.mutationCountCache, + () => this.clinicalAttributeCache, () => this.genomeNexusCache, () => this.genomeNexusMutationAssessorCache, () => this.discreteCNACache, @@ -3442,6 +3480,8 @@ export class ResultsViewPageStore { this.uniqueSampleKeyToTumorType.result!, this.generateGenomeNexusHgvsgUrl, this.clinicalDataGroupedBySampleMap, + this.mutationsTabClinicalAttributes, + this.clinicalAttributeIdToAvailableFrequency, this.genomeNexusClient, this.genomeNexusInternalClient, () => this.urlWrapper.query.mutations_transcript_id @@ -5325,6 +5365,10 @@ export class ResultsViewPageStore { return new MutationCountCache(); } + @cached @computed get clinicalAttributeCache() { + return new ClinicalAttributeCache(); + } + @cached @computed get pdbHeaderCache() { return new PdbHeaderCache(); } diff --git a/src/pages/resultsView/mutation/AddColumns.tsx b/src/pages/resultsView/mutation/AddColumns.tsx new file mode 100644 index 00000000000..9f5fbcabf6e --- /dev/null +++ b/src/pages/resultsView/mutation/AddColumns.tsx @@ -0,0 +1,254 @@ +import * as React from 'react'; +import * as _ from 'lodash'; +import MobxPromise from 'mobxpromise'; +import { observer } from 'mobx-react'; +import { action, computed, makeObservable, observable } from 'mobx'; +import { Checkbox } from 'react-bootstrap'; +import { remoteData, getTextWidth } from 'cbioportal-frontend-commons'; +import { ClinicalAttribute } from 'cbioportal-ts-api-client'; +import { IColumnVisibilityControlsProps } from 'shared/components/columnVisibilityControls/ColumnVisibilityControls'; +import { MSKTab, MSKTabs } from 'shared/components/MSKTabs/MSKTabs'; +import CustomDropdown from 'shared/components/oncoprint/controls/CustomDropdown'; +import AddChartByType from '../../studyView/addChartButton/addChartByType/AddChartByType'; +import { ChartDataCountSet } from '../../studyView/StudyViewUtils'; + +export interface IAddColumnsProps extends IColumnVisibilityControlsProps { + clinicalAttributes: ClinicalAttribute[]; + clinicalAttributeIdToAvailableFrequency: MobxPromise<{ + [clinicalAttributeId: string]: number; + }>; +} + +enum Tab { + MUTATIONS = 'Mutations', + CLINICAL = 'Clinical', +} + +type Option = { + key: string; + label: string; + selected: boolean; +}; + +const MIN_DROPDOWN_WIDTH = 400; +const CONTAINER_PADDING_WIDTH = 20; +const TAB_PADDING_WIDTH = 14; +const COUNT_PADDING_WIDTH = 17; +@observer +export default class AddColumns extends React.Component { + @observable tabId: Tab = Tab.MUTATIONS; + + constructor(props: IAddColumnsProps) { + super(props); + makeObservable(this); + } + + @action.bound + private updateTabId(newId: Tab) { + this.tabId = newId; + } + + @action.bound + private addAll(ids: string[], options: Option[]) { + if (this.props.onColumnToggled && options.length > 0) { + for (let option of options) { + if (!option.selected && ids.includes(option.key)) { + this.props.onColumnToggled(option.key); + } + } + } + } + + @action.bound + private clearAll(ids: string[], options: Option[]) { + if (this.props.onColumnToggled && options.length > 0) { + for (let option of options) { + if (option.selected && ids.includes(option.key)) { + this.props.onColumnToggled(option.key); + } + } + } + } + + @action.bound + private toggle(id: string) { + if (this.props.onColumnToggled) { + this.props.onColumnToggled(id); + } + } + + @computed get clinicalAttributeIds(): Set { + let ids: Set = new Set(); + this.props.clinicalAttributes.forEach(x => + ids.add(x.clinicalAttributeId) + ); + return ids + .add('CANCER_STUDY') + .add('CANCER_TYPE_DETAILED') + .add('MUTATION_COUNT'); + } + + @computed get mutationsOptions(): Option[] { + if (!this.props.columnVisibility) { + return []; + } + + return this.props.columnVisibility + .filter(col => !this.clinicalAttributeIds.has(col.id)) + .map(col => ({ + key: col.id, + label: col.name, + selected: col.visible, + })); + } + + @computed get mutationsTabContent() { + return ( + + this.addAll(ids, this.mutationsOptions) + } + onClearAll={(ids: string[]) => + this.clearAll(ids, this.mutationsOptions) + } + onToggleOption={this.toggle} + optionsGivenInSortedOrder={true} + width={this.dropdownWidth} + /> + ); + } + + @computed get clinicalOptions(): Option[] { + if (!this.props.columnVisibility) { + return []; + } + + return this.props.columnVisibility + .filter(col => this.clinicalAttributeIds.has(col.id)) + .map(col => ({ + key: col.id, + label: col.name, + selected: col.visible, + })); + } + + @computed get clinicalTabContent() { + return ( + + this.addAll(ids, this.clinicalOptions) + } + onClearAll={(ids: string[]) => + this.clearAll(ids, this.clinicalOptions) + } + onToggleOption={this.toggle} + optionsGivenInSortedOrder={false} + width={this.dropdownWidth} + /> + ); + } + + @computed get mutationsTabText() { + return ( + + {Tab.MUTATIONS} + + + {this.mutationsOptions.length} + + + + ); + } + + @computed get clinicalTabText() { + return ( + + {Tab.CLINICAL} + + + {this.clinicalOptions.length} + + + + ); + } + + private getTextPixel(text: string, fontSize: string) { + return Math.floor(getTextWidth(text, 'Helvetica Neue', fontSize)); + } + + @computed get dropdownWidth() { + let width = 2 * CONTAINER_PADDING_WIDTH; + const HEADER_FONT_SIZE = '14px'; + const COUNT_FONT_SIZE = '11px'; + + const textWidth = + this.getTextPixel(Tab.CLINICAL, HEADER_FONT_SIZE) + + TAB_PADDING_WIDTH; + const countTextWidth = + this.getTextPixel( + this.clinicalOptions.length.toString(), + COUNT_FONT_SIZE + ) + COUNT_PADDING_WIDTH; + width += textWidth + countTextWidth; + + return Math.max(width, MIN_DROPDOWN_WIDTH); + } + + render() { + const haveMutationsOptions = this.mutationsOptions.length > 0; + const haveClinicalOptions = this.clinicalOptions.length > 0; + + return ( +
+ +
+ {(haveMutationsOptions || haveClinicalOptions) && ( + + {haveMutationsOptions && ( + + {this.mutationsTabContent} + + )} + {haveClinicalOptions && ( + + {this.clinicalTabContent} + + )} + + )} +
+
+
+ ); + } +} diff --git a/src/pages/resultsView/mutation/Mutations.tsx b/src/pages/resultsView/mutation/Mutations.tsx index 4bcf7d3f9e8..5b127a24d28 100644 --- a/src/pages/resultsView/mutation/Mutations.tsx +++ b/src/pages/resultsView/mutation/Mutations.tsx @@ -226,6 +226,9 @@ export default class Mutations extends React.Component< pubMedCache={this.props.store.pubMedCache} cancerTypeCache={this.props.store.cancerTypeCache} mutationCountCache={this.props.store.mutationCountCache} + clinicalAttributeCache={ + this.props.store.clinicalAttributeCache + } genomeNexusCache={this.props.store.genomeNexusCache} genomeNexusMutationAssessorCache={ this.props.store.genomeNexusMutationAssessorCache diff --git a/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx b/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx index 02090041071..66e13da4ad6 100644 --- a/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx +++ b/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx @@ -9,6 +9,7 @@ import { EnsemblTranscript } from 'genome-nexus-ts-api-client'; import DiscreteCNACache from 'shared/cache/DiscreteCNACache'; import CancerTypeCache from 'shared/cache/CancerTypeCache'; import MutationCountCache from 'shared/cache/MutationCountCache'; +import ClinicalAttributeCache from 'shared/cache/ClinicalAttributeCache'; import { IMutationMapperProps, @@ -28,6 +29,7 @@ export interface IResultsViewMutationMapperProps extends IMutationMapperProps { discreteCNACache?: DiscreteCNACache; cancerTypeCache?: CancerTypeCache; mutationCountCache?: MutationCountCache; + clinicalAttributeCache?: ClinicalAttributeCache; existsSomeMutationWithAscnProperty: { [property: string]: boolean }; userEmailAddress: string; onClickSettingMenu?: (visible: boolean) => void; @@ -88,7 +90,8 @@ export default class ResultsViewMutationMapper extends MutationMapper< this.props.store.mutationData, this.props.store.indexedVariantAnnotations, this.props.store.activeTranscript, - this.props.store.clinicalDataGroupedBySampleMap + this.props.store.clinicalDataGroupedBySampleMap, + this.props.store.mutationsTabClinicalAttributes ) === 'pending' ); } @@ -122,6 +125,7 @@ export default class ResultsViewMutationMapper extends MutationMapper< } pubMedCache={this.props.pubMedCache} mutationCountCache={this.props.mutationCountCache} + clinicalAttributeCache={this.props.clinicalAttributeCache} genomeNexusCache={this.props.genomeNexusCache} genomeNexusMutationAssessorCache={ this.props.genomeNexusMutationAssessorCache @@ -164,6 +168,12 @@ export default class ResultsViewMutationMapper extends MutationMapper< existsSomeMutationWithAscnProperty={ this.props.existsSomeMutationWithAscnProperty } + mutationsTabClinicalAttributes={ + this.props.store.mutationsTabClinicalAttributes + } + clinicalAttributeIdToAvailableFrequency={ + this.props.store.clinicalAttributeIdToAvailableFrequency + } /> ); } diff --git a/src/pages/resultsView/mutation/ResultsViewMutationMapperStore.ts b/src/pages/resultsView/mutation/ResultsViewMutationMapperStore.ts index e3b010cc22e..801b2970c04 100644 --- a/src/pages/resultsView/mutation/ResultsViewMutationMapperStore.ts +++ b/src/pages/resultsView/mutation/ResultsViewMutationMapperStore.ts @@ -2,6 +2,7 @@ import { IHotspotIndex } from 'cbioportal-utils'; import { Mutation, Gene, + ClinicalAttribute, ClinicalData, CancerStudy, MolecularProfile, @@ -17,6 +18,7 @@ import { import { labelMobxPromises, MobxPromise, cached } from 'mobxpromise'; import { fetchCosmicData } from 'shared/lib/StoreUtils'; import MutationCountCache from 'shared/cache/MutationCountCache'; +import ClinicalAttributeCache from 'shared/cache/ClinicalAttributeCache'; import DiscreteCNACache from 'shared/cache/DiscreteCNACache'; import GenomeNexusCache from 'shared/cache/GenomeNexusCache'; import GenomeNexusMutationAssessorCache from 'shared/cache/GenomeNexusMutationAssessorCache'; @@ -43,6 +45,7 @@ export default class ResultsViewMutationMapperStore extends MutationMapperStore // and we will react when it changes to a new object. getMutations: () => Mutation[], private getMutationCountCache: () => MutationCountCache, + private getClinicalAttributeCache: () => ClinicalAttributeCache, private getGenomeNexusCache: () => GenomeNexusCache, private getGenomeNexusMutationAssessorCache: () => GenomeNexusMutationAssessorCache, private getDiscreteCNACache: () => DiscreteCNACache, @@ -69,6 +72,10 @@ export default class ResultsViewMutationMapperStore extends MutationMapperStore public clinicalDataGroupedBySampleMap: MobxPromise<{ [sampleId: string]: ClinicalData[]; }>, + public mutationsTabClinicalAttributes: MobxPromise, + public clinicalAttributeIdToAvailableFrequency: MobxPromise<{ + [clinicalAttributeId: string]: number; + }>, protected genomenexusClient?: GenomeNexusAPI, protected genomenexusInternalClient?: GenomeNexusAPIInternal, public getTranscriptId?: () => string @@ -99,10 +106,12 @@ export default class ResultsViewMutationMapperStore extends MutationMapperStore protected getDownloadDataFetcher(): MutationTableDownloadDataFetcher { return new MutationTableDownloadDataFetcher( this.mutationData, + this.mutationsTabClinicalAttributes, this.studyToMolecularProfileDiscrete, this.getGenomeNexusCache, this.getGenomeNexusMutationAssessorCache, this.getMutationCountCache, + this.getClinicalAttributeCache, this.getDiscreteCNACache ); } diff --git a/src/pages/resultsView/mutation/ResultsViewMutationTable.tsx b/src/pages/resultsView/mutation/ResultsViewMutationTable.tsx index 62ee8f7352b..6853ac3f116 100644 --- a/src/pages/resultsView/mutation/ResultsViewMutationTable.tsx +++ b/src/pages/resultsView/mutation/ResultsViewMutationTable.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; +import * as _ from 'lodash'; import { observer } from 'mobx-react'; +import MobxPromise from 'mobxpromise'; import { IMutationTableProps, MutationTableColumnType, @@ -7,15 +9,24 @@ import { } from 'shared/components/mutationTable/MutationTable'; import CancerTypeColumnFormatter from 'shared/components/mutationTable/column/CancerTypeColumnFormatter'; import TumorAlleleFreqColumnFormatter from 'shared/components/mutationTable/column/TumorAlleleFreqColumnFormatter'; -import { Mutation, ClinicalData } from 'cbioportal-ts-api-client'; +import { Mutation, ClinicalAttribute } from 'cbioportal-ts-api-client'; import ExonColumnFormatter from 'shared/components/mutationTable/column/ExonColumnFormatter'; +import ClinicalAttributeColumnFormatter from 'shared/components/mutationTable/column/ClinicalAttributeColumnFormatter'; import { ASCNAttributes } from 'shared/enums/ASCNEnums'; +import { IColumnVisibilityControlsProps } from 'shared/components/columnVisibilityControls/ColumnVisibilityControls'; +import AddColumns from './AddColumns'; +import ClinicalAttributeCache from 'shared/cache/ClinicalAttributeCache'; export interface IResultsViewMutationTableProps extends IMutationTableProps { // add results view specific props here if needed totalNumberOfExons?: string; isCanonicalTranscript: boolean | undefined; existsSomeMutationWithAscnProperty: { [property: string]: boolean }; + mutationsTabClinicalAttributes: MobxPromise; + clinicalAttributeIdToAvailableFrequency: MobxPromise<{ + [clinicalAttributeId: string]: number; + }>; + clinicalAttributeCache?: ClinicalAttributeCache; } // export default class ResultsViewMutationTable extends MutationTable< @@ -53,7 +64,7 @@ export default class ResultsViewMutationTable extends MutationTable< MutationTableColumnType.COSMIC, MutationTableColumnType.TUMOR_ALLELE_FREQ, MutationTableColumnType.NORMAL_ALLELE_FREQ, - MutationTableColumnType.CANCER_TYPE, + MutationTableColumnType.CANCER_TYPE_DETAILED, MutationTableColumnType.NUM_MUTATIONS, MutationTableColumnType.EXON, MutationTableColumnType.HGVSC, @@ -62,8 +73,30 @@ export default class ResultsViewMutationTable extends MutationTable< MutationTableColumnType.DBSNP, MutationTableColumnType.SIGNAL, ], + columnVisibilityProps: {}, }; + constructor(props: IResultsViewMutationTableProps) { + super(props); + this.props.columnVisibilityProps!.customDropdown = ( + columnVisibilityControlsProps: IColumnVisibilityControlsProps + ) => ( + + ); + } + componentWillUpdate(nextProps: IResultsViewMutationTableProps) { this._columns[MutationTableColumnType.STUDY].visible = !!( nextProps.studyIdToStudy && @@ -74,9 +107,57 @@ export default class ResultsViewMutationTable extends MutationTable< protected generateColumns() { super.generateColumns(); + // generate clinical attribute columns + let clinicalAttributes = this.props.mutationsTabClinicalAttributes + .result!; + for (let i = 0; i < clinicalAttributes.length; i++) { + const attributeId = clinicalAttributes[i].clinicalAttributeId; + if ( + this.props.columns && + !this.props.columns.includes(attributeId) + ) { + this.props.columns.push(attributeId); + } + + this._columns[attributeId] = { + id: attributeId, + name: clinicalAttributes[i].displayName, + render: ClinicalAttributeColumnFormatter.makeRenderFunction( + clinicalAttributes[i], + this.props.clinicalAttributeCache + ), + download: (d: Mutation[]) => + ClinicalAttributeColumnFormatter.getTextValue( + d, + clinicalAttributes[i], + this.props.clinicalAttributeCache + ), + sortBy: (d: Mutation[]) => + ClinicalAttributeColumnFormatter.sortBy( + d, + clinicalAttributes[i], + this.props.clinicalAttributeCache + ), + filter: ( + d: Mutation[], + filterString: string, + filterStringUpper: string + ) => + ClinicalAttributeColumnFormatter.filter( + d, + filterStringUpper, + clinicalAttributes[i], + this.props.clinicalAttributeCache + ), + tooltip: {clinicalAttributes[i].description}, + visible: false, + order: 300, + }; + } + // override default visibility for some columns this._columns[ - MutationTableColumnType.CANCER_TYPE + MutationTableColumnType.CANCER_TYPE_DETAILED ].visible = CancerTypeColumnFormatter.isVisible( this.props.dataStore ? this.props.dataStore.allData @@ -98,7 +179,10 @@ export default class ResultsViewMutationTable extends MutationTable< // order columns this._columns[MutationTableColumnType.STUDY].order = 0; this._columns[MutationTableColumnType.SAMPLE_ID].order = 10; - this._columns[MutationTableColumnType.CANCER_TYPE].order = 15; + if ('CANCER_TYPE' in this._columns) { + this._columns['CANCER_TYPE'].order = 14; + } + this._columns[MutationTableColumnType.CANCER_TYPE_DETAILED].order = 15; this._columns[MutationTableColumnType.PROTEIN_CHANGE].order = 20; this._columns[MutationTableColumnType.ANNOTATION].order = 30; @@ -137,7 +221,7 @@ export default class ResultsViewMutationTable extends MutationTable< // exclude this._columns[ - MutationTableColumnType.CANCER_TYPE + MutationTableColumnType.CANCER_TYPE_DETAILED ].shouldExclude = () => { return !this.props.uniqueSampleKeyToTumorType; }; diff --git a/src/pages/staticPages/tools/mutationMapper/MutationMapperToolStore.ts b/src/pages/staticPages/tools/mutationMapper/MutationMapperToolStore.ts index c307dd7d97d..ca6234f72a7 100644 --- a/src/pages/staticPages/tools/mutationMapper/MutationMapperToolStore.ts +++ b/src/pages/staticPages/tools/mutationMapper/MutationMapperToolStore.ts @@ -448,6 +448,7 @@ export default class MutationMapperToolStore { return new MutationTableDownloadDataFetcher( this.mutations, undefined, + undefined, () => this.genomeNexusCache, () => this.genomeNexusMutationAssessorCache ); diff --git a/src/pages/staticPages/tools/mutationMapper/StandaloneMutationTable.tsx b/src/pages/staticPages/tools/mutationMapper/StandaloneMutationTable.tsx index e0f5149189c..d10666b4c04 100644 --- a/src/pages/staticPages/tools/mutationMapper/StandaloneMutationTable.tsx +++ b/src/pages/staticPages/tools/mutationMapper/StandaloneMutationTable.tsx @@ -23,7 +23,7 @@ export default class StandaloneMutationTable extends MutationTable< ...MutationTable.defaultProps, columns: [ MutationTableColumnType.SAMPLE_ID, - MutationTableColumnType.CANCER_TYPE, + MutationTableColumnType.CANCER_TYPE_DETAILED, MutationTableColumnType.ANNOTATION, MutationTableColumnType.HGVSG, MutationTableColumnType.FUNCTIONAL_IMPACT, @@ -65,7 +65,7 @@ export default class StandaloneMutationTable extends MutationTable< : this.props.data ); this._columns[ - MutationTableColumnType.CANCER_TYPE + MutationTableColumnType.CANCER_TYPE_DETAILED ].visible = CancerTypeColumnFormatter.isVisible( this.props.dataStore ? this.props.dataStore.allData @@ -77,7 +77,7 @@ export default class StandaloneMutationTable extends MutationTable< // order columns //this._columns[MutationTableColumnType.STUDY].order = 0; this._columns[MutationTableColumnType.SAMPLE_ID].order = 10; - this._columns[MutationTableColumnType.CANCER_TYPE].order = 15; + this._columns[MutationTableColumnType.CANCER_TYPE_DETAILED].order = 15; this._columns[MutationTableColumnType.PROTEIN_CHANGE].order = 20; this._columns[MutationTableColumnType.ANNOTATION].order = 30; this._columns[MutationTableColumnType.FUNCTIONAL_IMPACT].order = 38; diff --git a/src/pages/studyView/addChartButton/addChartByType/AddChartByType.tsx b/src/pages/studyView/addChartButton/addChartByType/AddChartByType.tsx index 9af40e4d21a..2dcadcdf694 100644 --- a/src/pages/studyView/addChartButton/addChartByType/AddChartByType.tsx +++ b/src/pages/studyView/addChartButton/addChartByType/AddChartByType.tsx @@ -23,7 +23,7 @@ import ifNotDefined from '../../../../shared/lib/ifNotDefined'; export type AddChartOption = Omit; export interface IAddChartByTypeProps { options: Omit[]; - freqPromise: MobxPromise; + freqPromise?: MobxPromise; onAddAll: (keys: string[]) => void; onClearAll: (keys: string[]) => void; onToggleOption: (key: string) => void; @@ -59,12 +59,12 @@ export default class AddChartByType extends React.Component< @computed get options() { - if (this.props.freqPromise.isComplete) { + if (this.props.freqPromise?.isComplete) { const options = _.reduce( this.props.options, (acc, next) => { const disabled = - this.props.freqPromise.result![next.key] === 0; + this.props.freqPromise!.result![next.key] === 0; acc.push({ label: next.label, key: next.key, @@ -73,7 +73,7 @@ export default class AddChartByType extends React.Component< isSharedChart: next.isSharedChart, freq: disabled ? 0 - : this.props.freqPromise.result![next.key], + : this.props.freqPromise!.result![next.key], }); return acc; }, @@ -183,7 +183,9 @@ export default class AddChartByType extends React.Component< (this.props.shareCharts || this.props.deleteChart ? 70 : 0), defaultSortDirection: 'asc' as 'asc', }, - { + ]; + if (this.props.freqPromise) { + columns.push({ name: 'Freq', tooltip: ( @@ -206,7 +208,7 @@ export default class AddChartByType extends React.Component< } )} > - {this.props.freqPromise.isComplete + {this.props.freqPromise?.isComplete ? getFrequencyStr(option.freq) : ''} @@ -214,8 +216,8 @@ export default class AddChartByType extends React.Component< sortBy: (d: AddChartOption) => d.freq, defaultSortDirection: 'desc' as 'desc', width: 60, - }, - ]; + }); + } if (this.props.shareCharts || this.props.deleteChart) { columns.push({ @@ -358,7 +360,8 @@ export default class AddChartByType extends React.Component< style={{ display: 'flex', flexDirection: 'column' }} data-test="add-by-type" > - {this.props.freqPromise.isComplete && ( + {(!this.props.freqPromise || + this.props.freqPromise.isComplete) && ( )} - {this.props.freqPromise.isPending && ( + {this.props.freqPromise?.isPending && (
extends React.Component< const showDeselectAll = !noneSelected && this.props.removeAll; return ( - <> +
{showSelectAll && ( )} - +
); } diff --git a/src/shared/cache/ClinicalAttributeCache.ts b/src/shared/cache/ClinicalAttributeCache.ts new file mode 100644 index 00000000000..09550897135 --- /dev/null +++ b/src/shared/cache/ClinicalAttributeCache.ts @@ -0,0 +1,84 @@ +import client from '../api/cbioportalClientInstance'; +import LazyMobXCache from '../lib/LazyMobXCache'; +import { + ClinicalData, + ClinicalAttribute, + ClinicalDataMultiStudyFilter, +} from 'cbioportal-ts-api-client'; +import _ from 'lodash'; + +type Query = { + clinicalAttribute: ClinicalAttribute; + entityId: string; + studyId: string; +}; + +function queryToKey(q: Query) { + return `${q.clinicalAttribute.clinicalAttributeId}~${q.entityId}~${q.studyId}`; +} +function dataToKey(d: ClinicalData) { + const entityId = d.hasOwnProperty('sampleId') ? d.sampleId : d.patientId; + return `${d.clinicalAttributeId}~${entityId}~${d.studyId}`; +} + +export async function fetch(queries: Query[]): Promise { + if (queries.length > 0) { + const patientQueries = queries.filter( + q => q.clinicalAttribute.patientAttribute + ); + const sampleQueries = queries.filter( + q => !q.clinicalAttribute.patientAttribute + ); + + const patientFilter: ClinicalDataMultiStudyFilter = { + attributeIds: patientQueries.map( + q => q.clinicalAttribute.clinicalAttributeId + ), + identifiers: patientQueries.map(q => ({ + entityId: q.entityId, + studyId: q.studyId, + })), + }; + const sampleFilter: ClinicalDataMultiStudyFilter = { + attributeIds: sampleQueries.map( + q => q.clinicalAttribute.clinicalAttributeId + ), + identifiers: sampleQueries.map(q => ({ + entityId: q.entityId, + studyId: q.studyId, + })), + }; + + const clinicalData = []; + if (patientQueries.length > 0) { + clinicalData.push( + await client.fetchClinicalDataUsingPOST({ + clinicalDataType: 'PATIENT', + clinicalDataMultiStudyFilter: patientFilter, + }) + ); + } + if (sampleQueries.length > 0) { + clinicalData.push( + await client.fetchClinicalDataUsingPOST({ + clinicalDataType: 'SAMPLE', + clinicalDataMultiStudyFilter: sampleFilter, + }) + ); + } + + return Promise.all(clinicalData).then(d => _.flatten(d)); + } else { + return []; + } +} + +export default class ClinicalAttributeCache extends LazyMobXCache< + ClinicalData, + Query, + string +> { + constructor() { + super(queryToKey, dataToKey, fetch); + } +} diff --git a/src/shared/components/columnVisibilityControls/ColumnVisibilityControls.tsx b/src/shared/components/columnVisibilityControls/ColumnVisibilityControls.tsx index 507d8bb464e..aa223abc5a4 100644 --- a/src/shared/components/columnVisibilityControls/ColumnVisibilityControls.tsx +++ b/src/shared/components/columnVisibilityControls/ColumnVisibilityControls.tsx @@ -19,6 +19,7 @@ export interface IColumnVisibilityControlsProps { columnId: string, columnVisibility?: IColumnVisibilityDef[] ) => void; + customDropdown?: (props: IColumnVisibilityControlsProps) => JSX.Element; } /** @@ -40,6 +41,16 @@ export class ColumnVisibilityControls extends React.Component< } public render() { + return ( +
+ {this.props.customDropdown + ? this.props.customDropdown(this.props) + : this.defaultDropdown} +
+ ); + } + + private get defaultDropdown() { return ( = { + id?: string; name: string; headerRender?: (name: string) => JSX.Element; headerDownload?: (name: string) => string; @@ -61,8 +62,10 @@ export type Column = { sortBy?: | ((data: T) => number | null) | ((data: T) => string | null) + | ((data: T) => string | number | null) | ((data: T) => (number | null)[]) - | ((data: T) => (string | null)[]); + | ((data: T) => (string | null)[]) + | ((data: T) => (string | number | null)[]); render: (data: T) => JSX.Element; download?: (data: T) => string | string[]; tooltip?: JSX.Element; @@ -503,9 +506,9 @@ export class LazyMobXTableStore { this.columns.forEach((column: Column) => { colVisProp.push({ - id: column.name, + id: column.hasOwnProperty('id') ? column.id! : column.name, name: column.name, - visible: this.columnVisibility[column.name], + visible: this.isVisible(column), togglable: column.hasOwnProperty('togglable') ? column.togglable : true, @@ -725,7 +728,8 @@ export class LazyMobXTableStore { } public isVisible(column: Column): boolean { - return this.columnVisibility[column.name] || false; + const index = column.hasOwnProperty('id') ? column.id! : column.name; + return this.columnVisibility[index] || false; } constructor(lazyMobXTableProps: LazyMobXTableProps) { diff --git a/src/shared/components/mutationTable/MutationTable.tsx b/src/shared/components/mutationTable/MutationTable.tsx index 4e8b44550f7..9a9309b57e1 100644 --- a/src/shared/components/mutationTable/MutationTable.tsx +++ b/src/shared/components/mutationTable/MutationTable.tsx @@ -110,7 +110,7 @@ export interface IMutationTableProps { civicVariants?: RemoteData; mrnaExprRankMolecularProfileId?: string; discreteCNAMolecularProfileId?: string; - columns?: MutationTableColumnType[]; + columns?: ExtendedMutationTableColumnType[]; data?: Mutation[][]; dataStore?: ILazyMobXTableApplicationDataStore; downloadDataFetcher?: ILazyMobXTableApplicationLazyDownloadDataFetcher; @@ -168,7 +168,7 @@ export enum MutationTableColumnType { VAR_READS_N, REF_READS, VAR_READS, - CANCER_TYPE, + CANCER_TYPE_DETAILED, NUM_MUTATIONS, EXON, HGVSC, @@ -179,6 +179,7 @@ export enum MutationTableColumnType { GENE_PANEL, SIGNAL, } +type ExtendedMutationTableColumnType = MutationTableColumnType | string; type MutationTableColumn = Column & { order?: number; @@ -235,9 +236,10 @@ export function defaultFilter( export default class MutationTable< P extends IMutationTableProps > extends React.Component { - @observable protected _columns: { - [columnEnum: number]: MutationTableColumn; - }; + @observable protected _columns: Record< + ExtendedMutationTableColumnType, + MutationTableColumn + >; @observable.ref public table: LazyMobXTable | null = null; public static defaultProps = { @@ -257,7 +259,10 @@ export default class MutationTable< constructor(props: P) { super(props); makeObservable(this); - this._columns = {}; + this._columns = {} as Record< + ExtendedMutationTableColumnType, + MutationTableColumn + >; this.generateColumns(); } @@ -289,10 +294,14 @@ export default class MutationTable< } protected generateColumns() { - this._columns = {}; + this._columns = {} as Record< + ExtendedMutationTableColumnType, + MutationTableColumn + >; this._columns[MutationTableColumnType.STUDY] = { - name: 'Study', + id: 'CANCER_STUDY', + name: 'Study of Origin', render: (d: Mutation[]) => StudyColumnFormatter.renderFunction( d, @@ -323,6 +332,7 @@ export default class MutationTable< this.props.studyIdToStudy ); }, + tooltip: Study of Origin, visible: false, }; @@ -896,8 +906,9 @@ export default class MutationTable< align: 'left', }; - this._columns[MutationTableColumnType.CANCER_TYPE] = { - name: 'Cancer Type', + this._columns[MutationTableColumnType.CANCER_TYPE_DETAILED] = { + id: 'CANCER_TYPE_DETAILED', + name: 'Cancer Type Detailed', render: (d: Mutation[]) => CancerTypeColumnFormatter.render( d, @@ -923,10 +934,11 @@ export default class MutationTable< filterStringUpper, this.props.uniqueSampleKeyToTumorType ), - tooltip: Cancer Type, + tooltip: Cancer Type Detailed, }; this._columns[MutationTableColumnType.NUM_MUTATIONS] = { + id: 'MUTATION_COUNT', name: '# Mut in Sample', render: MutationCountColumnFormatter.makeRenderFunction(this), headerRender: (name: string) => ( @@ -1157,10 +1169,10 @@ export default class MutationTable< }; } - @computed protected get orderedColumns(): MutationTableColumnType[] { - const columns = (this.props.columns || []) as MutationTableColumnType[]; - - return _.sortBy(columns, (c: MutationTableColumnType) => { + @computed + protected get orderedColumns(): ExtendedMutationTableColumnType[] { + const columns = this.props.columns || []; + return _.sortBy(columns, (c: ExtendedMutationTableColumnType) => { let order: number = -1; if (this._columns[c] && this._columns[c].order) { @@ -1173,7 +1185,10 @@ export default class MutationTable< @computed protected get columns(): Column[] { return this.orderedColumns.reduce( - (columns: Column[], next: MutationTableColumnType) => { + ( + columns: Column[], + next: ExtendedMutationTableColumnType + ) => { let column = this._columns[next]; if ( diff --git a/src/shared/components/mutationTable/column/ClinicalAttributeColumnFormatter.tsx b/src/shared/components/mutationTable/column/ClinicalAttributeColumnFormatter.tsx new file mode 100644 index 00000000000..f131461eb5b --- /dev/null +++ b/src/shared/components/mutationTable/column/ClinicalAttributeColumnFormatter.tsx @@ -0,0 +1,88 @@ +import * as React from 'react'; +import LazyLoadedTableCell from 'shared/lib/LazyLoadedTableCell'; +import ClinicalAttributeCache from '../../../cache/ClinicalAttributeCache'; +import { + Mutation, + ClinicalData, + ClinicalAttribute, +} from 'cbioportal-ts-api-client'; +import { TruncatedText } from 'cbioportal-frontend-commons'; + +/** + * @author Rajat Sirohi + */ +export default class ClinicalAttributeColumnFormatter { + public static makeRenderFunction( + attribute: ClinicalAttribute, + clinicalAttributeCache?: ClinicalAttributeCache + ) { + return LazyLoadedTableCell( + (d: Mutation[]) => { + if (clinicalAttributeCache) { + return clinicalAttributeCache.get({ + clinicalAttribute: attribute, + entityId: attribute.patientAttribute + ? d[0].patientId + : d[0].sampleId, + studyId: d[0].studyId, + }); + } else { + return { + status: 'error', + data: null, + }; + } + }, + (t: ClinicalData) => ( + {t.value}
} + /> + ), + 'Clinical attribute not available for this sample.' + ); + } + + public static getTextValue( + d: Mutation[], + attribute: ClinicalAttribute, + clinicalAttributeCache?: ClinicalAttributeCache + ): string { + const clinicalDatum = clinicalAttributeCache?.get({ + clinicalAttribute: attribute, + entityId: attribute.patientAttribute + ? d[0].patientId + : d[0].sampleId, + studyId: d[0].studyId, + })?.data; + return clinicalDatum ? clinicalDatum.value : ''; + } + + public static sortBy( + d: Mutation[], + attribute: ClinicalAttribute, + clinicalAttributeCache?: ClinicalAttributeCache + ): string | number | null { + let textValue = this.getTextValue(d, attribute, clinicalAttributeCache); + let sortValue = + attribute.datatype === 'NUMBER' ? +textValue : textValue; + + return textValue === '' ? null : sortValue; + } + + public static filter( + d: Mutation[], + filterStringUpper: string, + attribute: ClinicalAttribute, + clinicalAttributeCache?: ClinicalAttributeCache + ): boolean { + if (attribute.datatype !== 'STRING') { + return false; + } + + return this.getTextValue(d, attribute, clinicalAttributeCache) + .toUpperCase() + .includes(filterStringUpper); + } +} diff --git a/src/shared/components/oncoprint/controls/CustomDropdown.tsx b/src/shared/components/oncoprint/controls/CustomDropdown.tsx index 97358b58b10..3ea33ee0f25 100644 --- a/src/shared/components/oncoprint/controls/CustomDropdown.tsx +++ b/src/shared/components/oncoprint/controls/CustomDropdown.tsx @@ -9,14 +9,18 @@ import _ from 'lodash'; export interface ICustomDropdownProps extends ButtonProps { titleElement?: JSX.Element; styles?: any; + buttonClassName?: string; } class CustomButton extends React.Component { // cant type this more specifically because of some typing issues w ES6 classes not having component.replaceState render() { const { bsRole, title, ...props } = this.props; + const className = this.props.buttonClassName + ? this.props.buttonClassName + : 'btn btn-default'; return ( - ); diff --git a/src/shared/lib/ISortMetric.ts b/src/shared/lib/ISortMetric.ts index e4ddeca56b4..49a977c9382 100644 --- a/src/shared/lib/ISortMetric.ts +++ b/src/shared/lib/ISortMetric.ts @@ -1,5 +1,7 @@ export type SortMetric = | ((d: T) => number | null) - | ((d: T) => (number | null)[]) | ((d: T) => string | null) - | ((d: T) => (string | null)[]); + | ((d: T) => string | number | null) + | ((d: T) => (number | null)[]) + | ((d: T) => (string | null)[]) + | ((d: T) => (string | number | null)[]); diff --git a/src/shared/lib/MutationTableDownloadDataFetcher.ts b/src/shared/lib/MutationTableDownloadDataFetcher.ts index cff901979e0..195d886fc55 100644 --- a/src/shared/lib/MutationTableDownloadDataFetcher.ts +++ b/src/shared/lib/MutationTableDownloadDataFetcher.ts @@ -5,6 +5,10 @@ import { default as MutationCountCache, fetch as fetchMutationCountData, } from 'shared/cache/MutationCountCache'; +import { + default as ClinicalAttributeCache, + fetch as fetchClinicalData, +} from 'shared/cache/ClinicalAttributeCache'; import { default as GenomeNexusCache, defaultGNFetch as fetchGenomeNexusData, @@ -13,7 +17,11 @@ import { default as DiscreteCNACache, fetch as fetchDiscreteCNAData, } from 'shared/cache/DiscreteCNACache'; -import { Mutation, MolecularProfile } from 'cbioportal-ts-api-client'; +import { + Mutation, + ClinicalAttribute, + MolecularProfile, +} from 'cbioportal-ts-api-client'; import _ from 'lodash'; import { default as GenomeNexusMutationAssessorCache, @@ -26,12 +34,14 @@ export class MutationTableDownloadDataFetcher constructor( private mutationData: MobxPromise, + private clinicalAttributes?: MobxPromise, private studyToMolecularProfileDiscrete?: { [studyId: string]: MolecularProfile; }, private genomeNexusCache?: () => GenomeNexusCache, private genomeNexusMutationAssessorCache?: () => GenomeNexusMutationAssessorCache, private mutationCountCache?: () => MutationCountCache, + private clinicalAttributeCache?: () => ClinicalAttributeCache, private discreteCNACache?: () => DiscreteCNACache ) { // TODO labelMobxPromises(this); ? @@ -90,6 +100,11 @@ export class MutationTableDownloadDataFetcher caches.push(this.mutationCountCache()); } + if (this.clinicalAttributeCache) { + promises.push(this.fetchAllClinicalData()); + caches.push(this.clinicalAttributeCache()); + } + if (this.discreteCNACache) { promises.push(this.fetchAllDiscreteCNAData()); caches.push(this.discreteCNACache()); @@ -111,6 +126,30 @@ export class MutationTableDownloadDataFetcher } } + private async fetchAllClinicalData() { + if ( + this.mutationData.result && + this.clinicalAttributes && + this.clinicalAttributes.result + ) { + const queries = _.flatten( + this.mutationData.result.map(mutation => + this.clinicalAttributes!.result!.map(attribute => ({ + clinicalAttribute: attribute, + entityId: attribute.patientAttribute + ? mutation.patientId + : mutation.sampleId, + studyId: mutation.studyId, + })) + ) + ); + + return await fetchClinicalData(queries); + } else { + return undefined; + } + } + private async fetchAllDiscreteCNAData() { if (this.mutationData.result) { const queries = this.mutationData.result.map(mutation => ({