From 3823c095275d9ec637baea13e8a576d97b9e9ca9 Mon Sep 17 00:00:00 2001 From: Srushti Gangireddy Date: Tue, 23 Jul 2024 11:02:01 -0500 Subject: [PATCH 1/7] wip --- .../publicapi/GenomicsController.java | 792 +++- .../src/main/resources/application.properties | 4 +- public-api/src/main/resources/public-api.yaml | 3304 +++++++++-------- .../genomic-view/genomic-view.component.tsx | 270 +- .../components/slider-filter/Surface.tsx | 74 + .../slider-filter/slider-components.tsx | 176 + .../slider-filter/slider-constants.tsx | 7 + .../sv-variant-filter-slider.component.tsx | 110 + .../sv-genomic-search.component.tsx | 157 + .../sv-variant-filter-chips.component.tsx | 163 + .../sv-variant-filter-item.component.tsx | 200 + .../sv-variant-filter-slider.component.tsx | 45 + .../sv-variant-filter.component.tsx | 188 + .../components/sv-variant-row.component.tsx | 170 + .../sv-variant-search.component.tsx | 225 ++ .../sv-variant-sort-item.component.tsx | 212 ++ .../components/sv-variant-table.component.tsx | 456 +++ .../components/table-paginator.component.tsx | 157 + 18 files changed, 5208 insertions(+), 1502 deletions(-) create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/Surface.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx diff --git a/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java b/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java index 39b8a40ef..c46e1b6b1 100644 --- a/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java +++ b/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java @@ -11,12 +11,18 @@ import org.springframework.http.ResponseEntity; import org.pmiops.workbench.model.Analysis; import org.pmiops.workbench.model.Variant; +import org.pmiops.workbench.model.SVVariant; import org.pmiops.workbench.model.VariantInfo; import org.pmiops.workbench.model.GenomicFilters; import org.pmiops.workbench.model.GenomicFilterOption; import org.pmiops.workbench.model.GenomicFilterOptionList; +import org.pmiops.workbench.model.SVGenomicFilters; +import org.pmiops.workbench.model.SVGenomicFilterOption; +import org.pmiops.workbench.model.SVGenomicFilterOptionList; import org.pmiops.workbench.model.GenomicSearchTermType; +import org.pmiops.workbench.model.SVGenomicSearchTermType; import org.pmiops.workbench.model.VariantListResponse; +import org.pmiops.workbench.model.SVVariantListResponse; import org.pmiops.workbench.exceptions.ServerErrorException; import org.pmiops.workbench.service.BigQueryService; import com.google.cloud.bigquery.FieldValue; @@ -29,8 +35,11 @@ import org.pmiops.workbench.model.AnalysisIdConstant; import org.pmiops.workbench.model.CommonStorageEnums; import org.pmiops.workbench.model.SearchVariantsRequest; +import org.pmiops.workbench.model.SearchSVVariantsRequest; import org.pmiops.workbench.model.VariantResultSizeRequest; +import org.pmiops.workbench.model.SVVariantResultSizeRequest; import org.pmiops.workbench.model.SortMetadata; +import org.pmiops.workbench.model.SortSVMetadata; import org.pmiops.workbench.model.SortColumnDetails; @RestController @@ -45,10 +54,18 @@ public class GenomicsController implements GenomicsApiDelegate { private static final String genomicRegionRegex = "(?i)([\"]*)(chr([0-9]{1,})*[XYxy]*:{0,}).*"; private static final String variantIdRegex = "(?i)([\"]*)((\\d{1,}|X|Y)-\\d{5,}-[A,C,T,G]{1,}-[A,C,T,G]{1,}).*"; + private static final String svVariantIdRegex = "(?i)([1-9]|1[0-9]|2[0-2]|X|Y)-\\d{1,}-[0-9a-fA-F]{4}"; private static final String rsNumberRegex = "(?i)(rs)(\\d{1,})"; private static final String COUNT_SQL_TEMPLATE = "SELECT count(*) as count FROM ${projectId}.${dataSetId}.wgs_variant"; + + private static final String SV_COUNT_SQL_TEMPLATE = "SELECT count(*) as count FROM ${projectId}.${dataSetId}.selected_sv_fields_db_with_id"; + private static final String WHERE_CONTIG = " where REGEXP_CONTAINS(contig, @contig)"; + + private static final String WHERE_CHROM = " where REGEXP_CONTAINS(chrom, @contig)"; private static final String AND_POSITION = " and position <= @high and position >= @low"; + + private static final String AND_POS = " and pos <= @high and pos >= @low"; private static final String WHERE_VARIANT_ID = " where variant_id = @variant_id"; // private static final String WHERE_GENE = ", unnest(split(genes, ', ')) AS gene\n" + @@ -60,6 +77,9 @@ public class GenomicsController implements GenomicsApiDelegate { // private static final String WHERE_GENE_EXACT = " where @genes in unnest(split(lower(genes), ', '))"; private static final String VARIANT_LIST_SQL_TEMPLATE = "SELECT variant_id, genes, (SELECT STRING_AGG(distinct d, \", \" order by d asc) FROM UNNEST(consequence) d) as cons_agg_str, " + "variant_type, protein_change, (SELECT STRING_AGG(distinct d, \", \" order by d asc) FROM UNNEST(clinical_significance) d) as clin_sig_agg_str, allele_count, allele_number, allele_frequency, homozygote_count FROM ${projectId}.${dataSetId}.wgs_variant"; + + private static final String SV_VARIANT_LIST_SQL_TEMPLATE = "SELECT variant_id, variant_type, consequence, position, a.size, \n" + + "a.allele_count, a.allele_number, a.allele_frequency, a.homozygote_count FROM ${projectId}.${dataSetId}.selected_sv_fields_db_with_id a"; private static final String VARIANT_DETAIL_SQL_TEMPLATE = "SELECT dna_change, transcript, ARRAY_TO_STRING(rs_number, ', ') as rs_number, gvs_afr_ac as afr_allele_count, gvs_afr_an as afr_allele_number, gvs_afr_af as afr_allele_frequency, gvs_afr_hc as afr_homozygote_count, gvs_eas_ac as eas_allele_count, gvs_eas_an as eas_allele_number, gvs_eas_af as eas_allele_frequency, gvs_eas_hc as eas_homozygote_count, " + "gvs_eur_ac as eur_allele_count, gvs_eur_an as eur_allele_number, gvs_eur_af as eur_allele_frequency, gvs_eur_hc as eur_homozygote_count, " + "gvs_amr_ac as amr_allele_count, gvs_amr_an as amr_allele_number, gvs_amr_af as amr_allele_frequency, gvs_amr_hc as amr_homozygote_count, " + @@ -122,6 +142,47 @@ public class GenomicsController implements GenomicsApiDelegate { "union all \n" + "select * from g;"; + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_GENE = "with a as\n" + + "(select 'Gene' as option, genes as genes, '' as variant_type, count(*) as gene_count, " + + "0 as variant_type_count, " + + "0 as min_count, 0 as max_count\n" + + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id tj, \n" + + " unnest(split(genes, ', ')) gene\n"; + + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_VAR_TYPE = " group by genes),\n" + + "b as \n" + + "(select 'Variant Type' as option, '' as genes, variant_type as variant_type, 0 as gene_count, count(*) as variant_type_count, 0 as min_count, 0 as max_count\n" + + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT = " group by variant_type),\n" + + "e as \n" + + "(select 'Allele Count' as option, '' as genes, '' as variant_type, \n" + + "0 as gene_count, 0 as variant_type_count,\n" + + "min(allele_count) as min_count, max(allele_count) as max_count\n" + + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_NUMBER = "),\n" + + "f as \n" + + "(select 'Allele Number' as option, '' as genes, '' as variant_type, \n" + + "0 as gene_count, 0 as variant_type_count,\n" + + "min(allele_number) as min_count, max(allele_number) as max_count\n" + + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; + + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_HOMOZYGOTE_COUNT = "),\n" + + "g as \n" + + "(select 'Homozygote Count' as option, '' as genes, '' as variant_type, \n" + + "0 as gene_count, 0 as variant_type_count,\n" + + "min(homozygote_count) as min_count, max(homozygote_count) as max_count\n" + + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_UNION = ")\n" + + "select * from a \n" + + "union all \n" + + "select * from b \n" + + "union all \n" + + "select * from e \n" + + "union all \n" + + "select * from f \n" + + "union all \n" + + "select * from g;"; + public GenomicsController() {} public GenomicsController(AchillesAnalysisService achillesAnalysisService, CdrVersionService cdrVersionService, @@ -169,7 +230,7 @@ public ResponseEntity getVariantSearchResultSize(VariantResultSizeRequest genes = searchTermType.getGenes(); low = searchTermType.getLow(); high = searchTermType.getHigh(); - contig = searchTermType.getContig(); + contig = "(?i)\\b" + searchTermType.getContig() + "\\b"; variant_id = searchTermType.getVariantId(); rs_id = searchTermType.getRsId(); whereGeneFlag = searchTermType.isWhereGeneFlag(); @@ -240,25 +301,452 @@ public ResponseEntity getVariantSearchResultSize(VariantResultSizeRequest } } } - GenomicFilterOption acFilter = filters.getAlleleCount(); + GenomicFilterOption acFilter = filters.getAlleleCount(); + if (acFilter != null && acFilter.isChecked()) { + Long minVal = acFilter.getMin(); + Long maxVal = acFilter.getMax(); + ALLELE_COUNT_FILTER = " AND allele_count BETWEEN " + minVal + " AND " + maxVal; + } + GenomicFilterOption anFilter = filters.getAlleleNumber(); + if (anFilter != null && anFilter.isChecked()) { + Long minVal = anFilter.getMin(); + Long maxVal = anFilter.getMax(); + ALLELE_NUMBER_FILTER = " AND allele_number BETWEEN " + minVal + " AND " + maxVal; + } + GenomicFilterOption afFilter = filters.getAlleleFrequency(); + if (afFilter != null && afFilter.isChecked()) { + Float minVal = afFilter.getMinFreq(); + Float maxVal = afFilter.getMaxFreq(); + ALLELE_FREQUENCY_FILTER = " AND allele_frequency BETWEEN " + minVal + " AND " + maxVal; + } + GenomicFilterOption hcFilter = filters.getHomozygoteCount(); + if (hcFilter != null && hcFilter.isChecked()) { + Long minVal = hcFilter.getMin(); + Long maxVal = hcFilter.getMax(); + HOMOZYGOTE_COUNT_FILTER = " AND homozygote_count BETWEEN " + minVal + " AND " + maxVal; + } + } + if (WHERE_GENE_IN.substring(WHERE_GENE_IN.length() - 1).equals(",")) { + geneFilterFlag = true; + WHERE_GENE_IN = WHERE_GENE_IN.substring(0, WHERE_GENE_IN.length()-1); + WHERE_GENE_IN += ") "; + } + if (WHERE_CON_IN.substring(WHERE_CON_IN.length() - 1).equals(",")) { + conFilterFlag = true; + WHERE_CON_IN = WHERE_CON_IN.substring(0, WHERE_CON_IN.length()-1); + WHERE_CON_IN += ")) "; + } + if (WHERE_VAR_TYPE_IN.substring(WHERE_VAR_TYPE_IN.length() - 1).equals(",")) { + varTypeFilterFlag = true; + WHERE_VAR_TYPE_IN = WHERE_VAR_TYPE_IN.substring(0, WHERE_VAR_TYPE_IN.length()-1); + } + if (WHERE_CLIN_IN.substring(WHERE_CLIN_IN.length() - 1).equals(",")) { + clinFilterFlag = true; + WHERE_CLIN_IN = WHERE_CLIN_IN.substring(0, WHERE_CLIN_IN.length()-1); + WHERE_CLIN_IN += ")) "; + } + if (geneFilterFlag) { + if (whereGeneFlag) { + finalSql = finalSql.replace(WHERE_GENE_REGEX, ""); + WHERE_GENE_IN = " WHERE" + WHERE_GENE_IN.substring(4); + } + finalSql += WHERE_GENE_IN; + } + if (conFilterFlag) { + finalSql += WHERE_CON_IN; + if (WHERE_CON_NULL.length() > 0) { + finalSql += WHERE_CON_NULL; + } + finalSql += ") "; + } else { + if (WHERE_CON_NULL.length() > 0) { + finalSql += " AND ARRAY_LENGTH(consequence) = 0"; + } + } + if (varTypeFilterFlag) { + finalSql += WHERE_VAR_TYPE_IN; + finalSql += ") "; + } + if (clinFilterFlag) { + finalSql += WHERE_CLIN_IN; + if (WHERE_CLIN_NULL.length() > 0) { + finalSql += WHERE_CLIN_NULL; + } + finalSql += ") "; + } else { + if (WHERE_CLIN_NULL.length() > 0) { + finalSql += " AND ARRAY_LENGTH(clinical_significance) = 0"; + } + } + if (ALLELE_COUNT_FILTER.length() > 0) { + finalSql += ALLELE_COUNT_FILTER; + } + if (ALLELE_NUMBER_FILTER.length() > 0) { + finalSql += ALLELE_NUMBER_FILTER; + } + if (ALLELE_FREQUENCY_FILTER.length() > 0) { + finalSql += ALLELE_FREQUENCY_FILTER; + } + if (HOMOZYGOTE_COUNT_FILTER.length() > 0) { + finalSql += HOMOZYGOTE_COUNT_FILTER; + } + + QueryJobConfiguration qjc = QueryJobConfiguration.newBuilder(finalSql) + .addNamedParameter("contig", QueryParameterValue.string(contig)) + .addNamedParameter("high", QueryParameterValue.int64(high)) + .addNamedParameter("low", QueryParameterValue.int64(low)) + .addNamedParameter("variant_id", QueryParameterValue.string(variant_id)) + .addNamedParameter("genes", QueryParameterValue.string(genes)) + .addNamedParameter("rs_id", QueryParameterValue.string(rs_id)) + .setUseLegacySql(false) + .build(); + qjc = bigQueryService.filterBigQueryConfig(qjc); + TableResult result = bigQueryService.executeQuery(qjc); + Map rm = bigQueryService.getResultMapper(result); + List row = result.iterateAll().iterator().next(); + return ResponseEntity.ok(bigQueryService.getLong(row, rm.get("count"))); + } + + @Override + public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequest variantResultSizeRequest) { + try { + cdrVersionService.setDefaultCdrVersion(); + } catch(NullPointerException ie) { + throw new ServerErrorException("Cannot set default cdr version"); + } + + String finalSql = SV_COUNT_SQL_TEMPLATE; + + String variant_id = ""; + String genes = ""; + Long low = 0L; + Long high = 0L; + boolean whereGeneFlag = false; + + String variantSearchTerm = variantResultSizeRequest.getQuery().trim(); + SVGenomicFilters filters = variantResultSizeRequest.getFilterMetadata(); + String searchTerm = variantSearchTerm; + + if (variantSearchTerm.startsWith("~")) { + searchTerm = variantSearchTerm.substring(1); + } + + String contig = "(?i)(" + searchTerm + ")$"; + // Make sure the search term is not empty + if (!Strings.isNullOrEmpty(searchTerm)) { + SVGenomicSearchTermType searchTermType = getSVSearchType(variantSearchTerm, searchTerm); + finalSql += searchTermType.getSearchSqlQuery(); + genes = searchTermType.getGenes(); + low = searchTermType.getLow(); + high = searchTermType.getHigh(); + contig = "(?i)\\b" + searchTermType.getContig() + "\\b"; + variant_id = searchTermType.getVariantId(); + whereGeneFlag = searchTermType.isWhereGeneFlag(); + } + + String WHERE_GENE_IN = " AND genes in ("; + String WHERE_VAR_TYPE_IN = "AND variant_type in ("; + String ALLELE_COUNT_FILTER = ""; + String ALLELE_NUMBER_FILTER = ""; + String ALLELE_FREQUENCY_FILTER = ""; + String HOMOZYGOTE_COUNT_FILTER = ""; + boolean geneFilterFlag = false; + boolean varTypeFilterFlag = false; + if (filters != null) { + SVGenomicFilterOptionList geneFilterList = filters.getGene(); + List geneFilters = geneFilterList.getItems(); + if (geneFilters != null && geneFilters.size() > 0 && geneFilterList.isFilterActive()) { + for(int i=0; i < geneFilters.size(); i++) { + SVGenomicFilterOption filter = geneFilters.get(i); + if (filter.isChecked() && !Strings.isNullOrEmpty(filter.getOption())) { + WHERE_GENE_IN += "\"" + filter.getOption().toUpperCase() + "\","; + } + } + } + SVGenomicFilterOptionList varTypeFilterList = filters.getVariantType(); + List varTypeFilters = varTypeFilterList.getItems(); + if (varTypeFilters != null && varTypeFilters.size() > 0 && varTypeFilterList.isFilterActive()) { + for(int i=0; i < varTypeFilters.size(); i++) { + SVGenomicFilterOption filter = varTypeFilters.get(i); + if (filter.isChecked()) { + if (!Strings.isNullOrEmpty(filter.getOption())) { + WHERE_VAR_TYPE_IN += "\"" + filter.getOption() + "\","; + } + } + } + } + SVGenomicFilterOption acFilter = filters.getAlleleCount(); + if (acFilter != null && acFilter.isChecked()) { + Long minVal = acFilter.getMin(); + Long maxVal = acFilter.getMax(); + ALLELE_COUNT_FILTER = " AND allele_count BETWEEN " + minVal + " AND " + maxVal; + } + SVGenomicFilterOption anFilter = filters.getAlleleNumber(); + if (anFilter != null && anFilter.isChecked()) { + Long minVal = anFilter.getMin(); + Long maxVal = anFilter.getMax(); + ALLELE_NUMBER_FILTER = " AND allele_number BETWEEN " + minVal + " AND " + maxVal; + } + SVGenomicFilterOption afFilter = filters.getAlleleFrequency(); + if (afFilter != null && afFilter.isChecked()) { + Float minVal = afFilter.getMinFreq(); + Float maxVal = afFilter.getMaxFreq(); + ALLELE_FREQUENCY_FILTER = " AND allele_frequency BETWEEN " + minVal + " AND " + maxVal; + } + SVGenomicFilterOption hcFilter = filters.getHomozygoteCount(); + if (hcFilter != null && hcFilter.isChecked()) { + Long minVal = hcFilter.getMin(); + Long maxVal = hcFilter.getMax(); + HOMOZYGOTE_COUNT_FILTER = " AND homozygote_count BETWEEN " + minVal + " AND " + maxVal; + } + } + if (WHERE_GENE_IN.substring(WHERE_GENE_IN.length() - 1).equals(",")) { + geneFilterFlag = true; + WHERE_GENE_IN = WHERE_GENE_IN.substring(0, WHERE_GENE_IN.length()-1); + WHERE_GENE_IN += ") "; + } + + if (WHERE_VAR_TYPE_IN.substring(WHERE_VAR_TYPE_IN.length() - 1).equals(",")) { + varTypeFilterFlag = true; + WHERE_VAR_TYPE_IN = WHERE_VAR_TYPE_IN.substring(0, WHERE_VAR_TYPE_IN.length()-1); + } + + if (geneFilterFlag) { + if (whereGeneFlag) { + finalSql = finalSql.replace(WHERE_GENE_REGEX, ""); + WHERE_GENE_IN = " WHERE" + WHERE_GENE_IN.substring(4); + } + finalSql += WHERE_GENE_IN; + } + + if (varTypeFilterFlag) { + finalSql += WHERE_VAR_TYPE_IN; + finalSql += ") "; + } + + if (ALLELE_COUNT_FILTER.length() > 0) { + finalSql += ALLELE_COUNT_FILTER; + } + if (ALLELE_NUMBER_FILTER.length() > 0) { + finalSql += ALLELE_NUMBER_FILTER; + } + if (ALLELE_FREQUENCY_FILTER.length() > 0) { + finalSql += ALLELE_FREQUENCY_FILTER; + } + if (HOMOZYGOTE_COUNT_FILTER.length() > 0) { + finalSql += HOMOZYGOTE_COUNT_FILTER; + } + + System.out.println("######################################################"); + System.out.println(finalSql); + System.out.println("######################################################"); + + + QueryJobConfiguration qjc = QueryJobConfiguration.newBuilder(finalSql) + .addNamedParameter("contig", QueryParameterValue.string(contig)) + .addNamedParameter("high", QueryParameterValue.int64(high)) + .addNamedParameter("low", QueryParameterValue.int64(low)) + .addNamedParameter("variant_id", QueryParameterValue.string(variant_id)) + .addNamedParameter("genes", QueryParameterValue.string(genes)) + .setUseLegacySql(false) + .build(); + + qjc = bigQueryService.filterBigQueryConfig(qjc); + TableResult result = bigQueryService.executeQuery(qjc); + Map rm = bigQueryService.getResultMapper(result); + List row = result.iterateAll().iterator().next(); + + System.out.println("######################################################"); + System.out.println(rm.get("count")); + System.out.println("######################################################"); + + return ResponseEntity.ok(bigQueryService.getLong(row, rm.get("count"))); + } + + + @Override + public ResponseEntity searchSVVariants(SearchSVVariantsRequest searchVariantsRequest) { + try { + cdrVersionService.setDefaultCdrVersion(); + } catch(NullPointerException ie) { + throw new ServerErrorException("Cannot set default cdr version"); + } + + String variantSearchTerm = searchVariantsRequest.getQuery().trim(); + Integer page = searchVariantsRequest.getPageNumber(); + Integer rowCount = searchVariantsRequest.getRowCount(); + SortSVMetadata sortMetadata = searchVariantsRequest.getSortMetadata(); + + SVGenomicFilters filters = searchVariantsRequest.getFilterMetadata(); + + String ORDER_BY_CLAUSE = " ORDER BY variant_id ASC"; + if (sortMetadata != null) { + SortColumnDetails variantIdColumnSortMetadata = sortMetadata.getVariantId(); + if (variantIdColumnSortMetadata != null && variantIdColumnSortMetadata.isSortActive()) { + if (variantIdColumnSortMetadata.getSortDirection().equals("desc")) { + ORDER_BY_CLAUSE = " ORDER BY variant_id DESC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY variant_id ASC"; + } + } + + SortColumnDetails variantTypeColumnSortMetadata = sortMetadata.getVariantType(); + if (variantTypeColumnSortMetadata != null && variantTypeColumnSortMetadata.isSortActive()) { + if (variantTypeColumnSortMetadata.getSortDirection().equals("desc")) { + ORDER_BY_CLAUSE = " ORDER BY variant_type DESC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY variant_type ASC"; + } + } + + SortColumnDetails consequenceColumnSortMetadata = sortMetadata.getConsequence(); + if (consequenceColumnSortMetadata != null && consequenceColumnSortMetadata.isSortActive()) { + if (consequenceColumnSortMetadata.getSortDirection().equals("desc")) { + ORDER_BY_CLAUSE = " ORDER BY consequence DESC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY consequence ASC"; + } + } + + SortColumnDetails positionColumnSortMetadata = sortMetadata.getPosition(); + if (positionColumnSortMetadata != null && positionColumnSortMetadata.isSortActive()) { + if (positionColumnSortMetadata.getSortDirection().equals("desc")) { + ORDER_BY_CLAUSE = " ORDER BY position DESC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY position ASC"; + } + } + + SortColumnDetails sizeColumnSortMetadata = sortMetadata.getSize(); + if (sizeColumnSortMetadata != null && sizeColumnSortMetadata.isSortActive()) { + if (sizeColumnSortMetadata.getSortDirection().equals("desc")) { + ORDER_BY_CLAUSE = " ORDER BY size DESC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY size ASC"; + } + } + + SortColumnDetails alleleCountColumnSortMetadata = sortMetadata.getAlleleCount(); + if (alleleCountColumnSortMetadata != null && alleleCountColumnSortMetadata.isSortActive()) { + if (alleleCountColumnSortMetadata.getSortDirection().equals("asc")) { + ORDER_BY_CLAUSE = " ORDER BY allele_count ASC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY allele_count DESC"; + } + } + SortColumnDetails alleleNumberColumnSortMetadata = sortMetadata.getAlleleNumber(); + if (alleleNumberColumnSortMetadata != null && alleleNumberColumnSortMetadata.isSortActive()) { + if (alleleNumberColumnSortMetadata.getSortDirection().equals("asc")) { + ORDER_BY_CLAUSE = " ORDER BY allele_number ASC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY allele_number DESC"; + } + } + SortColumnDetails alleleFrequencyColumnSortMetadata = sortMetadata.getAlleleFrequency(); + if (alleleFrequencyColumnSortMetadata != null && alleleFrequencyColumnSortMetadata.isSortActive()) { + if (alleleFrequencyColumnSortMetadata.getSortDirection().equals("asc")) { + ORDER_BY_CLAUSE = " ORDER BY allele_frequency ASC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY allele_frequency DESC"; + } + } + SortColumnDetails homozygoteCountColumnSortMetadata = sortMetadata.getHomozygoteCount(); + if (homozygoteCountColumnSortMetadata != null && homozygoteCountColumnSortMetadata.isSortActive()) { + if (homozygoteCountColumnSortMetadata.getSortDirection().equals("asc")) { + ORDER_BY_CLAUSE = " ORDER BY homozygote_count ASC"; + } else { + ORDER_BY_CLAUSE = " ORDER BY homozygote_count DESC"; + } + } + + } + StringBuilder finalSqlBuilder = new StringBuilder(SV_VARIANT_LIST_SQL_TEMPLATE); + String searchTerm = variantSearchTerm; + String variant_id = ""; + String genes = ""; + boolean whereVariantIdFlag = false; + boolean whereGeneFlag = false; + + Long low = 0L; + Long high = 0L; + + String finalSql = SV_VARIANT_LIST_SQL_TEMPLATE; + + if (variantSearchTerm.startsWith("~")) { + searchTerm = variantSearchTerm.substring(1); + } + + String contig = searchTerm; + + if (!Strings.isNullOrEmpty(searchTerm)) { + SVGenomicSearchTermType searchTermType = getSVSearchType(variantSearchTerm, searchTerm); + finalSql += searchTermType.getSearchSqlQuery(); + genes = searchTermType.getGenes(); + variant_id = searchTermType.getVariantId(); + whereGeneFlag = searchTermType.isWhereGeneFlag(); + low = searchTermType.getLow(); + high = searchTermType.getHigh(); + contig = "(?i)\\b" + searchTermType.getContig() + "\\b"; + } + + String WHERE_GENE_IN = " AND genes in ("; + + String WHERE_VAR_TYPE_IN = "AND variant_type in ("; + + String ALLELE_COUNT_FILTER = ""; + String ALLELE_NUMBER_FILTER = ""; + String ALLELE_FREQUENCY_FILTER = ""; + String HOMOZYGOTE_COUNT_FILTER = ""; + boolean geneFilterFlag = false; + + boolean varTypeFilterFlag = false; + + if (filters != null) { + SVGenomicFilterOptionList geneFilterList = filters.getGene(); + List geneFilters = geneFilterList.getItems(); + if (geneFilters != null && geneFilters.size() > 0 && geneFilterList.isFilterActive()) { + for(int i=0; i < geneFilters.size(); i++) { + SVGenomicFilterOption filter = geneFilters.get(i); + if (filter.isChecked() && !Strings.isNullOrEmpty(filter.getOption())) { + WHERE_GENE_IN += "\"" + filter.getOption().toUpperCase() + "\","; + } + } + } + + + SVGenomicFilterOptionList varTypeFilterList = filters.getVariantType(); + List varTypeFilters = varTypeFilterList.getItems(); + if (varTypeFilters != null && varTypeFilters.size() > 0 && varTypeFilterList.isFilterActive()) { + for(int i=0; i < varTypeFilters.size(); i++) { + SVGenomicFilterOption filter = varTypeFilters.get(i); + if (filter.isChecked()) { + if (!Strings.isNullOrEmpty(filter.getOption())) { + WHERE_VAR_TYPE_IN += "\"" + filter.getOption() + "\","; + } + } + } + } + + + SVGenomicFilterOption acFilter = filters.getAlleleCount(); if (acFilter != null && acFilter.isChecked()) { Long minVal = acFilter.getMin(); Long maxVal = acFilter.getMax(); ALLELE_COUNT_FILTER = " AND allele_count BETWEEN " + minVal + " AND " + maxVal; } - GenomicFilterOption anFilter = filters.getAlleleNumber(); + SVGenomicFilterOption anFilter = filters.getAlleleNumber(); if (anFilter != null && anFilter.isChecked()) { Long minVal = anFilter.getMin(); Long maxVal = anFilter.getMax(); ALLELE_NUMBER_FILTER = " AND allele_number BETWEEN " + minVal + " AND " + maxVal; } - GenomicFilterOption afFilter = filters.getAlleleFrequency(); + SVGenomicFilterOption afFilter = filters.getAlleleFrequency(); if (afFilter != null && afFilter.isChecked()) { Float minVal = afFilter.getMinFreq(); Float maxVal = afFilter.getMaxFreq(); ALLELE_FREQUENCY_FILTER = " AND allele_frequency BETWEEN " + minVal + " AND " + maxVal; } - GenomicFilterOption hcFilter = filters.getHomozygoteCount(); + SVGenomicFilterOption hcFilter = filters.getHomozygoteCount(); if (hcFilter != null && hcFilter.isChecked()) { Long minVal = hcFilter.getMin(); Long maxVal = hcFilter.getMax(); @@ -270,20 +758,12 @@ public ResponseEntity getVariantSearchResultSize(VariantResultSizeRequest WHERE_GENE_IN = WHERE_GENE_IN.substring(0, WHERE_GENE_IN.length()-1); WHERE_GENE_IN += ") "; } - if (WHERE_CON_IN.substring(WHERE_CON_IN.length() - 1).equals(",")) { - conFilterFlag = true; - WHERE_CON_IN = WHERE_CON_IN.substring(0, WHERE_CON_IN.length()-1); - WHERE_CON_IN += ")) "; - } + if (WHERE_VAR_TYPE_IN.substring(WHERE_VAR_TYPE_IN.length() - 1).equals(",")) { varTypeFilterFlag = true; WHERE_VAR_TYPE_IN = WHERE_VAR_TYPE_IN.substring(0, WHERE_VAR_TYPE_IN.length()-1); } - if (WHERE_CLIN_IN.substring(WHERE_CLIN_IN.length() - 1).equals(",")) { - clinFilterFlag = true; - WHERE_CLIN_IN = WHERE_CLIN_IN.substring(0, WHERE_CLIN_IN.length()-1); - WHERE_CLIN_IN += ")) "; - } + if (geneFilterFlag) { if (whereGeneFlag) { finalSql = finalSql.replace(WHERE_GENE_REGEX, ""); @@ -291,32 +771,14 @@ public ResponseEntity getVariantSearchResultSize(VariantResultSizeRequest } finalSql += WHERE_GENE_IN; } - if (conFilterFlag) { - finalSql += WHERE_CON_IN; - if (WHERE_CON_NULL.length() > 0) { - finalSql += WHERE_CON_NULL; - } - finalSql += ") "; - } else { - if (WHERE_CON_NULL.length() > 0) { - finalSql += " AND ARRAY_LENGTH(consequence) = 0"; - } - } + + if (varTypeFilterFlag) { finalSql += WHERE_VAR_TYPE_IN; finalSql += ") "; } - if (clinFilterFlag) { - finalSql += WHERE_CLIN_IN; - if (WHERE_CLIN_NULL.length() > 0) { - finalSql += WHERE_CLIN_NULL; - } - finalSql += ") "; - } else { - if (WHERE_CLIN_NULL.length() > 0) { - finalSql += " AND ARRAY_LENGTH(clinical_significance) = 0"; - } - } + + if (ALLELE_COUNT_FILTER.length() > 0) { finalSql += ALLELE_COUNT_FILTER; } @@ -329,25 +791,43 @@ public ResponseEntity getVariantSearchResultSize(VariantResultSizeRequest if (HOMOZYGOTE_COUNT_FILTER.length() > 0) { finalSql += HOMOZYGOTE_COUNT_FILTER; } + finalSql += ORDER_BY_CLAUSE; + finalSql += " LIMIT " + rowCount + " OFFSET " + ((Optional.ofNullable(page).orElse(1)-1)*rowCount); System.out.println("**************************"); System.out.println(finalSql); System.out.println("**************************"); QueryJobConfiguration qjc = QueryJobConfiguration.newBuilder(finalSql) + .addNamedParameter("variant_id", QueryParameterValue.string(variant_id)) + .addNamedParameter("genes", QueryParameterValue.string(genes)) .addNamedParameter("contig", QueryParameterValue.string(contig)) .addNamedParameter("high", QueryParameterValue.int64(high)) .addNamedParameter("low", QueryParameterValue.int64(low)) - .addNamedParameter("variant_id", QueryParameterValue.string(variant_id)) - .addNamedParameter("genes", QueryParameterValue.string(genes)) - .addNamedParameter("rs_id", QueryParameterValue.string(rs_id)) .setUseLegacySql(false) .build(); + qjc = bigQueryService.filterBigQueryConfig(qjc); TableResult result = bigQueryService.executeQuery(qjc); Map rm = bigQueryService.getResultMapper(result); - List row = result.iterateAll().iterator().next(); - return ResponseEntity.ok(bigQueryService.getLong(row, rm.get("count"))); + List variantList = new ArrayList<>(); + for (List row : result.iterateAll()) { + variantList.add(new SVVariant() + .variantId(bigQueryService.getString(row, rm.get("variant_id"))) + .variantType(bigQueryService.getString(row, rm.get("variant_type"))) + .consequence(bigQueryService.getString(row, rm.get("consequence"))) + .position(bigQueryService.getString(row, rm.get("position"))) + .size(bigQueryService.getLong(row, rm.get("size"))) + .alleleCount(bigQueryService.getLong(row, rm.get("allele_count"))) + .alleleNumber(bigQueryService.getLong(row, rm.get("allele_number"))) + .alleleFrequency(bigQueryService.getDouble(row, rm.get("allele_frequency"))) + .homozygoteCount(bigQueryService.getLong(row, rm.get("homozygote_count"))) + ); + } + + SVVariantListResponse variantListResponse = new SVVariantListResponse(); + variantListResponse.setItems(variantList); + return ResponseEntity.ok(variantListResponse); } @Override @@ -455,7 +935,7 @@ public ResponseEntity searchVariants(SearchVariantsRequest genes = searchTermType.getGenes(); low = searchTermType.getLow(); high = searchTermType.getHigh(); - contig = searchTermType.getContig(); + contig = "(?i)\\b" + searchTermType.getContig() + "\\b"; variant_id = searchTermType.getVariantId(); rs_id = searchTermType.getRsId(); whereGeneFlag = searchTermType.isWhereGeneFlag(); @@ -622,10 +1102,6 @@ public ResponseEntity searchVariants(SearchVariantsRequest finalSql += ORDER_BY_CLAUSE; finalSql += " LIMIT " + rowCount + " OFFSET " + ((Optional.ofNullable(page).orElse(1)-1)*rowCount); - System.out.println("**************************"); - System.out.println(finalSql); - System.out.println("**************************"); - QueryJobConfiguration qjc = QueryJobConfiguration.newBuilder(finalSql) .addNamedParameter("contig", QueryParameterValue.string(contig)) .addNamedParameter("high", QueryParameterValue.int64(high)) @@ -654,6 +1130,14 @@ public ResponseEntity searchVariants(SearchVariantsRequest ); } + + System.out.println(contig); + System.out.println("-------------------------------------------------"); + System.out.println("-------------------------------------------------"); + System.out.println(finalSql); + System.out.println("-------------------------------------------------"); + System.out.println("-------------------------------------------------"); + VariantListResponse variantListResponse = new VariantListResponse(); variantListResponse.setItems(variantList); return ResponseEntity.ok(variantListResponse); @@ -690,7 +1174,7 @@ public ResponseEntity getGenomicFilterOptions(String variantSear genes = searchTermType.getGenes(); low = searchTermType.getLow(); high = searchTermType.getHigh(); - contig = searchTermType.getContig(); + contig = "(?i)\\b" + searchTermType.getContig() + "\\b"; variant_id = searchTermType.getVariantId(); rs_id = searchTermType.getRsId(); whereGeneFlag = searchTermType.isWhereGeneFlag(); @@ -802,6 +1286,8 @@ public ResponseEntity getGenomicFilterOptions(String variantSear homozygoteCountFilter = genomicFilterOption; } } + + GenomicFilterOption alleleFrequencyFilter = new GenomicFilterOption(); alleleFrequencyFilter.setOption(""); alleleFrequencyFilter.setCount(0L); @@ -830,6 +1316,152 @@ public ResponseEntity getGenomicFilterOptions(String variantSear return ResponseEntity.ok(genomicFilters); } + @Override + public ResponseEntity getSVGenomicFilterOptions(String variantSearchTerm) { + try { + cdrVersionService.setDefaultCdrVersion(); + } catch(NullPointerException ie) { + throw new ServerErrorException("Cannot set default cdr version"); + } + String finalSql = SV_FILTER_OPTION_SQL_TEMPLATE_GENE; + + String genes = ""; + Long low = 0L; + Long high = 0L; + String variant_id = ""; + + String searchTerm = variantSearchTerm.trim(); + boolean whereGeneFlag = false; + boolean whereContigFlag = false; + boolean wherePositionFlag = false; + boolean whereVariantIdFlag = false; + if (variantSearchTerm.startsWith("~")) { + searchTerm = variantSearchTerm.substring(1); + } + String contig = searchTerm; + String searchSqlQuery = ""; + // Make sure the search term is not empty + if (!Strings.isNullOrEmpty(searchTerm)) { + SVGenomicSearchTermType searchTermType = getSVSearchType(variantSearchTerm, searchTerm); + searchSqlQuery += searchTermType.getSearchSqlQuery(); + genes = searchTermType.getGenes(); + low = searchTermType.getLow(); + high = searchTermType.getHigh(); + contig = "(?i)\\b" + searchTermType.getContig() + "\\b"; + variant_id = searchTermType.getVariantId(); + whereGeneFlag = searchTermType.isWhereGeneFlag(); + whereContigFlag = searchTermType.isWhereContigFlag(); + wherePositionFlag = searchTermType.isWherePositionFlag(); + whereVariantIdFlag = searchTermType.isWhereVariantIdFlag(); + } + + + finalSql = SV_FILTER_OPTION_SQL_TEMPLATE_GENE + searchSqlQuery + + SV_FILTER_OPTION_SQL_TEMPLATE_VAR_TYPE + searchSqlQuery + + SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT + searchSqlQuery + + SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_NUMBER + searchSqlQuery + + SV_FILTER_OPTION_SQL_TEMPLATE_HOMOZYGOTE_COUNT + searchSqlQuery + + SV_FILTER_OPTION_SQL_TEMPLATE_UNION; + + System.out.println("Test"); + System.out.println(finalSql); + System.out.println("Test"); + + QueryJobConfiguration qjc = QueryJobConfiguration.newBuilder(finalSql) + .addNamedParameter("contig", QueryParameterValue.string(contig)) + .addNamedParameter("high", QueryParameterValue.int64(high)) + .addNamedParameter("low", QueryParameterValue.int64(low)) + .addNamedParameter("variant_id", QueryParameterValue.string(variant_id)) + .addNamedParameter("genes", QueryParameterValue.string(genes)) + .setUseLegacySql(false) + .build(); + + qjc = bigQueryService.filterBigQueryConfig(qjc); + TableResult result = bigQueryService.executeQuery(qjc); + Map rm = bigQueryService.getResultMapper(result); + SVGenomicFilters genomicFilters = new SVGenomicFilters(); + + SVGenomicFilterOptionList varTypeFilterList = new SVGenomicFilterOptionList(); + SVGenomicFilterOptionList geneFilterList = new SVGenomicFilterOptionList(); + + List varTypeFilters = new ArrayList<>(); + List geneFilters = new ArrayList<>(); + + SVGenomicFilterOption alleleCountFilter = new SVGenomicFilterOption(); + SVGenomicFilterOption alleleNumberFilter = new SVGenomicFilterOption(); + SVGenomicFilterOption homozygoteCountFilter = new SVGenomicFilterOption(); + + for (List row : result.iterateAll()) { + String option = bigQueryService.getString(row, rm.get("option")); + String varType = bigQueryService.getString(row, rm.get("variant_type")); + Long variantTypeCount = bigQueryService.getLong(row, rm.get("variant_type_count")); + String gene = bigQueryService.getString(row, rm.get("genes")); + Long geneCount = bigQueryService.getLong(row, rm.get("gene_count")); + Long minCount = bigQueryService.getLong(row, rm.get("min_count")); + Long maxCount = bigQueryService.getLong(row, rm.get("max_count")); + SVGenomicFilterOption genomicFilterOption = new SVGenomicFilterOption(); + if (option.equals("Gene")) { + genomicFilterOption.setOption(gene); + genomicFilterOption.setCount(geneCount); + genomicFilterOption.setChecked(false); + genomicFilterOption.setMin(0L); + genomicFilterOption.setMax(0L); + geneFilters.add(genomicFilterOption); + } else if (option.equals("Variant Type")) { + genomicFilterOption.setOption(varType); + genomicFilterOption.setCount(variantTypeCount); + genomicFilterOption.setChecked(false); + genomicFilterOption.setMin(0L); + genomicFilterOption.setMax(0L); + varTypeFilters.add(genomicFilterOption); + } else if (option.equals("Allele Count")) { + genomicFilterOption.setOption(""); + genomicFilterOption.setCount(0L); + genomicFilterOption.setChecked(false); + genomicFilterOption.setMin(minCount); + genomicFilterOption.setMax(maxCount); + alleleCountFilter = genomicFilterOption; + } else if (option.equals("Allele Number")) { + genomicFilterOption.setOption(""); + genomicFilterOption.setCount(0L); + genomicFilterOption.setChecked(false); + genomicFilterOption.setMin(minCount); + genomicFilterOption.setMax(maxCount); + alleleNumberFilter = genomicFilterOption; + } else if (option.equals("Homozygote Count")) { + genomicFilterOption.setOption(""); + genomicFilterOption.setCount(0L); + genomicFilterOption.setChecked(false); + genomicFilterOption.setMin(minCount); + genomicFilterOption.setMax(maxCount); + homozygoteCountFilter = genomicFilterOption; + } + } + + SVGenomicFilterOption alleleFrequencyFilter = new SVGenomicFilterOption(); + alleleFrequencyFilter.setOption(""); + alleleFrequencyFilter.setCount(0L); + alleleFrequencyFilter.setChecked(false); + alleleFrequencyFilter.setMin(0L); + alleleFrequencyFilter.setMax(1L); + + varTypeFilterList.setItems(varTypeFilters); + varTypeFilterList.setFilterActive(false); + + geneFilterList.setItems(geneFilters); + geneFilterList.setFilterActive(false); + + + genomicFilters.variantType(varTypeFilterList); + genomicFilters.gene(geneFilterList); + genomicFilters.alleleCount(alleleCountFilter); + genomicFilters.alleleNumber(alleleNumberFilter); + genomicFilters.alleleFrequency(alleleFrequencyFilter); + genomicFilters.homozygoteCount(homozygoteCountFilter); + + return ResponseEntity.ok(genomicFilters); + } + @Override public ResponseEntity getChartData() { try { @@ -974,4 +1606,68 @@ public static GenomicSearchTermType getSearchType(String variantSearchTerm, Stri searchTermType.setSearchSqlQuery(searchSql); return searchTermType; } + + public static SVGenomicSearchTermType getSVSearchType(String variantSearchTerm, String searchTerm) { + SVGenomicSearchTermType searchTermType = new SVGenomicSearchTermType(); + String variant_id = ""; + boolean whereVariantIdFlag = false; + boolean whereGeneFlag = false; + boolean wherePositionFlag = false; + String contig = "(?i)(" + searchTerm + ")$"; + String searchSql = ""; + String genes = ""; + Long low = 0L; + Long high = 0L; + boolean whereContigFlag = false; + + if (searchTerm.matches(genomicRegionRegex)) { + String[] regionTermSplit = new String[0]; + if (searchTerm.contains(":")) { + regionTermSplit = searchTerm.split(":"); + contig = regionTermSplit[0].substring(0, 3).toLowerCase() + regionTermSplit[0].substring(3).toUpperCase(); + } + whereContigFlag = true; + searchSql = WHERE_CHROM; + if (regionTermSplit.length > 1) { + String[] rangeSplit = regionTermSplit[1].split("-"); + try { + if (rangeSplit.length == 2) { + low = Math.min(Long.valueOf(rangeSplit[0]), Long.valueOf(rangeSplit[1])); + high = Math.max(Long.valueOf(rangeSplit[0]), Long.valueOf(rangeSplit[1])); + wherePositionFlag = true; + searchSql += AND_POS; + } + } catch(NumberFormatException e) { + System.out.println("Trying to convert bad number."); + } + } + } else if (searchTerm.matches(svVariantIdRegex)) { + // Check if the search term matches variant id pattern + variant_id = searchTerm; + whereVariantIdFlag = true; + searchSql += WHERE_VARIANT_ID; + } else {// Check if the search term matches gene coding pattern + whereGeneFlag = true; + if (variantSearchTerm.startsWith("~")) { + genes = searchTerm.toUpperCase(); + } else { + // genes = "r\'(?i)(^|,)\\s*" + searchTerm.toUpperCase() + "\\s*(,|$)\'"; + // genes = "\\b" + searchTerm.toUpperCase() + "\\b"; + genes = "(?i)(^|,)\\s*" + searchTerm.toUpperCase() + "\\s*(,|$)"; + } + searchSql += WHERE_GENE_REGEX; + } + searchTermType.setVariantId(variant_id); + searchTermType.setWhereVariantIdFlag(whereVariantIdFlag); + searchTermType.setGenes(genes); + searchTermType.setWhereGeneFlag(whereGeneFlag); + searchTermType.setContig(contig); + searchTermType.setLow(low); + searchTermType.setHigh(high); + searchTermType.setWhereContigFlag(whereContigFlag); + searchTermType.setWherePositionFlag(wherePositionFlag); + + searchTermType.setSearchSqlQuery(searchSql); + return searchTermType; + } } \ No newline at end of file diff --git a/public-api/src/main/resources/application.properties b/public-api/src/main/resources/application.properties index 526b16b98..fdccebad0 100644 --- a/public-api/src/main/resources/application.properties +++ b/public-api/src/main/resources/application.properties @@ -1,6 +1,6 @@ # Uncomment the following to turn on full SQL debugging -logging.level.org.hibernate.SQL=DEBUG -logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +#logging.level.org.hibernate.SQL=DEBUG +#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE spring.jpa.properties.hibernate.show_sql=true #spring.jpa.properties.hibernate.format_sql=true #spring.jpa.properties.hibernate.type=trace diff --git a/public-api/src/main/resources/public-api.yaml b/public-api/src/main/resources/public-api.yaml index 73eae16ab..d3fd801ec 100644 --- a/public-api/src/main/resources/public-api.yaml +++ b/public-api/src/main/resources/public-api.yaml @@ -1,1681 +1,2093 @@ -openapi: 3.0.1 +# If validation fails, gradle:generateApi fails claiming this file does not exist. +# For separate validation (with some false positives), do: +# ./project.rb validate-swagger +swagger: '2.0' info: - title: AllOfUs Public API - description: The API for the AllOfUs data browser and public storefront. - termsOfService: http://www.pmi-ops.org/terms_of_service.html + version: "0.1.0" + title: "AllOfUs Public API" + description: "The API for the AllOfUs data browser and public storefront." + termsOfService: "http://www.pmi-ops.org/terms_of_service.html" contact: - name: developer_help@pmi-ops.org + name: "developer_help@pmi-ops.org" license: - name: BSD - version: 0.1.0 -servers: - - url: https://public-api.pmi-ops.org/ + name: "BSD" +host: "public-api.pmi-ops.org" +schemes: + - "https" +produces: + - "application/json" +securityDefinitions: + # Establish the fact that *some endpoints* are OAuth protected + # by defining an `aou_oauth` security mode, which we'll assing + # to any protected endpoints below. + aou_oauth: + # TODO: Vet/fix this token and/or authorization URL to work in practice. + # These are currently included simply to satisfy the Swagger specification, + # as this is not directly used to dictate oauth details (just used to + # annotate which methods require oauth). + authorizationUrl: "" + tokenUrl: "" + type: oauth2 + flow: accessCode security: - aou_oauth: [] + +# Throughout, we use integer/int64 in preference to string/date-time because Swagger's +# date formatting is inconsistent between server and client. Time values are stored as +# milliseconds since the UNIX epoch. + + +########################################################################################## +## PATHS +########################################################################################## + paths: + + # CDR versions ######################################################################### + /v1/cdrVersions: get: tags: - cdrVersions - description: Returns all curated data repository (CDR) versions that the user - has access to + description: Returns all curated data repository (CDR) versions that the user has access to operationId: getCdrVersions responses: - "200": + 200: description: A list of CDR versions. - content: - application/json: - schema: - $ref: '#/components/schemas/CdrVersionListResponse' + schema: + $ref: "#/definitions/CdrVersionListResponse" + + # Genomics ########################################################################### /v1/genomics/participant-counts: get: tags: - genomics description: Gets the participant counts - operationId: getParticipantCounts + operationId: "getParticipantCounts" + parameters: [] responses: - "200": + 200: description: A collection of results from achilles results view - content: - application/json: - schema: - $ref: '#/components/schemas/Analysis' + schema: + $ref: "#/definitions/Analysis" + /v1/genomics/search-variants: post: tags: - genomics + consumes: + - application/json description: Gets the list of matched variants - operationId: searchVariants - requestBody: - description: search concept request - content: - application/json: - schema: - $ref: '#/components/schemas/SearchVariantsRequest' - required: false + operationId: "searchVariants" + parameters: + - in: body + name: request + description: search concept request + schema: + $ref: "#/definitions/SearchVariantsRequest" responses: - "200": + 200: description: A collection of variants from the wgs_variant table - content: - application/json: - schema: - $ref: '#/components/schemas/VariantListResponse' - x-codegen-request-body-name: request + schema: + $ref: "#/definitions/VariantListResponse" + /v1/genomics/variant-search-result-size: post: tags: - genomics + consumes: + - application/json description: Gets the size of variant result list - operationId: getVariantSearchResultSize - requestBody: - description: variant result size request - content: - application/json: - schema: - $ref: '#/components/schemas/VariantResultSizeRequest' - required: false + operationId: "getVariantSearchResultSize" + parameters: + - in: body + name: request + description: variant result size request + schema: + $ref: "#/definitions/VariantResultSizeRequest" responses: - "200": + 200: description: Variant result list size - content: - application/json: - schema: - type: integer - format: int64 - x-codegen-request-body-name: request + schema: + type: integer + format: int64 + + /v1/genomics/sv-variant-search-result-size: + post: + tags: + - genomics + consumes: + - application/json + description: Gets the size of variant result list + operationId: "getSVVariantSearchResultSize" + parameters: + - in: body + name: request + description: variant result size request + schema: + $ref: "#/definitions/SVVariantResultSizeRequest" + responses: + 200: + description: Variant result list size + schema: + type: integer + format: int64 + /v1/genomics/chart-demo-data: get: tags: - genomics description: Gets the demographic participant counts - operationId: getChartData + operationId: "getChartData" + parameters: [] responses: - "200": + 200: description: A collection of results from achilles results view - content: - application/json: - schema: - $ref: '#/components/schemas/AnalysisListResponse' + schema: + $ref: "#/definitions/AnalysisListResponse" + /v1/genomics/variant-details/{variantId}: get: tags: - genomics description: Gets the variant details - operationId: getVariantDetails + operationId: "getVariantDetails" parameters: - - name: variantId - in: path - description: the variant id + - in: path + name: variantId + type: string required: true - schema: - type: string + description: the variant id responses: - "200": + 200: description: Variant Information - content: - application/json: - schema: - $ref: '#/components/schemas/VariantInfo' + schema: + $ref: "#/definitions/VariantInfo" + /v1/genomics/genomic-filter-options: get: tags: - genomics description: Gets the size of each genomic filter option - operationId: getGenomicFilterOptions + operationId: "getGenomicFilterOptions" parameters: - - name: variant-search-term - in: query - description: variant search term + - in: query + name: variant-search-term + type: string required: true + description: variant search term + responses: + 200: + description: A collection of filters schema: - type: string + $ref: "#/definitions/GenomicFilters" + + /v1/genomics/sv-genomic-filter-options: + get: + tags: + - genomics + description: Gets the size of each genomic filter option + operationId: "getSVGenomicFilterOptions" + parameters: + - in: query + name: variant-search-term + type: string + required: true + description: variant search term responses: - "200": + 200: description: A collection of filters - content: - application/json: - schema: - $ref: '#/components/schemas/GenomicFilters' + schema: + $ref: "#/definitions/SVGenomicFilters" + + /v1/genomics/search-sv-variants: + post: + tags: + - genomics + consumes: + - application/json + description: Gets the list of matched variants + operationId: "searchSVVariants" + parameters: + - in: body + name: request + description: search concept request + schema: + $ref: "#/definitions/SearchSVVariantsRequest" + responses: + 200: + description: A collection of variants from the wgs_variant table + schema: + $ref: "#/definitions/SVVariantListResponse" + + # Data Browser ####################################################################### /v1/config: get: tags: - config description: Returns some server configuration data. operationId: getConfig + security: [] responses: - "200": + 200: description: Configuration data - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigResponse' - security: [] + schema: + "$ref": "#/definitions/ConfigResponse" + /v1/databrowser/cdrversion-used: get: tags: - dataBrowser description: Gets the cdr versions - operationId: getCdrVersionUsed + operationId: "getCdrVersionUsed" + parameters: [] responses: - "200": + 200: description: Default cdr version - content: - application/json: - schema: - $ref: '#/components/schemas/CdrVersion' - "500": + schema: + "$ref": "#/definitions/CdrVersion" + 500: description: Internal Server Error - content: {} + /v1/databrowser/count-analysis: get: tags: - dataBrowser description: Gets EHR biological sex and age count analysis - operationId: getCountAnalysis + operationId: "getCountAnalysis" parameters: - - name: domain-id - in: query + - in: query + name: domain-id + type: string + required: true description: domain id + - in: query + name: domain-desc + type: string required: true - schema: - type: string - - name: domain-desc - in: query description: domain desc - required: true - schema: - type: string responses: - "200": + 200: description: A collection of count analysis for biological sex and age - content: - application/json: - schema: - $ref: '#/components/schemas/CountAnalysis' + schema: + $ref: "#/definitions/CountAnalysis" + /v1/databrowser/survey-question-counts: get: tags: - dataBrowser + description: Gets participant count of question by each stratum - operationId: getSurveyQuestionCounts + operationId: "getSurveyQuestionCounts" parameters: - - name: questionConceptId - in: query + - in: query + name: questionConceptId + type: string required: true - schema: - type: string - - name: questionPath - in: query + - in: query + name: questionPath + type: string required: true - schema: - type: string responses: - "200": + 200: description: Gender and age stratum counts for each question - content: - application/json: - schema: - $ref: '#/components/schemas/AnalysisListResponse' + schema: + $ref: "#/definitions/AnalysisListResponse" + /v1/databrowser/fitbit-analysis-results: get: tags: - dataBrowser description: Gets analysis results of fitbit - operationId: getFitbitAnalysisResults + operationId: "getFitbitAnalysisResults" parameters: - - name: concept-names - in: query - description: concept name + - in: query + name: concept-names + type: array + items: + type: string required: true - style: form - explode: false - schema: - type: array - items: - type: string + description: concept name responses: - "200": + 200: description: Analysis objects for each of fitbit measurement - content: - application/json: - schema: - $ref: '#/components/schemas/ConceptAnalysisListResponse' + schema: + $ref: "#/definitions/ConceptAnalysisListResponse" + + + /v1/databrowser/concept-analysis-results: get: tags: - dataBrowser + description: Gets analysis results for concept - operationId: getConceptAnalysisResults + operationId: "getConceptAnalysisResults" parameters: - - name: concept-ids - in: query - description: concept id + - in: query + name: concept-ids + type: array + items: + type: string required: true - style: form - explode: false - schema: - type: array - items: - type: string - - name: domain-id - in: query + description: concept id + - in: query + name: domain-id + type: string + required: false description: domain id - schema: - type: string responses: - "200": + 200: description: A collection of analysis for concept - content: - application/json: - schema: - $ref: '#/components/schemas/ConceptAnalysisListResponse' + schema: + $ref: "#/definitions/ConceptAnalysisListResponse" + /v1/databrowser/participant-count: get: tags: - dataBrowser + description: Gets results for an analysis id and stratum - operationId: getParticipantCount + operationId: "getParticipantCount" + parameters: [] responses: - "200": + 200: description: A collection of analysis results from achilles_results_view - content: - application/json: - schema: - $ref: '#/components/schemas/AchillesResult' + schema: + $ref: "#/definitions/AchillesResult" + /v1/databrowser/gender-count: get: tags: - dataBrowser + description: Gets results for an analysis id - operationId: getGenderAnalysis + operationId: "getGenderAnalysis" + parameters: [] responses: - "200": + 200: description: Gender analysis along with results - content: - application/json: - schema: - $ref: '#/components/schemas/Analysis' + schema: + $ref: "#/definitions/Analysis" + /v1/databrowser/getCriteriaRolledCounts: get: tags: - dataBrowser description: Gets the rolled up count from criteria - operationId: getCriteriaRolledCounts + operationId: "getCriteriaRolledCounts" parameters: - - name: conceptId - in: query + - in: query + name: conceptId + type: integer + format: int64 + required: true description: parent concept id + - in: query + name: domain + type: string required: true - schema: - type: integer - format: int64 - - name: domain - in: query description: parent concept domain - required: true - schema: - type: string responses: - "200": + 200: description: Rolled up counts of concept - content: - application/json: - schema: - $ref: '#/components/schemas/CriteriaParentResponse' + schema: + $ref: "#/definitions/CriteriaParentResponse" + /v1/databrowser/getCriteriaChildren: get: tags: - dataBrowser description: Gets the children when parent concept is expanded in ui - operationId: getCriteriaChildren + operationId: "getCriteriaChildren" parameters: - - name: parentId - in: query - description: parent id + - in: query + name: parentId + type: integer + format: int64 required: true - schema: - type: integer - format: int64 + description: parent id responses: - "200": + 200: description: Criteria children - content: - application/json: - schema: - $ref: '#/components/schemas/CriteriaListResponse' + schema: + $ref: "#/definitions/CriteriaListResponse" + /v1/databrowser/domain-totals: get: tags: - dataBrowser - description: Gets the domain filters and survey modules with the count of all - concepts and questions - operationId: getDomainTotals + description: Gets the domain filters and survey modules with the count of all concepts and questions + operationId: "getDomainTotals" parameters: - - name: searchWord - in: query + - in: query + name: searchWord + type: string description: search key word - schema: - type: string - - name: testFilter - in: query + - in: query + name: testFilter + type: integer + format: int32 description: measurement filter - schema: - type: integer - format: int32 - - name: orderFilter - in: query + - in: query + name: orderFilter + type: integer + format: int32 description: measurement filter - schema: - type: integer - format: int32 responses: - "200": + 200: description: A collection of domains and survey modules - content: - application/json: - schema: - $ref: '#/components/schemas/DomainInfosAndSurveyModulesResponse' + schema: + $ref: "#/definitions/DomainInfosAndSurveyModulesResponse" + /v1/databrowser/searchConcepts: post: tags: - dataBrowser + consumes: + - application/json description: Gets list of matched concepts - operationId: searchConcepts - requestBody: - description: search concept request - content: - application/json: - schema: - $ref: '#/components/schemas/SearchConceptsRequest' - required: false + operationId: "searchConcepts" + parameters: + - in: body + name: request + description: search concept request + schema: + $ref: "#/definitions/SearchConceptsRequest" responses: - "200": + 200: description: A collection of matched concepts - content: - application/json: - schema: - $ref: '#/components/schemas/ConceptListResponse' - x-codegen-request-body-name: request + schema: + $ref: "#/definitions/ConceptListResponse" + + /v1/databrowser/source-concepts: get: tags: - dataBrowser + description: Get children of the given concept - operationId: getSourceConcepts + operationId: "getSourceConcepts" parameters: - - name: concept_id - in: query - description: concept id to get maps to concepts + - in: query + name: concept_id + type: integer + format: int64 required: true - schema: - type: integer - format: int64 - - name: minCount - in: query + description: concept id to get maps to concepts + - in: query + name: minCount + type: integer + format: int32 + required: false description: minimum source count - schema: - type: integer - format: int32 responses: - "200": + 200: description: a collection of concepts - content: - application/json: - schema: - $ref: '#/components/schemas/ConceptListResponse' + schema: + $ref: "#/definitions/ConceptListResponse" + /v1/databrowser/survey-questions: get: tags: - dataBrowser description: Get survey questions - operationId: getSurveyQuestions + operationId: "getSurveyQuestions" parameters: - - name: survey_concept_id - in: query - description: survey concept id - required: true - schema: + - in: query + name: survey_concept_id type: integer format: int64 - - name: search_word - in: query - description: search word in surveys page - schema: + required: true + description: survey concept id + - in: query + name: search_word type: string + required: false + description: search word in surveys page responses: - "200": + 200: description: survey question fetch - content: - application/json: - schema: - $ref: '#/components/schemas/SurveyQuestionFetchResponse' + schema: + $ref: "#/definitions/SurveyQuestionFetchResponse" + /v1/databrowser/sub-questions: get: tags: - dataBrowser description: Get survey sub questions - operationId: getSubQuestions + operationId: "getSubQuestions" parameters: - - name: survey_concept_id - in: query + - in: query + name: survey_concept_id + type: integer + format: int64 + required: true description: survey concept id + - in: query + name: question_concept_id + type: integer + format: int64 required: true - schema: - type: integer - format: int64 - - name: question_concept_id - in: query description: question concept id + - in: query + name: answer_concept_id + type: integer + format: int64 required: true - schema: - type: integer - format: int64 - - name: answer_concept_id - in: query description: answer concept id + - in: query + name: level + type: integer + format: int32 required: true - schema: - type: integer - format: int64 - - name: level - in: query description: sub question level - required: true - schema: - type: integer - format: int32 - - name: path - in: query + - in: query + name: path + type: string + required: false description: question path - schema: - type: string responses: - "200": + 200: description: survey sub question fetch - content: - application/json: - schema: - $ref: '#/components/schemas/SurveyQuestionFetchResponse' + schema: + $ref: "#/definitions/SurveyQuestionFetchResponse" + /v1/databrowser/survey-question-results: get: tags: - dataBrowser description: Get Survey Question Results - operationId: getSurveyQuestionResults + operationId: "getSurveyQuestionResults" parameters: - - name: survey_concept_id - in: query + - in: query + name: survey_concept_id + type: integer + format: int64 + required: true description: survey concept id + - in: query + name: question_concept_id + type: integer + format: int64 required: true - schema: - type: integer - format: int64 - - name: question_concept_id - in: query description: question concept id - required: true - schema: - type: integer - format: int64 - - name: questionPath - in: query + - in: query + name: questionPath + type: string description: question path - schema: - type: string responses: - "200": + 200: description: survey question results - content: - application/json: - schema: - $ref: '#/components/schemas/AnalysisListResponse' + schema: + $ref: "#/definitions/AnalysisListResponse" + /v1/databrowser/survey-version-counts: get: tags: - dataBrowser description: Get Survey Version Counts - operationId: getSurveyVersionCounts + operationId: "getSurveyVersionCounts" parameters: - - name: survey_concept_id - in: query - description: survey concept id + - in: query + name: survey_concept_id + type: integer + format: int64 required: true - schema: - type: integer - format: int64 + description: survey concept id responses: - "200": + 200: description: survey version counts - content: - application/json: - schema: - $ref: '#/components/schemas/SurveyVersionCountResponse' -components: - schemas: - Concept: - required: - - conceptClassId - - conceptCode - - conceptId - - conceptName - - domainId - - standardConcept - - vocabularyId - type: object - properties: - conceptId: - type: integer - description: id of the concept - format: int64 - conceptName: - type: string - description: name of concept - domainId: - type: string - description: domain of concept - vocabularyId: - type: string - description: vocabulary of concept - conceptCode: - type: string - description: original vocab code of concept - conceptClassId: - type: string - description: class of concept - standardConcept: - type: string - description: standard concept value 1 char - countValue: - type: integer - description: est count in the cdr - format: int64 - sourceCountValue: - type: integer - description: est source count in the cdr - format: int64 - prevalence: - type: number - description: prevalence among participants percent count divided num participants - format: float - conceptSynonyms: - type: array - description: concept synonym names - items: - type: string - canSelect: - type: integer - description: filters clickable concepts - format: int32 - measurementConceptInfo: - $ref: '#/components/schemas/MeasurementConceptInfo' - drugBrands: - type: array - description: drug brand names - items: - type: string - standardConcepts: - type: array - items: - $ref: '#/components/schemas/Concept' - matchType: - $ref: '#/components/schemas/MatchType' - ConceptListResponse: - required: - - items - type: object - properties: + schema: + $ref: "#/definitions/SurveyVersionCountResponse" + +########################################################################################## +## DEFINITIONS +########################################################################################## +definitions: + Concept: + type: object + required: + - conceptId + - conceptName + - domainId + - vocabularyId + - conceptCode + - conceptClassId + - standardConcept + + properties: + conceptId: + description: id of the concept + type: integer + format: int64 + conceptName: + description: name of concept + type: string + domainId: + description: domain of concept + type: string + vocabularyId: + description: vocabulary of concept + type: string + conceptCode: + description: original vocab code of concept + type: string + conceptClassId: + description: class of concept + type: string + standardConcept: + description: standard concept value 1 char + type: string + countValue: + description: est count in the cdr + type: integer + format: int64 + sourceCountValue: + description: est source count in the cdr + type: integer + format: int64 + prevalence: + description: prevalence among participants percent count divided num participants + type: number + format: float + conceptSynonyms: + description: concept synonym names + type: "array" items: - type: array - items: - $ref: '#/components/schemas/Concept' - matchType: - $ref: '#/components/schemas/MatchType' - matchedConceptName: - type: string - description: matched concept name - SurveyQuestionFetchResponse: - type: object - properties: - questions: - $ref: '#/components/schemas/SurveyMetadataListResponse' - survey: - $ref: '#/components/schemas/SurveyModule' - SubQuestionFetchResponse: - type: object - properties: - questions: - $ref: '#/components/schemas/SurveyMetadataListResponse' - SurveyVersionCountResponse: - type: object - properties: - analyses: - $ref: '#/components/schemas/AnalysisListResponse' - MeasurementConceptInfo: - type: object - properties: - conceptId: - type: integer - format: int64 - hasValues: - type: integer - format: int32 - ConfigResponse: - type: object - properties: - projectId: - type: string - description: The cloud project in which this app is running. - dataBrowserIsSafe: - type: boolean - description: Whether the data is safe to show. An emergency page will show - if set to false. - publicApiKeyForErrorReports: - type: string - description: | - Stackdriver API key for error reporting, scoped to a particular - domain. If unset, Stackdriver error reporting should be disabled. - Criteria: - required: - - id - - name - - parentId - - path - - type - type: object - properties: - id: - type: integer - description: Primary identifier which is unique within a CDR version. - format: int64 - parentId: - type: integer - description: The parent id of the criteria. 0 if this is the root node of - a criteria tree - format: int64 - type: - type: string - description: type of this criteria - subtype: - type: string - description: sub type of this criteria - code: - type: string - description: concept code - name: - type: string - description: concept name - group: - type: boolean - description: boolean field which represents if the criteria has children - selectable: - type: boolean - description: boolean field which represents if the criteria is selectable - count: - type: integer - description: count - format: int64 - sourceCount: - type: integer - description: count - format: int64 - domainId: - type: string - description: domain id - conceptId: - type: string - description: concept id - path: - type: string - description: path of concept in the criteria tree - synonyms: - type: string - description: synonyms - canSelect: - type: integer - description: filters clickable concepts - format: int32 - CriteriaListResponse: - required: - - items - type: object - properties: + type: "string" + canSelect: + description: filters clickable concepts + type: integer + format: int32 + measurementConceptInfo: + description: measurement concept info + type: object + $ref: "#/definitions/MeasurementConceptInfo" + drugBrands: + description: drug brand names + type: "array" items: - type: array - items: - $ref: '#/components/schemas/Criteria' - CriteriaParentResponse: - type: object - properties: - parent: - $ref: '#/components/schemas/Criteria' - SurveyMetadata: - required: - - conceptId - - conceptName - - surveyName - type: object - properties: - id: - type: integer - description: id of each row - format: int64 - conceptId: - type: integer - description: id of the concept - format: int64 - conceptName: - type: string - description: name of concept - surveyName: - type: string - description: survey name - conceptCode: - type: string - description: original vocab code of concept - countValue: - type: integer - description: est count in the cdr - format: int64 - surveyConceptId: - type: integer - description: survey concept id - format: int64 - sub: - type: integer - description: flag to indicate if a question is a sub question - format: int32 - path: - type: string - description: question path - is_parent_question: - type: integer - description: flag to indicate if the question is parent question - format: int32 - orderNumber: - type: integer - description: question order number - format: int32 - questionString: - type: string - description: question string - type: - type: string - description: type of survey data - countAnalysis: - $ref: '#/components/schemas/Analysis' - genderAnalysis: - $ref: '#/components/schemas/Analysis' - ageAnalysis: - $ref: '#/components/schemas/Analysis' - versionAnalysis: - $ref: '#/components/schemas/Analysis' - participantCountAnalysis: - $ref: '#/components/schemas/Analysis' - SurveyMetadataListResponse: - required: - - items - type: object - properties: + type: "string" + standardConcepts: + type: "array" items: - type: array - items: - $ref: '#/components/schemas/SurveyMetadata' - AchillesResult: - required: - - analysisId - type: object - properties: - id: - type: integer - description: pk id for jpa code to work - format: int64 - analysisId: - type: integer - description: id analysis - format: int64 - stratum1: - type: string - description: stratum 1 - stratum2: - type: string - description: stratum 2 - stratum3: - type: string - description: stratum 3 - stratum4: - type: string - description: stratum 4 - stratum5: - type: string - description: stratum 5 - stratum6: - type: string - description: stratum6 - stratum7: - type: string - description: stratum7 - analysisStratumName: - type: string - description: stratum 5 Name - measurementValueType: - type: string - description: measurement value type (text / numeric) - countValue: - type: integer - description: count - format: int64 - sourceCountValue: - type: integer - description: source count - format: int64 - AchillesResultListResponse: - required: - - items - type: object - properties: + $ref: "#/definitions/Concept" + description: standard concepts associated with the matched concept + matchType: + description: match column type on which concept search was successful + $ref: "#/definitions/MatchType" + + ConceptListResponse: + type: object + required: + - items + properties: + items: + type: "array" items: - type: array - items: - $ref: '#/components/schemas/AchillesResult' - AchillesResultDist: - required: - - analysisId - type: object - properties: - id: - type: integer - description: pk id for jpa code to work - format: int64 - analysisId: - type: integer - description: id analysis - format: int64 - stratum1: - type: string - description: stratum 1 - stratum2: - type: string - description: stratum 2 - stratum3: - type: string - description: stratum 3 - stratum4: - type: string - description: stratum 4 - stratum5: - type: string - description: stratum 5 - stratum6: - type: string - description: stratum 6 - countValue: - type: integer - description: count - format: int64 - minValue: - type: number - description: min value - format: float - maxValue: - type: number - description: max value - format: float - avgValue: - type: number - description: avg value - format: float - stdevValue: - type: number - description: std deviation value - format: float - medianValue: - type: number - description: median value - format: float - p10Value: - type: number - description: 10th percentile value - format: float - p25Value: - type: number - description: 25th percentile value - format: float - p75Value: - type: number - description: 75th percentile value - format: float - p90Value: - type: number - description: 90th percentile value - format: float - Analysis: - required: - - analysisId - type: object - properties: - analysisId: - type: integer - description: id analysis - format: int64 - analysisName: - type: string - description: analysis name - stratum1Name: - type: string - description: usually concept name corresponding to stratum - stratum2Name: - type: string - description: usually concept name corresponding to stratum - stratum3Name: - type: string - description: usually concept name corresponding to stratum - stratum4Name: - type: string - description: usually concept name corresponding to stratum - stratum5Name: - type: string - description: usually concept name corresponding to stratum - stratum6Name: - type: string - description: usually concept name corresponding to stratum - stratum7Name: - type: string - description: usually concept name corresponding to stratum - chartType: - type: string - description: chart type to display for this analysis column pie box - dataType: - type: string - description: data type of this analysis count or distribution - results: - type: array - description: count results - items: - $ref: '#/components/schemas/AchillesResult' - distResults: - type: array - description: distribution results - items: - $ref: '#/components/schemas/AchillesResultDist' - unitName: - type: string - description: unit name - AnalysisListResponse: - required: - - items - type: object - properties: + $ref: "#/definitions/Concept" + matchType: + description: match column type on which concept search was successful + $ref: "#/definitions/MatchType" + matchedConceptName: + description: matched concept name + type: string + + SurveyQuestionFetchResponse: + type: object + required: + - items + properties: + questions: + description: survey questions + type: object + $ref: "#/definitions/SurveyMetadataListResponse" + survey: + $ref: "#/definitions/SurveyModule" + + SubQuestionFetchResponse: + type: object + required: + - items + properties: + questions: + description: survey questions + type: object + $ref: "#/definitions/SurveyMetadataListResponse" + + SurveyVersionCountResponse: + type: object + required: + - items + properties: + analyses: + description: analysis objects + type: object + $ref: "#/definitions/AnalysisListResponse" + + MeasurementConceptInfo: + type: object + properties: + conceptId: + type: integer + format: int64 + hasValues: + type: integer + format: int32 + + ConfigResponse: + type: object + properties: + projectId: + type: string + description: The cloud project in which this app is running. + dataBrowserIsSafe: + type: boolean + description: Whether the data is safe to show. An emergency page will show if set to false. + publicApiKeyForErrorReports: + type: string + description: | + Stackdriver API key for error reporting, scoped to a particular + domain. If unset, Stackdriver error reporting should be disabled. + Criteria: + type: object + required: + - id + - parentId + - type + - name + - path + properties: + id: + description: Primary identifier which is unique within a CDR version. + type: integer + format: int64 + parentId: + description: The parent id of the criteria. 0 if this is the root node of a criteria tree + type: integer + format: int64 + type: + description: type of this criteria + type: string + subtype: + description: sub type of this criteria + type: string + code: + description: concept code + type: string + name: + description: concept name + type: string + group: + description: boolean field which represents if the criteria has children + type: boolean + selectable: + description: boolean field which represents if the criteria is selectable + type: boolean + count: + description: count + type: integer + format: int64 + sourceCount: + description: count + type: integer + format: int64 + domainId: + description: domain id + type: string + conceptId: + description: concept id + type: string + path: + description: path of concept in the criteria tree + type: string + synonyms: + description: synonyms + type: string + canSelect: + description: filters clickable concepts + type: integer + format: int32 + + CriteriaListResponse: + type: object + required: + - items + properties: + items: + type: "array" items: - type: array - items: - $ref: '#/components/schemas/Analysis' - DomainInfosAndSurveyModulesResponse: - required: - - domainInfos - - surveyModules - type: object - properties: - domainInfos: - type: array - items: - $ref: '#/components/schemas/DomainInfo' - surveyModules: - type: array - items: - $ref: '#/components/schemas/SurveyModule' - ConceptAnalysis: - required: - - conceptId - type: object - properties: - conceptId: - type: string - description: concept id - countAnalysis: - $ref: '#/components/schemas/Analysis' - genderAnalysis: - $ref: '#/components/schemas/Analysis' - ageAnalysis: - $ref: '#/components/schemas/Analysis' - measurementValueGenderAnalysis: - type: array - description: measurement value gender analysis - items: - $ref: '#/components/schemas/Analysis' - measurementValueAgeAnalysis: - type: array - description: age analysis for measurement values - items: - $ref: '#/components/schemas/Analysis' - measurementDistributionAnalysis: - type: array - description: measurement distribution analysis - items: - $ref: '#/components/schemas/Analysis' - measurementGenderCountAnalysis: - type: array - description: measurement gender count by unit analysis - items: - $ref: '#/components/schemas/Analysis' - ehrCountAnalysis: - $ref: '#/components/schemas/Analysis' - participantCountAnalysis: - $ref: '#/components/schemas/Analysis' - ConceptAnalysisListResponse: - required: - - items - type: object - properties: + $ref: "#/definitions/Criteria" + + CriteriaParentResponse: + type: object + required: + - items + properties: + parent: + description: parent row + $ref: "#/definitions/Criteria" + + SurveyMetadata: + type: object + required: + - conceptId + - conceptName + - surveyName + + properties: + id: + description: id of each row + type: integer + format: int64 + conceptId: + description: id of the concept + type: integer + format: int64 + conceptName: + description: name of concept + type: string + surveyName: + description: survey name + type: string + conceptCode: + description: original vocab code of concept + type: string + countValue: + description: est count in the cdr + type: integer + format: int64 + surveyConceptId: + description: survey concept id + type: integer + format: int64 + sub: + description: flag to indicate if a question is a sub question + type: integer + format: int32 + path: + description: question path + type: string + is_parent_question: + description: flag to indicate if the question is parent question + type: integer + format: int32 + orderNumber: + description: question order number + type: integer + format: int32 + questionString: + description: question string + type: string + type: + description: type of survey data + type: string + countAnalysis: + description: count analysis + $ref: "#/definitions/Analysis" + genderAnalysis: + description: gender analysis + $ref: "#/definitions/Analysis" + ageAnalysis: + description: age analysis + $ref: "#/definitions/Analysis" + versionAnalysis: + description: version analysis + $ref: "#/definitions/Analysis" + participantCountAnalysis: + description: participant count analysis + $ref: "#/definitions/Analysis" + + SurveyMetadataListResponse: + type: object + required: + - items + properties: + items: + type: array items: - type: array - items: - $ref: '#/components/schemas/ConceptAnalysis' - CountAnalysis: - required: - - domainId - type: object - properties: - domainId: - type: string - description: domain id - genderCountAnalysis: - $ref: '#/components/schemas/Analysis' - ageCountAnalysis: - $ref: '#/components/schemas/Analysis' - StandardConceptFilter: - type: string - description: "filter on whether standard, non-standard, or all concepts should\ - \ be returned\\" - enum: - - ALL_CONCEPTS - - STANDARD_CONCEPTS - - NON_STANDARD_CONCEPTS - - STANDARD_OR_CODE_ID_MATCH - TestFilter: - type: string - description: filter to toggle lab measurement tests - enum: - - SELECTED - - UNSELECTED - OrderFilter: - type: string - description: filter to toggle lab measurement orders - enum: - - SELECTED - - UNSELECTED - Domain: - type: string - description: a domain for concepts corresponding to a table in the OMOP schema - enum: - - OBSERVATION - - PROCEDURE - - DRUG - - CONDITION - - MEASUREMENT - - DEVICE - - DEATH - - VISIT - MatchType: - type: string - description: match column type on concept search - enum: - - CONCEPT_CODE - - CONCEPT_ID - - CONCEPT_NAME - AnalysisIdConstant: - type: string - description: analysis id constant map - enum: - - PARTICIPANT_COUNT_ANALYSIS_ID - - GENDER_ANALYSIS - - COUNT_ANALYSIS_ID - - SURVEY_GENDER_COUNT_ANALYSIS_ID - - SURVEY_AGE_COUNT_ANALYSIS_ID - - SURVEY_PARTICIPANT_COUNT_ANALYSIS_ID - - GENDER_ANALYSIS_ID - - PARTICIPANT_COUNT_BY_DATE_ANALYSIS_ID - - AGE_ANALYSIS_ID - - SURVEY_VERSION_PARTICIPANT_COUNT_ANALYSIS_ID - - SURVEY_VERSION_QUESTION_COUNT_ANALYSIS_ID - - SURVEY_COUNT_ANALYSIS_ID - - SURVEY_GENDER_ANALYSIS_ID - - SURVEY_AGE_ANALYSIS_ID - - SURVEY_VERSION_ANALYSIS_ID - - SURVEY_QUESTION_GENDER_COUNT_ANALYSIS_ID - - SURVEY_QUESTION_AGE_COUNT_ANALYSIS_ID - - EHR_GENDER_COUNT_ANALYSIS_ID - - EHR_AGE_COUNT_ANALYSIS_ID - - RACE_ANALYSIS_ID - - ETHNICITY_ANALYSIS_ID - - MEASUREMENT_DIST_ANALYSIS_ID - - MEASUREMENT_GENDER_ANALYSIS_ID - - MEASUREMENT_GENDER_UNIT_ANALYSIS_ID - - RACE_ANALYSIS - - ETHNICITY_ANALYSIS - - GENO_GENDER_ANALYSIS - - GENO_AGE_ANALYSIS - - GENO_RACE_ANALYSIS - VariantResultSizeRequest: - required: - - query - type: object - properties: - query: - type: string - description: | - A query string that can be used to match a subset of the name (case-insensitively), the entire code value (case-insensitively), or the concept ID. - filterMetadata: - $ref: '#/components/schemas/GenomicFilters' - SearchVariantsRequest: - required: - - query - type: object - properties: - query: - type: string - description: | - A query string that can be used to match a subset of the name (case-insensitively), the entire code value (case-insensitively), or the concept ID. - pageNumber: - type: integer - description: By default it returns the first page and then its next pages - from that on. - rowCount: - type: integer - description: Number of rows user wants to view per page - sortMetadata: - $ref: '#/components/schemas/SortMetadata' - filterMetadata: - $ref: '#/components/schemas/GenomicFilters' - SearchConceptsRequest: - required: - - query - type: object - properties: - query: - type: string - description: | - A query string that can be used to match a subset of the name (case-insensitively), the entire code value (case-insensitively), or the concept ID. - standardConceptFilter: - $ref: '#/components/schemas/StandardConceptFilter' - vocabularyIds: - type: array - description: "The vocabulary ID for the concepts returned (e.g. SNOMED,\ - \ RxNorm)" - items: - type: string - domain: - $ref: '#/components/schemas/Domain' - maxResults: - type: integer - description: The maximum number of results returned. Defaults to 25. - format: int32 - minCount: - type: integer - description: The minimum count of concepts to be fetched - format: int32 - pageNumber: - type: integer - description: By default it returns the first page and then its next pages - from that on. - measurementTests: - type: integer - description: By default all the measurement tests are returned - measurementOrders: - type: integer - description: By default all the measurement orders are returned - CdrVersion: - required: - - cdrVersionId - - creationTime - - name - type: object - properties: - cdrVersionId: - type: string - name: - type: string - numParticipants: - type: integer - format: int32 - creationTime: - type: integer - description: Milliseconds since the UNIX epoch. - format: int64 - CdrVersionListResponse: - required: - - defaultCdrVersionId - - items - type: object - properties: + $ref: "#/definitions/SurveyMetadata" + + AchillesResult: + type: object + required: + - analysisId + + properties: + id: + description: pk id for jpa code to work + type: integer + format: int64 + analysisId: + description: id analysis + type: integer + format: int64 + stratum1: + description: stratum 1 + type: string + stratum2: + description: stratum 2 + type: string + stratum3: + description: stratum 3 + type: string + stratum4: + description: stratum 4 + type: string + stratum5: + description: stratum 5 + type: string + stratum6: + description: stratum6 + type: string + stratum7: + description: stratum7 + type: string + analysisStratumName: + description: stratum 5 Name + type: string + measurementValueType: + description: measurement value type (text / numeric) + type: string + countValue: + description: count + type: integer + format: int64 + sourceCountValue: + description: source count + type: integer + format: int64 + + AchillesResultListResponse: + type: object + required: + - items + properties: + items: + type: "array" items: - type: array - items: - $ref: '#/components/schemas/CdrVersion' - defaultCdrVersionId: - type: string - description: ID of the CDR versions that should be used by the user by default - DomainInfo: - required: - - allConceptCount - - description - - domain - - name - - participantCount - - standardConceptCount - type: object - properties: - domain: - $ref: '#/components/schemas/Domain' - domainConceptId: - type: integer - description: domain concept id - format: int64 - name: - type: string - description: display name of the domain - description: - type: string - description: description of the domain - standardConceptCount: - type: integer - description: number of standard concepts in this domain - format: int64 - allConceptCount: - type: integer - description: number of concepts in this domain (standard or non-standard) - format: int64 - participantCount: - type: integer - description: number of participants with data in the CDR for a concept in - this domain - format: int64 - SortMetadata: - type: object - properties: - variantId: - $ref: '#/components/schemas/SortColumnDetails' - gene: - $ref: '#/components/schemas/SortColumnDetails' - consequence: - $ref: '#/components/schemas/SortColumnDetails' - variantType: - $ref: '#/components/schemas/SortColumnDetails' - clinicalSignificance: - $ref: '#/components/schemas/SortColumnDetails' - alleleCount: - $ref: '#/components/schemas/SortColumnDetails' - alleleNumber: - $ref: '#/components/schemas/SortColumnDetails' - alleleFrequency: - $ref: '#/components/schemas/SortColumnDetails' - homozygoteCount: - $ref: '#/components/schemas/SortColumnDetails' - SortColumnDetails: - type: object - properties: - sortActive: - type: boolean - description: Flag for active sort - sortDirection: - type: string - description: Sort Direction - sortOrder: - type: integer - description: Sort Order - format: int32 - SurveyModule: - required: - - conceptId - - description - - name - - participantCount - - questionCount - type: object - properties: - conceptId: - type: integer - description: the concept ID for the survey module - format: int64 - name: - type: string - description: display name of the module - description: - type: string - description: description of the module - questionCount: - type: integer - description: number of questions in the module - format: int64 - participantCount: - type: integer - description: number of participants with data in the CDR for questions in - this module - format: int64 - orderNumber: - type: integer - description: survey release order number - format: int32 - ErrorResponse: - type: object - properties: - message: - type: string - description: Error message - statusCode: - type: integer - description: Http error status code - errorClassName: - type: string - errorCode: - $ref: '#/components/schemas/ErrorCode' - errorUniqueId: - type: string - description: "Unique ID for this error response, for correlation with backend\ - \ logs" - ErrorCode: - type: string - description: Short parsable error descriptions - enum: - - PARSE_ERROR - Variant: - required: - - variantId - type: object - properties: - variantId: - type: string - description: id variant - genes: - type: string - description: genes - consequence: - type: string - description: consequence - variantType: - type: string - description: variant type - proteinChange: - type: string - description: protein change - clinicalSignificance: - type: string - description: clinical significance - alleleCount: - type: integer - description: allele count - format: int64 - alleleNumber: - type: integer - description: allele number - format: int64 - alleleFrequency: - type: number - description: allele frequency - format: double - homozygoteCount: - type: integer - description: homozygote count - format: int64 - VariantListResponse: - required: - - items - type: object - properties: + $ref: "#/definitions/AchillesResult" + + AchillesResultDist: + type: object + required: + - analysisId + + properties: + id: + description: pk id for jpa code to work + type: integer + format: int64 + analysisId: + description: id analysis + type: integer + format: int64 + stratum1: + description: stratum 1 + type: string + stratum2: + description: stratum 2 + type: string + stratum3: + description: stratum 3 + type: string + stratum4: + description: stratum 4 + type: string + stratum5: + description: stratum 5 + type: string + stratum6: + description: stratum 6 + type: string + countValue: + description: count + type: integer + format: int64 + minValue: + description: min value + type: number + format: float + maxValue: + description: max value + type: number + format: float + avgValue: + description: avg value + type: number + format: float + stdevValue: + description: std deviation value + type: number + format: float + medianValue: + description: median value + type: number + format: float + p10Value: + description: 10th percentile value + type: number + format: float + p25Value: + description: 25th percentile value + type: number + format: float + p75Value: + description: 75th percentile value + type: number + format: float + p90Value: + description: 90th percentile value + type: number + format: float + + Analysis: + type: object + required: + - analysisId + properties: + analysisId: + description: id analysis + type: integer + format: int64 + analysisName: + description: analysis name + type: string + stratum1Name: + description: usually concept name corresponding to stratum + type: string + stratum2Name: + description: usually concept name corresponding to stratum + type: string + stratum3Name: + description: usually concept name corresponding to stratum + type: string + stratum4Name: + description: usually concept name corresponding to stratum + type: string + stratum5Name: + description: usually concept name corresponding to stratum + type: string + stratum6Name: + description: usually concept name corresponding to stratum + type: string + stratum7Name: + description: usually concept name corresponding to stratum + type: string + chartType: + description: chart type to display for this analysis column pie box + type: string + dataType: + description: data type of this analysis count or distribution + type: string + results: + description: count results + type: array items: - type: array - items: - $ref: '#/components/schemas/Variant' - GenomicFilters: - type: object - properties: - gene: - $ref: '#/components/schemas/GenomicFilterOptionList' - consequence: - $ref: '#/components/schemas/GenomicFilterOptionList' - variantType: - $ref: '#/components/schemas/GenomicFilterOptionList' - clinicalSignificance: - $ref: '#/components/schemas/GenomicFilterOptionList' - alleleCount: - $ref: '#/components/schemas/GenomicFilterOption' - alleleNumber: - $ref: '#/components/schemas/GenomicFilterOption' - alleleFrequency: - $ref: '#/components/schemas/GenomicFilterOption' - homozygoteCount: - $ref: '#/components/schemas/GenomicFilterOption' - GenomicFilterOption: - type: object - properties: - option: - type: string - description: Filter Name - count: - type: integer - description: count - format: int64 - min: - type: integer - description: min value - format: int64 - max: - type: integer - description: max value - format: int64 - minFreq: - type: number - description: min Allele Frequency - format: float - default: 0.0 - maxFreq: - type: number - description: max Allele Frequency - format: float - default: 1.0 - checked: - type: boolean - description: boolean flag - GenomicFilterOptionList: - required: - - items - type: object - properties: + $ref: "#/definitions/AchillesResult" + distResults: + description: distribution results + type: array + items: + $ref: "#/definitions/AchillesResultDist" + unitName: + description: unit name + type: string + + AnalysisListResponse: + type: object + required: + - items + properties: + items: + type: "array" + items: + $ref: "#/definitions/Analysis" + + DomainInfosAndSurveyModulesResponse: + type: object + required: + - domainInfos + - surveyModules + properties: + domainInfos: + type: "array" + items: + $ref: "#/definitions/DomainInfo" + surveyModules: + type: "array" + items: + $ref: "#/definitions/SurveyModule" + + ConceptAnalysis: + type: object + required: + - conceptId + properties: + conceptId: + description: concept id + type: string + countAnalysis: + description: count analysis + type: object + $ref: "#/definitions/Analysis" + genderAnalysis: + description: gender analysis + type: object + $ref: "#/definitions/Analysis" + ageAnalysis: + description: age analysis + type: object + $ref: "#/definitions/Analysis" + measurementValueGenderAnalysis: + description: measurement value gender analysis + type: array + items: + $ref: "#/definitions/Analysis" + measurementValueAgeAnalysis: + description: age analysis for measurement values + type: array + items: + $ref: "#/definitions/Analysis" + measurementDistributionAnalysis: + description: measurement distribution analysis + type: array + items: + $ref: "#/definitions/Analysis" + measurementGenderCountAnalysis: + description: measurement gender count by unit analysis + type: array + items: + $ref: "#/definitions/Analysis" + ehrCountAnalysis: + description: count analysis + type: object + $ref: "#/definitions/Analysis" + participantCountAnalysis: + description: participant count by date analysis + type: object + $ref: "#/definitions/Analysis" + + ConceptAnalysisListResponse: + type: object + required: + - items + properties: + items: + type: array + items: + $ref: "#/definitions/ConceptAnalysis" + + CountAnalysis: + type: object + required: + - domainId + properties: + domainId: + description: domain id + type: string + genderCountAnalysis: + description: biological sex count analysis + type: object + $ref: "#/definitions/Analysis" + ageCountAnalysis: + description: age count analysis + type: object + $ref: "#/definitions/Analysis" + + StandardConceptFilter: + type: string + description: filter on whether standard, non-standard, or all concepts should be returned\ + enum: &STANDARD_CONCEPT_FILTER [ALL_CONCEPTS, STANDARD_CONCEPTS, NON_STANDARD_CONCEPTS, STANDARD_OR_CODE_ID_MATCH] + TestFilter: + type: string + description: filter to toggle lab measurement tests + enum: &TEST_FILTER [SELECTED, UNSELECTED] + OrderFilter: + type: string + description: filter to toggle lab measurement orders + enum: &ORDER_FILTER [SELECTED, UNSELECTED] + Domain: + type: string + description: a domain for concepts corresponding to a table in the OMOP schema + enum: + - OBSERVATION + - PROCEDURE + - DRUG + - CONDITION + - MEASUREMENT + - DEVICE + - DEATH + - VISIT + MatchType: + type: string + description: match column type on concept search + enum: + - CONCEPT_CODE + - CONCEPT_ID + - CONCEPT_NAME + AnalysisIdConstant: + type: string + description: analysis id constant map + enum: + - PARTICIPANT_COUNT_ANALYSIS_ID + - GENDER_ANALYSIS + - COUNT_ANALYSIS_ID + - SURVEY_GENDER_COUNT_ANALYSIS_ID + - SURVEY_AGE_COUNT_ANALYSIS_ID + - SURVEY_PARTICIPANT_COUNT_ANALYSIS_ID + - GENDER_ANALYSIS_ID + - PARTICIPANT_COUNT_BY_DATE_ANALYSIS_ID + - AGE_ANALYSIS_ID + - SURVEY_VERSION_PARTICIPANT_COUNT_ANALYSIS_ID + - SURVEY_VERSION_QUESTION_COUNT_ANALYSIS_ID + - SURVEY_COUNT_ANALYSIS_ID + - SURVEY_GENDER_ANALYSIS_ID + - SURVEY_AGE_ANALYSIS_ID + - SURVEY_VERSION_ANALYSIS_ID + - SURVEY_QUESTION_GENDER_COUNT_ANALYSIS_ID + - SURVEY_QUESTION_AGE_COUNT_ANALYSIS_ID + - EHR_GENDER_COUNT_ANALYSIS_ID + - EHR_AGE_COUNT_ANALYSIS_ID + - RACE_ANALYSIS_ID + - ETHNICITY_ANALYSIS_ID + - MEASUREMENT_DIST_ANALYSIS_ID + - MEASUREMENT_GENDER_ANALYSIS_ID + - MEASUREMENT_GENDER_UNIT_ANALYSIS_ID + - RACE_ANALYSIS + - ETHNICITY_ANALYSIS + - GENO_GENDER_ANALYSIS + - GENO_AGE_ANALYSIS + - GENO_RACE_ANALYSIS + + VariantResultSizeRequest: + type: object + required: + - query + properties: + query: + type: string + description: > + A query string that can be used to match a subset of the name (case-insensitively), + the entire code value (case-insensitively), or the concept ID. + filterMetadata: + type: object + description: filters + $ref: "#/definitions/GenomicFilters" + + SVVariantResultSizeRequest: + type: object + required: + - query + properties: + query: + type: string + description: > + A query string that can be used to match a subset of the name (case-insensitively), + the entire code value (case-insensitively), or the concept ID. + filterMetadata: + type: object + description: filters + $ref: "#/definitions/SVGenomicFilters" + + SearchVariantsRequest: + type: object + required: + - query + properties: + query: + type: string + description: > + A query string that can be used to match a subset of the name (case-insensitively), + the entire code value (case-insensitively), or the concept ID. + pageNumber: + type: integer + default: 0 + description: By default it returns the first page and then its next pages from that on. + rowCount: + type: integer + default: 50 + description: Number of rows user wants to view per page + sortMetadata: + type: object + description: sort metadata + $ref: "#/definitions/SortMetadata" + filterMetadata: + type: object + description: filters + $ref: "#/definitions/GenomicFilters" + + SearchSVVariantsRequest: + type: object + required: + - query + properties: + query: + type: string + description: > + A query string that can be used to match a subset of the name (case-insensitively), + the entire code value (case-insensitively), or the concept ID. + pageNumber: + type: integer + default: 0 + description: By default it returns the first page and then its next pages from that on. + rowCount: + type: integer + default: 50 + description: Number of rows user wants to view per page + sortMetadata: + type: object + description: sort metadata + $ref: "#/definitions/SortSVMetadata" + filterMetadata: + type: object + description: filters + $ref: "#/definitions/SVGenomicFilters" + + SearchConceptsRequest: + type: object + required: + - query + properties: + query: + type: string + description: > + A query string that can be used to match a subset of the name (case-insensitively), + the entire code value (case-insensitively), or the concept ID. + standardConceptFilter: + description: > + STANDARD_CONCEPTS if only standard concepts should be returned, + NON_STANDARD_CONCEPTS if only non-standard + concepts should be returned; defaults to ALL_CONCEPTS, meaning both + standard and non-standard concepts will be returned. + $ref: "#/definitions/StandardConceptFilter" + vocabularyIds: + type: array items: - type: array - items: - $ref: '#/components/schemas/GenomicFilterOption' - filterActive: - type: boolean - description: boolean flag to know whether the filter is active - default: false - GenomicSearchTermType: - type: object - properties: - variant_id: - type: string - description: variant property to build sql query - whereVariantIdFlag: - type: boolean - contig: - type: string - description: contig property to build sql query - low: - type: integer - description: position low - format: int64 - high: - type: integer - description: position high - format: int64 - whereContigFlag: - type: boolean - wherePositionFlag: - type: boolean - rs_id: - type: string - description: rsnumber property to build sql query - whereRsIdFlag: - type: boolean - genes: - type: string - description: genes property to build sql query - whereGeneFlag: - type: boolean - description: flag to determine if the search type is gene - searchSqlQuery: - type: string - description: sql query formed - VariantInfo: - required: - - variantId - type: object - properties: - variantId: - type: string - description: variant id - dnaChange: - type: string - description: dna change - transcript: - type: string - description: transcript - rsNumber: type: string - description: rs number - afrAlleleCount: - type: integer - description: African Ancestry Allele Count - format: int64 - afrAlleleNumber: - type: integer - description: African Ancestry Allele Number - format: int64 - afrAlleleFrequency: - type: number - description: African Ancestry Allele Frequency - format: double - afrHomozygoteCount: - type: integer - description: African Ancestry Homozygote Count - format: int64 - easAlleleCount: - type: integer - description: East Asian Ancestry Allele Count - format: int64 - easAlleleNumber: - type: integer - description: East Asian Ancestry Allele Number - format: int64 - easAlleleFrequency: - type: number - description: East Asian Ancestry Allele Frequency - format: double - easHomozygoteCount: - type: integer - description: East Asian Ancestry Homozygote Count - format: int64 - eurAlleleCount: - type: integer - description: European Ancestry Allele Count - format: int64 - eurAlleleNumber: - type: integer - description: European Ancestry Allele Number - format: int64 - eurAlleleFrequency: - type: number - description: European Ancestry Allele Frequency - format: double - eurHomozygoteCount: - type: integer - description: European Ancestry Homozygote Count - format: int64 - amrAlleleCount: - type: integer - description: Latin American Ancestry Allele Count - format: int64 - amrAlleleNumber: - type: integer - description: Latin American Ancestry Allele Number - format: int64 - amrAlleleFrequency: - type: number - description: Latin American Ancestry Allele Frequency - format: double - amrHomozygoteCount: - type: integer - description: Latin American Ancestry Homozygote Count - format: int64 - midAlleleCount: - type: integer - description: Middle Eastern Ancestry Allele Count - format: int64 - midAlleleNumber: - type: integer - description: Middle Eastern Ancestry Allele Number - format: int64 - midAlleleFrequency: - type: number - description: Middle Eastern Ancestry Allele Frequency - format: double - midHomozygoteCount: - type: integer - description: Middle Eastern Ancestry Homozygote Count - format: int64 - sasAlleleCount: - type: integer - description: South Asian Ancestry Allele Count - format: int64 - sasAlleleNumber: - type: integer - description: South Asian Ancestry Allele Number - format: int64 - sasAlleleFrequency: - type: number - description: South Asian Ancestry Allele Frequency - format: double - sasHomozygoteCount: - type: integer - description: South Asian Ancestry Homozygote Count - format: int64 - othAlleleCount: - type: integer - description: Other Ancestry Allele Count - format: int64 - othAlleleNumber: - type: integer - description: Other Ancestry Allele Number - format: int64 - othAlleleFrequency: - type: number - description: Other Ancestry Allele Frequency - format: double - othHomozygoteCount: - type: integer - description: Other Homozygote Count - format: int64 - totalAlleleCount: - type: integer - description: Total Allele Count - format: int64 - totalAlleleNumber: - type: integer - description: Total Allele Number - format: int64 - totalAlleleFrequency: - type: number - description: Total Allele Frequency - format: double - totalHomozygoteCount: - type: integer - description: Total Homozygote Count - format: int64 - securitySchemes: - aou_oauth: - type: oauth2 - flows: - authorizationCode: - authorizationUrl: "" - tokenUrl: "" - scopes: {} -x-original-swagger-version: "2.0" + description: The vocabulary ID for the concepts returned (e.g. SNOMED, RxNorm) + domain: + description: > + The domain for the concepts returned (e.g. OBSERVATION, DRUG). Note that this may map + to multiple domain ID values in OMOP. + $ref: "#/definitions/Domain" + maxResults: + type: integer + format: int32 + description: The maximum number of results returned. Defaults to 25. + minCount: + type: integer + format: int32 + description: The minimum count of concepts to be fetched + pageNumber: + type: integer + default: 0 + description: By default it returns the first page and then its next pages from that on. + measurementTests: + type: integer + default: 1 + description: By default all the measurement tests are returned + measurementOrders: + type: integer + default: 1 + description: By default all the measurement orders are returned + + CdrVersion: + type: object + required: + - cdrVersionId + - name + - creationTime + properties: + cdrVersionId: + type: string + name: + type: string + numParticipants: + type: integer + format: int32 + creationTime: + type: integer + format: int64 + description: Milliseconds since the UNIX epoch. + + CdrVersionListResponse: + type: object + required: + - items + - defaultCdrVersionId + properties: + items: + type: "array" + items: + $ref: "#/definitions/CdrVersion" + defaultCdrVersionId: + type: "string" + description: ID of the CDR versions that should be used by the user by default + + DomainInfo: + type: object + required: + - domain + - name + - description + - standardConceptCount + - allConceptCount + - participantCount + properties: + domain: + description: the domain ID + $ref: "#/definitions/Domain" + domainConceptId: + description: domain concept id + type: integer + format: int64 + name: + description: display name of the domain + type: string + description: + description: description of the domain + type: string + standardConceptCount: + description: number of standard concepts in this domain + type: integer + format: int64 + allConceptCount: + description: number of concepts in this domain (standard or non-standard) + type: integer + format: int64 + participantCount: + description: number of participants with data in the CDR for a concept in this domain + type: integer + format: int64 + + SortMetadata: + type: object + properties: + variantId: + description: variant id column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + gene: + description: gene column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + consequence: + description: consequence column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + variantType: + description: Variant Type column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + clinicalSignificance: + description: Clinical significance column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + alleleCount: + description: allele count column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + alleleNumber: + description: allele number column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + alleleFrequency: + description: allele frequency column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + homozygoteCount: + description: homozygote count column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + + SortColumnDetails: + type: object + properties: + sortActive: + description: Flag for active sort + type: boolean + sortDirection: + description: Sort Direction + type: string + sortOrder: + description: Sort Order + type: integer + format: int32 + + SortSVMetadata: + type: object + properties: + variantId: + description: variant id column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + variantType: + description: gene column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + consequence: + description: gene column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + position: + description: consequence column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + size: + description: Variant Type column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + alleleCount: + description: allele count column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + alleleNumber: + description: allele number column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + alleleFrequency: + description: allele frequency column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + homozygoteCount: + description: homozygote count column metadata + type: object + $ref: "#/definitions/SortColumnDetails" + + SurveyModule: + type: object + required: + - conceptId + - name + - description + - questionCount + - participantCount + properties: + conceptId: + description: the concept ID for the survey module + type: integer + format: int64 + name: + description: display name of the module + type: string + description: + description: description of the module + type: string + questionCount: + description: number of questions in the module + type: integer + format: int64 + participantCount: + description: number of participants with data in the CDR for questions in this module + type: integer + format: int64 + orderNumber: + description: survey release order number + type: integer + format: int32 + + ErrorResponse: + type: object + properties: + message: + description: Error message + type: string + statusCode: + description: Http error status code + type: integer + errorClassName: + type: string + errorCode: + description: Short description of the type of error + $ref: "#/definitions/ErrorCode" + errorUniqueId: + type: string + description: Unique ID for this error response, for correlation with backend logs + + ErrorCode: + type: string + description: Short parsable error descriptions + enum: [ + PARSE_ERROR + ] + + Variant: + type: object + required: + - variantId + properties: + variantId: + description: id variant + type: string + genes: + description: genes + type: string + consequence: + description: consequence + type: string + variantType: + description: variant type + type: string + proteinChange: + description: protein change + type: string + clinicalSignificance: + description: clinical significance + type: string + alleleCount: + description: allele count + type: integer + format: int64 + alleleNumber: + description: allele number + type: integer + format: int64 + alleleFrequency: + description: allele frequency + type: number + format: double + homozygoteCount: + description: homozygote count + type: integer + format: int64 + + VariantListResponse: + type: object + required: + - items + properties: + items: + type: "array" + items: + $ref: "#/definitions/Variant" + + SVVariant: + type: object + required: + - variantId + properties: + variantId: + description: id variant + type: string + variantType: + description: variant type + type: string + consequence: + description: consequence + type: string + position: + description: POS - END + type: string + size: + description: size + type: integer + format: int64 + alleleCount: + description: allele count + type: integer + format: int64 + alleleNumber: + description: allele number + type: integer + format: int64 + alleleFrequency: + description: allele frequency + type: number + format: double + homozygoteCount: + description: number of homozygotes + type: integer + format: int64 + + SVVariantListResponse: + type: object + required: + - items + properties: + items: + type: "array" + items: + $ref: "#/definitions/SVVariant" + + GenomicFilters: + type: object + properties: + gene: + description: Gene Filters + type: object + $ref: "#/definitions/GenomicFilterOptionList" + consequence: + description: Consequence Filters + type: object + $ref: "#/definitions/GenomicFilterOptionList" + variantType: + description: Variant Type Filters + type: object + $ref: "#/definitions/GenomicFilterOptionList" + clinicalSignificance: + description: Clinical Significance Filters + type: object + $ref: "#/definitions/GenomicFilterOptionList" + alleleCount: + description: Allele Count Range Filter + type: object + $ref: "#/definitions/GenomicFilterOption" + alleleNumber: + description: Allele Number Range Filter + type: object + $ref: "#/definitions/GenomicFilterOption" + alleleFrequency: + description: Allele Frequency Range Filter + type: object + $ref: "#/definitions/GenomicFilterOption" + homozygoteCount: + description: Homozygote Count Range Filter + type: object + $ref: "#/definitions/GenomicFilterOption" + + SVGenomicFilters: + type: object + properties: + gene: + description: Gene Filters + type: object + $ref: "#/definitions/SVGenomicFilterOptionList" + consequence: + description: Consequence Filters + type: object + $ref: "#/definitions/SVGenomicFilterOptionList" + variantType: + description: Variant Type Filters + type: object + $ref: "#/definitions/SVGenomicFilterOptionList" + clinicalSignificance: + description: Clinical Significance Filters + type: object + $ref: "#/definitions/SVGenomicFilterOptionList" + alleleCount: + description: Allele Count Range Filter + type: object + $ref: "#/definitions/SVGenomicFilterOption" + alleleNumber: + description: Allele Number Range Filter + type: object + $ref: "#/definitions/SVGenomicFilterOption" + alleleFrequency: + description: Allele Frequency Range Filter + type: object + $ref: "#/definitions/SVGenomicFilterOption" + homozygoteCount: + description: Homozygote Count Range Filter + type: object + $ref: "#/definitions/SVGenomicFilterOption" + + GenomicFilterOption: + type: object + properties: + option: + description: Filter Name + type: string + count: + description: count + type: integer + format: int64 + min: + description: min value + type: integer + format: int64 + max: + description: max value + type: integer + format: int64 + minFreq: + description: min Allele Frequency + type: number + format: float + default: 0 + maxFreq: + description: max Allele Frequency + type: number + format: float + default: 1 + checked: + description: boolean flag + type: boolean + + SVGenomicFilterOption: + type: object + properties: + option: + description: Filter Name + type: string + count: + description: count + type: integer + format: int64 + min: + description: min value + type: integer + format: int64 + max: + description: max value + type: integer + format: int64 + minFreq: + description: min Allele Frequency + type: number + format: float + default: 0 + maxFreq: + description: max Allele Frequency + type: number + format: float + default: 1 + checked: + description: boolean flag + type: boolean + + GenomicFilterOptionList: + type: object + required: + - items + properties: + items: + type: "array" + items: + $ref: "#/definitions/GenomicFilterOption" + filterActive: + description: boolean flag to know whether the filter is active + type: boolean + default: false + + SVGenomicFilterOptionList: + type: object + required: + - items + properties: + items: + type: "array" + items: + $ref: "#/definitions/SVGenomicFilterOption" + filterActive: + description: boolean flag to know whether the filter is active + type: boolean + default: false + + GenomicSearchTermType: + type: object + properties: + variant_id: + description: variant property to build sql query + type: string + whereVariantIdFlag: + type: boolean + contig: + description: contig property to build sql query + type: string + low: + description: position low + type: integer + format: int64 + high: + description: position high + type: integer + format: int64 + whereContigFlag: + type: boolean + wherePositionFlag: + type: boolean + rs_id: + description: rsnumber property to build sql query + type: string + whereRsIdFlag: + type: boolean + genes: + description: genes property to build sql query + type: string + whereGeneFlag: + description: flag to determine if the search type is gene + type: boolean + searchSqlQuery: + description: sql query formed + type: string + + SVGenomicSearchTermType: + type: object + properties: + variant_id: + description: variant property to build sql query + type: string + whereVariantIdFlag: + type: boolean + genes: + description: genes property to build sql query + type: string + whereGeneFlag: + description: flag to determine if the search type is gene + type: boolean + contig: + description: contig property to build sql query + type: string + low: + description: position low + type: integer + format: int64 + high: + description: position high + type: integer + format: int64 + whereContigFlag: + type: boolean + wherePositionFlag: + type: boolean + searchSqlQuery: + description: sql query formed + type: string + + VariantInfo: + type: object + required: + - variantId + properties: + variantId: + description: variant id + type: string + dnaChange: + description: dna change + type: string + transcript: + description: transcript + type: string + rsNumber: + description: rs number + type: string + afrAlleleCount: + description: African Ancestry Allele Count + type: integer + format: int64 + afrAlleleNumber: + description: African Ancestry Allele Number + type: integer + format: int64 + afrAlleleFrequency: + description: African Ancestry Allele Frequency + type: number + format: double + afrHomozygoteCount: + description: African Ancestry Homozygote Count + type: integer + format: int64 + easAlleleCount: + description: East Asian Ancestry Allele Count + type: integer + format: int64 + easAlleleNumber: + description: East Asian Ancestry Allele Number + type: integer + format: int64 + easAlleleFrequency: + description: East Asian Ancestry Allele Frequency + type: number + format: double + easHomozygoteCount: + description: East Asian Ancestry Homozygote Count + type: integer + format: int64 + eurAlleleCount: + description: European Ancestry Allele Count + type: integer + format: int64 + eurAlleleNumber: + description: European Ancestry Allele Number + type: integer + format: int64 + eurAlleleFrequency: + description: European Ancestry Allele Frequency + type: number + format: double + eurHomozygoteCount: + description: European Ancestry Homozygote Count + type: integer + format: int64 + amrAlleleCount: + description: Latin American Ancestry Allele Count + type: integer + format: int64 + amrAlleleNumber: + description: Latin American Ancestry Allele Number + type: integer + format: int64 + amrAlleleFrequency: + description: Latin American Ancestry Allele Frequency + type: number + format: double + amrHomozygoteCount: + description: Latin American Ancestry Homozygote Count + type: integer + format: int64 + midAlleleCount: + description: Middle Eastern Ancestry Allele Count + type: integer + format: int64 + midAlleleNumber: + description: Middle Eastern Ancestry Allele Number + type: integer + format: int64 + midAlleleFrequency: + description: Middle Eastern Ancestry Allele Frequency + type: number + format: double + midHomozygoteCount: + description: Middle Eastern Ancestry Homozygote Count + type: integer + format: int64 + sasAlleleCount: + description: South Asian Ancestry Allele Count + type: integer + format: int64 + sasAlleleNumber: + description: South Asian Ancestry Allele Number + type: integer + format: int64 + sasAlleleFrequency: + description: South Asian Ancestry Allele Frequency + type: number + format: double + sasHomozygoteCount: + description: South Asian Ancestry Homozygote Count + type: integer + format: int64 + othAlleleCount: + description: Other Ancestry Allele Count + type: integer + format: int64 + othAlleleNumber: + description: Other Ancestry Allele Number + type: integer + format: int64 + othAlleleFrequency: + description: Other Ancestry Allele Frequency + type: number + format: double + othHomozygoteCount: + description: Other Homozygote Count + type: integer + format: int64 + totalAlleleCount: + description: Total Allele Count + type: integer + format: int64 + totalAlleleNumber: + description: Total Allele Number + type: integer + format: int64 + totalAlleleFrequency: + description: Total Allele Frequency + type: number + format: double + totalHomozygoteCount: + description: Total Homozygote Count + type: integer + format: int64 \ No newline at end of file diff --git a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx index 8bbd8e417..10f519afc 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx @@ -8,15 +8,16 @@ import { reactStyles } from "app/utils"; import { triggerEvent } from "app/utils/google_analytics"; import { urlParamsStore } from "app/utils/navigation"; import { - GenomicFilters, - SearchVariantsRequest, - Variant, + GenomicFilters, SVGenomicFilters, + SearchVariantsRequest, SearchSVVariantsRequest, + Variant, SVVariant, } from "publicGenerated"; -import { SortColumnDetails, SortMetadata } from "publicGenerated/fetch"; +import { SortColumnDetails, SortMetadata, SortSVMetadata } from "publicGenerated/fetch"; import { GenomicFaqComponent } from "./components/genomic-faq.component"; import { GenomicSearchComponent } from "./components/genomic-search.component"; +import { SVGenomicSearchComponent } from "../sv-genomic-view/components/sv-genomic-search.component"; const styles = reactStyles({ title: { @@ -113,18 +114,24 @@ const styles = reactStyles({ interface State { selectionId: number; searchResults: Variant[]; + searchSVResults: SVVariant[]; loadingResults: boolean; variantListSize: number; loadingVariantListSize: boolean; searchTerm: string; + svSearchTerm: string; currentPage: number; rowCount: number; participantCount: string; chartData: any; sortMetadata: SortMetadata; + svSortMetadata: SortSVMetadata; filterMetadata: GenomicFilters; submittedFilterMetadata: GenomicFilters; filteredMetadata: GenomicFilters; + svFilterMetadata: SVGenomicFilters; + submittedSVFilterMetadata: SVGenomicFilters; + svFilteredMetadata: SVGenomicFilters; filterChipsShow: boolean; scrollClean: boolean; } @@ -162,6 +169,39 @@ class SortMetadataClass implements SortMetadata { } } +class SortSVMetadataClass implements SortSVMetadata { + variantId: any; + variantType: any; + consequence: any; + position: any; + size: any; + alleleCount: any; + alleleNumber: any; + alleleFrequency: any; + homozygoteCount: any; + constructor( + variantId: any, + variantType: any, + consequence: any, + position: any, + size: any, + alleleCount: any, + alleleNumber: any, + alleleFrequency: any, + homozygoteCount: any + ) { + this.variantId = variantId; + this.variantType = variantType; + this.consequence = consequence; + this.position = position; + this.size = size; + this.alleleCount = alleleCount; + this.alleleNumber = alleleNumber; + this.alleleFrequency = alleleFrequency; + this.homozygoteCount = homozygoteCount; + } +} + class SortColumnDetailsClass implements SortColumnDetails { sortActive: boolean; sortDirection: string; @@ -193,18 +233,23 @@ export const GenomicViewComponent = withRouteData( this.state = { selectionId: 2, searchResults: [], + searchSVResults: [], loadingResults: null, variantListSize: null, loadingVariantListSize: null, searchTerm: " ", + svSearchTerm: " ", currentPage: null, rowCount: 50, participantCount: null, chartData: null, filterChipsShow: false, filterMetadata: null, + svFilterMetadata: null, filteredMetadata: undefined, submittedFilterMetadata: undefined, + svFilteredMetadata: undefined, + submittedSVFilterMetadata: undefined, scrollClean: true, sortMetadata: new SortMetadataClass( new SortColumnDetailsClass(true, "asc", 1), @@ -217,6 +262,17 @@ export const GenomicViewComponent = withRouteData( new SortColumnDetailsClass(false, "asc", 8), new SortColumnDetailsClass(false, "asc", 9), ), + svSortMetadata: new SortSVMetadataClass( + new SortColumnDetailsClass(true, "asc", 1), + new SortColumnDetailsClass(false, "asc", 2), + new SortColumnDetailsClass(false, "asc", 3), + new SortColumnDetailsClass(false, "asc", 4), + new SortColumnDetailsClass(false, "asc", 5), + new SortColumnDetailsClass(false, "asc", 6), + new SortColumnDetailsClass(false, "asc", 7), + new SortColumnDetailsClass(false, "asc", 8), + new SortColumnDetailsClass(false, "asc", 9), + ), }; } @@ -228,6 +284,10 @@ export const GenomicViewComponent = withRouteData( { id: 1, label: "Participant Demographics", + }, + { + id: 4, + label: "SV Variants", } ]; title = "SNV/Indel Variants"; @@ -238,6 +298,10 @@ export const GenomicViewComponent = withRouteData( this.changeUrl(); }, 1000); + svSearch = _.debounce((svSearchTerm: string) => { + this.getSVVariantSearch(svSearchTerm); + }, 1000); + clearSortMetadata() { const { sortMetadata } = this.state; for (const smKey in sortMetadata) { @@ -278,6 +342,27 @@ export const GenomicViewComponent = withRouteData( }); } + getSVSearchSize(searchTerm: string, filtered: boolean) { + if (!filtered) { + this.getSVFilterMetadata(searchTerm); + } + const variantSizeRequest = { + query: searchTerm, + filterMetadata: this.state.svFilterMetadata + }; + + genomicsApi().getSVVariantSearchResultSize(variantSizeRequest).then( + result => { + this.setState({ + variantListSize: searchTerm !== '' ? result : 0, + loadingVariantListSize: false + }); + } + ).catch(e => { + console.log(e, 'error'); + }); + } + getFilterMetadata(searchTerm: string) { genomicsApi().getGenomicFilterOptions(searchTerm).then( result => { @@ -292,6 +377,18 @@ export const GenomicViewComponent = withRouteData( }); } + getSVFilterMetadata(searchTerm: string) { + genomicsApi().getSVGenomicFilterOptions(searchTerm).then( + result => { + result.gene.items.forEach(el => { el.checked = false; }); + this.setState({ svFilterMetadata: result, submittedSVFilterMetadata: { ...result } }); + localStorage.setItem("svOriginalFilterMetadata", JSON.stringify(result)); + } + ).catch(e => { + console.log(e, 'error'); + }); + } + getVariantSearch(searchTerm: string) { this.getSearchSize(searchTerm, false); localStorage.setItem("searchWord", searchTerm); @@ -318,6 +415,33 @@ export const GenomicViewComponent = withRouteData( } } + getSVVariantSearch(svSearchTerm: string) { + this.getSVSearchSize(svSearchTerm, false); + localStorage.setItem("svSearchWord", svSearchTerm); + + if (svSearchTerm !== "") { + triggerEvent( + "svGenomicsPageSearch", + "Search", + "Search In Genomics Data", + "Genomic Search", + svSearchTerm, + null + ); + this.setState( + { loadingResults: true, currentPage: 1, rowCount: 200 }, + () => { + this.fetchSVVariantData(); + } + ); + } else { + this.setState({ + searchSVResults: null, + loadingResults: false, + }); + } + } + componentCleanup() { // this will hold the cleanup code localStorage.setItem("searchWord", ""); @@ -372,6 +496,27 @@ export const GenomicViewComponent = withRouteData( }); } + handleSVPageChange(info) { + this.setState( + { loadingResults: true, currentPage: info.selectedPage }, + () => { + this.fetchSVVariantData(); + } + ); + } + + handleSVRowCountChange(info) { + this.setState({ loadingResults: true, rowCount: +info.rowCount }, () => { + this.fetchSVVariantData(); + }); + } + + handleSVSortClick(sortMetadataTemp) { + this.setState({ sortMetadata: sortMetadataTemp }, () => { + this.fetchSVVariantData(); + }); + } + fetchVariantData() { const { searchTerm, currentPage, sortMetadata, rowCount, filterMetadata } = this.state; @@ -393,6 +538,26 @@ export const GenomicViewComponent = withRouteData( }); } + fetchSVVariantData() { + const { svSearchTerm, currentPage, svSortMetadata, rowCount, svFilterMetadata } = this.state; + const searchRequest: SearchSVVariantsRequest = { + query: svSearchTerm, + pageNumber: currentPage, + rowCount: rowCount, + sortMetadata: svSortMetadata, + filterMetadata: svFilterMetadata + }; + + genomicsApi() + .searchSVVariants(searchRequest) + .then((results) => { + this.setState({ + searchSVResults: results.items, + loadingResults: false, + }); + }); + } + filterGenomics(filteredMetadata: GenomicFilters, sortMetadata: SortMetadata) { const { searchTerm, rowCount } = this.state; const searchRequest = { @@ -410,6 +575,25 @@ export const GenomicViewComponent = withRouteData( }); } + filterSVGenomics(filteredMetadata: SVGenomicFilters, sortMetadata: SortSVMetadata) { + const { svSearchTerm, rowCount } = this.state; + const searchRequest = { + query: svSearchTerm, + pageNumber: 1, + rowCount: rowCount, + filterMetadata: filteredMetadata, + sortMetadata: sortMetadata, + }; + + genomicsApi() + .searchSVVariants(searchRequest) + .then((results) => { + console.log('Am i here with results?'); + console.log(results); + this.setState({ searchSVResults: results.items,loadingResults:false }); + }); + } + topBarClick(selected: number) { this.setState({ selectionId: selected, @@ -426,6 +610,12 @@ export const GenomicViewComponent = withRouteData( } } + handleSVSearchTerm(searchTerm: string) { + if (this.state.svSearchTerm !== searchTerm) { + this.setState({ svFilterMetadata: null, svSearchTerm: searchTerm, loadingResults: true, loadingVariantListSize: true }, () => this.svSearch(searchTerm)); + } + } + componentDidMount() { window.addEventListener("beforeunload", this.componentCleanup); const { search } = urlParamsStore.getValue(); @@ -449,6 +639,19 @@ export const GenomicViewComponent = withRouteData( this.getSearchSize(this.state.searchTerm, true); } + handleSVFilterSubmit(filteredMetadata: SVGenomicFilters, sortMetadata: SortSVMetadata) { + + if (filteredMetadata['alleleFrequency']['checked']) { + filteredMetadata['alleleFrequency']['maxFreq'] = filteredMetadata['alleleFrequency']['max']; + filteredMetadata['alleleFrequency']['minFreq'] = filteredMetadata['alleleFrequency']['min']; + } + + this.setState({ submittedSVFilterMetadata: { ...filteredMetadata } }); + this.filterSVGenomics(filteredMetadata, sortMetadata); + this.getSVSearchSize(this.state.svSearchTerm, true); + + } + handleScrollBottom() { this.setState({ currentPage: this.state.currentPage + 1, loadingResults: true, scrollClean: false }) const { searchTerm, currentPage, sortMetadata, rowCount, filterMetadata } = this.state; @@ -470,9 +673,31 @@ export const GenomicViewComponent = withRouteData( }); } + handleSVScrollBottom() { + this.setState({ currentPage: this.state.currentPage + 1, loadingResults: true, scrollClean: false }) + const { searchTerm, currentPage, sortMetadata, rowCount, filterMetadata } = this.state; + const searchRequest: SearchVariantsRequest = { + query: searchTerm, + pageNumber: currentPage, + rowCount: rowCount, + sortMetadata: sortMetadata, + filterMetadata: filterMetadata, + }; + + genomicsApi() + .searchVariants(searchRequest) + .then((results) => { + this.setState({ + searchResults: [...this.state.searchResults, ...results.items], + loadingResults: false, + }); + }); + } + render() { - const { currentPage, selectionId, loadingVariantListSize, variantListSize, loadingResults, searchResults, - participantCount, chartData, rowCount, searchTerm, filterMetadata, sortMetadata, submittedFilterMetadata, + const { currentPage, selectionId, loadingVariantListSize, variantListSize, loadingResults, searchResults, searchSVResults, + participantCount, chartData, rowCount, searchTerm, filterMetadata, svFilterMetadata, sortMetadata, svSortMetadata, + submittedFilterMetadata, submittedSVFilterMetadata, scrollClean } = this.state; return @@ -550,6 +775,39 @@ export const GenomicViewComponent = withRouteData( scrollClean={scrollClean} /> )} + {(selectionId === 4) && ( + { + this.handleSVSearchTerm(svSearchWord); + this.setState({ svSearchTerm: svSearchWord }); + }} + onPageChange={(info) => { + this.handleSVPageChange(info); + }} + onRowCountChange={(info) => { + this.handleSVRowCountChange(info); + }} + onSortClick={(sortSVMetadata) => { + this.handleSVSortClick(sortSVMetadata); + }} + onFilterSubmit={(filteredMetadata: GenomicFilters, svSortMetadata: SortSVMetadata) => { + this.handleSVFilterSubmit(filteredMetadata, svSortMetadata); + }} + onScrollBottom={() => this.handleSVScrollBottom()} + currentPage={currentPage} + rowCount={rowCount} + variantListSize={variantListSize} + loadingVariantListSize={loadingVariantListSize} + loadingResults={loadingResults} + svResults={searchSVResults} + participantCount={participantCount} + searchTerm={searchTerm} + filterMetadata={svFilterMetadata} + submittedFilterMetadata={submittedSVFilterMetadata} + sortMetadata={svSortMetadata} + scrollClean={scrollClean} + /> + )} {selectionId === 3 && ( this.handleFaqClose()} /> diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/Surface.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/Surface.tsx new file mode 100644 index 000000000..4e203e984 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/Surface.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; + +import * as PropTypes from "prop-types"; +export default function Surface(props) { + const { className, view, trbl, style, children, ...other } = props; + const paddingBottom = `${Math.round((view[1] / view[0]) * 100)}%`; + + // uses bottom-padding hack. See https://css-tricks.com/scale-svg/ + return ( +
+ + {children} + +
+ Min + Max +
+
+ ); +} + +Surface.propTypes = { + /** + * SVG children to be rendered + */ + children: PropTypes.node, + /** + * The CSS class name of the root div element. + */ + className: PropTypes.string, + /** + * Styles to be spread on the root div element. + */ + style: PropTypes.object, + /** + * Top, right, bottom, left (trbl) margins. + */ + trbl: PropTypes.array, + /** + * Width and height attributes of the SVG view box. + */ + view: PropTypes.array +}; + +Surface.defaultProps = { + view: [1000, 500], + trbl: [10, 10, 10, 10] +}; \ No newline at end of file diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx new file mode 100644 index 000000000..68790a7d3 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx @@ -0,0 +1,176 @@ +import * as React from "react"; +import * as PropTypes from "prop-types"; +import { view, trbl, dims } from "./slider-constants"; +// ******************************************************* +// RAIL +// ******************************************************* +export function SliderRail({ getRailProps }) { + const h0 = dims[1] * 0.2; // inner - visible + const h1 = dims[1] * 0.6; // outer - invisible w/ events + + return ( + + + + + ); +} + +SliderRail.propTypes = { + getRailProps: PropTypes.func.isRequired, +}; + +// ******************************************************* +// HANDLE COMPONENT +// ******************************************************* +export function Handle({ handle: { id, percent, value },domain, category, getHandleProps }) { + + value = (category === 'alleleFrequency') ? value.toFixed(2): value; + const r0 = dims[1] * 0.4; // inner - visible + const r1 = dims[1] * 0.6; // outer - invisible w/ events + return ( + + + {value} + + + + + ); +} + +Handle.propTypes = { + domain: PropTypes.array.isRequired, + handle: PropTypes.shape({ + id: PropTypes.string.isRequired, + value: PropTypes.number.isRequired, + percent: PropTypes.number.isRequired + }).isRequired, + getHandleProps: PropTypes.func.isRequired, + disabled: PropTypes.bool +}; + +Handle.defaultProps = { + disabled: false +}; + +// ******************************************************* +// TRACK COMPONENT +// ******************************************************* +export function Track({ source, target, getTrackProps }) { + const x0 = dims[0] * (source.percent / 100); + const x1 = dims[0] * (target.percent / 100); + const h0 = dims[1] * 0.2; // inner - visible + const h1 = dims[1] * 0.6; // outer - invisible w/ events + + return ( + + + + + ); +} + +Track.propTypes = { + source: PropTypes.shape({ + id: PropTypes.string.isRequired, + value: PropTypes.number.isRequired, + percent: PropTypes.number.isRequired + }).isRequired, + target: PropTypes.shape({ + id: PropTypes.string.isRequired, + value: PropTypes.number.isRequired, + percent: PropTypes.number.isRequired + }).isRequired, + getTrackProps: PropTypes.func.isRequired, + disabled: PropTypes.bool +}; + +Track.defaultProps = { + disabled: false +}; + +// ******************************************************* +// TICK COMPONENT +// ******************************************************* +export function Tick({ tick, count, format }) { + const x = dims[0] * (tick.percent / 100); + const h = dims[1] * 0.2; // inner - visible + + return ( + + + + {format(tick.value)} + + + ); +} + +Tick.propTypes = { + tick: PropTypes.shape({ + id: PropTypes.string.isRequired, + value: PropTypes.number.isRequired, + percent: PropTypes.number.isRequired + }).isRequired, + format: PropTypes.func.isRequired +}; + +Tick.defaultProps = { + format: d => d +}; diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx new file mode 100644 index 000000000..6f0b3ff31 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx @@ -0,0 +1,7 @@ +export const view = [1000, 150]; // ViewBox: Width, Height +export const trbl = [20, 50, 20, 50]; // Margins: Top, Right, Bottom, Left + +export const dims = [ + view[0] - trbl[1] - trbl[3], // Adjusted dimensions width + view[1] - trbl[0] - trbl[2] // Adjusted dimensions height +]; diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx new file mode 100644 index 000000000..4ef9e4b41 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx @@ -0,0 +1,110 @@ +import * as React from "react"; +import { Handles, Rail, Slider, Tracks } from "react-compound-slider"; +import { Handle, SliderRail, Track } from "./slider-components"; // example render components - source below +import { dims, trbl, view } from "./slider-constants"; +import Surface from "./Surface"; + +const sliderProps = { + width: dims[0], + height: dims[1], + fill: "none", + opacity: 0.5, +}; +interface Props { + category: string; + filterItem: any; + ogFilterItem: any; + onSliderChange: Function; +} + +interface State { + domain: Array; + defaultValues: Array; + min: Number; + max: Number; +} + +export const SVVariantFilterSliderComponent = (class extends React.Component { + + constructor(props: Props) { + super(props); + this.state = { + domain: [this.props.ogFilterItem.min, this.props.ogFilterItem.max], + defaultValues: [this.props.filterItem.min, this.props.filterItem.max], + min: this.props.filterItem.min, + max: this.props.filterItem.max + } + } + + onUpdate(vals) { + this.props.onSliderChange(vals); + this.setState({ + min: vals[0], + max: vals[1] + }); + + } + + render() { + const { domain, defaultValues } = this.state; + const {filterItem, ogFilterItem, category} = this.props; + return
+ + { this.onUpdate(e) }} + rootProps={sliderProps} + values={defaultValues}> + + {({ getRailProps }) => } + + {/* + {({ ticks }) => ( + + {ticks.map(tick => ( + + ))} + + )} + */} + + {({ tracks, getTrackProps }) => ( + + {tracks.map(({ id, source, target }) => ( + + ))} + + )} + + + {({ handles, getHandleProps }) => ( + + {handles.map(handle => ( + + ))} + + )} + + + +
+ } +}) diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx new file mode 100644 index 000000000..b3d1745b3 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx @@ -0,0 +1,157 @@ +import * as React from "react"; + +import { reactStyles } from "app/utils"; +import { environment } from "environments/environment"; +import { GenomicFilters, SVVariant } from "publicGenerated"; +import { SortMetadata } from "publicGenerated/fetch"; +import { SVVariantSearchComponent } from "../../sv-genomic-view/components/sv-variant-search.component"; +import { SVVariantTableComponent } from "../../sv-genomic-view/components/sv-variant-table.component"; + +const styles = reactStyles({ + border: { + background: 'white', + padding: '2em', + paddingTop: '1em', + }, + titleBox: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + }, + boxHeading: { + margin: 0, + fontFamily: 'GothamBook, Arial, sans-serif', + fontWeight: 100, + fontStyle: 'normal', + fontSize: '.8em', + fontStretch: 'normal', + lineHeight: '1.47em', + letterSpacing: 'normal', + textAlign: 'left', + color: '#262262', + }, +}); + +interface Props { + onSearchInput: Function; + onPageChange: Function; + onRowCountChange: Function; + onSortClick: Function; + onFilterSubmit: Function; + onScrollBottom: Function; + variantListSize: number; + loadingVariantListSize: boolean; + loadingResults: boolean; + svResults: SVVariant[]; + currentPage: number; + rowCount: number; + participantCount: string; + searchTerm: string; + filterMetadata: GenomicFilters; + submittedFilterMetadata: GenomicFilters; + sortMetadata: SortMetadata; + scrollClean: boolean; +} + +interface State { + searchTerm: string; + filterMetadata: GenomicFilters; + submittedFilterMetadata: GenomicFilters; + sortMetadata: SortMetadata; + filtered: boolean; +} + +export class SVGenomicSearchComponent extends React.Component { + scrollDiv: any; + constructor(props: Props) { + super(props); + this.scrollDiv = React.createRef(); + this.state = { + searchTerm: null, + filterMetadata: this.props.filterMetadata, + sortMetadata: this.props.sortMetadata, + submittedFilterMetadata: this.props.submittedFilterMetadata, + filtered: false + }; + } + + componentDidUpdate(prevProps: Readonly) { + const { searchTerm, filterMetadata, submittedFilterMetadata } = this.props; + if (prevProps.searchTerm !== searchTerm) { + this.setState({ searchTerm: searchTerm }); + } + if (prevProps.filterMetadata !== filterMetadata) { + this.setState({ filterMetadata: filterMetadata }); + } + if (prevProps.submittedFilterMetadata !== submittedFilterMetadata) { + this.setState({ submittedFilterMetadata: submittedFilterMetadata }); + } + } + + handlePageChange(info) { + this.props.onPageChange(info); + { !environment.infiniteSrcoll && this.scrollDiv.current.scrollIntoView({ behavior: "smooth" }); } + } + + handleRowCountChange(info) { + this.props.onRowCountChange(info); + this.scrollDiv.current.scrollIntoView({ behavior: "smooth" }); + } + + handleSortClick(sortMetadata) { + this.props.onSortClick(sortMetadata); + } + + handleFilterSubmit(filteredMetadata, sortMetadata) { + this.props.onFilterSubmit(filteredMetadata, sortMetadata); + this.setState({filtered:true}); + setTimeout(() => { + this.setState({filtered:false}); + }, 1000); + } + + handleScrollBottom() { + this.props.onScrollBottom(); + } + + render() { + const { searchTerm, filterMetadata, sortMetadata, submittedFilterMetadata,filtered } = this.state; + const { currentPage, loadingResults, svResults, variantListSize, loadingVariantListSize, onSearchInput, + rowCount,scrollClean } = this.props; + return +
+

+ TBA +

+
+ { onSearchInput(searchWord); this.setState({ searchTerm: searchWord }); }} + onFilterSubmit={(filteredMetadata, sortMetadata) => { this.handleFilterSubmit(filteredMetadata, sortMetadata); }} + loadingResults={loadingResults} + loadingVariantListSize={loadingVariantListSize} + searchTerm={searchTerm} + variantListSize={variantListSize} + filterMetadata={filterMetadata} + sortMetadata={sortMetadata} + submittedFilterMetadata={submittedFilterMetadata} + onSortChange={(e) => this.handleSortClick(e)} + scrollClean={scrollClean} /> + { onSearchInput(searchWord); this.setState({ searchTerm: searchWord }); }} + onRowCountChange={(info: any) => this.handleRowCountChange(info)} + onPageChange={(info: any) => this.handlePageChange(info)} + onSortClick={(sortMetadata: any) => this.handleSortClick(sortMetadata)} + onScrollBottom={() => { environment.infiniteSrcoll && this.handleScrollBottom() }} + currentPage={currentPage} + rowCount={rowCount} + sortMetadata={sortMetadata} + filtered = {filtered} + /> +
; + } +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx new file mode 100644 index 000000000..a0e2fe3de --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx @@ -0,0 +1,163 @@ +import { reactStyles } from 'app/utils'; +import { GenomicFilters } from 'publicGenerated'; +import * as React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faXmark } from '@fortawesome/free-solid-svg-icons'; + +interface Props { + filteredMetadata: GenomicFilters; + onChipChange: Function; +} +interface State { + chips: Array; + +} + +const lables = { + gene: 'Gene', + consequence: 'Consequence', + variantType: 'Variant Type', + clinicalSignificance: 'ClinVar Significance', + alleleNumber: 'Allele Number', + alleleFrequency: 'Allele Frequency', + alleleCount: 'Allele Count', + homozygoteCount: 'Homozygote Count' +}; + +const styles = reactStyles({ + + chipCat: { + display: 'flex', + flexWrap: 'wrap', + alignItems: 'center', + paddingRight: '0.25rem' + }, + chip: { + display: 'flex', + alignItems: 'center', + border: '1px #216fb4 solid', + color: 'rgb(33, 111, 180)', + padding: '.05rem .5rem', + borderRadius: '15px', + fontFamily: 'GothamBold', + margin: '.25rem .25rem' + }, + chipFormat: { + fontSize: '.8em', + display: 'flex', + flexWrap: 'wrap' + }, + chipLayout: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center' + } +}); +export class SVVariantFilterChips extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + chips: [] + }; + } + + formatChips(filteredMetadata): Array { + const displayArr = []; + for (const key in filteredMetadata) { + if (Object.prototype.hasOwnProperty.call(filteredMetadata, key) && filteredMetadata[key] !== undefined) { + const allChecked = Array.isArray(filteredMetadata[key]) && filteredMetadata[key].every((t => t.checked)); + if (!allChecked) { + let el = filteredMetadata[key]; + if (!el.hasOwnProperty("filterActive")) { + if (el.min < 1) { + el.min = +el.min.toFixed(2); + } + if (el.max < 1) { + el.max = +el.max.toFixed(2); + } + } + displayArr.push({ cat: key, data: el }); + } + + } + } + return displayArr; + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { + if (prevProps !== this.props) { + this.setState({ chips: this.formatChips(this.props.filteredMetadata) }); + } + } + + removeChip(item, cat) { + const {filteredMetadata} = this.props; + if (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { + filteredMetadata[cat.toString()].items = filteredMetadata[cat.toString()].items.filter(el => { + if (item === el) { + item.checked = false; + } + return el; + }); + } else { + filteredMetadata[cat.toString()].checked = false; + try { + let originalFilterMetadata = JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'); + filteredMetadata[cat.toString()]['min'] = originalFilterMetadata[cat.toString()]['min']; + filteredMetadata[cat.toString()]['max'] = originalFilterMetadata[cat.toString()]['max']; + } catch (e) { + console.log('Error') + } + } + const allFalse = (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) && + filteredMetadata[cat.toString()].items.every(t => t.checked === false); + + if (allFalse && filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { + filteredMetadata[cat.toString()].filterActive = false; + } + this.props.onChipChange(filteredMetadata); + } + + render() { + const { chips } = this.state; + return
+ {chips.length > 0 && chips.map((el, count) => { + if (el.data.hasOwnProperty("filterActive")) { + return
{el.data.items.some((p) => p.checked) &&
{lables[el.cat.toString()]} + {el.data.items.map((item, i) => { + const chipLabel = item.option ? item.option : '(undefined)'; + return
+ {item.checked &&
+ {chipLabel.replace(/_/g, " ")} + this.removeChip(item, el.cat)} + icon={faXmark} className="clear-search-icon" + caria-hidden='true'/> +
} +
; + })} +
} +
; + } else { + return
{el.data.checked &&
+ {el.data.checked && +
{lables[el.cat.toString()]} +
+ Min  + {el.data.min} +  | Max  + {el.data.max} + this.removeChip(el, el.cat)} + icon={faXmark} className="clear-search-icon" + caria-hidden='true'/> +
+
} +
} +
; + } + + })} +
; + } +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx new file mode 100644 index 000000000..da65491bf --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx @@ -0,0 +1,200 @@ +import * as React from "react"; + +import { Cat } from "./sv-variant-filter.component"; +import { reactStyles } from "app/utils"; +import { ClrIcon } from "app/utils/clr-icon"; +import { SVVariantFilterSliderComponent } from './slider-filter/sv-variant-filter-slider.component' + +const styles = reactStyles({ + filterItem: { + width: "100%", + padding: ".5rem", + paddingBottom: "0", + display: "flex", + justifyContent: "space-between", + alignItems: "center", + color: "#262262", + fontSize: ".8em", + letterSpacing: 0, + lineHeight: "16px", + cursor: "pointer" + }, + filterItemClosed: { + transform: "rotate(90deg)", + }, + filterItemOpen: { + transform: "rotate(180deg)", + }, + selectContainer: { + width: "100%", + display: "flex", + }, + textFilter: { + border: "solid rgba(74,74,74,0.4) 1px", + }, + selectBtn: { + border: "none", + background: "transparent", + color: "#216FB4", + }, + selectNoBtn: { + border: "none", + background: "transparent", + color: "#216FB4", + }, + filterItemForm: { + display: "flex", + overflow: 'hidden', + flexDirection: "column", + paddingLeft: "1rem", + paddingTop:".25rem" + }, + filterItemOption: { + fontSize: ".8em", + display: "flex", + }, + filterItemCheck: { + marginRight: ".25rem", + height: ".8rem", + width: ".8rem", + marginTop: "0.1rem", + }, + filterItemLabel: { + width: '80%', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflow:'hidden' + // wordWrap: "break-word", + }, + filterSlider: { + padding: "1rem 0", + }, +}); + +const css = ` + .slider { + -webkit-appearance: none; + width: 100%; + height: 25px; + background: transparent; + outline: none; + opacity: 0.7; + -webkit-transition: .2s; + transition: opacity .2s; + } +`; + +interface Props { + filterItem: any; + category: Cat; + onFilterChange: Function; + cleared: Boolean; +} +interface State { + filterItemOpen: Boolean; + filterItemState: any; + filterCheckMap: any; + ogFilterMetaData: string; +} + +export class SVVariantFilterItemComponent extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + filterItemOpen: false, + filterItemState: props.filterItem || '', + filterCheckMap: props.filterItem || '', + ogFilterMetaData: JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}')[this.props.category.field.toString()] + }; + } + + componentDidMount(): void { + if (Array.isArray(this.state.filterCheckMap.items) && this.state.filterCheckMap.items.every(t => t.checked)) { + this.state.filterCheckMap.items.forEach(i => i.checked = false); + } + } + + filterClick() { + this.setState({ filterItemOpen: !this.state.filterItemOpen }); + } + + // filterBySearch(e) { + // if (e.target.value) { + // this.setState({ + // filterItemState: this.state.filterItemState.filter( + // (item) => + // item.option && item.option.toLowerCase().startsWith(e.target.value) + // ), + // }); + // } else { + // this.setState({ filterItemState: this.state.filterCheckMap }); + // } + // } + + + handleCheck(filteredItem) { + const {filterItemState, filterCheckMap} = this.state; + let newFilterItemState = { ...filterItemState}; + let newFilterCheckMap = { ...filterCheckMap}; + const filtered = this.state.filterItemState.items.map(el => el === filteredItem ? { ...el, checked: !filteredItem.checked } : el); + const filterCheckedFlag = (filtered.find(x => x.checked === true)) ? true : false; + newFilterItemState.items = filtered; + newFilterItemState.filterActive = filterCheckedFlag; + newFilterCheckMap.items = filtered; + newFilterCheckMap.filterActive = filterCheckedFlag; + this.setState({ + filterItemState: newFilterItemState, + filterCheckMap: newFilterCheckMap + }); + this.props.onFilterChange(newFilterItemState, this.props.category); + } + + + handleSliderChange(vals, filterItem) { + const updatedFilterItem = {...filterItem}; + updatedFilterItem.min = vals[0]; + updatedFilterItem.max = vals[1]; + updatedFilterItem.checked = true; + this.props.onFilterChange(updatedFilterItem, this.props.category); + } + + render(): React.ReactNode { + const { category,cleared, filterItem } = this.props; + const { filterItemOpen, filterItemState, ogFilterMetaData } = this.state; + + return + +
this.filterClick()} style={styles.filterItem}> + {category.display} +
+
+ {(cleared && filterItemOpen && Array.isArray(filterItemState.items)) ?
+ {/* this.filterBySearch(e)} /> +
+ Select + | + +
*/} + {filterItemState.items.map((item: any, index: number) => { + const key = 'option' + index; + const itemLabel = item.option ? item.option : '(undefined)'; + return + this.handleCheck(item)} + id={item.option} + style={styles.filterItemCheck} + type='checkbox' name={item.option} + checked={item.checked} /> + + ; + })} +
: +
{filterItemOpen && + this.handleSliderChange(e, filterItemState)} />} +
} +
; + } +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx new file mode 100644 index 000000000..379b44370 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx @@ -0,0 +1,45 @@ +import { reactStyles } from 'app/utils'; +import * as React from 'react'; + +interface Props { + min: Number; + max: Number; + ogFilterItem: any; + onSliderChange: Function; +} + +const styles = reactStyles({ + sliderFormat: { display: 'flex' } +}); + +export class SVVariantFilterSliderComponent extends React.Component { + constructor(props: Props) { + super(props); + } + + + render(): React.ReactNode { + const { min, max, ogFilterItem } = this.props; + // const {sliderValue} = this.state + console.log(ogFilterItem, 'realy'); + + return +
+ this.props.onSliderChange(e, false)} /> + this.props.onSliderChange(e, true)} /> +
+
; + } + +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx new file mode 100644 index 000000000..3bec50837 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx @@ -0,0 +1,188 @@ +import * as React from "react"; + +import { SVVariantFilterItemComponent } from "./sv-variant-filter-item.component"; +import { SVVariantSortItemComponent } from "./sv-variant-sort-item.component"; +import { reactStyles } from "app/utils"; +import { GenomicFilters } from "publicGenerated"; +import { SortMetadata } from "publicGenerated/fetch"; + + +const styles = reactStyles({ + filterBox: { + top: ".5rem", + position: "absolute", + padding: ".25rem", + zIndex: 12, + borderRadius: "0 1px 1px 0", + backgroundColor: "#FFFFFF", + boxShadow: + "0 1px 3px 0 rgba(0,0,0,0.15), 0 0 2px 0 rgba(0,0,0,0.25), 0 2px 2px 0 rgba(0,0,0,0.15)", + width: "264px", + height: "421px", + display: "grid", + gridTemplateRows: "84% 16%", + }, + filterItemHandleClosed: { + transform: "rotate(90deg)", + }, + sortByContainer: { + paddingTop: ".5rem", + paddingRight: ".5rem", + }, + actionBtnContainer: { + position: "absolute", + bottom: ".5rem", + width: "100%", + display: "flex", + justifyContent:"space-around", + fontSize: "1.1em", + }, + clearBtn: { + textTransform: "uppercase", + borderRadius: "2px", + padding: "1rem", + border: "none", + background: "transparent", + width: "45%", + cursor: "pointer", + }, + applyBtn: { + textTransform: "uppercase", + borderRadius: "2px", + padding: "1rem", + border: "none", + background: "#262262", + color: "white", + width: "45%", + cursor: "pointer", + }, + filterItems: { + maxHeight: "340px", + overflowY: "auto", + }, +}); + + +export interface Cat { + display: String; + field: String; +} + +interface Props { + filterMetadata: GenomicFilters; + sortMetadata: SortMetadata; + onFilterSubmit: Function; + onSortChange: Function; +} +interface State { + filterCats: Cat[]; + filteredMetadata: any; + filterMetadata: any; + cleared: Boolean; + ogFilterMetaData: any; + sortMetadata: SortMetadata; +} + +export class SVVariantFilterComponent extends React.Component { + + constructor(props: Props) { + super(props); + this.state = { + filterCats: [ + { display: 'Gene', field: 'gene' }, + { display: 'Consequence', field: 'consequence' }, + { display: 'Variant Type', field: 'variantType' }, + { display: 'ClinVar Significance', field: 'clinicalSignificance' }, + { display: 'Allele Count', field: 'alleleCount' }, + { display: 'Allele Number', field: 'alleleNumber' }, + { display: 'Allele Frequency', field: 'alleleFrequency' }, + { display: 'Homozygote Count', field: 'homozygoteCount' }, + ], + filteredMetadata: this.props.filterMetadata, + filterMetadata: this.props.filterMetadata, + cleared: true, + ogFilterMetaData: JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'), + sortMetadata: this.props.sortMetadata, + }; + } + + handleFilterChange(filteredItem: GenomicFilters, cat: Cat) { + const filterMetadataChange = this.props.filterMetadata; + filterMetadataChange[cat.field.toString()] = filteredItem; + this.setState({ filterMetadata: filterMetadataChange }); + } + + handleSortChange(sortedItem: SortMetadata) { + this.setState({ sortMetadata: sortedItem }); + } + + submitFilter(filteredMetadata: GenomicFilters) { + // tslint:disable-next-line: forin + for (const key in filteredMetadata) { + const filterItem = filteredMetadata[key]; + const touched = Array.isArray(filterItem) && filterItem.some((t => t.checked)); + if (Array.isArray(filterItem)) { + if (!touched) { + filteredMetadata[key] = filterItem.forEach((item) => { + item.checked = true; + }); + filteredMetadata[key] = filterItem; + } + } + } + filteredMetadata = this.state.filteredMetadata; + const sortMetadata = this.state.sortMetadata; + this.props.onFilterSubmit(filteredMetadata, sortMetadata); + } + + handleClear() { + const ogFilterMetaData = JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'); + // tslint:disable-next-line: forin + for (const key in this.state.filteredMetadata) { + this.state.filteredMetadata[key] = ogFilterMetaData[key]; + } + + const { sortMetadata } = this.state; + for (const smKey in sortMetadata) { + sortMetadata[smKey].sortActive = false; + sortMetadata[smKey].sortDirection = "asc"; + } + sortMetadata['variantId'].sortActive = true; + sortMetadata['variantId'].sortDirection = "asc"; + + this.setState({ cleared: false, filteredMetadata: this.state.filteredMetadata, sortMetadata: sortMetadata }, () => this.setState({ cleared: true })); + this.props.onFilterSubmit(this.state.filteredMetadata, sortMetadata); + + } + + render() { + const { filterMetadata } = this.props; + const { filterCats, filteredMetadata, cleared, sortMetadata } = this.state; + return +
+
+ {filterCats.map((cat, index) => { + const key = 'cat' + index; + { + return cleared && filterMetadata && filteredMetadata && + this.handleFilterChange(e, cat)} + key={key} + category={cat} + cleared={cleared} + filterItem={filteredMetadata[cat.field.toString()]} />; + } + }) + } +
+ { this.handleSortChange(e)} sortMetadata={sortMetadata} />} +
+
+
+ + +
+
+
; + } +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx new file mode 100644 index 000000000..1a2d7f2ef --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx @@ -0,0 +1,170 @@ +import * as React from "react"; + +import { genomicsApi } from "app/services/swagger-fetch-clients"; +import { reactStyles } from "app/utils"; +import { ClrIcon } from "app/utils/clr-icon"; +import { SVVariant } from "publicGenerated"; + + +const styles = reactStyles({ + variant: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + height: "100%", + borderRight: "1px solid #CCCCCC", + boxShadow: "rgb(204 204 204) 0.2rem 0px 8px -2px", + background: "white", + color: "#216FB4", + position: "sticky", + left: 0, + }, + caretIcon: { + fontFamily: "gothamBold,Arial, Helvetica, sans-serif", + fontWeight: "bold", + }, + rowItem: { + width: "100%", + paddingTop: ".5rem", + paddingBottom: ".5rem", + paddingLeft: ".75rem", + }, + first: { + paddingLeft: ".5rem", + }, + last: { + paddingRight: ".5rem", + }, + variantId: { + wordBreak: "break-all", + cursor: "pointer", + display: "inline-flex", + flexDirection: "row", + }, + variantIdText: { + width: "90%", + }, + variantIconText: { + paddingLeft: "0.75rem", + paddingRight: "0.75rem", + display: "flex", + alignItems: "center", + }, + multipleValVariantItem: { + overflowWrap: "break-word", + wordBreak: "break-word", + }, +}); + +const css = ` +.row-layout { + display: grid; + grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + align-items: center; + width: 72rem; + background: white; + font-size: .8em; + border-bottom: 1px solid #CCCCCC; + position: relative; +} + +@media (max-width: 900px) { + .row-layout { + grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + width: 72rem; + } +} + +`; + +interface Props { + variant: SVVariant; + allowParentScroll: Function; +} + +interface State { + mouseOverExpanded: boolean; +} + +export class SVVariantRowComponent extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + mouseOverExpanded: false, + }; + } + + getVariantDetails(variantId: string) { + } + + handleClick(variantId?: string) { + + } + + +render() { + const { variant } = this.props; + return ( + + +
+
this.handleClick(variant.variantId)} + style={styles.variant} + > +
+
+ {variant.variantId.length > 40 ? ( + + {variant.variantId.substr(0, 40)} … + + ) : ( + variant.variantId + )} +
+
+ { }} + size="lg" + shape="caret" + dir="down" + /> +
+
+
+
+ {variant.variantType} +
+
+ {variant.consequence} +
+
+ {variant.position} +
+
+ {variant.size} +
+
+ {variant.alleleCount} +
+
+ {variant.alleleNumber} +
+
+ {variant.alleleFrequency} +
+
+ {variant.homozygoteCount} +
+
+
+ ); +} +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx new file mode 100644 index 000000000..d5ba6bf44 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx @@ -0,0 +1,225 @@ +import * as React from "react"; +import { environment } from "environments/environment"; +import { SearchComponent } from "app/data-browser/search/home-search.component"; +import { reactStyles } from "app/utils"; +import { ClrIcon } from "app/utils/clr-icon"; +import { Spinner } from "app/utils/spinner"; +import { SVGenomicFilters } from "publicGenerated"; +import { SortSVMetadata } from "publicGenerated/fetch"; +import { SVVariantFilterChips } from './sv-variant-filter-chips.component' +import { SVVariantFilterComponent } from "./sv-variant-filter.component"; + +const styles = reactStyles({ + searchBar: { + paddingRight: "2rem", + width: "calc(100% - 16rem)", + minWidth: "20rem" + }, + searchHelpText: { + paddingTop: "2em", + lineHeight: "1.2em", + fontSize: "0.75em", + }, + loading: { + transform: "scale(.3)", + marginLeft: "-1rem", + }, + resultSize: { + fontSize:"1.2em" + }, + filterBtn: { + fontFamily: "gothamBold", + color: "#216FB4", + cursor: "Pointer", + width: "fit-content" + }, + filterContainer: { + position: "relative", + }, + resultInfo: { + display:"grid", + gridTemplateColumns: "11.5rem 1fr", + alignItems: "baseline" + } +}); + +const css = ` +.search-container { + padding-top: 1em; + padding-bottom: 1em; + display: flex; + flex-wrap:wrap-reverse; + align-items: flex-end; + +} +@media (max-width: 1220px) { + .search-container { + // flex-direction: column; + // align-items: flex-start; + } +} +`; + +export interface Chip { + cat: any; + data: SVGenomicFilters; +} +interface Props { + onSearchTerm: Function; + onFilterSubmit: Function; + searchTerm: string; + variantListSize: number; + filterMetadata: SVGenomicFilters; + submittedFilterMetadata: SVGenomicFilters; + sortMetadata: SortSVMetadata; + onSortChange: Function; + loadingResults: boolean; + loadingVariantListSize: boolean; + scrollClean: boolean; +} +interface State { + filteredMetadata: SVGenomicFilters; + filteredMetaMap: SVGenomicFilters; + submittedFilterMetadata: SVGenomicFilters; + filterMetadata: SVGenomicFilters; + sortMetadata: SortSVMetadata; + filterShow: Boolean; + searchWord: string; + scrollClean: boolean; +} + +export class SVVariantSearchComponent extends React.Component { + private filterWrapperRef; + constructor(props: Props) { + super(props); + this.state = { + searchWord: '', + filterShow: false, + filteredMetadata: undefined, + filteredMetaMap: undefined, + filterMetadata: this.props.filterMetadata, + submittedFilterMetadata: this.props.submittedFilterMetadata, + sortMetadata: this.props.sortMetadata, + scrollClean: this.props.scrollClean + }; + if (this.state.searchWord !== '') { + this.props.onSearchTerm(this.state.searchWord); + } + this.filterWrapperRef = React.createRef(); + this.handleClickOutside = this.handleClickOutside.bind(this); + } + + handleChange(val: string) { + if (val == '') { this.setState({ scrollClean: true }) } + this.props.onSearchTerm(val); + this.setState({ searchWord: val, filteredMetaMap: null, filterShow: false }); + } + + componentWillUpdate(nextProps: Readonly, nextState: Readonly, nextContext: any): void { + if (this.props.scrollClean != nextProps.scrollClean) { + this.setState({ scrollClean: nextProps.scrollClean }) + } + } + componentDidMount() { + document.addEventListener("mousedown", this.handleClickOutside); + } + + componentWillUnmount() { + document.removeEventListener("mousedown", this.handleClickOutside); + } + + handleClickOutside(event) { + const { filterShow } = this.state; + if (this.filterWrapperRef && !this.filterWrapperRef.current.contains(event.target)) { + if (filterShow) { + this.setState({ filterShow: !this.state.filterShow }); + } + } + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly) { + const { searchTerm, filterMetadata, submittedFilterMetadata } = this.props; + + if (prevProps.searchTerm !== searchTerm) { + this.setState({ searchWord: searchTerm }); + } + if (prevProps.filterMetadata !== filterMetadata) { + this.setState({ filterMetadata: filterMetadata }); + } + if (prevProps.submittedFilterMetadata !== submittedFilterMetadata) { + this.setState({ submittedFilterMetadata: submittedFilterMetadata }); + } + } + + showFilter() { + this.setState({ filterShow: !this.state.filterShow }); + } + + handleFilterSubmit(filteredMetadata: SVGenomicFilters, sortMetadata: SortSVMetadata) { + this.setState({ filteredMetadata: filteredMetadata }); + this.props.onFilterSubmit(filteredMetadata, sortMetadata); + this.setState({ filterShow: false }); + } + + handleChipChange(changes) { + // this.setState({ filteredMetaMap: changes }); + const sortMetadata = this.state.sortMetadata; + this.handleFilterSubmit(changes, sortMetadata); + } + + handleSortChange(sortChange: any) { + this.setState({ sortMetadata: sortChange }); + this.props.onSortChange(sortChange); + } + + render() { + const { searchWord, filterShow, sortMetadata, submittedFilterMetadata, scrollClean } = this.state; + const { filterMetadata } = this.props + const { variantListSize, loadingResults, loadingVariantListSize } = this.props; + const variantListSizeDisplay = variantListSize ? variantListSize.toLocaleString() : 0; + return + +
+
+ this.handleChange(val)} + onClear={() => this.handleChange('')} placeholderText='Search by variant' /> +
+
+ Examples by query type:

+ Variant: 1-104946932-0fa1

+
+
+ { + submittedFilterMetadata && + this.handleChipChange(changes)} /> + } +
+ {((!loadingResults && !loadingVariantListSize) && (variantListSize > 0) && environment.genoFilters) ?
this.showFilter()} + style={styles.filterBtn}> Filter & Sort
: + scrollClean ?
:
this.showFilter()} + style={styles.filterBtn}> Filter & Sort
} + + { + (!loadingResults && !loadingVariantListSize && searchWord) ? {(!loadingResults && !loadingVariantListSize) ? variantListSizeDisplay : + } variants : + scrollClean ?
: {variantListSizeDisplay} variants + } +
+
+ {environment.genoFilters &&
+ {filterShow && + this.handleFilterSubmit(filteredMetadata, sortMetadata)} + onSortChange={(e) => this.handleSortChange(e)} + />} +
+ } + +
; + } +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx new file mode 100644 index 000000000..1e1410444 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx @@ -0,0 +1,212 @@ +import * as React from "react"; +import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; +import { reactStyles } from "app/utils"; +import { ClrIcon } from "app/utils/clr-icon"; +import { SortMetadata } from "publicGenerated/fetch"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; + +const styles = reactStyles({ + sortItem: { + width: "100%", + paddingLeft: ".5rem", + fontSize: "0.8em", + display: "flex", + justifyContent: "space-between", + alignItems: "center", + color: "#262262", + letterSpacing: 0, + lineHeight: "16px", + cursor: "pointer" + }, + sortItemClosed: { + transform: "rotate(90deg)", + }, + sortItemOpen: { + transform: "rotate(180deg)", + }, + sortItemForm: { + fontSize: "0.8em", + display: "flex", + overflow: 'visible', + flexDirection: "column", + paddingLeft: "1rem", + paddingTop: ".25rem", + }, + sortItemOption: { + fontSize: ".8em", + display: "flex", + }, + sortItemCheck: { + marginRight: ".25rem", + height: ".8rem", + width: ".8rem", + marginTop: "0.1rem", + }, + sortItemLabel: { + width: '80%', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflow: 'hidden' + // wordWrap: "break-word", + }, + activeSort: { + fontFamily: 'gothamBold' + } +}); + +const css = ` +.tooltip { + position: relative; + display: inline-block; +} + +.tooltip .tooltiptext { + visibility: hidden; + width: 185px; + font-size: 14px; + font-family: GothamBook, Arial, sans-serif; + background-color: #FFFFFF; + color: #302C71; + text-align: left; + border-spacing: 5px; + padding: 5px; + position: absolute; + z-index: 9999; + bottom: 125%; + left: 10%; +} + +.tooltip:focus .tooltiptext, .tooltip:hover .tooltiptext { + visibility: visible; +} + +.tooltiptext { + margin: 3%; + line-height: normal; + outline: 2px solid #302C71; + box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.15); +} +`; + +const lables = { + gene: 'Gene', + consequence: 'Consequence', + variantType: 'Variant Type', + clinicalSignificance: 'ClinVar Significance', + alleleNumber: 'Allele Number', + alleleFrequency: 'Allele Frequency', + alleleCount: 'Allele Count' +}; + +interface Props { + cleared: Boolean; + onSortChange: Function; + sortMetadata: SortMetadata; +} + +interface State { + sortItemOpen: Boolean; + sortMetadata: SortMetadata; + filterCats: any[]; + sortCats: any[]; +} + +export class SVVariantSortItemComponent extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + sortItemOpen: false, + sortMetadata: props.sortMetadata, + filterCats: [ + { display: 'Gene', field: 'gene' }, + { display: 'Consequence', field: 'consequence' }, + { display: 'Variant Type', field: 'variantType' }, + { display: 'ClinVar Significance', field: 'clinicalSignificance' }, + { display: 'Allele Count', field: 'alleleCount' }, + { display: 'Allele Number', field: 'alleleNumber' }, + { display: 'Allele Frequency', field: 'alleleFrequency' }, + { display: 'Homozygote Count', field: 'homozygoteCount' }, + ], + sortCats: [ + { display: 'Variant ID', field: 'variantId'}, + { display: 'Gene', field: 'gene' }, + { display: 'Consequence', field: 'consequence' }, + { display: 'Variant Type', field: 'variantType' }, + { display: 'ClinVar Significance', field: 'clinicalSignificance' }, + { display: 'Allele Count', field: 'alleleCount' }, + { display: 'Allele Number', field: 'alleleNumber' }, + { display: 'Allele Frequency', field: 'alleleFrequency' }, + { display: 'Homozygote Count', field: 'homozygoteCount' }, + ] + }; + } + + componentDidMount(): void { + } + + sortClick() { + this.setState({ sortItemOpen: !this.state.sortItemOpen }); + } + + + + clickToSort(field) { + const { sortMetadata } = this.state; + + if (sortMetadata[field].sortActive) { + if (sortMetadata[field].sortDirection === 'asc') { + sortMetadata[field].sortDirection = 'desc' + } else { + sortMetadata[field].sortDirection = 'asc' + } + for (const item in sortMetadata) { + if (item !== field) { + sortMetadata[item].sortActive = false; + sortMetadata[item].sortDirection = 'desc'; + } + } + } else { + sortMetadata[field].sortActive = true; + for (const item in sortMetadata) { + if (item !== field) { + sortMetadata[item].sortActive = false; + sortMetadata[item].sortDirection = 'asc'; + } + } + } + this.setState({ sortMetadata: this.state.sortMetadata }); + } + + render(): React.ReactNode { + const { cleared } = this.props; + const { sortItemOpen, sortMetadata, filterCats, sortCats } = this.state; + + return + +
this.sortClick()} style={styles.sortItem}> + Sort By +
+
+ {(cleared && sortItemOpen) && +
+ {sortCats && sortCats.map((cat, index) => { + return
this.clickToSort(cat.field)}> + +
{cat.display} + Click to select
ascending or descending
+
+ {sortMetadata[cat.field].sortActive && sortMetadata[cat.field].sortDirection === 'asc' && +
+
+ })} +
} +
; + } +} \ No newline at end of file diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx new file mode 100644 index 000000000..ca77d27ca --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx @@ -0,0 +1,456 @@ +import * as React from "react"; +import { reactStyles } from "app/utils"; +import { Spinner } from "app/utils/spinner"; +import { environment } from "environments/environment"; +import { SVVariant } from "publicGenerated"; +import { SortSVMetadata } from "publicGenerated/fetch"; +import { TablePaginatorComponent } from "./table-paginator.component"; +import { SVVariantRowComponent } from "./sv-variant-row.component"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; + +const styles = reactStyles({ + tableContainer: { + borderTop: "1px solid #CCCCCC", + borderLeft: "1px solid #CCCCCC", + borderRight: "1px solid #CCCCCC", + borderBottom: "none", + borderRadius: "3px 3px 0 0", + background: "#FAFAFA", + marginTop: "0.5rem", + overflowY: environment.infiniteSrcoll ? "scroll" : "hidden", + height: environment.infiniteSrcoll ? "30rem" : "", + }, + noScroll: { + overflowX: "scroll" + }, + tableFrame: { + border: "1px solid #CCCCCC", + borderRadius: "3px", + background: "#FAFAFA", + marginTop: "0.5rem", + height: "25rem", + }, + headingItem: { + fontSize: ".8em", + paddingTop: ".5rem", + paddingBottom: ".5rem", + paddingLeft: ".75rem", + }, + headingLabel: { + borderBottom: "1px dashed", + }, + first: { + paddingLeft: ".5rem", + position: "sticky", + left: 0, + background: "#f9f9fa", + }, + last: { + paddingRight: ".5rem", + }, + center: { + display: "flex", + height: "100%", + width: "100%", + justifyContent: "center", + alignItems: "center", + }, + helpTextContainer: { + display: "flex", + height: "100%", + margin: "0 auto", + width: "70%", + justifyContent: "center", + alignItems: "flex-start", + flexDirection: "column", + }, + helpText: { + margin: 0, + fontFamily: "GothamBook, Arial, sans-serif", + fontWeight: 100, + fontStyle: "normal", + fontSize: "1em", + fontStretch: "normal", + lineHeight: "1.47em", + letterSpacing: "normal", + textAlign: "left", + color: "#262262", + }, + helpSearchDiv: { + display: "inline", + textDecoration: "underline", + cursor: "pointer", + }, +}); + +const css = ` +.header-layout { + display: grid; + grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + background: #f9f9fa; + font-family: gothamBold,Arial, Helvetica,sans-serif; + width: 72rem; + position: sticky; + left: 0; + top:0; + z-index:10; + border-bottom: 1px solid #CCCCCC; +} +@media (max-width: 900px) { + .header-layout { + grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + width: 72rem; + } +} + .paginator { + background: #f9f9fa; + border-bottom: 1px solid #CCCCCC; + border-right: 1px solid #CCCCCC; + border-left: 1px solid #CCCCCC; + border-top: none; + border-radius: 0 0 3px 3px; + display: flex; + flex-direction: row; + gap: 2em; + justify-content: space-between; + } + @media (max-width: 600px) { + .paginator { + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + gap: 0; + } + } +`; + +interface Props { + onPageChange: Function; + onSearchTerm: Function; + onSortClick: Function; + onRowCountChange: Function; + onScrollBottom: Function; + svResults: SVVariant[]; + variantListSize: number; + loadingVariantListSize: boolean; + loadingResults: boolean; + searchTerm: string; + currentPage: number; + rowCount: number; + sortMetadata: SortSVMetadata; + filtered: boolean; +} + +interface State { + loading: boolean; + svResults: SVVariant[]; + sortMetadata: any; + allowParentScroll: Boolean; + +} + +export class SVVariantTableComponent extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + loading: props.loadingResults, + svResults: props.svResults, + sortMetadata: props.sortMetadata, + allowParentScroll: true + }; + } + scrollAreaRef: React.RefObject = React.createRef(); + + observer = null; + + columnNames = [ + "Variant ID", + "Gene", + "Consequence", + "Variant Type", + "Clinical Significance", + "Allele Count", + "Allele Number", + "Allele Frequency", + "Homozygote Count", + ]; + debounceTimer = null; + + componentDidUpdate(prevProps: Readonly) { + const { svResults, loadingResults, filtered } = this.props; + + if (filtered) { + this.scrollAreaToTop(); + + } + if (prevProps.svResults !== svResults) { + this.setState({ svResults: svResults, loading: loadingResults }); + } + + + + } + + handleScrollEnd = (event) => { + clearTimeout(this.debounceTimer); + this.debounceTimer = setTimeout(() => { + const scrollArea = document.querySelector('.scroll-area'); + if (scrollArea) { + const scrollTop = scrollArea.scrollTop; + const scrollHeight = scrollArea.scrollHeight; + // trigger scroll at 35% + const scrolledToBottom = scrollTop / scrollHeight > .35; + if (scrolledToBottom && this.props.currentPage < this.props.variantListSize / this.props.rowCount) { + // Fetch new data and append + this.props.onScrollBottom(); + } + } + }, 150); + }; + + + + handlePageClick = (data) => { + const { searchTerm } = this.props; + this.setState({ loading: true }); + this.props.onPageChange({ selectedPage: data, searchTerm: searchTerm }); + }; + + handleRowCountChange = (data) => { + this.props.onRowCountChange({ rowCount: data }); + }; + + sortClick(key: string) { + const { sortMetadata } = this.state; + if (sortMetadata[key].sortActive) { + const direction = sortMetadata[key].sortDirection; + direction === "desc" + ? (sortMetadata[key].sortDirection = "asc") + : (sortMetadata[key].sortDirection = "desc"); + } else { + sortMetadata[key].sortActive = true; + } + + for (const sKey in sortMetadata) { + if (sKey !== key) { + sortMetadata[sKey].sortActive = false; + sortMetadata[sKey].sortDirection = "desc"; + } + } + this.setState({ sortMetadata: sortMetadata }, () => { + this.props.onSortClick(this.state.sortMetadata); + }); + } + + searchItem(searchTerm: string) { + this.props.onSearchTerm(searchTerm); + } + + // Function to scroll the div with class 'scroll-area' to the top + scrollAreaToTop = () => { + if (this.scrollAreaRef.current) { + this.scrollAreaRef.current.scrollTop = 0; + } + }; + + setArrowIcon(varName: string) { + const { sortMetadata } = this.state; + return sortMetadata[varName].sortDirection === "asc" ? faArrowUp : faArrowDown; + } + + + + + + render() { + const { loadingVariantListSize, loadingResults, variantListSize, rowCount, currentPage } = + this.props; + const { loading, svResults, sortMetadata, allowParentScroll } = this.state; + styles.noScroll.overflowX = !allowParentScroll ? "hidden":"scroll"; + return ( + + + {!loading && + !loadingVariantListSize && + svResults && + svResults.length ? ( +
+
+
+ + Variant ID + + {sortMetadata.variantId.sortActive && ( + + )} +
+
+ + Variant Type + + {sortMetadata.variantType.sortActive && ( + + )} +
+
+ + Consequence + + {sortMetadata.consequence.sortActive && ( + + )} +
+
+ + Position + + {sortMetadata.position.sortActive && ( + + )} +
+
+ + Size + + {sortMetadata.size.sortActive && ( + + )} +
+
+ + Allele Count + + {sortMetadata.alleleCount.sortActive && ( + + )} +
+
+ + Allele Number + + {sortMetadata.alleleNumber.sortActive && ( + + )} +
+
+ + Allele Frequency + + {sortMetadata.alleleFrequency.sortActive && ( + + )} +
+
+ + Homozygote Count + + {sortMetadata.homozygoteCount.sortActive && ( + + )} +
+
+ + {svResults && + svResults.map((variant, index) => { + return ( + this.setState({allowParentScroll:!this.state.allowParentScroll})} + /> + ); + })} + {environment.infiniteSrcoll &&
+ {currentPage < variantListSize / rowCount && loadingResults && } +
} +
+ ) : ( +
+ {(loading || loadingVariantListSize || loadingResults) && ( +
+ {" "} +
+ )} + {(!svResults || + (svResults && svResults.length === 0)) && ( +
+
+ Enter a query in the search bar or get started with an example query: +
+
+ Variant:{" "} +
this.searchItem("1-104946932-0fa1")} + style={styles.helpSearchDiv} + > + 1-104946932-0fa1 +
+
+
+ )} +
+ )} + {!loading && + !loadingVariantListSize && + svResults && + variantListSize > rowCount && ( +
+ {!environment.infiniteSrcoll && + { + this.handlePageClick(info); + }} + onRowCountChange={(info) => { + this.handleRowCountChange(info); + }} + /> + } +
+ )} +
+ ); + } +} \ No newline at end of file diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx new file mode 100644 index 000000000..9d5e111b9 --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx @@ -0,0 +1,157 @@ +import * as React from "react"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faAngleLeft } from '@fortawesome/free-solid-svg-icons'; +import { faAngleRight } from '@fortawesome/free-solid-svg-icons'; + +import { reactStyles } from "app/utils"; + +const styles = reactStyles({ + pageNum: { + alignSelf: "flex-end", + fontSize: "12px", + marginTop: "1em", + marginBottom: "1em", + }, + pageButton: { + textDecoration: "none", + display: "inline-block", + marginRight: "1em", + marginLeft: "1em", + color: "black", + border: "1px solid rgb(33, 111, 180)", + background: "none", + cursor: "pointer", + }, + disabledPageButton: { + textDecoration: "none", + display: "inline-block", + marginRight: "1em", + marginLeft: "1em", + color: "grey", + border: "1px solid rgb(33, 111, 180)", + background: "none", + cursor: "none", + pointerEvents: "none", + }, + enabledIcon: { + color: "black", + }, + disabledIcon: { + color: "#ddd", + }, +}); + +const css = ` + .paginator { + flex-direction: column; + align-items: flex-end; + justify-content: flex-end; + } + .page-drop-down-label { + display: flex; + flex-direction: row; + align-items: center; + padding-left: 1em; + justify-content: space-between; + gap: 1em; + } + @media (max-width: 600px) { + .paginator { + flex-direction: column; + align-items: flex-end; + gap: 0; + justify-content: flex-end; + } + } +`; + +interface Props { + pageCount: number; + variantListSize: number; + currentPage: number; + resultsSize: number; + onPageChange: Function; + rowCount: number; + onRowCountChange: Function; +} + +interface State { + currentPage: number; + rowCount: number; +} + +export class TablePaginatorComponent extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + currentPage: props.currentPage ? props.currentPage : 1, + rowCount: props.rowCount ? props.rowCount : 50, + }; + } + + handleChange(event) { + this.setState({ currentPage: +event.target.value }, () => { + this.props.onPageChange(this.state.currentPage); + }); + } + + rowCountChange(event) { + this.setState({ rowCount: +event.target.value }, () => { + this.props.onRowCountChange(this.state.rowCount); + }); + } + + render() { + const { currentPage, rowCount } = this.state; + const { pageCount } = this.props; + return ( + + + {/* +
+ +
+ */} +
+ + Page {currentPage} of {pageCount} + +
+
+ ); + } +} From 0b287367cf4ae607e6496b8b89da46a25526792d Mon Sep 17 00:00:00 2001 From: Srushti Gangireddy Date: Wed, 24 Jul 2024 12:39:39 -0500 Subject: [PATCH 2/7] wip --- .../publicapi/GenomicsController.java | 188 +++++++++- public-api/src/main/resources/public-api.yaml | 172 ++++++++- .../genomic-view/genomic-view.component.tsx | 24 +- .../sv-variant-expanded.component.tsx | 336 ++++++++++++++++++ .../sv-variant-filter-chips.component.tsx | 7 +- .../sv-variant-filter.component.tsx | 7 +- .../components/sv-variant-row.component.tsx | 40 ++- .../sv-variant-sort-item.component.tsx | 42 ++- .../components/sv-variant-table.component.tsx | 4 +- 9 files changed, 757 insertions(+), 63 deletions(-) create mode 100644 public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx diff --git a/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java b/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java index c46e1b6b1..909c27ecf 100644 --- a/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java +++ b/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java @@ -13,6 +13,7 @@ import org.pmiops.workbench.model.Variant; import org.pmiops.workbench.model.SVVariant; import org.pmiops.workbench.model.VariantInfo; +import org.pmiops.workbench.model.SVVariantInfo; import org.pmiops.workbench.model.GenomicFilters; import org.pmiops.workbench.model.GenomicFilterOption; import org.pmiops.workbench.model.GenomicFilterOptionList; @@ -88,6 +89,16 @@ public class GenomicsController implements GenomicsApiDelegate { "gvs_oth_ac as oth_allele_count, gvs_oth_an as oth_allele_number, gvs_oth_af as oth_allele_frequency, gvs_oth_hc as oth_homozygote_count, " + "gvs_all_ac as total_allele_count, gvs_all_an as total_allele_number, gvs_all_af as total_allele_frequency, homozygote_count as total_homozygote_count from ${projectId}.${dataSetId}.wgs_variant"; + private static final String SV_VARIANT_DETAIL_SQL_TEMPLATE = "SELECT variant_type, consequence_genes, position, size, \n" + + "afr_ac as afr_allele_count, afr_an as afr_allele_number, afr_af as afr_allele_frequency, afr_n_homalt as afr_homozygote_count, \n" + + "eas_ac as eas_allele_count, eas_an as eas_allele_number, eas_af as eas_allele_frequency, eas_n_homalt as eas_homozygote_count, \n" + + "eur_ac as eur_allele_count, eur_an as eur_allele_number, eur_af as eur_allele_frequency, eur_n_homalt as eur_homozygote_count, \n" + + "amr_ac as amr_allele_count, amr_an as amr_allele_number, amr_af as amr_allele_frequency, amr_n_homalt as amr_homozygote_count, \n" + + "mid_ac as mid_allele_count, mid_an as mid_allele_number, mid_af as mid_allele_frequency, mid_n_homalt as mid_homozygote_count, \n" + + "sas_ac as sas_allele_count, sas_an as sas_allele_number, sas_af as sas_allele_frequency, sas_n_homalt as sas_homozygote_count, \n" + + "oth_ac as oth_allele_count, oth_an as oth_allele_number, oth_af as oth_allele_frequency, oth_n_homalt as oth_homozygote_count, \n" + + "allele_count as total_allele_count, allele_number as total_allele_number, allele_frequency as total_allele_frequency, homozygote_count as total_homozygote_count from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; + private static final String FILTER_OPTION_SQL_TEMPLATE_GENE = "with a as\n" + "(select 'Gene' as option, genes as genes, '' as conseq, '' as variant_type, '' as clin_significance, count(*) as gene_count, " + "0 as con_count, 0 as variant_type_count, " + @@ -143,33 +154,36 @@ public class GenomicsController implements GenomicsApiDelegate { "select * from g;"; private static final String SV_FILTER_OPTION_SQL_TEMPLATE_GENE = "with a as\n" + - "(select 'Gene' as option, genes as genes, '' as variant_type, count(*) as gene_count, " + - "0 as variant_type_count, " + + "(select distinct 'Gene' as option, genes as genes, '' as variant_type, '' as consequence, \n" + "0 as min_count, 0 as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id tj, \n" + " unnest(split(genes, ', ')) gene\n"; private static final String SV_FILTER_OPTION_SQL_TEMPLATE_VAR_TYPE = " group by genes),\n" + "b as \n" + - "(select 'Variant Type' as option, '' as genes, variant_type as variant_type, 0 as gene_count, count(*) as variant_type_count, 0 as min_count, 0 as max_count\n" + + "(select distinct 'Variant Type' as option, '' as genes, variant_type as variant_type, '' as consequence, 0 as min_count, 0 as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; - private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT = " group by variant_type),\n" + + + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_CON = " group by variant_type), \n" + + "c as\n" + + "(select distinct 'Consequence' as option, '' as genes, '' as variant_type, con as consequence,\n" + + "0 as min_count, 0 as max_count\n" + + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id tj, \n" + + " unnest(split(consequence, ', ')) con\n"; + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT = " group by con),\n" + "e as \n" + - "(select 'Allele Count' as option, '' as genes, '' as variant_type, \n" + - "0 as gene_count, 0 as variant_type_count,\n" + + "(select distinct 'Allele Count' as option, '' as genes, '' as variant_type, '' as consequence, \n" + "min(allele_count) as min_count, max(allele_count) as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_NUMBER = "),\n" + "f as \n" + - "(select 'Allele Number' as option, '' as genes, '' as variant_type, \n" + - "0 as gene_count, 0 as variant_type_count,\n" + + "(select distinct 'Allele Number' as option, '' as genes, '' as variant_type, '' as consequence, \n" + "min(allele_number) as min_count, max(allele_number) as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; private static final String SV_FILTER_OPTION_SQL_TEMPLATE_HOMOZYGOTE_COUNT = "),\n" + "g as \n" + - "(select 'Homozygote Count' as option, '' as genes, '' as variant_type, \n" + - "0 as gene_count, 0 as variant_type_count,\n" + + "(select distinct 'Homozygote Count' as option, '' as genes, '' as variant_type, '' as consequence, \n" + "min(homozygote_count) as min_count, max(homozygote_count) as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; private static final String SV_FILTER_OPTION_SQL_TEMPLATE_UNION = ")\n" + @@ -177,6 +191,8 @@ public class GenomicsController implements GenomicsApiDelegate { "union all \n" + "select * from b \n" + "union all \n" + + "select * from c \n" + + "union all \n" + "select * from e \n" + "union all \n" + "select * from f \n" + @@ -237,8 +253,9 @@ public ResponseEntity getVariantSearchResultSize(VariantResultSizeRequest } String WHERE_GENE_IN = " AND genes in ("; - String WHERE_CON_IN = " AND (EXISTS (SELECT con FROM UNNEST (consequence) as con where con in ("; + String WHERE_CON_IN = " AND (EXISTS (SELECT con FROM UNNEST (consequence) as con where con in ( \n"; String WHERE_CON_NULL = ""; + String WHERE_SV_CON_IN = " AND (EXISTS (SELECT con FROM UNNEST (split(consequence, ', ')) as con where con in ( \n"; String WHERE_CLIN_IN = " AND (EXISTS (SELECT clin FROM UNNEST (clinical_significance) as clin where clin in ("; String WHERE_CLIN_NULL = ""; String WHERE_VAR_TYPE_IN = "AND variant_type in ("; @@ -446,12 +463,15 @@ public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequ String WHERE_GENE_IN = " AND genes in ("; String WHERE_VAR_TYPE_IN = "AND variant_type in ("; + String WHERE_CON_NULL = ""; + String WHERE_SV_CON_IN = " AND (EXISTS (SELECT con FROM UNNEST (split(consequence, ', ')) as con where con in ( \n"; String ALLELE_COUNT_FILTER = ""; String ALLELE_NUMBER_FILTER = ""; String ALLELE_FREQUENCY_FILTER = ""; String HOMOZYGOTE_COUNT_FILTER = ""; boolean geneFilterFlag = false; boolean varTypeFilterFlag = false; + boolean conFilterFlag = false; if (filters != null) { SVGenomicFilterOptionList geneFilterList = filters.getGene(); List geneFilters = geneFilterList.getItems(); @@ -463,6 +483,20 @@ public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequ } } } + SVGenomicFilterOptionList conFilterList = filters.getConsequence(); + List conFilters = conFilterList.getItems(); + if (conFilters != null && conFilters.size() > 0 && conFilterList.isFilterActive()) { + for(int i=0; i < conFilters.size(); i++) { + SVGenomicFilterOption filter = conFilters.get(i); + if (filter.isChecked()) { + if (!Strings.isNullOrEmpty(filter.getOption())) { + WHERE_SV_CON_IN += "\"" + filter.getOption() + "\","; + } else { + WHERE_CON_NULL = " OR ARRAY_LENGTH(consequence) = 0"; + } + } + } + } SVGenomicFilterOptionList varTypeFilterList = filters.getVariantType(); List varTypeFilters = varTypeFilterList.getItems(); if (varTypeFilters != null && varTypeFilters.size() > 0 && varTypeFilterList.isFilterActive()) { @@ -505,6 +539,11 @@ public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequ WHERE_GENE_IN = WHERE_GENE_IN.substring(0, WHERE_GENE_IN.length()-1); WHERE_GENE_IN += ") "; } + if (WHERE_SV_CON_IN.substring(WHERE_SV_CON_IN.length() - 1).equals(",")) { + conFilterFlag = true; + WHERE_SV_CON_IN = WHERE_SV_CON_IN.substring(0, WHERE_SV_CON_IN.length()-1); + WHERE_SV_CON_IN += ")) "; + } if (WHERE_VAR_TYPE_IN.substring(WHERE_VAR_TYPE_IN.length() - 1).equals(",")) { varTypeFilterFlag = true; @@ -519,6 +558,18 @@ public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequ finalSql += WHERE_GENE_IN; } + if (conFilterFlag) { + finalSql += WHERE_SV_CON_IN; + if (WHERE_CON_NULL.length() > 0) { + finalSql += WHERE_CON_NULL; + } + finalSql += ") "; + } else { + if (WHERE_CON_NULL.length() > 0) { + finalSql += " AND ARRAY_LENGTH(consequence) = 0"; + } + } + if (varTypeFilterFlag) { finalSql += WHERE_VAR_TYPE_IN; finalSql += ") "; @@ -693,12 +744,16 @@ public ResponseEntity searchSVVariants(SearchSVVariantsRe String WHERE_VAR_TYPE_IN = "AND variant_type in ("; + String WHERE_CON_NULL = ""; + String WHERE_SV_CON_IN = " AND (EXISTS (SELECT con FROM UNNEST (split(consequence, ', ')) as con where con in ( \n"; + + String ALLELE_COUNT_FILTER = ""; String ALLELE_NUMBER_FILTER = ""; String ALLELE_FREQUENCY_FILTER = ""; String HOMOZYGOTE_COUNT_FILTER = ""; boolean geneFilterFlag = false; - + boolean conFilterFlag = false; boolean varTypeFilterFlag = false; if (filters != null) { @@ -727,6 +782,20 @@ public ResponseEntity searchSVVariants(SearchSVVariantsRe } } + SVGenomicFilterOptionList conFilterList = filters.getConsequence(); + List conFilters = conFilterList.getItems(); + if (conFilters != null && conFilters.size() > 0 && conFilterList.isFilterActive()) { + for(int i=0; i < conFilters.size(); i++) { + SVGenomicFilterOption filter = conFilters.get(i); + if (filter.isChecked()) { + if (!Strings.isNullOrEmpty(filter.getOption())) { + WHERE_SV_CON_IN += "\"" + filter.getOption() + "\","; + } else { + WHERE_CON_NULL = " OR ARRAY_LENGTH(consequence) = 0"; + } + } + } + } SVGenomicFilterOption acFilter = filters.getAlleleCount(); if (acFilter != null && acFilter.isChecked()) { @@ -764,6 +833,12 @@ public ResponseEntity searchSVVariants(SearchSVVariantsRe WHERE_VAR_TYPE_IN = WHERE_VAR_TYPE_IN.substring(0, WHERE_VAR_TYPE_IN.length()-1); } + if (WHERE_SV_CON_IN.substring(WHERE_SV_CON_IN.length() - 1).equals(",")) { + conFilterFlag = true; + WHERE_SV_CON_IN = WHERE_SV_CON_IN.substring(0, WHERE_SV_CON_IN.length()-1); + WHERE_SV_CON_IN += ")) "; + } + if (geneFilterFlag) { if (whereGeneFlag) { finalSql = finalSql.replace(WHERE_GENE_REGEX, ""); @@ -778,6 +853,17 @@ public ResponseEntity searchSVVariants(SearchSVVariantsRe finalSql += ") "; } + if (conFilterFlag) { + finalSql += WHERE_SV_CON_IN; + if (WHERE_CON_NULL.length() > 0) { + finalSql += WHERE_CON_NULL; + } + finalSql += ") "; + } else { + if (WHERE_CON_NULL.length() > 0) { + finalSql += " AND ARRAY_LENGTH(consequence) = 0"; + } + } if (ALLELE_COUNT_FILTER.length() > 0) { finalSql += ALLELE_COUNT_FILTER; @@ -966,6 +1052,7 @@ public ResponseEntity searchVariants(SearchVariantsRequest } } } + GenomicFilterOptionList conFilterList = filters.getConsequence(); List conFilters = conFilterList.getItems(); if (conFilters != null && conFilters.size() > 0 && conFilterList.isFilterActive()) { @@ -1358,6 +1445,7 @@ public ResponseEntity getSVGenomicFilterOptions(String variant finalSql = SV_FILTER_OPTION_SQL_TEMPLATE_GENE + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_VAR_TYPE + searchSqlQuery + + SV_FILTER_OPTION_SQL_TEMPLATE_CON + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_NUMBER + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_HOMOZYGOTE_COUNT + searchSqlQuery @@ -1383,9 +1471,11 @@ public ResponseEntity getSVGenomicFilterOptions(String variant SVGenomicFilterOptionList varTypeFilterList = new SVGenomicFilterOptionList(); SVGenomicFilterOptionList geneFilterList = new SVGenomicFilterOptionList(); + SVGenomicFilterOptionList conFilterList = new SVGenomicFilterOptionList(); List varTypeFilters = new ArrayList<>(); List geneFilters = new ArrayList<>(); + List conFilters = new ArrayList<>(); SVGenomicFilterOption alleleCountFilter = new SVGenomicFilterOption(); SVGenomicFilterOption alleleNumberFilter = new SVGenomicFilterOption(); @@ -1394,26 +1484,29 @@ public ResponseEntity getSVGenomicFilterOptions(String variant for (List row : result.iterateAll()) { String option = bigQueryService.getString(row, rm.get("option")); String varType = bigQueryService.getString(row, rm.get("variant_type")); - Long variantTypeCount = bigQueryService.getLong(row, rm.get("variant_type_count")); String gene = bigQueryService.getString(row, rm.get("genes")); - Long geneCount = bigQueryService.getLong(row, rm.get("gene_count")); + String conseq = bigQueryService.getString(row, rm.get("consequence")); Long minCount = bigQueryService.getLong(row, rm.get("min_count")); Long maxCount = bigQueryService.getLong(row, rm.get("max_count")); SVGenomicFilterOption genomicFilterOption = new SVGenomicFilterOption(); if (option.equals("Gene")) { genomicFilterOption.setOption(gene); - genomicFilterOption.setCount(geneCount); genomicFilterOption.setChecked(false); genomicFilterOption.setMin(0L); genomicFilterOption.setMax(0L); geneFilters.add(genomicFilterOption); } else if (option.equals("Variant Type")) { genomicFilterOption.setOption(varType); - genomicFilterOption.setCount(variantTypeCount); genomicFilterOption.setChecked(false); genomicFilterOption.setMin(0L); genomicFilterOption.setMax(0L); varTypeFilters.add(genomicFilterOption); + } else if (option.equals("Consequence")) { + genomicFilterOption.setOption(conseq); + genomicFilterOption.setChecked(false); + genomicFilterOption.setMin(0L); + genomicFilterOption.setMax(0L); + conFilters.add(genomicFilterOption); } else if (option.equals("Allele Count")) { genomicFilterOption.setOption(""); genomicFilterOption.setCount(0L); @@ -1451,7 +1544,10 @@ public ResponseEntity getSVGenomicFilterOptions(String variant geneFilterList.setItems(geneFilters); geneFilterList.setFilterActive(false); + conFilterList.setItems(conFilters); + conFilterList.setFilterActive(false); + genomicFilters.consequence(conFilterList); genomicFilters.variantType(varTypeFilterList); genomicFilters.gene(geneFilterList); genomicFilters.alleleCount(alleleCountFilter); @@ -1490,6 +1586,7 @@ public ResponseEntity getVariantDetails(String variant_id) { .addNamedParameter("variant_id", QueryParameterValue.string(variant_id)) .setUseLegacySql(false) .build(); + qjc = bigQueryService.filterBigQueryConfig(qjc); TableResult result = bigQueryService.executeQuery(qjc); Map rm = bigQueryService.getResultMapper(result); @@ -1534,6 +1631,65 @@ public ResponseEntity getVariantDetails(String variant_id) { return ResponseEntity.ok(variantInfo); } + @Override + public ResponseEntity getSVVariantDetails(String variant_id) { + try { + cdrVersionService.setDefaultCdrVersion(); + } catch(NullPointerException ie) { + throw new ServerErrorException("Cannot set default cdr version"); + } + + String finalSql = SV_VARIANT_DETAIL_SQL_TEMPLATE + WHERE_VARIANT_ID; + QueryJobConfiguration qjc = QueryJobConfiguration.newBuilder(finalSql) + .addNamedParameter("variant_id", QueryParameterValue.string(variant_id)) + .setUseLegacySql(false) + .build(); + qjc = bigQueryService.filterBigQueryConfig(qjc); + TableResult result = bigQueryService.executeQuery(qjc); + Map rm = bigQueryService.getResultMapper(result); + List row = result.iterateAll().iterator().next(); + SVVariantInfo variantInfo = new SVVariantInfo() + .variantId(variant_id) + .variantType(bigQueryService.getString(row, rm.get("variant_type"))) + .consequenceGenes(bigQueryService.getString(row, rm.get("consequence_genes"))) + .position(bigQueryService.getString(row, rm.get("position"))) + .size(bigQueryService.getLong(row, rm.get("size"))) + .afrAlleleCount(bigQueryService.getLong(row, rm.get("afr_allele_count"))) + .afrAlleleNumber(bigQueryService.getLong(row, rm.get("afr_allele_number"))) + .afrAlleleFrequency(bigQueryService.getDouble(row, rm.get("afr_allele_frequency"))) + .afrHomozygoteCount(bigQueryService.getLong(row, rm.get("afr_homozygote_count"))) + .easAlleleCount(bigQueryService.getLong(row, rm.get("eas_allele_count"))) + .easAlleleNumber(bigQueryService.getLong(row, rm.get("eas_allele_number"))) + .easAlleleFrequency(bigQueryService.getDouble(row, rm.get("eas_allele_frequency"))) + .easHomozygoteCount(bigQueryService.getLong(row, rm.get("eas_homozygote_count"))) + .eurAlleleCount(bigQueryService.getLong(row, rm.get("eur_allele_count"))) + .eurAlleleNumber(bigQueryService.getLong(row, rm.get("eur_allele_number"))) + .eurAlleleFrequency(bigQueryService.getDouble(row, rm.get("eur_allele_frequency"))) + .eurHomozygoteCount(bigQueryService.getLong(row, rm.get("eur_homozygote_count"))) + .amrAlleleCount(bigQueryService.getLong(row, rm.get("amr_allele_count"))) + .amrAlleleNumber(bigQueryService.getLong(row, rm.get("amr_allele_number"))) + .amrAlleleFrequency(bigQueryService.getDouble(row, rm.get("amr_allele_frequency"))) + .amrHomozygoteCount(bigQueryService.getLong(row, rm.get("amr_homozygote_count"))) + .midAlleleCount(bigQueryService.getLong(row, rm.get("mid_allele_count"))) + .midAlleleNumber(bigQueryService.getLong(row, rm.get("mid_allele_number"))) + .midAlleleFrequency(bigQueryService.getDouble(row, rm.get("mid_allele_frequency"))) + .midHomozygoteCount(bigQueryService.getLong(row, rm.get("mid_homozygote_count"))) + .sasAlleleCount(bigQueryService.getLong(row, rm.get("sas_allele_count"))) + .sasAlleleNumber(bigQueryService.getLong(row, rm.get("sas_allele_number"))) + .sasAlleleFrequency(bigQueryService.getDouble(row, rm.get("sas_allele_frequency"))) + .sasHomozygoteCount(bigQueryService.getLong(row, rm.get("sas_homozygote_count"))) + .othAlleleCount(bigQueryService.getLong(row, rm.get("oth_allele_count"))) + .othAlleleNumber(bigQueryService.getLong(row, rm.get("oth_allele_number"))) + .othAlleleFrequency(bigQueryService.getDouble(row, rm.get("oth_allele_frequency"))) + .othHomozygoteCount(bigQueryService.getLong(row, rm.get("oth_homozygote_count"))) + .totalAlleleCount(bigQueryService.getLong(row, rm.get("total_allele_count"))) + .totalAlleleNumber(bigQueryService.getLong(row, rm.get("total_allele_number"))) + .totalAlleleFrequency(bigQueryService.getDouble(row, rm.get("total_allele_frequency"))) + .totalHomozygoteCount(bigQueryService.getLong(row, rm.get("total_homozygote_count"))); + return ResponseEntity.ok(variantInfo); + } + + public static GenomicSearchTermType getSearchType(String variantSearchTerm, String searchTerm) { GenomicSearchTermType searchTermType = new GenomicSearchTermType(); String genes = ""; diff --git a/public-api/src/main/resources/public-api.yaml b/public-api/src/main/resources/public-api.yaml index d3fd801ec..0a8283dc2 100644 --- a/public-api/src/main/resources/public-api.yaml +++ b/public-api/src/main/resources/public-api.yaml @@ -164,6 +164,24 @@ paths: schema: $ref: "#/definitions/VariantInfo" + /v1/genomics/sv-variant-details/{variantId}: + get: + tags: + - genomics + description: Gets the variant details + operationId: "getSVVariantDetails" + parameters: + - in: path + name: variantId + type: string + required: true + description: the variant id + responses: + 200: + description: Variant Information + schema: + $ref: "#/definitions/SVVariantInfo" + /v1/genomics/genomic-filter-options: get: tags: @@ -1762,10 +1780,6 @@ definitions: description: Variant Type Filters type: object $ref: "#/definitions/SVGenomicFilterOptionList" - clinicalSignificance: - description: Clinical Significance Filters - type: object - $ref: "#/definitions/SVGenomicFilterOptionList" alleleCount: description: Allele Count Range Filter type: object @@ -2087,6 +2101,156 @@ definitions: description: Total Allele Frequency type: number format: double + totalHomozygoteCount: + description: Total Homozygote Count + type: integer + format: int64 + + SVVariantInfo: + type: object + required: + - variantId + properties: + variantId: + description: variant id + type: string + variantType: + description: variant type + type: string + consequenceGenes: + description: Consequence(s) + associated gene(s) + type: string + position: + description: POS - END + type: string + size: + description: size + type: integer + format: int64 + afrAlleleCount: + description: African Ancestry Allele Count + type: integer + format: int64 + afrAlleleNumber: + description: African Ancestry Allele Number + type: integer + format: int64 + afrAlleleFrequency: + description: African Ancestry Allele Frequency + type: number + format: double + afrHomozygoteCount: + description: African Ancestry Homozygote Count + type: integer + format: int64 + easAlleleCount: + description: East Asian Ancestry Allele Count + type: integer + format: int64 + easAlleleNumber: + description: East Asian Ancestry Allele Number + type: integer + format: int64 + easAlleleFrequency: + description: East Asian Ancestry Allele Frequency + type: number + format: double + easHomozygoteCount: + description: East Asian Ancestry Homozygote Count + type: integer + format: int64 + eurAlleleCount: + description: European Ancestry Allele Count + type: integer + format: int64 + eurAlleleNumber: + description: European Ancestry Allele Number + type: integer + format: int64 + eurAlleleFrequency: + description: European Ancestry Allele Frequency + type: number + format: double + eurHomozygoteCount: + description: European Ancestry Homozygote Count + type: integer + format: int64 + amrAlleleCount: + description: Latin American Ancestry Allele Count + type: integer + format: int64 + amrAlleleNumber: + description: Latin American Ancestry Allele Number + type: integer + format: int64 + amrAlleleFrequency: + description: Latin American Ancestry Allele Frequency + type: number + format: double + amrHomozygoteCount: + description: Latin American Ancestry Homozygote Count + type: integer + format: int64 + midAlleleCount: + description: Middle Eastern Ancestry Allele Count + type: integer + format: int64 + midAlleleNumber: + description: Middle Eastern Ancestry Allele Number + type: integer + format: int64 + midAlleleFrequency: + description: Middle Eastern Ancestry Allele Frequency + type: number + format: double + midHomozygoteCount: + description: Middle Eastern Ancestry Homozygote Count + type: integer + format: int64 + sasAlleleCount: + description: South Asian Ancestry Allele Count + type: integer + format: int64 + sasAlleleNumber: + description: South Asian Ancestry Allele Number + type: integer + format: int64 + sasAlleleFrequency: + description: South Asian Ancestry Allele Frequency + type: number + format: double + sasHomozygoteCount: + description: South Asian Ancestry Homozygote Count + type: integer + format: int64 + othAlleleCount: + description: Other Ancestry Allele Count + type: integer + format: int64 + othAlleleNumber: + description: Other Ancestry Allele Number + type: integer + format: int64 + othAlleleFrequency: + description: Other Ancestry Allele Frequency + type: number + format: double + othHomozygoteCount: + description: Other Homozygote Count + type: integer + format: int64 + totalAlleleCount: + description: Total Allele Count + type: integer + format: int64 + totalAlleleNumber: + description: Total Allele Number + type: integer + format: int64 + totalAlleleFrequency: + description: Total Allele Frequency + type: number + format: double totalHomozygoteCount: description: Total Homozygote Count type: integer diff --git a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx index 10f519afc..602dcdad3 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx @@ -496,6 +496,12 @@ export const GenomicViewComponent = withRouteData( }); } + handleSVSortClick(sortMetadataTemp) { + this.setState({ svSortMetadata: sortMetadataTemp }, () => { + this.fetchSVVariantData(); + }); + } + handleSVPageChange(info) { this.setState( { loadingResults: true, currentPage: info.selectedPage }, @@ -511,12 +517,6 @@ export const GenomicViewComponent = withRouteData( }); } - handleSVSortClick(sortMetadataTemp) { - this.setState({ sortMetadata: sortMetadataTemp }, () => { - this.fetchSVVariantData(); - }); - } - fetchVariantData() { const { searchTerm, currentPage, sortMetadata, rowCount, filterMetadata } = this.state; @@ -675,20 +675,20 @@ export const GenomicViewComponent = withRouteData( handleSVScrollBottom() { this.setState({ currentPage: this.state.currentPage + 1, loadingResults: true, scrollClean: false }) - const { searchTerm, currentPage, sortMetadata, rowCount, filterMetadata } = this.state; + const { svSearchTerm, currentPage, svSortMetadata, rowCount, svFilterMetadata } = this.state; const searchRequest: SearchVariantsRequest = { - query: searchTerm, + query: svSearchTerm, pageNumber: currentPage, rowCount: rowCount, - sortMetadata: sortMetadata, - filterMetadata: filterMetadata, + sortMetadata: svSortMetadata, + filterMetadata: svFilterMetadata, }; genomicsApi() - .searchVariants(searchRequest) + .searchSVVariants(searchRequest) .then((results) => { this.setState({ - searchResults: [...this.state.searchResults, ...results.items], + searchSVResults: [...this.state.searchSVResults, ...results.items], loadingResults: false, }); }); diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx new file mode 100644 index 000000000..91b49676b --- /dev/null +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx @@ -0,0 +1,336 @@ +import * as React from "react"; + +import { PopulationChartReactComponent } from "app/data-browser/views/genomic-view/components/population-chart.component"; +import { reactStyles } from "app/utils"; +import { ClrIcon } from "app/utils/clr-icon"; +import { prepVariantPopulationDetails } from "app/utils/constants"; +import { Spinner } from "app/utils/spinner"; +import { SVVariant, SVVariantInfo } from "publicGenerated"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCircle } from '@fortawesome/free-solid-svg-icons'; + +const css = ` +.exit{ + width:2rem; + height:2rem; + color:#216FB4; +} +.pop-table-container { + display: grid; + grid-template-columns: 65% 35%; + text-align: left; + align-items: center; + margin-top: 1rem; +} +.pop-title { + font-weight: bold; + font-family: gothamBold,Arial, Helvetica, sans-serif; + font-size: 18px; + margin-top: 2rem; +} +.pop-table { + display: grid; + grid-template-columns: 20% 20% 20% 20% 20%; + font-size: 14px; + } +.pop-desc { + display: flex; + flex-direction: row; + align-items: center; +} +.body { + display: grid; + grid-template-columns: 26% 25% 25% 24%; + column-gap: 1rem; + row-gap: 1rem; + padding-top: 1rem; + font-size: 14px; + width: 100%; +} + +@media (max-width: 900px) { + .pop-table-container{ + display: flex; + flex-direction: column-reverse; + } + .pop-title { + text-align: center; + } + .body{ + grid-template-columns: 33.33% 33.33% 33.33%; + // padding-right: 2rem; + } +} +} + +`; + +const styles = reactStyles({ + rsLink: { + cursor: "pointer", + color: "rgb(0, 121, 184)", + textDecoration: "underline", + }, + variantExpanded: { + position: "sticky", + overflowX: "scroll", + width: "100%", + background: "#ECF1F4", + left: "0px", + padding: ".5em", + // paddingLeft: "1em", + zIndex: "9", + borderTop: "1px solid rgb(204, 204, 204)", + + }, + top: { + position: "relative", + display: "flex", + justifyContent: "space-between", + alignItems: "center", + borderBottom: "1px solid #979797", + width: "100%", + margin: "0", + paddingBottom: ".5rem", + }, + variantId: { + fontSize: "18px", + display: "flex", + alignItems: "center", + }, + variantIdLabel: { + width: "fitContent", + whiteSpace: "nowrap", + }, + + catHeading: { + fontFamily: "gothamBold,Arial, Helvetica, sans-serif", + }, + catInfo: { + overflowWrap: "anywhere", + height: "1em", + display: "inline-block", + }, + totalCatHeading: { + fontFamily: "gothamBold,Arial, Helvetica, sans-serif", + marginLeft: "1.6em", + }, + loading: { + transform: "scale(.5)", + }, + popTableContainer: { + display: "grid", + gridTemplateColumns: "60% 40%", + textAlign: "left", + alignItems: "center", + }, + // popTable: { + // display: 'grid', + // gridTemplateColumns: '25% 25% 25% 25%', + // fontSize: '14px' + // }, + popTableHeading: { + padding: ".5rem", + paddingBottom: "0", + paddingTop: "0", + textAlign: "center" + }, + popTableBody: { + borderBottom: "1px solid #DDE0E4", + borderLeft: "1px solid #DDE0E4", + marginBottom: "2rem", + }, + popTableData: { + border: "1px solid #DDE0E4", + borderBottom: "none", + borderLeft: "none", + padding: ".5rem", + display: "flex", + flexDirection: "row", + alignItems: "center" + }, + closeIcon: { + cursor: "pointer", + }, +}); + +interface Props { + closed: Function; + hovered: Function; + variant: SVVariant; + variantDetails: SVVariantInfo; + loading: boolean; +} +// tslint:disable-next-line:no-empty-interface +interface State { } + +export class SVVariantExpandedComponent extends React.Component { + constructor(props: Props) { + super(props); + } + handleMouseOver = () => { + this.props.hovered(true) + + } + render() { + const { variantDetails, variant, loading } = this.props; + let variantPopulationDetails: any[] = []; + // const rsLink = "https://www.ncbi.nlm.nih.gov/snp/?term=" + variantDetails.rsNumber; + if (!loading) { + variantPopulationDetails = prepVariantPopulationDetails(variantDetails); + } + + return ( + + +
+
+
+ + Variant ID: {" "} + {!loading ? ( + + {variant.variantId} + + ) : ( +
+ +
+ )}{" "} +
+
+ this.props.closed()} + className="exit" + shape="window-close" + style={styles.closeIcon} + /> +
+
+ {!loading && ( + +
+
+ Variant Type: +
+ {variant.variantType ? variant.variantType : "-"} +
+
+ Consequence(s) + associated gene(s): +
+ {variantDetails.consequenceGenes ? variantDetails.consequenceGenes.replace(/;/g, '\n') : "-"} +
+
+ Position: +
+ {variant.position ? variant.position : "-"} +
+
+ Size: +
+ + {variantDetails.size ? variantDetails.size : "-"} + +
+
+

Genetic Ancestry Populations

+
+
+
+
+
+ Allele
Count
+
+
+ Allele
Number
+
+
+ Allele
Frequency
+
+
+ Homozygote
Count
+
+
+
+ {variantPopulationDetails.map((item, index) => { + const colorStyle = { color: item.color }; + return ( +
+
+ {item.Ancestry !== "Total" ? ( + + + {item.Ancestry}{" "} + + ) : ( + + {item.Ancestry} + + )}{" "} +
+
+ {item.Ancestry !== "Total" ? ( + + {item.AlleleCount.toLocaleString()} + + ) : ( + + {item.AlleleCount.toLocaleString()} + + )} +
+
+ {item.Ancestry !== "Total" ? ( + + {item.AlleleNumber.toLocaleString()} + + ) : ( + + {item.AlleleNumber.toLocaleString()} + + )} +
+
+ {item.Ancestry !== "Total" ? ( + + {item.AlleleFrequency > 0 + ? item.AlleleFrequency.toFixed(6) + : item.AlleleFrequency} + + ) : ( + + {item.AlleleFrequency > 0 + ? item.AlleleFrequency.toFixed(6) + : item.AlleleFrequency} + + )} +
+
+ {item.Ancestry !== "Total" ? ( + + {item.HomozygoteCount.toLocaleString()} + + ) : ( + + {item.HomozygoteCount.toLocaleString()} + + )} +
+
+ ); + })} +
+
+ +
+
+ )} +
+
+
+ ); + } +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx index a0e2fe3de..039e0466d 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx @@ -14,10 +14,11 @@ interface State { } const lables = { - gene: 'Gene', - consequence: 'Consequence', + variantId: 'Variant Id', variantType: 'Variant Type', - clinicalSignificance: 'ClinVar Significance', + consequence: 'Consequence', + position: 'Position', + size: 'Size', alleleNumber: 'Allele Number', alleleFrequency: 'Allele Frequency', alleleCount: 'Allele Count', diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx index 3bec50837..017704bb9 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx @@ -89,10 +89,11 @@ export class SVVariantFilterComponent extends React.Component { super(props); this.state = { filterCats: [ - { display: 'Gene', field: 'gene' }, - { display: 'Consequence', field: 'consequence' }, + { display: 'Variant ID', field: 'variantId' }, { display: 'Variant Type', field: 'variantType' }, - { display: 'ClinVar Significance', field: 'clinicalSignificance' }, + { display: 'Consequence', field: 'consequence' }, + { display: 'Position', field: 'position' }, + { display: 'Size', field: 'size' }, { display: 'Allele Count', field: 'alleleCount' }, { display: 'Allele Number', field: 'alleleNumber' }, { display: 'Allele Frequency', field: 'alleleFrequency' }, diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx index 1a2d7f2ef..5402e4c6f 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx @@ -3,7 +3,8 @@ import * as React from "react"; import { genomicsApi } from "app/services/swagger-fetch-clients"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; -import { SVVariant } from "publicGenerated"; +import { SVVariant, SVVariantInfo } from "publicGenerated"; +import { SVVariantExpandedComponent } from "./sv-variant-expanded.component"; const styles = reactStyles({ @@ -59,7 +60,7 @@ const styles = reactStyles({ const css = ` .row-layout { display: grid; - grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + grid-template-columns: 10rem 7rem 11rem 8rem 5rem 7rem 7rem 8rem 9rem; align-items: center; width: 72rem; background: white; @@ -70,7 +71,7 @@ const css = ` @media (max-width: 900px) { .row-layout { - grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + grid-template-columns: 10rem 7rem 11rem 8rem 5rem 7rem 7rem 8rem 9rem; width: 72rem; } } @@ -83,30 +84,60 @@ interface Props { } interface State { + svVariantExpanded: boolean; mouseOverExpanded: boolean; + variantDetails: SVVariantInfo; + loadingVarDetails: boolean; } export class SVVariantRowComponent extends React.Component { constructor(props: Props) { super(props); this.state = { + svVariantExpanded: false, mouseOverExpanded: false, + variantDetails: null, + loadingVarDetails: true, }; } getVariantDetails(variantId: string) { + genomicsApi() + .getSVVariantDetails(variantId) + .then((results: SVVariantInfo) => { + this.setState({ + variantDetails: results, + loadingVarDetails: false, + }); + }); } handleClick(variantId?: string) { - + if (variantId) { + this.getVariantDetails(variantId); + } + this.setState({ + svVariantExpanded: !this.state.svVariantExpanded, + }); + {} } render() { const { variant } = this.props; + const { svVariantExpanded, variantDetails, loadingVarDetails } = this.state; return ( + {!loadingVarDetails && svVariantExpanded ? ( + this.handleClick()} + hovered={() => this.state.mouseOverExpanded ? this.props.allowParentScroll(true): this.props.allowParentScroll(false)} + /> + ) : (
this.handleClick(variant.variantId)} @@ -164,6 +195,7 @@ render() { {variant.homozygoteCount}
+ )}
); } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx index 1e1410444..2531d5491 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; -import { SortMetadata } from "publicGenerated/fetch"; +import { SortSVMetadata } from "publicGenerated/fetch"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; @@ -91,24 +91,26 @@ const css = ` `; const lables = { - gene: 'Gene', - consequence: 'Consequence', - variantType: 'Variant Type', - clinicalSignificance: 'ClinVar Significance', - alleleNumber: 'Allele Number', - alleleFrequency: 'Allele Frequency', - alleleCount: 'Allele Count' + variantId: 'Variant Id', + variantType: 'Variant Type', + consequence: 'Consequence', + position: 'Position', + size: 'Size', + alleleNumber: 'Allele Number', + alleleFrequency: 'Allele Frequency', + alleleCount: 'Allele Count', + homozygoteCount: 'Homozygote Count' }; interface Props { cleared: Boolean; onSortChange: Function; - sortMetadata: SortMetadata; + sortMetadata: SortSVMetadata; } interface State { sortItemOpen: Boolean; - sortMetadata: SortMetadata; + sortMetadata: SortSVMetadata; filterCats: any[]; sortCats: any[]; } @@ -120,21 +122,22 @@ export class SVVariantSortItemComponent extends React.Component { sortItemOpen: false, sortMetadata: props.sortMetadata, filterCats: [ - { display: 'Gene', field: 'gene' }, - { display: 'Consequence', field: 'consequence' }, { display: 'Variant Type', field: 'variantType' }, - { display: 'ClinVar Significance', field: 'clinicalSignificance' }, + { display: 'Variant ID', field: 'variantId' }, + { display: 'Consequence', field: 'consequence' }, + { display: 'Position', field: 'position' }, + { display: 'Size', field: 'size' }, { display: 'Allele Count', field: 'alleleCount' }, { display: 'Allele Number', field: 'alleleNumber' }, { display: 'Allele Frequency', field: 'alleleFrequency' }, { display: 'Homozygote Count', field: 'homozygoteCount' }, ], sortCats: [ - { display: 'Variant ID', field: 'variantId'}, - { display: 'Gene', field: 'gene' }, - { display: 'Consequence', field: 'consequence' }, { display: 'Variant Type', field: 'variantType' }, - { display: 'ClinVar Significance', field: 'clinicalSignificance' }, + { display: 'Variant ID', field: 'variantId' }, + { display: 'Consequence', field: 'consequence' }, + { display: 'Position', field: 'position' }, + { display: 'Size', field: 'size' }, { display: 'Allele Count', field: 'alleleCount' }, { display: 'Allele Number', field: 'alleleNumber' }, { display: 'Allele Frequency', field: 'alleleFrequency' }, @@ -150,8 +153,6 @@ export class SVVariantSortItemComponent extends React.Component { this.setState({ sortItemOpen: !this.state.sortItemOpen }); } - - clickToSort(field) { const { sortMetadata } = this.state; @@ -192,6 +193,9 @@ export class SVVariantSortItemComponent extends React.Component { {(cleared && sortItemOpen) &&
{sortCats && sortCats.map((cat, index) => { + console.log(cat); + console.log(cat.field); + console.log(sortMetadata); return
this.clickToSort(cat.field)}>
{cat.display} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx index ca77d27ca..60d854862 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx @@ -88,7 +88,7 @@ const styles = reactStyles({ const css = ` .header-layout { display: grid; - grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + grid-template-columns: 10rem 7rem 11rem 8rem 5rem 7rem 7rem 8rem 9rem; background: #f9f9fa; font-family: gothamBold,Arial, Helvetica,sans-serif; width: 72rem; @@ -100,7 +100,7 @@ const css = ` } @media (max-width: 900px) { .header-layout { - grid-template-columns: 10rem 7rem 7rem 7rem 9rem 7rem 7rem 8rem 10rem; + grid-template-columns: 10rem 7rem 11rem 8rem 5rem 7rem 7rem 8rem 9rem; width: 72rem; } } From 04e55e7c64f60c0040d1a89b9aff54c36d937f90 Mon Sep 17 00:00:00 2001 From: Srushti Gangireddy Date: Mon, 12 Aug 2024 15:34:14 -0500 Subject: [PATCH 3/7] trying to fix errors --- .../cdr-version/cdr-version-info.tsx | 5 +- .../chart-age/chart-age-react.component.tsx | 8 +- .../chart-biosex-react.component.tsx | 8 +- .../chart-fitbit-react.component.tsx | 23 +- .../chart-value-react.component.tsx | 35 +- .../chart-sources-react.component.tsx | 5 +- .../react-survey-answer-chart.component.tsx | 46 +- .../react-base-chart/base-chart.service.ts | 24 +- .../source-tree-react.component.tsx | 4 +- .../pfhh-survey-table-react.component.tsx | 95 ++-- .../survey-version-table-react.component.tsx | 29 +- .../tooltip-no-icon-react.component.tsx | 17 +- .../tooltip/tooltip-react.component.tsx | 38 +- .../search/home-search.component.tsx | 4 +- .../data-browser/services/tooltip.service.ts | 17 +- .../concept-chart-react.component.tsx | 210 ++++--- .../concept-row-react.component.tsx | 84 +-- .../ehr-view/ehr-view-react.component.tsx | 147 +++-- .../error-message-react.component.tsx | 10 +- .../fitbit-view-react.component.tsx | 20 +- .../#variant-filter-slider.component.tsx | 75 +-- .../components/genomic-chart.component.tsx | 91 +-- .../components/genomic-faq.component.tsx | 6 +- .../components/genomic-overview.component.tsx | 131 +++-- .../components/genomic-search.component.tsx | 162 ++++-- .../components/slider-filter/Surface.tsx | 28 +- .../slider-filter/slider-components.tsx | 88 +-- .../slider-filter/slider-constants.tsx | 2 +- .../variant-filter-slider.component.tsx | 127 +++-- .../components/table-paginator.component.tsx | 22 +- .../components/variant-expanded.component.tsx | 106 ++-- .../variant-filter-chips.component.tsx | 330 ++++++----- .../variant-filter-item.component.tsx | 146 +++-- .../variant-filter-slider.component.tsx | 73 +-- .../components/variant-filter.component.tsx | 342 ++++++----- .../components/variant-row.component.tsx | 172 +++--- .../components/variant-search.component.tsx | 191 ++++--- .../variant-sort-item.component.tsx | 187 +++--- .../components/variant-table.component.tsx | 236 ++++---- .../genomic-view/genomic-view.component.tsx | 536 +++++++++++------- .../views/pm/pm-react.component.tsx | 452 ++++++++------- ...genomic-call-to-action-react.component.tsx | 110 ++-- .../home-view-react.component.tsx | 362 +++++++----- .../survey-answer-react.component.tsx | 5 +- .../survey-question-react.component.tsx | 6 +- .../survey-react-view.component.tsx | 101 ++-- .../components/slider-filter/Surface.tsx | 28 +- .../slider-filter/slider-components.tsx | 88 +-- .../slider-filter/slider-constants.tsx | 2 +- .../sv-variant-filter-slider.component.tsx | 127 +++-- .../sv-genomic-search.component.tsx | 150 +++-- .../sv-variant-expanded.component.tsx | 72 ++- .../sv-variant-filter-chips.component.tsx | 335 ++++++----- .../sv-variant-filter-item.component.tsx | 151 +++-- .../sv-variant-filter-slider.component.tsx | 71 +-- .../sv-variant-filter.component.tsx | 347 ++++++------ .../components/sv-variant-row.component.tsx | 134 ++--- .../sv-variant-search.component.tsx | 185 ++++-- .../sv-variant-sort-item.component.tsx | 197 ++++--- .../components/sv-variant-table.component.tsx | 180 +++--- .../components/table-paginator.component.tsx | 22 +- .../src/app/guards/is-safe-guard.service.ts | 2 +- .../src/app/services/server-config.service.ts | 2 +- .../breadcrumb/breadcrumb-react.component.tsx | 6 +- .../shared/components/rh-header/rh-header.tsx | 310 ++++++++-- .../shared/services/header-footer.service.ts | 200 +++---- public-ui/src/app/utils/constants.tsx | 58 +- public-ui/src/app/utils/db-config.service.ts | 2 +- public-ui/src/app/utils/icons.tsx | 5 +- public-ui/src/app/utils/spinner.tsx | 3 - .../src/environments/environment.prod.ts | 2 +- .../src/environments/environment.stable.ts | 2 +- .../src/environments/environment.staging.ts | 2 +- public-ui/src/environments/test-env-base.ts | 2 +- public-ui/src/types/index.d.ts | 2 +- 75 files changed, 4489 insertions(+), 3114 deletions(-) diff --git a/public-ui/src/app/data-browser/cdr-version/cdr-version-info.tsx b/public-ui/src/app/data-browser/cdr-version/cdr-version-info.tsx index 39c034628..cbb71a4ae 100644 --- a/public-ui/src/app/data-browser/cdr-version/cdr-version-info.tsx +++ b/public-ui/src/app/data-browser/cdr-version/cdr-version-info.tsx @@ -51,8 +51,9 @@ export class CdrVersionReactComponent extends React.Component<{}, State> { - Data includes {Number(numParticipants).toLocaleString()} participants as of {' '} - {creationTime.getMonth() + 1}/{creationTime.getDate()}/{creationTime.getFullYear()}. + Data includes {Number(numParticipants).toLocaleString()}{" "} + participants as of {creationTime.getMonth() + 1}/ + {creationTime.getDate()}/{creationTime.getFullYear()}. ) diff --git a/public-ui/src/app/data-browser/charts/chart-age/chart-age-react.component.tsx b/public-ui/src/app/data-browser/charts/chart-age/chart-age-react.component.tsx index eb049cbd7..d0bd70662 100644 --- a/public-ui/src/app/data-browser/charts/chart-age/chart-age-react.component.tsx +++ b/public-ui/src/app/data-browser/charts/chart-age/chart-age-react.component.tsx @@ -265,13 +265,17 @@ export class AgeChartReactComponent extends React.Component { : ageCountResults[0].countValue; ageCountTooltip += "Total Count = " + totCount + ""; } - const count = concept.countValue <= 20 ? "≤ 20" : concept.countValue.toLocaleString(); + const count = + concept.countValue <= 20 + ? "≤ 20" + : concept.countValue.toLocaleString(); pointData.push({ toolTipHelpText: '
' + count.toLocaleString() + " participants were ages within range of " + - concept.analysisStratumName + ".
", + concept.analysisStratumName + + ".
", name: concept.analysisStratumName, y: concept.countValue, concept: "", diff --git a/public-ui/src/app/data-browser/charts/chart-biosex/chart-biosex-react.component.tsx b/public-ui/src/app/data-browser/charts/chart-biosex/chart-biosex-react.component.tsx index 2b55896b6..21b7e323a 100644 --- a/public-ui/src/app/data-browser/charts/chart-biosex/chart-biosex-react.component.tsx +++ b/public-ui/src/app/data-browser/charts/chart-biosex/chart-biosex-react.component.tsx @@ -139,7 +139,8 @@ export class BioSexChartReactComponent extends React.Component { (domain === "ehr" ? x.stratum4 : x.stratum2) === (domain === "ehr" ? a.stratum2 : a.stratum5) )[0]; - const count = a.countValue <= 20 ? "≤ 20" : a.countValue.toLocaleString(); + const count = + a.countValue <= 20 ? "≤ 20" : a.countValue.toLocaleString(); const totalCount = bsResult.countValue <= 20 ? "≤ 20" @@ -232,7 +233,10 @@ export class BioSexChartReactComponent extends React.Component { : genderCountResults[0].countValue; genderCountTooltip += "Total Count = " + totCount + ""; } - const count = concept.countValue <= 20 ? "≤ 20" : concept.countValue.toLocaleString(); + const count = + concept.countValue <= 20 + ? "≤ 20" + : concept.countValue.toLocaleString(); pointData.push({ toolTipHelpText: '
' + diff --git a/public-ui/src/app/data-browser/charts/chart-fitbit/chart-fitbit-react.component.tsx b/public-ui/src/app/data-browser/charts/chart-fitbit/chart-fitbit-react.component.tsx index 69cd906db..5902721cf 100644 --- a/public-ui/src/app/data-browser/charts/chart-fitbit/chart-fitbit-react.component.tsx +++ b/public-ui/src/app/data-browser/charts/chart-fitbit/chart-fitbit-react.component.tsx @@ -73,13 +73,19 @@ export class ChartFitbitReactComponent extends React.Component { useHTML: true, }; newBaseOptions.tooltip.outside = true; - newBaseOptions.series = [{ - name: undefined, data: series, size: undefined, shadow: undefined, innerSize: undefined, - showInLegend: undefined, - dataLabels: { - enabled: false, + newBaseOptions.series = [ + { + name: undefined, + data: series, + size: undefined, + shadow: undefined, + innerSize: undefined, + showInLegend: undefined, + dataLabels: { + enabled: false, + }, }, - }]; + ]; this.setState({ options: newBaseOptions }); } @@ -87,7 +93,10 @@ export class ChartFitbitReactComponent extends React.Component { const pointData = []; const categoryArr = []; for (const concept of concepts.results) { - const count = concept.countValue <= 20 ? "≤ 20" : concept.countValue.toLocaleString(); + const count = + concept.countValue <= 20 + ? "≤ 20" + : concept.countValue.toLocaleString(); const totalCount = this.props.countAnalysis && this.props.countAnalysis.results ? this.props.countAnalysis.results[0].countValue.toLocaleString() diff --git a/public-ui/src/app/data-browser/charts/chart-measurement-values/chart-value-react.component.tsx b/public-ui/src/app/data-browser/charts/chart-measurement-values/chart-value-react.component.tsx index c19fd94fe..91f87e943 100644 --- a/public-ui/src/app/data-browser/charts/chart-measurement-values/chart-value-react.component.tsx +++ b/public-ui/src/app/data-browser/charts/chart-measurement-values/chart-value-react.component.tsx @@ -28,8 +28,8 @@ export class ValueReactChartComponent extends React.Component { this.state = { options: null, pageX: 0, - pageY: 0 - }; + pageY: 0, + }; } componentDidMount() { @@ -47,8 +47,7 @@ export class ValueReactChartComponent extends React.Component { pageX: event.pageX, pageY: event.pageY, }); - } - + }; getChartOptions() { const { conceptId } = this.props; @@ -198,7 +197,9 @@ export class ValueReactChartComponent extends React.Component { participantCountText = "Participant Count: ≤ 20 "; } else { participantCountText = - "Participant Count: " + a.countValue.toLocaleString() + ""; + "Participant Count: " + + a.countValue.toLocaleString() + + ""; } toolTipText = @@ -245,7 +246,9 @@ export class ValueReactChartComponent extends React.Component { participantCountText = "Participant Count: ≤ 20 "; } else { participantCountText = - "Participant Count: " + a.countValue.toLocaleString() + ""; + "Participant Count: " + + a.countValue.toLocaleString() + + ""; } if (a.stratum2 !== "No unit") { tooltipText = @@ -377,16 +380,16 @@ export class ValueReactChartComponent extends React.Component { render() { const { options } = this.state; return ( - -
- {options && ( - - )} -
+ +
+ {options && ( + + )} +
); } diff --git a/public-ui/src/app/data-browser/charts/chart-sources/chart-sources-react.component.tsx b/public-ui/src/app/data-browser/charts/chart-sources/chart-sources-react.component.tsx index 01266e6b5..d137bec53 100644 --- a/public-ui/src/app/data-browser/charts/chart-sources/chart-sources-react.component.tsx +++ b/public-ui/src/app/data-browser/charts/chart-sources/chart-sources-react.component.tsx @@ -72,7 +72,10 @@ export class SourcesChartReactComponent extends React.Component { return a.sourceCountValue < b.sourceCountValue; }); for (const a of concepts) { - const count = a.sourceCountValue <= 20 ? "≤ 20" : a.sourceCountValue.toLocaleString(); + const count = + a.sourceCountValue <= 20 + ? "≤ 20" + : a.sourceCountValue.toLocaleString(); const toolTipText = '
' + a.conceptName + diff --git a/public-ui/src/app/data-browser/charts/chart-survey-answers/react-survey-answer-chart.component.tsx b/public-ui/src/app/data-browser/charts/chart-survey-answers/react-survey-answer-chart.component.tsx index 3c12a7873..22c63565d 100644 --- a/public-ui/src/app/data-browser/charts/chart-survey-answers/react-survey-answer-chart.component.tsx +++ b/public-ui/src/app/data-browser/charts/chart-survey-answers/react-survey-answer-chart.component.tsx @@ -1,8 +1,8 @@ import * as React from "react"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCircle } from '@fortawesome/free-solid-svg-icons'; import * as highCharts from "highcharts"; import HighchartsReact from "highcharts-react-official"; +import { faCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { getBaseOptions, @@ -154,7 +154,7 @@ export class SurveyAnswerChartReactComponent extends React.Component< tempArr.push({ name: val, data: [], - tooltip: [] + tooltip: [], }); } // remove duplicates @@ -165,14 +165,15 @@ export class SurveyAnswerChartReactComponent extends React.Component< if (sortedAnswers.hasOwnProperty(prop)) { categoryArr.push(prop); tempArr.forEach((stack) => { - - let filterAnswer = sortedAnswers[prop].filter( a => a['stratum4'] === stack.name ); + const filterAnswer = sortedAnswers[prop].filter( + (a) => a.stratum4 === stack.name + ); if (filterAnswer.length > 0) { stack.data.push(filterAnswer[0].countValue); - stack.tooltip.push(''); + stack.tooltip.push(""); } else { stack.data.push(0); - stack.tooltip.push('Not present in this version of the survey'); + stack.tooltip.push("Not present in this version of the survey"); } /* sortedAnswers[prop].forEach((answer) => { @@ -182,7 +183,6 @@ export class SurveyAnswerChartReactComponent extends React.Component< }); */ }); - } } this.setState({ chartSeries: tempArr, categoryArr: categoryArr }, () => { @@ -223,22 +223,30 @@ export class SurveyAnswerChartReactComponent extends React.Component< newBaseOptions.tooltip.backgroundColor = "transparent"; newBaseOptions.tooltip.formatter = function () { const count = this.point.y <= 20 ? "≤ 20" : this.point.y; - let cat = this.point.category.split(' '); - if (this.point.category === 'New Year 2022') { - cat[0] = 'New Year'; - cat[1] = '2022'; + const cat = this.point.category.split(" "); + if (this.point.category === "New Year 2022") { + cat[0] = "New Year"; + cat[1] = "2022"; } let toolTipText = this.point.series.name; const surveyVersion = surveyVersions.find((obj) => { - return (obj.monthName === cat[0] && obj.year === cat[1]); + return obj.monthName === cat[0] && obj.year === cat[1]; }); - const surveyVersionParticipantCounts = surveyVersion ? surveyVersion.participants : 0; - let percentage = (this.point.y <= 20) ? ((20 / surveyVersionParticipantCounts) * 100).toFixed() : - ((count / surveyVersionParticipantCounts) * 100).toFixed(); - if (this.point.y == 0 && this.point.series.name.toLowerCase() !== 'did not answer') { - toolTipText += '\n This option was not available in this version of the survey'; + const surveyVersionParticipantCounts = surveyVersion + ? surveyVersion.participants + : 0; + const percentage = + this.point.y <= 20 + ? ((20 / surveyVersionParticipantCounts) * 100).toFixed() + : ((count / surveyVersionParticipantCounts) * 100).toFixed(); + if ( + this.point.y == 0 && + this.point.series.name.toLowerCase() !== "did not answer" + ) { + toolTipText += + "\n This option was not available in this version of the survey"; } - this.point.toolTipHelpText = ` + this.point.toolTipHelpText = `
${toolTipText} ${count.toLocaleString()} Participants diff --git a/public-ui/src/app/data-browser/charts/react-base-chart/base-chart.service.ts b/public-ui/src/app/data-browser/charts/react-base-chart/base-chart.service.ts index 8f8a729f7..8b1fb7bde 100644 --- a/public-ui/src/app/data-browser/charts/react-base-chart/base-chart.service.ts +++ b/public-ui/src/app/data-browser/charts/react-base-chart/base-chart.service.ts @@ -83,7 +83,7 @@ export const baseOptions = { }, tooltip: { borderWidth: 0, // This will remove the border or outline - backgroundColor: 'transparent', + backgroundColor: "transparent", shadow: false, followPointer: true, outside: false, @@ -112,10 +112,10 @@ export const baseOptions = { enabled: true, borderColor: "transparent", borderRadius: "0px", - padding:0, + padding: 0, style: { color: "#302C71", - outline:"none", + outline: "none", whiteSpace: "normal", zIndex: undefined, }, @@ -260,11 +260,19 @@ export const baseOptions = { }, }, }, - series: [{ name: undefined, data: [], size: undefined, shadow: undefined, innerSize: undefined, - showInLegend: undefined, - dataLabels: { - enabled: false, - },}], + series: [ + { + name: undefined, + data: [], + size: undefined, + shadow: undefined, + innerSize: undefined, + showInLegend: undefined, + dataLabels: { + enabled: false, + }, + }, + ], }; export const genomicOptions = { diff --git a/public-ui/src/app/data-browser/components/source-tree/source-tree-react.component.tsx b/public-ui/src/app/data-browser/components/source-tree/source-tree-react.component.tsx index d465ee3ad..f6e2aec85 100644 --- a/public-ui/src/app/data-browser/components/source-tree/source-tree-react.component.tsx +++ b/public-ui/src/app/data-browser/components/source-tree/source-tree-react.component.tsx @@ -28,12 +28,12 @@ const styles = reactStyles({ color: "#2991cf", transition: ".1s transform linear", marginTop: "0.1rem", - cursor: "pointer" + cursor: "pointer", }, treeRow: { textAlign: "left", marginLeft: "1em", - cursor: "pointer" + cursor: "pointer", }, childNode: { marginLeft: "1em", diff --git a/public-ui/src/app/data-browser/components/survey-version-table/pfhh-survey-table-react.component.tsx b/public-ui/src/app/data-browser/components/survey-version-table/pfhh-survey-table-react.component.tsx index ec476f9f0..f41e8d3c0 100644 --- a/public-ui/src/app/data-browser/components/survey-version-table/pfhh-survey-table-react.component.tsx +++ b/public-ui/src/app/data-browser/components/survey-version-table/pfhh-survey-table-react.component.tsx @@ -56,8 +56,8 @@ div.version-box-item:nth-child(1),div.version-box-row span:nth-child(1){ const containerElementName = "root"; interface Props { - questionCount: number; - participantCount: number; + questionCount: number; + participantCount: number; } export class PfhhSurveyTableReactComponent extends React.Component< @@ -72,13 +72,18 @@ export class PfhhSurveyTableReactComponent extends React.Component< } render() { - const fhh_survey_pdf_link = '/assets/surveys/Family_Health_History_Survey_English.pdf'; - const fhh_survey_pdf_link_spanish = '/assets/surveys/Family_Health_History_Survey_Spanish.pdf'; - const pmh_survey_pdf_link = '/assets/surveys/Personal_Medical_History_Survey_English.pdf'; - const pmh_survey_pdf_link_spanish = '/assets/surveys/Personal_Medical_History_Survey_Spanish.pdf'; - const pafhh_survey_pdf_link = '/assets/surveys/Survey_PaFHH_Eng_Src.pdf'; - const pafhh_survey_pdf_link_spanish = '/assets/surveys/Survey_PaFHH_spn_Src.pdf'; - const {questionCount, participantCount} = this.props; + const fhh_survey_pdf_link = + "/assets/surveys/Family_Health_History_Survey_English.pdf"; + const fhh_survey_pdf_link_spanish = + "/assets/surveys/Family_Health_History_Survey_Spanish.pdf"; + const pmh_survey_pdf_link = + "/assets/surveys/Personal_Medical_History_Survey_English.pdf"; + const pmh_survey_pdf_link_spanish = + "/assets/surveys/Personal_Medical_History_Survey_Spanish.pdf"; + const pafhh_survey_pdf_link = "/assets/surveys/Survey_PaFHH_Eng_Src.pdf"; + const pafhh_survey_pdf_link_spanish = + "/assets/surveys/Survey_PaFHH_spn_Src.pdf"; + const { questionCount, participantCount } = this.props; console.log(questionCount); @@ -92,43 +97,47 @@ export class PfhhSurveyTableReactComponent extends React.Component<
PDF
-
- Personal and Family Health History - - - English - {" "}|{" "} - - Spanish - - -
-
- Personal Medical History - - - English - {" "}|{" "} - - Spanish - - -
-
- Family Health History - - - English - {" "}|{" "} - - Spanish - - -
+
+ + Personal and Family Health History + + + + English + {" "} + |{" "} + + Spanish + + +
+
+ Personal Medical History + + + English + {" "} + |{" "} + + Spanish + + +
+
+ Family Health History + + + English + {" "} + |{" "} + + Spanish + + +
); } } - diff --git a/public-ui/src/app/data-browser/components/survey-version-table/survey-version-table-react.component.tsx b/public-ui/src/app/data-browser/components/survey-version-table/survey-version-table-react.component.tsx index 51881feb0..6e56c374c 100644 --- a/public-ui/src/app/data-browser/components/survey-version-table/survey-version-table-react.component.tsx +++ b/public-ui/src/app/data-browser/components/survey-version-table/survey-version-table-react.component.tsx @@ -72,8 +72,8 @@ export class SurveyVersionTableReactComponent extends React.Component< render() { const { surveyVersions } = this.props; - console.log(surveyVersions,'surveyversions'); - + console.log(surveyVersions, "surveyversions"); + return (
@@ -87,29 +87,32 @@ export class SurveyVersionTableReactComponent extends React.Component< {!!surveyVersions && surveyVersions.map((survey) => { switch (survey.monthName) { - case 'New Year': - survey.pdfLink = survey.pdfLink.replace(' ', '_'); + case "New Year": + survey.pdfLink = survey.pdfLink.replace(" ", "_"); break; - case 'Nov': - survey.monthName='November' + case "Nov": + survey.monthName = "November"; break; - case 'Dec': - survey.monthName='December' + case "Dec": + survey.monthName = "December"; break; - case 'Feb': - survey.monthName='February' + case "Feb": + survey.monthName = "February"; break; - } return (
{survey.monthName} {survey.year} - + English - {" "}|{" "} + {" "} + |{" "} Spanish diff --git a/public-ui/src/app/data-browser/components/tooltip/tooltip-no-icon-react.component.tsx b/public-ui/src/app/data-browser/components/tooltip/tooltip-no-icon-react.component.tsx index 6daab18b9..c53405f93 100644 --- a/public-ui/src/app/data-browser/components/tooltip/tooltip-no-icon-react.component.tsx +++ b/public-ui/src/app/data-browser/components/tooltip/tooltip-no-icon-react.component.tsx @@ -60,25 +60,18 @@ export class TooltipNoIconReactComponent extends React.Component { super(props); } - tooltipHover() { - } + tooltipHover() {} render() { const tabIndex = 0; return ( -
- {this.props.text} - - Click to copy - +
+ {this.props.text} + Click to copy
- ); + ); } } - diff --git a/public-ui/src/app/data-browser/components/tooltip/tooltip-react.component.tsx b/public-ui/src/app/data-browser/components/tooltip/tooltip-react.component.tsx index 68c41d70b..bb1331507 100644 --- a/public-ui/src/app/data-browser/components/tooltip/tooltip-react.component.tsx +++ b/public-ui/src/app/data-browser/components/tooltip/tooltip-react.component.tsx @@ -1,9 +1,9 @@ import * as React from "react"; + import { getTooltip } from "app/data-browser/services/tooltip.service"; import { ClrIcon } from "app/utils/clr-icon"; import { triggerEvent } from "app/utils/google_analytics"; - export const tooltipCss = ` .tooltip { position: relative; @@ -66,23 +66,20 @@ export class TooltipReactComponent extends React.Component { super(props); this.state = { overflowX: 0, - left: 0 - } + left: 0, + }; } divRef: any = React.createRef(); componentDidMount() { - document.addEventListener('resize', this.tooltipHover); + document.addEventListener("resize", this.tooltipHover); } componentWillUnmount() { - window.removeEventListener('resize', this.tooltipHover); + window.removeEventListener("resize", this.tooltipHover); } - - - detectOverflow = () => { const div = this.divRef.current; const body = document.body; @@ -91,14 +88,17 @@ export class TooltipReactComponent extends React.Component { const divRect = div.getBoundingClientRect(); const bodyRect = body.getBoundingClientRect(); if (divRect && bodyRect) { - const overflowX = divRect.right + 150 > bodyRect.right ? divRect.right - bodyRect.right + 150 : 0; + const overflowX = + divRect.right + 150 > bodyRect.right + ? divRect.right - bodyRect.right + 150 + : 0; this.setState({ overflowX: overflowX, - left: divRect.left - }) + left: divRect.left, + }); } } - } + }; tooltipHover(e) { triggerEvent( @@ -124,11 +124,11 @@ export class TooltipReactComponent extends React.Component { const { overflowX, left } = this.state; const move = overflowX > 0 ? -overflowX - 28 : 0; - return ( -
{ className={iconClass} style={{ width: 18, height: 18 }} /> - + {getTooltip(this.props.tooltipKey).map((tooltip, index) => { if (index === 1 || index === 3) { return ( diff --git a/public-ui/src/app/data-browser/search/home-search.component.tsx b/public-ui/src/app/data-browser/search/home-search.component.tsx index d757e8b47..8f72afaa9 100644 --- a/public-ui/src/app/data-browser/search/home-search.component.tsx +++ b/public-ui/src/app/data-browser/search/home-search.component.tsx @@ -1,10 +1,10 @@ import * as React from "react"; +import { faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faXmark } from '@fortawesome/free-solid-svg-icons'; const styles = reactStyles({ searchTitle: { diff --git a/public-ui/src/app/data-browser/services/tooltip.service.ts b/public-ui/src/app/data-browser/services/tooltip.service.ts index 68215eab7..af7a2803f 100644 --- a/public-ui/src/app/data-browser/services/tooltip.service.ts +++ b/public-ui/src/app/data-browser/services/tooltip.service.ts @@ -8,7 +8,9 @@ export interface Tooltip { // Const for tooltip component export const tooltips = { genomicsCTA: { - texts: ["Register for the Researcher Workbench to access the full genomic dataset and start your project.",] + texts: [ + "Register for the Researcher Workbench to access the full genomic dataset and start your project.", + ], }, "Search Across Data Types": { texts: [ @@ -25,7 +27,7 @@ export const tooltips = { ], }, conceptCopyHelpText: { - texts: [`Click to copy`] + texts: [`Click to copy`], }, conceptCodeHelpText: { texts: [ @@ -278,7 +280,7 @@ export class TooltipService { ], }, conceptCopyHelpText: { - texts: [`Click to copy`] + texts: [`Click to copy`], }, conceptCodeHelpText: { texts: [ @@ -298,11 +300,10 @@ export class TooltipService { genderIdentityQuestionHelpText: { texts: [ `Participants self-identify their race and ethnicity.`, - `All of Us`, - `does not ask for verification of Tribal enrollment or descendancy from participants + `All of Us`, + `does not ask for verification of Tribal enrollment or descendancy from participants who self-identify as American Indian/Alaska Native. Because participants can select - more than one option, counts and percentages may not add up to 100%.` - + more than one option, counts and percentages may not add up to 100%.`, ], }, matchingConceptsHelpText: { @@ -372,7 +373,7 @@ export class TooltipService { participants' immediate biological family members`, "personal and family health history": `This survey collects information about the medical history of participant and their immediate biological family members`, - "personal & family health history": `This survey collects information about the medical history of + "personal & family health history": `This survey collects information about the medical history of participant and their immediate biological family members`, "health care access & utilization": `This survey asks questions about participants' access to and use of health care.`, diff --git a/public-ui/src/app/data-browser/views/concept-chart/concept-chart-react.component.tsx b/public-ui/src/app/data-browser/views/concept-chart/concept-chart-react.component.tsx index 38fb354fa..d70509c81 100644 --- a/public-ui/src/app/data-browser/views/concept-chart/concept-chart-react.component.tsx +++ b/public-ui/src/app/data-browser/views/concept-chart/concept-chart-react.component.tsx @@ -7,8 +7,8 @@ import { BioSexChartReactComponent } from "app/data-browser/charts/chart-biosex/ import { ValueReactChartComponent } from "app/data-browser/charts/chart-measurement-values/chart-value-react.component"; import { SourcesChartReactComponent } from "app/data-browser/charts/chart-sources/chart-sources-react.component"; import { SourceTreeComponent } from "app/data-browser/components/source-tree/source-tree-react.component"; -import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; import { TooltipNoIconReactComponent } from "app/data-browser/components/tooltip/tooltip-no-icon-react.component"; +import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; import { ErrorMessageReactComponent } from "app/data-browser/views/error-message/error-message-react.component"; import { dataBrowserApi } from "app/services/swagger-fetch-clients"; import { reactStyles } from "app/utils"; @@ -76,21 +76,21 @@ const styles = reactStyles({ paddingBottom: "1em", }, valuesChart: { - fontSize: '0.8em' + fontSize: "0.8em", }, unitPanel: { - display: 'flex', + display: "flex", flexDirection: "row", - gap: '1em', - justifyContent: 'space-between', - flexWrap:"wrap" + gap: "1em", + justifyContent: "space-between", + flexWrap: "wrap", }, noUnitPanel: { - display: 'flex', + display: "flex", flexDirection: "row", - gap: '1em', - justifyContent: 'center' - } + gap: "1em", + justifyContent: "center", + }, }); const cssStyles = ` @@ -251,19 +251,23 @@ export class ConceptChartReactComponent extends React.Component { } selectConceptCode(attrType: string) { - const { concept, searchTerm } = this.props; - const selectedConcept = this.state.selectedTreeNode ? this.state.selectedTreeNode : concept; - const selectedText = (attrType === 'id') ? selectedConcept.conceptId : - (selectedConcept.conceptCode ? selectedConcept.conceptCode : selectedConcept.code); - navigator.clipboard.writeText( - selectedText - ); - this.setState({ - showConceptCopyAlert: true, - }); - setTimeout(() => { - this.setState({ showConceptCopyAlert: false }); - }, 500); + const { concept, searchTerm } = this.props; + const selectedConcept = this.state.selectedTreeNode + ? this.state.selectedTreeNode + : concept; + const selectedText = + attrType === "id" + ? selectedConcept.conceptId + : selectedConcept.conceptCode + ? selectedConcept.conceptCode + : selectedConcept.code; + navigator.clipboard.writeText(selectedText); + this.setState({ + showConceptCopyAlert: true, + }); + setTimeout(() => { + this.setState({ showConceptCopyAlert: false }); + }, 500); } selectGraphType(g) { @@ -464,7 +468,10 @@ export class ConceptChartReactComponent extends React.Component { this.setState({ selectedTreeNode: selectedNode, selectedTreeConcept: id, - sourceConcepts: results.items.length > 10 ? results.items.slice(0, 10) : results.items + sourceConcepts: + results.items.length > 10 + ? results.items.slice(0, 10) + : results.items, }); }) .catch((e) => { @@ -566,26 +573,30 @@ export class ConceptChartReactComponent extends React.Component {
) : ( graphToShow === "Values" && ( -
-
- {unitNames.map((unit, index) => { - return ( -
{ - e.stopPropagation(); - this.showMeasurementGenderHistogram(unit); - }} - > - {unit} -
- ); - })} +
+
+ {unitNames.map((unit, index) => { + return ( +
{ + e.stopPropagation(); + this.showMeasurementGenderHistogram(unit); + }} + > + {unit} +
+ ); + })}
{mixtureOfValues && @@ -614,28 +625,28 @@ export class ConceptChartReactComponent extends React.Component { className="ehr-m-chart-layout" style={styles.ehrMChartLayout} > - {genderResults && toDisplayMeasurementGenderAnalysis ? ( - genderResults.map((gender, index) => { - return ( -
- -
- ); - }) - ) : ( - !loading &&

No Values Available

- )} + {genderResults && toDisplayMeasurementGenderAnalysis + ? genderResults.map((gender, index) => { + return ( +
+ +
+ ); + }) + : !loading && ( +

No Values Available

+ )}
@@ -648,9 +659,16 @@ export class ConceptChartReactComponent extends React.Component { style={styles.sourcesChart} key="sources-chart" > - {showConceptCopyAlert && (
-
Link copied to clipboard
-
)} + {showConceptCopyAlert && ( +
+
Link copied to clipboard
+
+ )}
{ : concept.conceptName}

-
{e.stopPropagation(); - this.selectConceptCode("code"); }}> - - {selectedTreeNode - ? selectedTreeNode.type - : concept.vocabularyId} - Code:{" "} - {selectedTreeNode - ? selectedTreeNode.code - : - } - +
{ + e.stopPropagation(); + this.selectConceptCode("code"); + }} + > + + {selectedTreeNode + ? selectedTreeNode.type + : concept.vocabularyId} + Code:{" "} + {selectedTreeNode ? ( + selectedTreeNode.code + ) : ( + + )} +
-

{e.stopPropagation(); this.selectConceptCode("id"); }}> +

{ + e.stopPropagation(); + this.selectConceptCode("id"); + }} + > OMOP Concept Id:{" "} - {selectedTreeNode - ? selectedTreeNode.conceptId - : } + {selectedTreeNode ? ( + selectedTreeNode.conceptId + ) : ( + + )}

{selectedTreeNode && selectedTreeNode.canSelect === 1 && ( { showMoreDrugBrands: false, showConceptChart: props.selectedConcept ? props.selectedConcept && - props.selectedConcept.conceptId === props.concept.conceptId + props.selectedConcept.conceptId === props.concept.conceptId : props.searchTerm - ? parseInt(props.searchTerm, 10) === props.concept.conceptId - : false, + ? parseInt(props.searchTerm, 10) === props.concept.conceptId + : false, graphToShow: props.domain.name.toLowerCase() === "labs & measurements" ? GraphType.Values @@ -179,18 +179,17 @@ export class ConceptRowReactComponent extends React.Component { showCopyAlert: false, selectedConcept: this.props.selectedConcept, }; - - } componentDidMount() { - const { selectedConcept, concept, searchTerm, counter, totalResults } = this.props; + const { selectedConcept, concept, searchTerm, counter, totalResults } = + this.props; if ( selectedConcept ? selectedConcept && selectedConcept.conceptId === concept.conceptId : searchTerm - ? parseInt(searchTerm, 10) === concept.conceptId - : false + ? parseInt(searchTerm, 10) === concept.conceptId + : false ) { this.myRef.current.scrollIntoView(); } @@ -259,9 +258,10 @@ export class ConceptRowReactComponent extends React.Component { graphToShow === GraphType.Sources ) { this.setState({ - graphToShow: this.props.domain.name.toLowerCase() === "labs & measurements" - ? GraphType.Values - : GraphType.BiologicalSex, + graphToShow: + this.props.domain.name.toLowerCase() === "labs & measurements" + ? GraphType.Values + : GraphType.BiologicalSex, }); } else { this.setState({ @@ -270,8 +270,8 @@ export class ConceptRowReactComponent extends React.Component { chartType === "sources" ? GraphType.Sources : name.toLowerCase() === "labs & measurements" - ? GraphType.Values - : GraphType.BiologicalSex, + ? GraphType.Values + : GraphType.BiologicalSex, }); } } else { @@ -281,8 +281,8 @@ export class ConceptRowReactComponent extends React.Component { chartType === "sources" ? GraphType.Sources : name.toLowerCase() === "labs & measurements" - ? GraphType.Values - : GraphType.BiologicalSex, + ? GraphType.Values + : GraphType.BiologicalSex, }); } } @@ -290,14 +290,11 @@ export class ConceptRowReactComponent extends React.Component { shareConcept(e: any) { const { concept, searchTerm } = this.props; let path = window.location.pathname; - if (searchTerm && (searchTerm.length > 0) && path.indexOf(searchTerm) > 0) { + if (searchTerm && searchTerm.length > 0 && path.indexOf(searchTerm) > 0) { path = path.substring(0, path.lastIndexOf("/")); } navigator.clipboard.writeText( - window.location.origin + - path + - "/" + - concept.conceptId + window.location.origin + path + "/" + concept.conceptId ); this.setState({ showCopyAlert: true, @@ -328,7 +325,6 @@ export class ConceptRowReactComponent extends React.Component { showCopyAlert, } = this.state; - const id = "c" + concept.conceptId; const tabIndex = 0; const tooltipAction = @@ -340,8 +336,8 @@ export class ConceptRowReactComponent extends React.Component { const synonymsStr = showMoreSynonyms ? synonymString : synonymString - ? synonymString.substring(0, 100) - : null; + ? synonymString.substring(0, 100) + : null; const drugBrandsStr = showMoreDrugBrands ? concept.drugBrands.join(", ") : concept.drugBrands.slice(0, 10).join(", "); @@ -354,11 +350,14 @@ export class ConceptRowReactComponent extends React.Component { ? "Exact source concept match:" : "Standard concept match:"; - return ( -
+
{ @@ -410,7 +409,10 @@ export class ConceptRowReactComponent extends React.Component { className="test-span" style={styles.measurementTypeSpan} > - + {
)} {showConceptChart && graphToShow && ( -
e.stopPropagation()} className="row-expansion"> -
this.expandRow()}>
+
e.stopPropagation()} + className="row-expansion" + > +
this.expandRow()} + > + +
a.conceptId), matchType: results.matchType, top10Results: - currentPage === 1 ? results.items.slice(0, 10) : (top10Results ? top10Results : results.items.slice(0, 10)), + currentPage === 1 + ? results.items.slice(0, 10) + : top10Results + ? top10Results + : results.items.slice(0, 10), loading: false, medlineTerm: medlineTerm, medlinePlusLink: medlinePlusLink, @@ -503,7 +507,12 @@ export const EhrViewReactComponent = withRouteData( } handleChange(val) { - this.setState({ searchWord: val, currentPage: 1, showTopConcepts: true }, () => {localStorage.setItem("searchText", val);}); + this.setState( + { searchWord: val, currentPage: 1, showTopConcepts: true }, + () => { + localStorage.setItem("searchText", val); + } + ); this.domainTotals(); } @@ -531,8 +540,8 @@ export const EhrViewReactComponent = withRouteData( ? top10Results.length === 1 ? top10Results.length + " " + title.slice(0, -1) : top10Results.length < 10 - ? top10Results.length + " " + title - : 10 + " " + title + ? top10Results.length + " " + title + : 10 + " " + title : "No results available"; } @@ -556,7 +565,7 @@ export const EhrViewReactComponent = withRouteData( measurementOrders: measurementOrderFilter ? 1 : 0, }; this.setState({ - currentPage: data + 1 + currentPage: data + 1, }); this.fetchConcepts(searchRequest, true); }; @@ -564,16 +573,17 @@ export const EhrViewReactComponent = withRouteData( handleScrollEnd = (event) => { clearTimeout(this.debounceTimer); this.debounceTimer = setTimeout(() => { - // Calculate the height of the viewport const viewportHeight = window.innerHeight; - + // Calculate the scroll position as a percentage - const scrollPosition = (window.scrollY / (document.documentElement.scrollHeight - viewportHeight)) * 100; + const scrollPosition = + (window.scrollY / + (document.documentElement.scrollHeight - viewportHeight)) * + 100; if (scrollPosition >= 60) { - // You can perform your actions here - this.addMoreResults(); + this.addMoreResults(); } }, 150); // Detect when it is scrolled down 60% @@ -585,7 +595,9 @@ export const EhrViewReactComponent = withRouteData( flipMeasurementTypeFilter() { const { selectedMeasurementTypeFilter } = this.state; - this.setState({ selectedMeasurementTypeFilter: !selectedMeasurementTypeFilter }); + this.setState({ + selectedMeasurementTypeFilter: !selectedMeasurementTypeFilter, + }); } getDropdownDisplayStyle() { @@ -594,9 +606,7 @@ export const EhrViewReactComponent = withRouteData( } return { display: "none" }; } - handleEnd() { - - } + handleEnd() {} render() { const { @@ -621,7 +631,7 @@ export const EhrViewReactComponent = withRouteData( measurementTestFilter, measurementOrderFilter, top10Results, - endReached + endReached, } = this.state; const maxResults = 50; const noMatchFilter = 1; @@ -649,9 +659,12 @@ export const EhrViewReactComponent = withRouteData( />
{loading && } - {domain && !loading && ((concepts && concepts.length > 0) || - (domain.domain.toLowerCase() === "measurement" && !measurementTestFilter && !measurementOrderFilter)) - && ( + {domain && + !loading && + ((concepts && concepts.length > 0) || + (domain.domain.toLowerCase() === "measurement" && + !measurementTestFilter && + !measurementOrderFilter)) && (
-
+
@@ -769,8 +785,8 @@ export const EhrViewReactComponent = withRouteData( className="medline-link" style={styles.medlineLink} > - Interested in general health information related - to "{medlineTerm}"? + Interested in general health information + related to "{medlineTerm}"?
0 && ( -
+
Note: {concepts[0].vocabularyId}{" "} {concepts[0].conceptCode}' {concepts[0].conceptName}' maps to Standard - Vocabulary {standardConcepts[0].vocabularyId} + Vocabulary{" "} + {standardConcepts[0].vocabularyId} {standardConcepts[0].conceptCode} ' {standardConcepts[0].conceptName}'. Standard - vocabularies capture data across a variety of - source vocabularies. + vocabularies capture data across a variety + of source vocabularies.
)}
@@ -829,18 +849,27 @@ export const EhrViewReactComponent = withRouteData( />
- {domain.domain.toLowerCase() === "measurement" && ( + {domain.domain.toLowerCase() === + "measurement" && (
Data Type
- -
+
@@ -893,7 +929,9 @@ export const EhrViewReactComponent = withRouteData( htmlFor="checkbox2" className="checkbox-label" > - {" "} + {" "} Orders
@@ -914,7 +952,7 @@ export const EhrViewReactComponent = withRouteData( domain={domain} totalResults={totalResults} maxResults={maxResults} - currentPage={1} //always on page one if infinite + currentPage={1} // always on page one if infinite counter={index} searchTerm={searchWord} totalParticipants={totalParticipants} @@ -930,11 +968,22 @@ export const EhrViewReactComponent = withRouteData( ? "standard" : "source" } - endReached={() => this.setState({ endReached: true })} + endReached={() => + this.setState({ endReached: true }) + } /> ); })} - {!endReached && } + {!endReached && ( + + + + )}
)} diff --git a/public-ui/src/app/data-browser/views/error-message/error-message-react.component.tsx b/public-ui/src/app/data-browser/views/error-message/error-message-react.component.tsx index 9915d3b5b..2abe81cc8 100644 --- a/public-ui/src/app/data-browser/views/error-message/error-message-react.component.tsx +++ b/public-ui/src/app/data-browser/views/error-message/error-message-react.component.tsx @@ -41,13 +41,15 @@ export class ErrorMessageReactComponent extends React.Component { style={{ width: 26, height: 26 }} /> - {(dataType === "data") && + {dataType === "data" && "Sorry, the data are currently unavailable. Please try refreshing the page or returning home."} - {(dataType === "chart") && "Sorry, the chart cannot be displayed. Please try refreshing the page."} - {(dataType === "noResults") && "The search term returned no results. Please try changing it."} + {dataType === "chart" && + "Sorry, the chart cannot be displayed. Please try refreshing the page."} + {dataType === "noResults" && + "The search term returned no results. Please try changing it."}
); } -} \ No newline at end of file +} diff --git a/public-ui/src/app/data-browser/views/fitbit-view/fitbit-view-react.component.tsx b/public-ui/src/app/data-browser/views/fitbit-view/fitbit-view-react.component.tsx index 1dd1f9cfa..d83275bc6 100644 --- a/public-ui/src/app/data-browser/views/fitbit-view/fitbit-view-react.component.tsx +++ b/public-ui/src/app/data-browser/views/fitbit-view/fitbit-view-react.component.tsx @@ -1,5 +1,6 @@ import * as React from "react"; +import { environment } from "environments/environment"; import { withRouteData } from "app/components/app-router"; import { AgeChartReactComponent } from "app/data-browser/charts/chart-age/chart-age-react.component"; import { BioSexChartReactComponent } from "app/data-browser/charts/chart-biosex/chart-biosex-react.component"; @@ -10,7 +11,6 @@ import { reactStyles } from "app/utils"; import { fitbitConcepts } from "app/utils/constants"; import { urlParamsStore } from "app/utils/navigation"; import { Spinner } from "app/utils/spinner"; -import { environment } from "environments/environment"; const styles = reactStyles({ fmLayout: { @@ -51,7 +51,7 @@ const styles = reactStyles({ // padding: "0.5rem", // fontSize: "0.8em", // borderBottom: "1px solid #0079B8", - // borderTop: "2px solid #0079B8", + // borderTop: "2px solid #0079B8", // borderLeft: "2px solid #0079B8", // borderRight: "2px solid #0079B8", // cursor: "pointer", @@ -136,7 +136,6 @@ div.fm-menu-item-container:nth-child(7) > div:nth-child(1){ } `; - interface State { concepts: any; domainCountAnalysis: any; @@ -176,11 +175,11 @@ export const FitbitReactComponent = withRouteData( "Heart Rate (Summary)", "Heart rate (minute-level)", "Activity Daily Summary", - "Activity intraday steps (minute-level)" + "Activity intraday steps (minute-level)", ]; if (fitbitUpdateFlag) { - FITBIT_MEASUREMENTS.push('Sleep Daily Summary'); - FITBIT_MEASUREMENTS.push('Sleep Level (Sequence by level)'); + FITBIT_MEASUREMENTS.push("Sleep Daily Summary"); + FITBIT_MEASUREMENTS.push("Sleep Level (Sequence by level)"); } dataBrowserApi() .getFitbitAnalysisResults(FITBIT_MEASUREMENTS) @@ -264,9 +263,9 @@ export const FitbitReactComponent = withRouteData( concepts.map((concept, index) => { const conceptClass = selectedDisplay.toLowerCase() === - concept.displayName.toLowerCase() - ? 'fb-menu-item active' - : 'fb-menu-item '; + concept.displayName.toLowerCase() + ? "fb-menu-item active" + : "fb-menu-item "; return (
this.setGraphs(concept)} > -
{ - constructor(props: Props) { - console.log(props.ref,'propsreeef'); - - super(props); - } - + constructor(props: Props) { + console.log(props.ref, "propsreeef"); - render(): React.ReactNode { - const { min, max, ogFilterItem } = this.props; - // const {sliderValue} = this.state - console.log(ogFilterItem, 'realy'); + super(props); + } - return -
- this.props.onSliderChange(e, false)} /> - this.props.onSliderChange(e, true)} /> -
-
; - } + render(): React.ReactNode { + const { min, max, ogFilterItem } = this.props; + // const {sliderValue} = this.state + console.log(ogFilterItem, "realy"); + return ( + +
+ this.props.onSliderChange(e, false)} + /> + this.props.onSliderChange(e, true)} + /> +
+
+ ); + } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/genomic-chart.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/genomic-chart.component.tsx index e95874745..a0e077042 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/genomic-chart.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/genomic-chart.component.tsx @@ -1,8 +1,8 @@ import * as React from "react"; import * as highCharts from "highcharts"; import HighchartsReact from "highcharts-react-official"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCircle } from '@fortawesome/free-solid-svg-icons'; +import { faCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { AGE_STRATUM_MAP, @@ -65,7 +65,9 @@ export class GenomicChartComponent extends React.Component { componentWillReceiveProps(nextProps) { if (nextProps.selectedGenotype !== this.state.selectedGenotype) { - this.setState({ selectedGenotype: nextProps.selectedGenotype }, () => {this.dataToOptions();}); + this.setState({ selectedGenotype: nextProps.selectedGenotype }, () => { + this.dataToOptions(); + }); } } @@ -85,7 +87,9 @@ export class GenomicChartComponent extends React.Component { ]; const sortingSexArr = ["Female", "Male", "Other"]; let participantTypeCount = 0; - participantTypeCount = counts.results.filter((c) => c.stratum4 === selectedGenotype)[0].countValue; + participantTypeCount = counts.results.filter( + (c) => c.stratum4 === selectedGenotype + )[0].countValue; let selectedData: Array = []; chartOptions.chart.type = data.chartType; chartOptions.xAxis.categories = []; @@ -97,22 +101,27 @@ export class GenomicChartComponent extends React.Component { result.stratum2 = GENDER_STRATUM_MAP[result.stratum2]; } else if (AGE_STRATUM_MAP[result.stratum2]) { result.stratum2 = AGE_STRATUM_MAP[result.stratum2]; - }}); - let selectedResults = data.results.filter((r) => r.stratum4 === selectedGenotype); + } + }); + const selectedResults = data.results.filter( + (r) => r.stratum4 === selectedGenotype + ); this.addMissingDemoResults(selectedResults, data.analysisId); selectedResults.forEach((result) => { - const percent: any = - (result.countValue / participantTypeCount) * 100; - let resultText = result.countValue <= 20 ? "≤ 20" : result.countValue.toLocaleString(); + const percent: any = (result.countValue / participantTypeCount) * 100; + const resultText = + result.countValue <= 20 + ? "≤ 20" + : result.countValue.toLocaleString(); toolTipHelpText = - `` + - result.stratum2 + - `
` + - resultText + - ` + `` + + result.stratum2 + + `
` + + resultText + + ` participants, ` + - parseFloat(percent).toFixed(2) + - `%`; + parseFloat(percent).toFixed(2) + + `%`; if (result.stratum4 === selectedGenotype) { selectedData.push({ cat: result.stratum2, @@ -141,7 +150,7 @@ export class GenomicChartComponent extends React.Component { name: selectedGenotype, data: selectedData, color: this.props.color, - } + }, ]; this.setState({ options: chartOptions, @@ -154,16 +163,16 @@ export class GenomicChartComponent extends React.Component { render() { const { options, selectedGenotype } = this.state; - const { title,color } = this.props; + const { title, color } = this.props; let legendText = selectedGenotype; - if (selectedGenotype === 'micro-array') { - legendText = 'Genotyping Arrays'; - } else if (selectedGenotype === 'wgs_longread') { - legendText = 'Long-Read WGS'; - } else if (selectedGenotype === 'wgs_shortread') { - legendText = 'Short-Read WGS'; - } else if (selectedGenotype === 'wgs_structural_variants') { - legendText = 'Short-Read WGS Structural Variants'; + if (selectedGenotype === "micro-array") { + legendText = "Genotyping Arrays"; + } else if (selectedGenotype === "wgs_longread") { + legendText = "Long-Read WGS"; + } else if (selectedGenotype === "wgs_shortread") { + legendText = "Short-Read WGS"; + } else if (selectedGenotype === "wgs_structural_variants") { + legendText = "Short-Read WGS Structural Variants"; } return ( @@ -190,14 +199,30 @@ export class GenomicChartComponent extends React.Component { const uniqueStratums: string[] = []; let fullStratums = []; if (analysisId === 3501) { - fullStratums = ["Other", "Male", "Female"]; + fullStratums = ["Other", "Male", "Female"]; } else { - if (analysisId === 3502) { - fullStratums = ['18-29', '30-39', '40-49', '50-59', '60-69', '70-79', '80-89', '89+']; - } else { - fullStratums = ["White", "Asian", "Black, African American, or African", "Hispanic, Latino, or Spanish", - "More than one category", "Other", "Prefer Not To Answer"]; - } + if (analysisId === 3502) { + fullStratums = [ + "18-29", + "30-39", + "40-49", + "50-59", + "60-69", + "70-79", + "80-89", + "89+", + ]; + } else { + fullStratums = [ + "White", + "Asian", + "Black, African American, or African", + "Hispanic, Latino, or Spanish", + "More than one category", + "Other", + "Prefer Not To Answer", + ]; + } } for (const result of results) { if (uniqueStratums.indexOf(result.stratum2) <= -1) { diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/genomic-faq.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/genomic-faq.component.tsx index 7f28b75b0..4c3f27c00 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/genomic-faq.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/genomic-faq.component.tsx @@ -50,7 +50,7 @@ interface Props { closed: Function; } // tslint:disable-next-line:no-empty-interface -interface State { } +interface State {} const css = ` `; @@ -92,8 +92,8 @@ export class GenomicFaqComponent extends React.Component {
The Data Browser allows researchers to plan studies using - All of Us data. Using the Variant Search, researchers can - conduct preliminary exploration of allele frequencies by broad + All of Us data. Using the Variant Search, researchers + can conduct preliminary exploration of allele frequencies by broad genetic ancestry categories and can observe how patterns of variation might differ between groups with different genetic ancestries. However, researchers may wish to conduct more in-depth diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/genomic-overview.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/genomic-overview.component.tsx index 1a7bd63a8..630eff60d 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/genomic-overview.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/genomic-overview.component.tsx @@ -1,5 +1,7 @@ import * as React from "react"; + import { reactStyles } from "app/utils"; + import { GenomicChartComponent } from "./genomic-chart.component"; const css = ` @@ -100,10 +102,9 @@ const styles = reactStyles({ }, selectGenotypeData: { marginBottom: "1em", - width: '80%', + width: "80%", // minWidth: " 5rem" - } - + }, }); interface Props { participantCount: string; @@ -128,8 +129,8 @@ export class GenomicOverviewComponent extends React.Component { sexAtBirthData: [], currentAgeData: [], participantCounts: [], - selectedGenotype: 'wgs_shortread', - color: '#6F98A0' + selectedGenotype: "wgs_shortread", + color: "#6F98A0", }; this.onGenotypeSelect = this.onGenotypeSelect.bind(this); } @@ -187,22 +188,19 @@ export class GenomicOverviewComponent extends React.Component { onGenotypeSelect(event) { this.setState({ selectedGenotype: event.target.value }); switch (event.target.value) { - case 'micro-array': - this.setState({ color: '#FAAF56' }); + case "micro-array": + this.setState({ color: "#FAAF56" }); break; - case 'wgs_structural_variants': - this.setState({ color: '#93003A' }); + case "wgs_structural_variants": + this.setState({ color: "#93003A" }); break; - case 'wgs_shortread': - this.setState({ color: '#6F98A0' }); + case "wgs_shortread": + this.setState({ color: "#6F98A0" }); break; - case 'wgs_longread': - this.setState({ color: '#01429D' }); + case "wgs_longread": + this.setState({ color: "#01429D" }); break; - - } - } render() { @@ -213,7 +211,7 @@ export class GenomicOverviewComponent extends React.Component { participantCounts, selectedGenotype, loading, - color + color, } = this.state; const { participantCount } = this.props; let countResults = []; @@ -225,10 +223,18 @@ export class GenomicOverviewComponent extends React.Component { if (!loading && participantCounts) { countResults = participantCounts[0].results; - wgsSRParticipantCount = countResults.filter((r) => r.stratum4 === "wgs_shortread")[0].countValue; - wgsLRParticipantCount = countResults.filter((r) => r.stratum4 === "wgs_longread")[0].countValue; - wgsSVParticipantCount = countResults.filter((r) => r.stratum4 === "wgs_structural_variants")[0].countValue; - arrayParticipantCount = countResults.filter((r) => r.stratum4 === "micro-array")[0].countValue; + wgsSRParticipantCount = countResults.filter( + (r) => r.stratum4 === "wgs_shortread" + )[0].countValue; + wgsLRParticipantCount = countResults.filter( + (r) => r.stratum4 === "wgs_longread" + )[0].countValue; + wgsSVParticipantCount = countResults.filter( + (r) => r.stratum4 === "wgs_structural_variants" + )[0].countValue; + arrayParticipantCount = countResults.filter( + (r) => r.stratum4 === "micro-array" + )[0].countValue; } return ( @@ -236,26 +242,73 @@ export class GenomicOverviewComponent extends React.Component {
{!loading && ( -
+
- {wgsSRParticipantCount > 0 && ( - - )} - {wgsLRParticipantCount > 0 && ( - - )} - {wgsSVParticipantCount > 0 && ( - - )} - {arrayParticipantCount > 0 && ( - - )} + {wgsSRParticipantCount > 0 && ( + + + + )} + {wgsLRParticipantCount > 0 && ( + + + + )} + {wgsSVParticipantCount > 0 && ( + + + + )} + {arrayParticipantCount > 0 && ( + + + + )}
-
)} + + )} {!loading && ( { filterMetadata: this.props.filterMetadata, sortMetadata: this.props.sortMetadata, submittedFilterMetadata: this.props.submittedFilterMetadata, - filtered: false + filtered: false, }; } @@ -90,7 +91,10 @@ export class GenomicSearchComponent extends React.Component { handlePageChange(info) { this.props.onPageChange(info); - { !environment.infiniteSrcoll && this.scrollDiv.current.scrollIntoView({ behavior: "smooth" }); } + { + !environment.infiniteSrcoll && + this.scrollDiv.current.scrollIntoView({ behavior: "smooth" }); + } } handleRowCountChange(info) { @@ -104,9 +108,9 @@ export class GenomicSearchComponent extends React.Component { handleFilterSubmit(filteredMetadata, sortMetadata) { this.props.onFilterSubmit(filteredMetadata, sortMetadata); - this.setState({filtered:true}); + this.setState({ filtered: true }); setTimeout(() => { - this.setState({filtered:false}); + this.setState({ filtered: false }); }, 1000); } @@ -115,47 +119,83 @@ export class GenomicSearchComponent extends React.Component { } render() { - const { searchTerm, filterMetadata, sortMetadata, submittedFilterMetadata,filtered } = this.state; - const { currentPage, loadingResults, searchResults, variantListSize, loadingVariantListSize, onSearchInput, - rowCount,scrollClean } = this.props; - return -
- { onSearchInput(searchWord); this.setState({ searchTerm: searchWord }); }} - onFilterSubmit={(filteredMetadata, sortMetadata) => { this.handleFilterSubmit(filteredMetadata, sortMetadata); }} - loadingResults={loadingResults} - loadingVariantListSize={loadingVariantListSize} - searchTerm={searchTerm} - variantListSize={variantListSize} - filterMetadata={filterMetadata} - sortMetadata={sortMetadata} - submittedFilterMetadata={submittedFilterMetadata} - onSortChange={(e) => this.handleSortClick(e)} - scrollClean={scrollClean} /> - { onSearchInput(searchWord); this.setState({ searchTerm: searchWord }); }} - onRowCountChange={(info: any) => this.handleRowCountChange(info)} - onPageChange={(info: any) => this.handlePageChange(info)} - onSortClick={(sortMetadata: any) => this.handleSortClick(sortMetadata)} - onScrollBottom={() => { environment.infiniteSrcoll && this.handleScrollBottom() }} - currentPage={currentPage} - rowCount={rowCount} - sortMetadata={sortMetadata} - filtered = {filtered} - /> - ; + const { + searchTerm, + filterMetadata, + sortMetadata, + submittedFilterMetadata, + filtered, + } = this.state; + const { + currentPage, + loadingResults, + searchResults, + variantListSize, + loadingVariantListSize, + onSearchInput, + rowCount, + scrollClean, + } = this.props; + return ( + +
+

+ Explore allele frequencies for a gene or genomic region and drill + down into variants to view select annotations and genetic ancestry + associations. Variants are based on short-read whole genome + sequencing and called against the GRCh38/hg38 genome reference. + Learn more: + + Variant Annotation Table. + +

+
+ { + onSearchInput(searchWord); + this.setState({ searchTerm: searchWord }); + }} + onFilterSubmit={(filteredMetadata, sortMetadata) => { + this.handleFilterSubmit(filteredMetadata, sortMetadata); + }} + loadingResults={loadingResults} + loadingVariantListSize={loadingVariantListSize} + searchTerm={searchTerm} + variantListSize={variantListSize} + filterMetadata={filterMetadata} + sortMetadata={sortMetadata} + submittedFilterMetadata={submittedFilterMetadata} + onSortChange={(e) => this.handleSortClick(e)} + scrollClean={scrollClean} + /> + { + onSearchInput(searchWord); + this.setState({ searchTerm: searchWord }); + }} + onRowCountChange={(info: any) => this.handleRowCountChange(info)} + onPageChange={(info: any) => this.handlePageChange(info)} + onSortClick={(sortMetadata: any) => + this.handleSortClick(sortMetadata) + } + onScrollBottom={() => { + environment.infiniteSrcoll && this.handleScrollBottom(); + }} + currentPage={currentPage} + rowCount={rowCount} + sortMetadata={sortMetadata} + filtered={filtered} + /> +
+ ); } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/Surface.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/Surface.tsx index 4e203e984..da6bee3cc 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/Surface.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/Surface.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import * as React from "react"; import * as PropTypes from "prop-types"; export default function Surface(props) { @@ -14,7 +14,7 @@ export default function Surface(props) { position: "relative", width: "90%", height: "0px", - padding: "11% 0" + padding: "11% 0", }} {...other} > @@ -26,18 +26,20 @@ export default function Surface(props) { width: "100%", height: "100%", left: 0, - top: 0 + top: 0, }} > {children} -
+
Min Max
@@ -65,10 +67,10 @@ Surface.propTypes = { /** * Width and height attributes of the SVG view box. */ - view: PropTypes.array + view: PropTypes.array, }; Surface.defaultProps = { view: [1000, 500], - trbl: [10, 10, 10, 10] -}; \ No newline at end of file + trbl: [10, 10, 10, 10], +}; diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-components.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-components.tsx index 68790a7d3..397c5f1e3 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-components.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-components.tsx @@ -1,6 +1,8 @@ import * as React from "react"; + import * as PropTypes from "prop-types"; -import { view, trbl, dims } from "./slider-constants"; + +import { dims, trbl, view } from "./slider-constants"; // ******************************************************* // RAIL // ******************************************************* @@ -40,35 +42,47 @@ SliderRail.propTypes = { // ******************************************************* // HANDLE COMPONENT // ******************************************************* -export function Handle({ handle: { id, percent, value },domain, category, getHandleProps }) { - - value = (category === 'alleleFrequency') ? value.toFixed(2): value; +export function Handle({ + handle: { id, percent, value }, + domain, + category, + getHandleProps, +}) { + value = category === "alleleFrequency" ? value.toFixed(2) : value; const r0 = dims[1] * 0.4; // inner - visible const r1 = dims[1] * 0.6; // outer - invisible w/ events - return ( + return ( - - {value} - - - + + + {value} + + + + ); } @@ -78,14 +92,14 @@ Handle.propTypes = { handle: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, getHandleProps: PropTypes.func.isRequired, - disabled: PropTypes.bool + disabled: PropTypes.bool, }; Handle.defaultProps = { - disabled: false + disabled: false, }; // ******************************************************* @@ -117,19 +131,19 @@ Track.propTypes = { source: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, target: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, getTrackProps: PropTypes.func.isRequired, - disabled: PropTypes.bool + disabled: PropTypes.bool, }; Track.defaultProps = { - disabled: false + disabled: false, }; // ******************************************************* @@ -166,11 +180,11 @@ Tick.propTypes = { tick: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, - format: PropTypes.func.isRequired + format: PropTypes.func.isRequired, }; Tick.defaultProps = { - format: d => d + format: (d) => d, }; diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-constants.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-constants.tsx index 6f0b3ff31..5a1084596 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-constants.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/slider-constants.tsx @@ -3,5 +3,5 @@ export const trbl = [20, 50, 20, 50]; // Margins: Top, Right, Bottom, Left export const dims = [ view[0] - trbl[1] - trbl[3], // Adjusted dimensions width - view[1] - trbl[0] - trbl[2] // Adjusted dimensions height + view[1] - trbl[0] - trbl[2], // Adjusted dimensions height ]; diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/variant-filter-slider.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/variant-filter-slider.component.tsx index 54547ab0a..533f98145 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/variant-filter-slider.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/slider-filter/variant-filter-slider.component.tsx @@ -1,5 +1,6 @@ import * as React from "react"; import { Handles, Rail, Slider, Tracks } from "react-compound-slider"; + import { Handle, SliderRail, Track } from "./slider-components"; // example render components - source below import { dims, trbl, view } from "./slider-constants"; import Surface from "./Surface"; @@ -24,48 +25,57 @@ interface State { max: Number; } -export const VariantFilterSliderComponent = (class extends React.Component { - +export const VariantFilterSliderComponent = class extends React.Component< + Props, + State +> { constructor(props: Props) { super(props); this.state = { domain: [this.props.ogFilterItem.min, this.props.ogFilterItem.max], defaultValues: [this.props.filterItem.min, this.props.filterItem.max], min: this.props.filterItem.min, - max: this.props.filterItem.max - } + max: this.props.filterItem.max, + }; } onUpdate(vals) { this.props.onSliderChange(vals); this.setState({ min: vals[0], - max: vals[1] + max: vals[1], }); - } render() { const { domain, defaultValues } = this.state; - const {filterItem, ogFilterItem, category} = this.props; - return
- - { this.onUpdate(e) }} - rootProps={sliderProps} - values={defaultValues}> - - {({ getRailProps }) => } - - {/* + const { filterItem, ogFilterItem, category } = this.props; + return ( +
+ + { + this.onUpdate(e); + }} + rootProps={sliderProps} + values={defaultValues} + > + + {({ getRailProps }) => } + + {/* {({ ticks }) => ( {ticks.map(tick => ( @@ -74,37 +84,38 @@ export const VariantFilterSliderComponent = (class extends React.Component )} */} - - {({ tracks, getTrackProps }) => ( - - {tracks.map(({ id, source, target }) => ( - - ))} - - )} - - - {({ handles, getHandleProps }) => ( - - {handles.map(handle => ( - - ))} - - )} - - - -
+ + {({ tracks, getTrackProps }) => ( + + {tracks.map(({ id, source, target }) => ( + + ))} + + )} + + + {({ handles, getHandleProps }) => ( + + {handles.map((handle) => ( + + ))} + + )} + +
+
+
+ ); } -}) +}; diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/table-paginator.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/table-paginator.component.tsx index 9d5e111b9..f0ab9d943 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/table-paginator.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/table-paginator.component.tsx @@ -1,7 +1,7 @@ import * as React from "react"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faAngleLeft } from '@fortawesome/free-solid-svg-icons'; -import { faAngleRight } from '@fortawesome/free-solid-svg-icons'; +import { faAngleLeft } from "@fortawesome/free-solid-svg-icons"; +import { faAngleRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { reactStyles } from "app/utils"; @@ -132,7 +132,12 @@ export class TablePaginatorComponent extends React.Component { }); }} > - + Page {currentPage} of {pageCount}
diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-expanded.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-expanded.component.tsx index 71e88a3b0..38865ab42 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-expanded.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-expanded.component.tsx @@ -1,4 +1,6 @@ import * as React from "react"; +import { faCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { PopulationChartReactComponent } from "app/data-browser/views/genomic-view/components/population-chart.component"; import { reactStyles } from "app/utils"; @@ -6,8 +8,6 @@ import { ClrIcon } from "app/utils/clr-icon"; import { prepVariantPopulationDetails } from "app/utils/constants"; import { Spinner } from "app/utils/spinner"; import { Variant, VariantInfo } from "publicGenerated"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCircle } from '@fortawesome/free-solid-svg-icons'; const css = ` .exit{ @@ -81,7 +81,6 @@ const styles = reactStyles({ // paddingLeft: "1em", zIndex: "9", borderTop: "1px solid rgb(204, 204, 204)", - }, top: { position: "relative", @@ -133,7 +132,7 @@ const styles = reactStyles({ padding: ".5rem", paddingBottom: "0", paddingTop: "0", - textAlign: "center" + textAlign: "center", }, popTableBody: { borderBottom: "1px solid #DDE0E4", @@ -147,7 +146,7 @@ const styles = reactStyles({ padding: ".5rem", display: "flex", flexDirection: "row", - alignItems: "center" + alignItems: "center", }, closeIcon: { cursor: "pointer", @@ -162,16 +161,15 @@ interface Props { loading: boolean; } // tslint:disable-next-line:no-empty-interface -interface State { } +interface State {} export class VariantExpandedComponent extends React.Component { constructor(props: Props) { super(props); } handleMouseOver = () => { - this.props.hovered(true) - - } + this.props.hovered(true); + }; render() { const { variantDetails, variant, loading } = this.props; let variantPopulationDetails: any[] = []; @@ -184,12 +182,14 @@ export class VariantExpandedComponent extends React.Component {
-
+
Variant ID: {" "} {!loading ? ( - + {variant.variantId} ) : ( @@ -198,7 +198,7 @@ export class VariantExpandedComponent extends React.Component {
)}{" "} -
+
this.props.closed()} className="exit" @@ -213,23 +213,35 @@ export class VariantExpandedComponent extends React.Component {
Gene:
- {variant.genes ? variant.genes : "-"} + + {variant.genes ? variant.genes : "-"} +
Consequence:
- {variant.consequence ? variant.consequence.replace(/_/g, ' ') : "-"} + + {variant.consequence + ? variant.consequence.replace(/_/g, " ") + : "-"} +
Variant Type:
- {variant.variantType ? variant.variantType.replace(/_/g, ' ') : "-"} + + {variant.variantType + ? variant.variantType.replace(/_/g, " ") + : "-"} +
Transcript:
- {variantDetails.transcript ? variantDetails.transcript : "-"} + {variantDetails.transcript + ? variantDetails.transcript + : "-"}
@@ -238,33 +250,41 @@ export class VariantExpandedComponent extends React.Component { {variantDetails.rsNumber ? [ - - {variantDetails.rsNumber} - , - ] + + {variantDetails.rsNumber} + , + ] : "-"}
DNA Change:
- {variantDetails.dnaChange ? variantDetails.dnaChange : "-"} + + {variantDetails.dnaChange + ? variantDetails.dnaChange + : "-"} +
Protein Change:
- {variant.proteinChange ? variant.proteinChange : "-"} + + {variant.proteinChange ? variant.proteinChange : "-"} +
ClinVar Significance:
- {variant.clinicalSignificance ? variant.clinicalSignificance.replace(/_/g, ' ') : "-"} + {variant.clinicalSignificance + ? variant.clinicalSignificance.replace(/_/g, " ") + : "-"}
@@ -274,16 +294,28 @@ export class VariantExpandedComponent extends React.Component {
- Allele
Count
+ + Allele
+ Count +
- Allele
Number
+ + Allele
+ Number +
- Allele
Frequency
+ + Allele
+ Frequency +
- Homozygote
Count
+ + Homozygote
+ Count +
@@ -294,8 +326,14 @@ export class VariantExpandedComponent extends React.Component {
{item.Ancestry !== "Total" ? ( - + {item.Ancestry}{" "} ) : ( diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-chips.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-chips.component.tsx index 10f3a227b..4b295240e 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-chips.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-chips.component.tsx @@ -1,163 +1,215 @@ -import { reactStyles } from 'app/utils'; -import { GenomicFilters } from 'publicGenerated'; -import * as React from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faXmark } from '@fortawesome/free-solid-svg-icons'; +import * as React from "react"; +import { faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { reactStyles } from "app/utils"; +import { GenomicFilters } from "publicGenerated"; interface Props { - filteredMetadata: GenomicFilters; - onChipChange: Function; + filteredMetadata: GenomicFilters; + onChipChange: Function; } interface State { - chips: Array; - + chips: Array; } const lables = { - gene: 'Gene', - consequence: 'Consequence', - variantType: 'Variant Type', - clinicalSignificance: 'ClinVar Significance', - alleleNumber: 'Allele Number', - alleleFrequency: 'Allele Frequency', - alleleCount: 'Allele Count', - homozygoteCount: 'Homozygote Count' + gene: "Gene", + consequence: "Consequence", + variantType: "Variant Type", + clinicalSignificance: "ClinVar Significance", + alleleNumber: "Allele Number", + alleleFrequency: "Allele Frequency", + alleleCount: "Allele Count", + homozygoteCount: "Homozygote Count", }; const styles = reactStyles({ - - chipCat: { - display: 'flex', - flexWrap: 'wrap', - alignItems: 'center', - paddingRight: '0.25rem' - }, - chip: { - display: 'flex', - alignItems: 'center', - border: '1px #216fb4 solid', - color: 'rgb(33, 111, 180)', - padding: '.05rem .5rem', - borderRadius: '15px', - fontFamily: 'GothamBold', - margin: '.25rem .25rem' - }, - chipFormat: { - fontSize: '.8em', - display: 'flex', - flexWrap: 'wrap' - }, - chipLayout: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center' - } + chipCat: { + display: "flex", + flexWrap: "wrap", + alignItems: "center", + paddingRight: "0.25rem", + }, + chip: { + display: "flex", + alignItems: "center", + border: "1px #216fb4 solid", + color: "rgb(33, 111, 180)", + padding: ".05rem .5rem", + borderRadius: "15px", + fontFamily: "GothamBold", + margin: ".25rem .25rem", + }, + chipFormat: { + fontSize: ".8em", + display: "flex", + flexWrap: "wrap", + }, + chipLayout: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + }, }); export class VariantFilterChips extends React.Component { - constructor(props: Props) { - super(props); - this.state = { - chips: [] - }; - } - - formatChips(filteredMetadata): Array { - const displayArr = []; - for (const key in filteredMetadata) { - if (Object.prototype.hasOwnProperty.call(filteredMetadata, key)) { - const allChecked = Array.isArray(filteredMetadata[key]) && filteredMetadata[key].every((t => t.checked)); - if (!allChecked) { - let el = filteredMetadata[key]; - if (!el.hasOwnProperty("filterActive")) { - if (el.min < 1) { - el.min = +el.min.toFixed(2); - } - if (el.max < 1) { - el.max = +el.max.toFixed(2); - } - } - displayArr.push({ cat: key, data: el }); - } + constructor(props: Props) { + super(props); + this.state = { + chips: [], + }; + } + formatChips(filteredMetadata): Array { + const displayArr = []; + for (const key in filteredMetadata) { + if (Object.prototype.hasOwnProperty.call(filteredMetadata, key)) { + const allChecked = + Array.isArray(filteredMetadata[key]) && + filteredMetadata[key].every((t) => t.checked); + if (!allChecked) { + const el = filteredMetadata[key]; + if (!el.hasOwnProperty("filterActive")) { + if (el.min < 1) { + el.min = +el.min.toFixed(2); + } + if (el.max < 1) { + el.max = +el.max.toFixed(2); } + } + displayArr.push({ cat: key, data: el }); } - return displayArr; + } } + return displayArr; + } - componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { - if (prevProps !== this.props) { - this.setState({ chips: this.formatChips(this.props.filteredMetadata) }); - } + componentDidUpdate( + prevProps: Readonly, + prevState: Readonly, + snapshot?: any + ): void { + if (prevProps !== this.props) { + this.setState({ chips: this.formatChips(this.props.filteredMetadata) }); } + } - removeChip(item, cat) { - const {filteredMetadata} = this.props; - if (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { - filteredMetadata[cat.toString()].items = filteredMetadata[cat.toString()].items.filter(el => { - if (item === el) { - item.checked = false; - } - return el; - }); - } else { - filteredMetadata[cat.toString()].checked = false; - try { - let originalFilterMetadata = JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'); - filteredMetadata[cat.toString()]['min'] = originalFilterMetadata[cat.toString()]['min']; - filteredMetadata[cat.toString()]['max'] = originalFilterMetadata[cat.toString()]['max']; - } catch (e) { - console.log('Error') - } - } - const allFalse = (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) && - filteredMetadata[cat.toString()].items.every(t => t.checked === false); - - if (allFalse && filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { - filteredMetadata[cat.toString()].filterActive = false; + removeChip(item, cat) { + const { filteredMetadata } = this.props; + if (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { + filteredMetadata[cat.toString()].items = filteredMetadata[ + cat.toString() + ].items.filter((el) => { + if (item === el) { + item.checked = false; } - this.props.onChipChange(filteredMetadata); + return el; + }); + } else { + filteredMetadata[cat.toString()].checked = false; + try { + const originalFilterMetadata = JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + ); + filteredMetadata[cat.toString()].min = + originalFilterMetadata[cat.toString()].min; + filteredMetadata[cat.toString()].max = + originalFilterMetadata[cat.toString()].max; + } catch (e) { + console.log("Error"); + } } + const allFalse = + filteredMetadata[cat.toString()].hasOwnProperty("filterActive") && + filteredMetadata[cat.toString()].items.every((t) => t.checked === false); - render() { - const { chips } = this.state; - return
- {chips.length > 0 && chips.map((el, count) => { - if (el.data.hasOwnProperty("filterActive")) { - return
{el.data.items.some((p) => p.checked) &&
{lables[el.cat.toString()]} - {el.data.items.map((item, i) => { - const chipLabel = item.option ? item.option : '(undefined)'; - return
- {item.checked &&
- {chipLabel.replace(/_/g, " ")} - this.removeChip(item, el.cat)} - icon={faXmark} className="clear-search-icon" - caria-hidden='true'/> -
} -
; - })} -
} -
; - } else { - return
{el.data.checked &&
- {el.data.checked && -
{lables[el.cat.toString()]} -
- Min  - {el.data.min} -  | Max  - {el.data.max} - this.removeChip(el, el.cat)} - icon={faXmark} className="clear-search-icon" - caria-hidden='true'/> -
-
} -
} -
; - } - - })} -
; + if ( + allFalse && + filteredMetadata[cat.toString()].hasOwnProperty("filterActive") + ) { + filteredMetadata[cat.toString()].filterActive = false; } + this.props.onChipChange(filteredMetadata); + } + + render() { + const { chips } = this.state; + return ( +
+ {chips.length > 0 && + chips.map((el, count) => { + if (el.data.hasOwnProperty("filterActive")) { + return ( +
+ {" "} + {el.data.items.some((p) => p.checked) && ( +
+ {lables[el.cat.toString()]} + {el.data.items.map((item, i) => { + const chipLabel = item.option + ? item.option + : "(undefined)"; + return ( +
+ {item.checked && ( +
+ {chipLabel.replace(/_/g, " ")} + this.removeChip(item, el.cat)} + icon={faXmark} + className="clear-search-icon" + caria-hidden="true" + /> +
+ )} +
+ ); + })} +
+ )} +
+ ); + } else { + return ( +
+ {el.data.checked && ( +
+ {el.data.checked && ( +
+ {lables[el.cat.toString()]} +
+ + Min  + + {el.data.min} + +  | Max  + + {el.data.max} + this.removeChip(el, el.cat)} + icon={faXmark} + className="clear-search-icon" + caria-hidden="true" + /> +
+
+ )} +
+ )} +
+ ); + } + })} +
+ ); + } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-item.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-item.component.tsx index c1f00d0ee..23c56517b 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-item.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-item.component.tsx @@ -3,7 +3,8 @@ import * as React from "react"; import { Cat } from "app/data-browser/views/genomic-view/components/variant-filter.component"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; -import { VariantFilterSliderComponent } from './slider-filter/variant-filter-slider.component' + +import { VariantFilterSliderComponent } from "./slider-filter/variant-filter-slider.component"; const styles = reactStyles({ filterItem: { @@ -17,7 +18,7 @@ const styles = reactStyles({ fontSize: ".8em", letterSpacing: 0, lineHeight: "16px", - cursor: "pointer" + cursor: "pointer", }, filterItemClosed: { transform: "rotate(90deg)", @@ -44,10 +45,10 @@ const styles = reactStyles({ }, filterItemForm: { display: "flex", - overflow: 'hidden', + overflow: "hidden", flexDirection: "column", paddingLeft: "1rem", - paddingTop:".25rem" + paddingTop: ".25rem", }, filterItemOption: { fontSize: ".8em", @@ -60,10 +61,10 @@ const styles = reactStyles({ marginTop: "0.1rem", }, filterItemLabel: { - width: '80%', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow:'hidden' + width: "80%", + whiteSpace: "nowrap", + textOverflow: "ellipsis", + overflow: "hidden", // wordWrap: "break-word", }, filterSlider: { @@ -102,20 +103,25 @@ export class VariantFilterItemComponent extends React.Component { super(props); this.state = { filterItemOpen: false, - filterItemState: props.filterItem || '', - filterCheckMap: props.filterItem || '', - ogFilterMetaData: JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}')[this.props.category.field.toString()] - }; + filterItemState: props.filterItem || "", + filterCheckMap: props.filterItem || "", + ogFilterMetaData: JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + )[this.props.category.field.toString()], + }; } componentDidMount(): void { - if (Array.isArray(this.state.filterCheckMap.items) && this.state.filterCheckMap.items.every(t => t.checked)) { - this.state.filterCheckMap.items.forEach(i => i.checked = false); + if ( + Array.isArray(this.state.filterCheckMap.items) && + this.state.filterCheckMap.items.every((t) => t.checked) + ) { + this.state.filterCheckMap.items.forEach((i) => (i.checked = false)); } } filterClick() { - console.log('Am i clicked ?'); + console.log("Am i clicked ?"); this.setState({ filterItemOpen: !this.state.filterItemOpen }); } @@ -132,27 +138,29 @@ export class VariantFilterItemComponent extends React.Component { // } // } - handleCheck(filteredItem) { - const {filterItemState, filterCheckMap} = this.state; - let newFilterItemState = { ...filterItemState}; - let newFilterCheckMap = { ...filterCheckMap}; - const filtered = this.state.filterItemState.items.map(el => el === filteredItem ? { ...el, checked: !filteredItem.checked } : el); - const filterCheckedFlag = (filtered.find(x => x.checked === true)) ? true : false; + const { filterItemState, filterCheckMap } = this.state; + const newFilterItemState = { ...filterItemState }; + const newFilterCheckMap = { ...filterCheckMap }; + const filtered = this.state.filterItemState.items.map((el) => + el === filteredItem ? { ...el, checked: !filteredItem.checked } : el + ); + const filterCheckedFlag = filtered.find((x) => x.checked === true) + ? true + : false; newFilterItemState.items = filtered; newFilterItemState.filterActive = filterCheckedFlag; newFilterCheckMap.items = filtered; newFilterCheckMap.filterActive = filterCheckedFlag; this.setState({ filterItemState: newFilterItemState, - filterCheckMap: newFilterCheckMap + filterCheckMap: newFilterCheckMap, }); this.props.onFilterChange(newFilterItemState, this.props.category); } - handleSliderChange(vals, filterItem) { - const updatedFilterItem = {...filterItem}; + const updatedFilterItem = { ...filterItem }; updatedFilterItem.min = vals[0]; updatedFilterItem.max = vals[1]; updatedFilterItem.checked = true; @@ -160,42 +168,72 @@ export class VariantFilterItemComponent extends React.Component { } render(): React.ReactNode { - const { category,cleared, filterItem } = this.props; + const { category, cleared, filterItem } = this.props; const { filterItemOpen, filterItemState, ogFilterMetaData } = this.state; - return - -
this.filterClick()} style={styles.filterItem}> - {category.display} -
-
- {(cleared && filterItemOpen && Array.isArray(filterItemState.items)) ?
- {/* this.filterBySearch(e)} /> + return ( + + +
this.filterClick()} style={styles.filterItem}> + {category.display} +
+ +
+
+ {cleared && filterItemOpen && Array.isArray(filterItemState.items) ? ( +
+ {/* this.filterBySearch(e)} />
Select |
*/} - {filterItemState.items.map((item: any, index: number) => { - const key = 'option' + index; - const itemLabel = item.option ? item.option : '(undefined)'; - return - this.handleCheck(item)} - id={item.option} - style={styles.filterItemCheck} - type='checkbox' name={item.option} - checked={item.checked} /> - - ; - })} -
: -
{filterItemOpen && - this.handleSliderChange(e, filterItemState)} />} -
} -
; + {filterItemState.items.map((item: any, index: number) => { + const key = "option" + index; + const itemLabel = item.option ? item.option : "(undefined)"; + return ( + + this.handleCheck(item)} + id={item.option} + style={styles.filterItemCheck} + type="checkbox" + name={item.option} + checked={item.checked} + /> + + + ); + })} +
+ ) : ( +
+ {filterItemOpen && ( + + this.handleSliderChange(e, filterItemState) + } + /> + )} +
+ )} +
+ ); } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-slider.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-slider.component.tsx index 3212c5f87..2e4930f9d 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-slider.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter-slider.component.tsx @@ -1,46 +1,49 @@ -import { reactStyles } from 'app/utils'; -import * as React from 'react'; +import * as React from "react"; + +import { reactStyles } from "app/utils"; interface Props { - min: Number; - max: Number; - ogFilterItem: any; - onSliderChange: Function; + min: Number; + max: Number; + ogFilterItem: any; + onSliderChange: Function; } const styles = reactStyles({ - sliderFormat: { display: 'flex' } + sliderFormat: { display: "flex" }, }); export class VariantFilterSliderComponent extends React.Component { - constructor(props: Props) { - super(props); - } - - - render(): React.ReactNode { - const { min, max, ogFilterItem } = this.props; - // const {sliderValue} = this.state - console.log(ogFilterItem, 'realy'); - console.log('Am i here'); + constructor(props: Props) { + super(props); + } - return -
- this.props.onSliderChange(e, false)} /> - this.props.onSliderChange(e, true)} /> -
-
; - } + render(): React.ReactNode { + const { min, max, ogFilterItem } = this.props; + // const {sliderValue} = this.state + console.log(ogFilterItem, "realy"); + console.log("Am i here"); + return ( + +
+ this.props.onSliderChange(e, false)} + /> + this.props.onSliderChange(e, true)} + /> +
+
+ ); + } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter.component.tsx index b913ed3d9..78cb25987 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-filter.component.tsx @@ -6,183 +6,211 @@ import { reactStyles } from "app/utils"; import { GenomicFilters } from "publicGenerated"; import { SortMetadata } from "publicGenerated/fetch"; - const styles = reactStyles({ - filterBox: { - top: ".5rem", - position: "absolute", - padding: ".25rem", - zIndex: 12, - borderRadius: "0 1px 1px 0", - backgroundColor: "#FFFFFF", - boxShadow: - "0 1px 3px 0 rgba(0,0,0,0.15), 0 0 2px 0 rgba(0,0,0,0.25), 0 2px 2px 0 rgba(0,0,0,0.15)", - width: "264px", - height: "421px", - display: "grid", - gridTemplateRows: "84% 16%", - }, - filterItemHandleClosed: { - transform: "rotate(90deg)", - }, - sortByContainer: { - paddingTop: ".5rem", - paddingRight: ".5rem", - }, - actionBtnContainer: { - position: "absolute", - bottom: ".5rem", - width: "100%", - display: "flex", - justifyContent:"space-around", - fontSize: "1.1em", - }, - clearBtn: { - textTransform: "uppercase", - borderRadius: "2px", - padding: "1rem", - border: "none", - background: "transparent", - width: "45%", - cursor: "pointer", - }, - applyBtn: { - textTransform: "uppercase", - borderRadius: "2px", - padding: "1rem", - border: "none", - background: "#262262", - color: "white", - width: "45%", - cursor: "pointer", - }, - filterItems: { - maxHeight: "340px", - overflowY: "auto", - }, + filterBox: { + top: ".5rem", + position: "absolute", + padding: ".25rem", + zIndex: 12, + borderRadius: "0 1px 1px 0", + backgroundColor: "#FFFFFF", + boxShadow: + "0 1px 3px 0 rgba(0,0,0,0.15), 0 0 2px 0 rgba(0,0,0,0.25), 0 2px 2px 0 rgba(0,0,0,0.15)", + width: "264px", + height: "421px", + display: "grid", + gridTemplateRows: "84% 16%", + }, + filterItemHandleClosed: { + transform: "rotate(90deg)", + }, + sortByContainer: { + paddingTop: ".5rem", + paddingRight: ".5rem", + }, + actionBtnContainer: { + position: "absolute", + bottom: ".5rem", + width: "100%", + display: "flex", + justifyContent: "space-around", + fontSize: "1.1em", + }, + clearBtn: { + textTransform: "uppercase", + borderRadius: "2px", + padding: "1rem", + border: "none", + background: "transparent", + width: "45%", + cursor: "pointer", + }, + applyBtn: { + textTransform: "uppercase", + borderRadius: "2px", + padding: "1rem", + border: "none", + background: "#262262", + color: "white", + width: "45%", + cursor: "pointer", + }, + filterItems: { + maxHeight: "340px", + overflowY: "auto", + }, }); - export interface Cat { - display: String; - field: String; + display: String; + field: String; } interface Props { - filterMetadata: GenomicFilters; - sortMetadata: SortMetadata; - onFilterSubmit: Function; - onSortChange: Function; + filterMetadata: GenomicFilters; + sortMetadata: SortMetadata; + onFilterSubmit: Function; + onSortChange: Function; } interface State { - filterCats: Cat[]; - filteredMetadata: any; - filterMetadata: any; - cleared: Boolean; - ogFilterMetaData: any; - sortMetadata: SortMetadata; + filterCats: Cat[]; + filteredMetadata: any; + filterMetadata: any; + cleared: Boolean; + ogFilterMetaData: any; + sortMetadata: SortMetadata; } export class VariantFilterComponent extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + filterCats: [ + { display: "Gene", field: "gene" }, + { display: "Consequence", field: "consequence" }, + { display: "Variant Type", field: "variantType" }, + { display: "ClinVar Significance", field: "clinicalSignificance" }, + { display: "Allele Count", field: "alleleCount" }, + { display: "Allele Number", field: "alleleNumber" }, + { display: "Allele Frequency", field: "alleleFrequency" }, + { display: "Homozygote Count", field: "homozygoteCount" }, + ], + filteredMetadata: this.props.filterMetadata, + filterMetadata: this.props.filterMetadata, + cleared: true, + ogFilterMetaData: JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + ), + sortMetadata: this.props.sortMetadata, + }; + } - constructor(props: Props) { - super(props); - this.state = { - filterCats: [ - { display: 'Gene', field: 'gene' }, - { display: 'Consequence', field: 'consequence' }, - { display: 'Variant Type', field: 'variantType' }, - { display: 'ClinVar Significance', field: 'clinicalSignificance' }, - { display: 'Allele Count', field: 'alleleCount' }, - { display: 'Allele Number', field: 'alleleNumber' }, - { display: 'Allele Frequency', field: 'alleleFrequency' }, - { display: 'Homozygote Count', field: 'homozygoteCount' }, - ], - filteredMetadata: this.props.filterMetadata, - filterMetadata: this.props.filterMetadata, - cleared: true, - ogFilterMetaData: JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'), - sortMetadata: this.props.sortMetadata, - }; - } - - handleFilterChange(filteredItem: GenomicFilters, cat: Cat) { - const filterMetadataChange = this.props.filterMetadata; - filterMetadataChange[cat.field.toString()] = filteredItem; - this.setState({ filterMetadata: filterMetadataChange }); - } + handleFilterChange(filteredItem: GenomicFilters, cat: Cat) { + const filterMetadataChange = this.props.filterMetadata; + filterMetadataChange[cat.field.toString()] = filteredItem; + this.setState({ filterMetadata: filterMetadataChange }); + } - handleSortChange(sortedItem: SortMetadata) { - this.setState({ sortMetadata: sortedItem }); - } + handleSortChange(sortedItem: SortMetadata) { + this.setState({ sortMetadata: sortedItem }); + } - submitFilter(filteredMetadata: GenomicFilters) { - // tslint:disable-next-line: forin - for (const key in filteredMetadata) { - const filterItem = filteredMetadata[key]; - const touched = Array.isArray(filterItem) && filterItem.some((t => t.checked)); - if (Array.isArray(filterItem)) { - if (!touched) { - filteredMetadata[key] = filterItem.forEach((item) => { - item.checked = true; - }); - filteredMetadata[key] = filterItem; - } - } + submitFilter(filteredMetadata: GenomicFilters) { + // tslint:disable-next-line: forin + for (const key in filteredMetadata) { + const filterItem = filteredMetadata[key]; + const touched = + Array.isArray(filterItem) && filterItem.some((t) => t.checked); + if (Array.isArray(filterItem)) { + if (!touched) { + filteredMetadata[key] = filterItem.forEach((item) => { + item.checked = true; + }); + filteredMetadata[key] = filterItem; } - filteredMetadata = this.state.filteredMetadata; - const sortMetadata = this.state.sortMetadata; - this.props.onFilterSubmit(filteredMetadata, sortMetadata); + } } + filteredMetadata = this.state.filteredMetadata; + const sortMetadata = this.state.sortMetadata; + this.props.onFilterSubmit(filteredMetadata, sortMetadata); + } - handleClear() { - const ogFilterMetaData = JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'); - // tslint:disable-next-line: forin - for (const key in this.state.filteredMetadata) { - this.state.filteredMetadata[key] = ogFilterMetaData[key]; - } - - const { sortMetadata } = this.state; - for (const smKey in sortMetadata) { - sortMetadata[smKey].sortActive = false; - sortMetadata[smKey].sortDirection = "asc"; - } - sortMetadata['variantId'].sortActive = true; - sortMetadata['variantId'].sortDirection = "asc"; + handleClear() { + const ogFilterMetaData = JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + ); + // tslint:disable-next-line: forin + for (const key in this.state.filteredMetadata) { + this.state.filteredMetadata[key] = ogFilterMetaData[key]; + } - this.setState({ cleared: false, filteredMetadata: this.state.filteredMetadata, sortMetadata: sortMetadata }, () => this.setState({ cleared: true })); - this.props.onFilterSubmit(this.state.filteredMetadata, sortMetadata); - + const { sortMetadata } = this.state; + for (const smKey in sortMetadata) { + sortMetadata[smKey].sortActive = false; + sortMetadata[smKey].sortDirection = "asc"; } + sortMetadata.variantId.sortActive = true; + sortMetadata.variantId.sortDirection = "asc"; + + this.setState( + { + cleared: false, + filteredMetadata: this.state.filteredMetadata, + sortMetadata: sortMetadata, + }, + () => this.setState({ cleared: true }) + ); + this.props.onFilterSubmit(this.state.filteredMetadata, sortMetadata); + } - render() { - const { filterMetadata } = this.props; - const { filterCats, filteredMetadata, cleared, sortMetadata } = this.state; - return -
-
- {filterCats.map((cat, index) => { - const key = 'cat' + index; - { - return cleared && filterMetadata && filteredMetadata && - this.handleFilterChange(e, cat)} - key={key} - category={cat} - cleared={cleared} - filterItem={filteredMetadata[cat.field.toString()]} />; - } - }) - } -
- { this.handleSortChange(e)} sortMetadata={sortMetadata} />} -
-
-
- - -
+ render() { + const { filterMetadata } = this.props; + const { filterCats, filteredMetadata, cleared, sortMetadata } = this.state; + return ( + +
+
+ {filterCats.map((cat, index) => { + const key = "cat" + index; + { + return ( + cleared && + filterMetadata && + filteredMetadata && ( + this.handleFilterChange(e, cat)} + key={key} + category={cat} + cleared={cleared} + filterItem={filteredMetadata[cat.field.toString()]} + /> + ) + ); + } + })} +
+ { + this.handleSortChange(e)} + sortMetadata={sortMetadata} + /> + }
- ; - } +
+
+ + +
+
+
+ ); + } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-row.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-row.component.tsx index 8d4694178..97a0bf810 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-row.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-row.component.tsx @@ -119,99 +119,109 @@ export class VariantRowComponent extends React.Component { this.setState({ variantExpanded: !this.state.variantExpanded, }); - {} + { + } } - -render() { - const { variant } = this.props; - const { variantExpanded, variantDetails, loadingVarDetails } = this.state; - return ( - - - {!loadingVarDetails && variantExpanded ? ( - this.handleClick()} - hovered={() => this.state.mouseOverExpanded ? this.props.allowParentScroll(true): this.props.allowParentScroll(false)} - /> - ) : ( -
-
this.handleClick(variant.variantId)} - style={styles.variant} - > + render() { + const { variant } = this.props; + const { variantExpanded, variantDetails, loadingVarDetails } = this.state; + return ( + + + {!loadingVarDetails && variantExpanded ? ( + this.handleClick()} + hovered={() => + this.state.mouseOverExpanded + ? this.props.allowParentScroll(true) + : this.props.allowParentScroll(false) + } + /> + ) : ( +
this.handleClick(variant.variantId)} + style={styles.variant} > -
- {variant.variantId.length > 40 ? ( - - {variant.variantId.substr(0, 40)} … - - ) : ( - variant.variantId - )} -
-
- { }} - size="lg" - shape="caret" - dir="down" - /> +
+
+ {variant.variantId.length > 40 ? ( + + {variant.variantId.substr(0, 40)} … + + ) : ( + variant.variantId + )} +
+
+ {}} + size="lg" + shape="caret" + dir="down" + /> +
-
-
- {variant.genes && variant.genes.length ? ( -
{variant.genes}
- ) : ( -
-
- )} -
-
-
- {variant.consequence && variant.consequence.length ? ( - {variant.consequence.replace(/_/g, " ")} +
+ {variant.genes && variant.genes.length ? ( +
{variant.genes}
) : ( - - +
-
)}
-
-
- {variant.variantType ? ( -
- {variant.variantType} +
+
+ {variant.consequence && variant.consequence.length ? ( + {variant.consequence.replace(/_/g, " ")} + ) : ( + - + )}
- ) : ( -
–
- )} -
-
-
- {variant.clinicalSignificance && - variant.clinicalSignificance.length ? ( - {variant.clinicalSignificance} +
+
+ {variant.variantType ? ( +
+ {variant.variantType} +
) : ( - - +
–
)}
+
+
+ {variant.clinicalSignificance && + variant.clinicalSignificance.length ? ( + {variant.clinicalSignificance} + ) : ( + - + )} +
+
+
+ {variant.alleleCount.toLocaleString()} +
+
+ {variant.alleleNumber.toLocaleString()} +
+
{variant.alleleFrequency}
+
+ {variant.homozygoteCount.toLocaleString()} +
-
{variant.alleleCount.toLocaleString()}
-
{variant.alleleNumber.toLocaleString()}
-
{variant.alleleFrequency}
-
{variant.homozygoteCount.toLocaleString()}
-
- )} - - ); -} + )} + + ); + } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-search.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-search.component.tsx index f8a035a11..79699ee05 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-search.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-search.component.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { VariantFilterChips } from './variant-filter-chips.component' + import { environment } from "environments/environment"; import { SearchComponent } from "app/data-browser/search/home-search.component"; import { VariantFilterComponent } from "app/data-browser/views/genomic-view/components/variant-filter.component"; @@ -9,11 +9,13 @@ import { Spinner } from "app/utils/spinner"; import { GenomicFilters } from "publicGenerated"; import { SortMetadata } from "publicGenerated/fetch"; +import { VariantFilterChips } from "./variant-filter-chips.component"; + const styles = reactStyles({ searchBar: { paddingRight: "2rem", width: "calc(100% - 16rem)", - minWidth: "20rem" + minWidth: "20rem", }, searchHelpText: { paddingTop: "2em", @@ -25,22 +27,22 @@ const styles = reactStyles({ marginLeft: "-1rem", }, resultSize: { - fontSize:"1.2em" + fontSize: "1.2em", }, filterBtn: { fontFamily: "gothamBold", color: "#216FB4", cursor: "Pointer", - width: "fit-content" + width: "fit-content", }, filterContainer: { position: "relative", }, resultInfo: { - display:"grid", + display: "grid", gridTemplateColumns: "11.5rem 1fr", - alignItems: "baseline" - } + alignItems: "baseline", + }, }); const css = ` @@ -93,16 +95,16 @@ export class VariantSearchComponent extends React.Component { constructor(props: Props) { super(props); this.state = { - searchWord: '', + searchWord: "", filterShow: false, filteredMetadata: undefined, filteredMetaMap: undefined, filterMetadata: this.props.filterMetadata, submittedFilterMetadata: this.props.submittedFilterMetadata, sortMetadata: this.props.sortMetadata, - scrollClean: this.props.scrollClean + scrollClean: this.props.scrollClean, }; - if (this.state.searchWord !== '') { + if (this.state.searchWord !== "") { this.props.onSearchTerm(this.state.searchWord); } this.filterWrapperRef = React.createRef(); @@ -110,14 +112,24 @@ export class VariantSearchComponent extends React.Component { } handleChange(val: string) { - if (val == '') { this.setState({ scrollClean: true }) } + if (val == "") { + this.setState({ scrollClean: true }); + } this.props.onSearchTerm(val); - this.setState({ searchWord: val, filteredMetaMap: null, filterShow: false }); + this.setState({ + searchWord: val, + filteredMetaMap: null, + filterShow: false, + }); } - componentWillUpdate(nextProps: Readonly, nextState: Readonly, nextContext: any): void { + componentWillUpdate( + nextProps: Readonly, + nextState: Readonly, + nextContext: any + ): void { if (this.props.scrollClean != nextProps.scrollClean) { - this.setState({ scrollClean: nextProps.scrollClean }) + this.setState({ scrollClean: nextProps.scrollClean }); } } componentDidMount() { @@ -130,7 +142,10 @@ export class VariantSearchComponent extends React.Component { handleClickOutside(event) { const { filterShow } = this.state; - if (this.filterWrapperRef && !this.filterWrapperRef.current.contains(event.target)) { + if ( + this.filterWrapperRef && + !this.filterWrapperRef.current.contains(event.target) + ) { if (filterShow) { this.setState({ filterShow: !this.state.filterShow }); } @@ -155,7 +170,10 @@ export class VariantSearchComponent extends React.Component { this.setState({ filterShow: !this.state.filterShow }); } - handleFilterSubmit(filteredMetadata: GenomicFilters, sortMetadata: SortMetadata) { + handleFilterSubmit( + filteredMetadata: GenomicFilters, + sortMetadata: SortMetadata + ) { this.setState({ filteredMetadata: filteredMetadata }); this.props.onFilterSubmit(filteredMetadata, sortMetadata); this.setState({ filterShow: false }); @@ -173,56 +191,99 @@ export class VariantSearchComponent extends React.Component { } render() { - const { searchWord, filterShow, sortMetadata, submittedFilterMetadata, scrollClean } = this.state; - const { filterMetadata } = this.props - const { variantListSize, loadingResults, loadingVariantListSize } = this.props; - const variantListSizeDisplay = variantListSize ? variantListSize.toLocaleString() : 0; - return - -
-
- this.handleChange(val)} - onClear={() => this.handleChange('')} placeholderText='Search by gene, variant, rs number, or genomic region' /> + const { + searchWord, + filterShow, + sortMetadata, + submittedFilterMetadata, + scrollClean, + } = this.state; + const { filterMetadata } = this.props; + const { variantListSize, loadingResults, loadingVariantListSize } = + this.props; + const variantListSizeDisplay = variantListSize + ? variantListSize.toLocaleString() + : 0; + return ( + + +
+
+ this.handleChange(val)} + onClear={() => this.handleChange("")} + placeholderText="Search by gene, variant, rs number, or genomic region" + /> +
+
+ Examples by query type:

+ Gene: BRCA2

+ Variant: 13-32355250-T-C

+ RS Number: rs169547

+ Genomic Region: chr13:32355000-32375000 +
-
- Examples by query type:

- Gene: BRCA2

- Variant: 13-32355250-T-C

- RS Number: rs169547

- Genomic Region: chr13:32355000-32375000 + {submittedFilterMetadata && ( + this.handleChipChange(changes)} + /> + )} +
+ {!loadingResults && + !loadingVariantListSize && + variantListSize > 0 && + environment.genoFilters ? ( +
this.showFilter()} style={styles.filterBtn}> + Filter & Sort +
+ ) : scrollClean ? ( +
+ ) : ( +
this.showFilter()} style={styles.filterBtn}> + Filter & Sort +
+ )} + + {!loadingResults && !loadingVariantListSize && searchWord ? ( + + {!loadingResults && !loadingVariantListSize ? ( + variantListSizeDisplay + ) : ( + + + + )}{" "} + variants + + ) : scrollClean ? ( +
+ ) : ( + + {variantListSizeDisplay} variants + + )} +
-
- { - submittedFilterMetadata && - this.handleChipChange(changes)} /> - } -
- {((!loadingResults && !loadingVariantListSize) && (variantListSize > 0) && environment.genoFilters) ?
this.showFilter()} - style={styles.filterBtn}> Filter & Sort
: - scrollClean ?
:
this.showFilter()} - style={styles.filterBtn}> Filter & Sort
} - - { - (!loadingResults && !loadingVariantListSize && searchWord) ? {(!loadingResults && !loadingVariantListSize) ? variantListSizeDisplay : - } variants : - scrollClean ?
: {variantListSizeDisplay} variants - } -
-
- {environment.genoFilters &&
- {filterShow && - this.handleFilterSubmit(filteredMetadata, sortMetadata)} - onSortChange={(e) => this.handleSortChange(e)} - />} -
- } - -
; + {environment.genoFilters && ( +
+ {filterShow && ( + this.handleFilterSubmit(filteredMetadata, sortMetadata)} + onSortChange={(e) => this.handleSortChange(e)} + /> + )} +
+ )} + + ); } } diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-sort-item.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-sort-item.component.tsx index 6b7eee5c0..a8f35d9b9 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-sort-item.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-sort-item.component.tsx @@ -1,11 +1,12 @@ import * as React from "react"; +import { faArrowUp } from "@fortawesome/free-solid-svg-icons"; +import { faArrowDown } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; import { SortMetadata } from "publicGenerated/fetch"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; -import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; const styles = reactStyles({ sortItem: { @@ -18,7 +19,7 @@ const styles = reactStyles({ color: "#262262", letterSpacing: 0, lineHeight: "16px", - cursor: "pointer" + cursor: "pointer", }, sortItemClosed: { transform: "rotate(90deg)", @@ -29,7 +30,7 @@ const styles = reactStyles({ sortItemForm: { fontSize: "0.8em", display: "flex", - overflow: 'visible', + overflow: "visible", flexDirection: "column", paddingLeft: "1rem", paddingTop: ".25rem", @@ -45,15 +46,15 @@ const styles = reactStyles({ marginTop: "0.1rem", }, sortItemLabel: { - width: '80%', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow: 'hidden' + width: "80%", + whiteSpace: "nowrap", + textOverflow: "ellipsis", + overflow: "hidden", // wordWrap: "break-word", }, activeSort: { - fontFamily: 'gothamBold' - } + fontFamily: "gothamBold", + }, }); const css = ` @@ -91,13 +92,13 @@ const css = ` `; const lables = { - gene: 'Gene', - consequence: 'Consequence', - variantType: 'Variant Type', - clinicalSignificance: 'ClinVar Significance', - alleleNumber: 'Allele Number', - alleleFrequency: 'Allele Frequency', - alleleCount: 'Allele Count' + gene: "Gene", + consequence: "Consequence", + variantType: "Variant Type", + clinicalSignificance: "ClinVar Significance", + alleleNumber: "Allele Number", + alleleFrequency: "Allele Frequency", + alleleCount: "Allele Count", }; interface Props { @@ -120,51 +121,48 @@ export class VariantSortItemComponent extends React.Component { sortItemOpen: false, sortMetadata: props.sortMetadata, filterCats: [ - { display: 'Gene', field: 'gene' }, - { display: 'Consequence', field: 'consequence' }, - { display: 'Variant Type', field: 'variantType' }, - { display: 'ClinVar Significance', field: 'clinicalSignificance' }, - { display: 'Allele Count', field: 'alleleCount' }, - { display: 'Allele Number', field: 'alleleNumber' }, - { display: 'Allele Frequency', field: 'alleleFrequency' }, - { display: 'Homozygote Count', field: 'homozygoteCount' }, + { display: "Gene", field: "gene" }, + { display: "Consequence", field: "consequence" }, + { display: "Variant Type", field: "variantType" }, + { display: "ClinVar Significance", field: "clinicalSignificance" }, + { display: "Allele Count", field: "alleleCount" }, + { display: "Allele Number", field: "alleleNumber" }, + { display: "Allele Frequency", field: "alleleFrequency" }, + { display: "Homozygote Count", field: "homozygoteCount" }, ], sortCats: [ - { display: 'Variant ID', field: 'variantId'}, - { display: 'Gene', field: 'gene' }, - { display: 'Consequence', field: 'consequence' }, - { display: 'Variant Type', field: 'variantType' }, - { display: 'ClinVar Significance', field: 'clinicalSignificance' }, - { display: 'Allele Count', field: 'alleleCount' }, - { display: 'Allele Number', field: 'alleleNumber' }, - { display: 'Allele Frequency', field: 'alleleFrequency' }, - { display: 'Homozygote Count', field: 'homozygoteCount' }, - ] + { display: "Variant ID", field: "variantId" }, + { display: "Gene", field: "gene" }, + { display: "Consequence", field: "consequence" }, + { display: "Variant Type", field: "variantType" }, + { display: "ClinVar Significance", field: "clinicalSignificance" }, + { display: "Allele Count", field: "alleleCount" }, + { display: "Allele Number", field: "alleleNumber" }, + { display: "Allele Frequency", field: "alleleFrequency" }, + { display: "Homozygote Count", field: "homozygoteCount" }, + ], }; } - componentDidMount(): void { - } + componentDidMount(): void {} sortClick() { this.setState({ sortItemOpen: !this.state.sortItemOpen }); } - - clickToSort(field) { const { sortMetadata } = this.state; if (sortMetadata[field].sortActive) { - if (sortMetadata[field].sortDirection === 'asc') { - sortMetadata[field].sortDirection = 'desc' + if (sortMetadata[field].sortDirection === "asc") { + sortMetadata[field].sortDirection = "desc"; } else { - sortMetadata[field].sortDirection = 'asc' + sortMetadata[field].sortDirection = "asc"; } for (const item in sortMetadata) { if (item !== field) { sortMetadata[item].sortActive = false; - sortMetadata[item].sortDirection = 'desc'; + sortMetadata[item].sortDirection = "desc"; } } } else { @@ -172,7 +170,7 @@ export class VariantSortItemComponent extends React.Component { for (const item in sortMetadata) { if (item !== field) { sortMetadata[item].sortActive = false; - sortMetadata[item].sortDirection = 'asc'; + sortMetadata[item].sortDirection = "asc"; } } } @@ -183,30 +181,77 @@ export class VariantSortItemComponent extends React.Component { const { cleared } = this.props; const { sortItemOpen, sortMetadata, filterCats, sortCats } = this.state; - return - -
this.sortClick()} style={styles.sortItem}> - Sort By -
-
- {(cleared && sortItemOpen) && -
- {sortCats && sortCats.map((cat, index) => { - return
this.clickToSort(cat.field)}> - -
{cat.display} - Click to select
ascending or descending
-
- {sortMetadata[cat.field].sortActive && sortMetadata[cat.field].sortDirection === 'asc' && -
-
- })} -
} -
; + return ( + + +
this.sortClick()} style={styles.sortItem}> + Sort By +
+ +
+
+ {cleared && sortItemOpen && ( +
+ {sortCats && + sortCats.map((cat, index) => { + return ( +
this.clickToSort(cat.field)} + > + +
+ {cat.display} + + Click to select
+ ascending or descending +
+
+ {sortMetadata[cat.field].sortActive && + sortMetadata[cat.field].sortDirection === "asc" && ( +
+
+ ); + })} +
+ )} +
+ ); } -} \ No newline at end of file +} diff --git a/public-ui/src/app/data-browser/views/genomic-view/components/variant-table.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/components/variant-table.component.tsx index 82e388f8e..b13cd5e91 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/components/variant-table.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/components/variant-table.component.tsx @@ -1,14 +1,16 @@ import * as React from "react"; +import { faArrowUp } from "@fortawesome/free-solid-svg-icons"; +import { faArrowDown } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { environment } from "environments/environment"; import { reactStyles } from "app/utils"; import { Spinner } from "app/utils/spinner"; -import { environment } from "environments/environment"; import { Variant } from "publicGenerated"; import { SortMetadata } from "publicGenerated/fetch"; + import { TablePaginatorComponent } from "./table-paginator.component"; import { VariantRowComponent } from "./variant-row.component"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; -import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; const styles = reactStyles({ tableContainer: { @@ -23,7 +25,7 @@ const styles = reactStyles({ height: environment.infiniteSrcoll ? "30rem" : "", }, noScroll: { - overflowX: "scroll" + overflowX: "scroll", }, tableFrame: { border: "1px solid #CCCCCC", @@ -148,7 +150,6 @@ interface State { searchResults: Variant[]; sortMetadata: any; allowParentScroll: Boolean; - } export class VariantTableComponent extends React.Component { @@ -158,7 +159,7 @@ export class VariantTableComponent extends React.Component { loading: props.loadingResults, searchResults: props.searchResults, sortMetadata: props.sortMetadata, - allowParentScroll: true + allowParentScroll: true, }; } scrollAreaRef: React.RefObject = React.createRef(); @@ -182,29 +183,28 @@ export class VariantTableComponent extends React.Component { const { searchResults, loadingResults, filtered } = this.props; // console.log(loadingResults,'loadingResults'); - if (filtered) { this.scrollAreaToTop(); - } if (prevProps.searchResults !== searchResults) { this.setState({ searchResults: searchResults, loading: loadingResults }); } - - - } handleScrollEnd = (event) => { clearTimeout(this.debounceTimer); this.debounceTimer = setTimeout(() => { - const scrollArea = document.querySelector('.scroll-area'); + const scrollArea = document.querySelector(".scroll-area"); if (scrollArea) { const scrollTop = scrollArea.scrollTop; const scrollHeight = scrollArea.scrollHeight; // trigger scroll at 35% - const scrolledToBottom = scrollTop / scrollHeight > .35; - if (scrolledToBottom && this.props.currentPage < this.props.variantListSize / this.props.rowCount) { + const scrolledToBottom = scrollTop / scrollHeight > 0.35; + if ( + scrolledToBottom && + this.props.currentPage < + this.props.variantListSize / this.props.rowCount + ) { // Fetch new data and append this.props.onScrollBottom(); } @@ -212,8 +212,6 @@ export class VariantTableComponent extends React.Component { }, 150); }; - - handlePageClick = (data) => { const { searchTerm } = this.props; this.setState({ loading: true }); @@ -259,134 +257,119 @@ export class VariantTableComponent extends React.Component { setArrowIcon(varName: string) { const { sortMetadata } = this.state; - return sortMetadata[varName].sortDirection === "asc" ? faArrowUp : faArrowDown; + return sortMetadata[varName].sortDirection === "asc" + ? faArrowUp + : faArrowDown; } - - - - render() { - const { loadingVariantListSize, loadingResults, variantListSize, rowCount, currentPage } = - this.props; - const { loading, searchResults, sortMetadata, allowParentScroll } = this.state; - styles.noScroll.overflowX = !allowParentScroll ? "hidden":"scroll"; - + const { + loadingVariantListSize, + loadingResults, + variantListSize, + rowCount, + currentPage, + } = this.props; + const { loading, searchResults, sortMetadata, allowParentScroll } = + this.state; + styles.noScroll.overflowX = !allowParentScroll ? "hidden" : "scroll"; + return ( {!loading && - !loadingVariantListSize && - searchResults && - searchResults.length ? ( -
+ !loadingVariantListSize && + searchResults && + searchResults.length ? ( +
- - Variant ID - + Variant ID {sortMetadata.variantId.sortActive && ( )}
- - Gene - + Gene {sortMetadata.gene.sortActive && ( )}
- - Consequence - + Consequence {sortMetadata.consequence.sortActive && ( )}
- - Variant Type - + Variant Type {sortMetadata.variantType.sortActive && ( )}
- - ClinVar Significance - + ClinVar Significance {sortMetadata.clinicalSignificance.sortActive && ( )}
- - Allele Count - + Allele Count {sortMetadata.alleleCount.sortActive && ( )}
- - Allele Number - + Allele Number {sortMetadata.alleleNumber.sortActive && ( )}
- - Allele Frequency - + Allele Frequency {sortMetadata.alleleFrequency.sortActive && ( )}
- - Homozygote Count - + Homozygote Count {sortMetadata.homozygoteCount.sortActive && ( )} @@ -396,16 +379,24 @@ export class VariantTableComponent extends React.Component { {searchResults && searchResults.map((variant, index) => { return ( - this.setState({allowParentScroll:!this.state.allowParentScroll})} + allowParentScroll={() => + this.setState({ + allowParentScroll: !this.state.allowParentScroll, + }) + } /> ); })} - {environment.infiniteSrcoll &&
- {currentPage < variantListSize / rowCount && loadingResults && } -
} + {environment.infiniteSrcoll && ( +
+ {currentPage < variantListSize / rowCount && loadingResults && ( + + )} +
+ )}
) : (
@@ -416,48 +407,49 @@ export class VariantTableComponent extends React.Component { )} {(!searchResults || (searchResults && searchResults.length === 0)) && ( -
-
- Enter a query in the search bar or get started with an example query: -
-
- Gene:{" "} -
this.searchItem("BRCA2")} - style={styles.helpSearchDiv} - > - BRCA2 -
+
+
+ Enter a query in the search bar or get started with an example + query: +
+
+ Gene:{" "} +
this.searchItem("BRCA2")} + style={styles.helpSearchDiv} + > + BRCA2
-
- Variant:{" "} -
this.searchItem("13-32355250-T-C")} - style={styles.helpSearchDiv} - > - 13-32355250-T-C -
+
+
+ Variant:{" "} +
this.searchItem("13-32355250-T-C")} + style={styles.helpSearchDiv} + > + 13-32355250-T-C
-
- RS Number:{" "} -
this.searchItem("rs169547")} - style={styles.helpSearchDiv} - > - rs169547 -
+
+
+ RS Number:{" "} +
this.searchItem("rs169547")} + style={styles.helpSearchDiv} + > + rs169547
-
- Genomic region:{" "} -
this.searchItem("chr13:32355000-32375000")} - style={styles.helpSearchDiv} - > - chr13:32355000-32375000 -
+
+
+ Genomic region:{" "} +
this.searchItem("chr13:32355000-32375000")} + style={styles.helpSearchDiv} + > + chr13:32355000-32375000
- )} +
+ )}
)} {!loading && @@ -465,7 +457,7 @@ export class VariantTableComponent extends React.Component { searchResults && variantListSize > rowCount && (
- {!environment.infiniteSrcoll && + {!environment.infiniteSrcoll && ( { this.handleRowCountChange(info); }} /> - } + )}
)} ); } -} \ No newline at end of file +} diff --git a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx index 116fad853..7b1593f7d 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx @@ -1,6 +1,7 @@ import * as React from "react"; import _ from "lodash"; +import { SVGenomicSearchComponent } from "../sv-genomic-view/components/sv-genomic-search.component"; import { withRouteData } from "app/components/app-router"; import { GenomicOverviewComponent } from "app/data-browser/views/genomic-view/components/genomic-overview.component"; import { genomicsApi } from "app/services/swagger-fetch-clients"; @@ -8,16 +9,21 @@ import { reactStyles } from "app/utils"; import { triggerEvent } from "app/utils/google_analytics"; import { urlParamsStore } from "app/utils/navigation"; import { - GenomicFilters, SVGenomicFilters, - SearchVariantsRequest, SearchSVVariantsRequest, - Variant, SVVariant, + GenomicFilters, + SearchSVVariantsRequest, + SearchVariantsRequest, + SVGenomicFilters, + SVVariant, + Variant, } from "publicGenerated"; - -import { SortColumnDetails, SortMetadata, SortSVMetadata } from "publicGenerated/fetch"; +import { + SortColumnDetails, + SortMetadata, + SortSVMetadata, +} from "publicGenerated/fetch"; import { GenomicFaqComponent } from "./components/genomic-faq.component"; import { GenomicSearchComponent } from "./components/genomic-search.component"; -import { SVGenomicSearchComponent } from "../sv-genomic-view/components/sv-genomic-search.component"; const styles = reactStyles({ title: { @@ -48,14 +54,14 @@ const styles = reactStyles({ display: "flex", alignItems: "center", width: "100%", - whiteSpace: "nowrap" + whiteSpace: "nowrap", }, topBarItem: { fontSize: "1em", width: "100%", cursor: "pointer", padding: "1em 2em", - borderBottom: "3px solid #216fb4" + borderBottom: "3px solid #216fb4", }, topBarItemText: { width: "75%", @@ -74,7 +80,7 @@ const styles = reactStyles({ color: "#302C71", margin: "0", fontSize: ".8em", - background:"white" + background: "white", }, headingLayout: { display: "flex", @@ -92,9 +98,9 @@ const styles = reactStyles({ innerContainer: { background: "white", padding: "1em", - position:"relative", - marginTop:"-2px", - zIndex:5 + position: "relative", + marginTop: "-2px", + zIndex: 5, }, faqHeading: { fontSize: "0.8em", @@ -260,7 +266,7 @@ export const GenomicViewComponent = withRouteData( new SortColumnDetailsClass(false, "asc", 6), new SortColumnDetailsClass(false, "asc", 7), new SortColumnDetailsClass(false, "asc", 8), - new SortColumnDetailsClass(false, "asc", 9), + new SortColumnDetailsClass(false, "asc", 9) ), svSortMetadata: new SortSVMetadataClass( new SortColumnDetailsClass(true, "asc", 1), @@ -271,7 +277,7 @@ export const GenomicViewComponent = withRouteData( new SortColumnDetailsClass(false, "asc", 6), new SortColumnDetailsClass(false, "asc", 7), new SortColumnDetailsClass(false, "asc", 8), - new SortColumnDetailsClass(false, "asc", 9), + new SortColumnDetailsClass(false, "asc", 9) ), }; } @@ -288,7 +294,7 @@ export const GenomicViewComponent = withRouteData( { id: 4, label: "SV Variants", - } + }, ]; title = "SNV/Indel Variants"; @@ -299,7 +305,7 @@ export const GenomicViewComponent = withRouteData( }, 1000); svSearch = _.debounce((svSearchTerm: string) => { - this.getSVVariantSearch(svSearchTerm); + this.getSVVariantSearch(svSearchTerm); }, 1000); clearSortMetadata() { @@ -308,8 +314,8 @@ export const GenomicViewComponent = withRouteData( sortMetadata[smKey].sortActive = false; sortMetadata[smKey].sortDirection = "asc"; } - sortMetadata['variantId'].sortActive = true; - sortMetadata['variantId'].sortDirection = "asc"; + sortMetadata.variantId.sortActive = true; + sortMetadata.variantId.sortDirection = "asc"; this.setState({ sortMetadata: sortMetadata }); } @@ -328,18 +334,19 @@ export const GenomicViewComponent = withRouteData( } const variantSizeRequest = { query: searchTerm, - filterMetadata: this.state.filterMetadata + filterMetadata: this.state.filterMetadata, }; - genomicsApi().getVariantSearchResultSize(variantSizeRequest).then( - result => { + genomicsApi() + .getVariantSearchResultSize(variantSizeRequest) + .then((result) => { this.setState({ - variantListSize: searchTerm !== '' ? result : 0, - loadingVariantListSize: false + variantListSize: searchTerm !== "" ? result : 0, + loadingVariantListSize: false, }); - } - ).catch(e => { - console.log(e, 'error'); - }); + }) + .catch((e) => { + console.log(e, "error"); + }); } getSVSearchSize(searchTerm: string, filtered: boolean) { @@ -348,45 +355,68 @@ export const GenomicViewComponent = withRouteData( } const variantSizeRequest = { query: searchTerm, - filterMetadata: this.state.svFilterMetadata + filterMetadata: this.state.svFilterMetadata, }; - genomicsApi().getSVVariantSearchResultSize(variantSizeRequest).then( - result => { + genomicsApi() + .getSVVariantSearchResultSize(variantSizeRequest) + .then((result) => { this.setState({ - variantListSize: searchTerm !== '' ? result : 0, - loadingVariantListSize: false + variantListSize: searchTerm !== "" ? result : 0, + loadingVariantListSize: false, }); - } - ).catch(e => { - console.log(e, 'error'); - }); + }) + .catch((e) => { + console.log(e, "error"); + }); } getFilterMetadata(searchTerm: string) { - genomicsApi().getGenomicFilterOptions(searchTerm).then( - result => { - result.gene.items.forEach(el => { el.checked = false; }); - result.consequence.items.forEach(el => { el.checked = false; }); - result.clinicalSignificance.items.forEach(el => { el.checked = false; }); - this.setState({ filterMetadata: result, submittedFilterMetadata: { ...result } }); - localStorage.setItem("originalFilterMetadata", JSON.stringify(result)); - } - ).catch(e => { - console.log(e, 'error'); - }); + genomicsApi() + .getGenomicFilterOptions(searchTerm) + .then((result) => { + result.gene.items.forEach((el) => { + el.checked = false; + }); + result.consequence.items.forEach((el) => { + el.checked = false; + }); + result.clinicalSignificance.items.forEach((el) => { + el.checked = false; + }); + this.setState({ + filterMetadata: result, + submittedFilterMetadata: { ...result }, + }); + localStorage.setItem( + "originalFilterMetadata", + JSON.stringify(result) + ); + }) + .catch((e) => { + console.log(e, "error"); + }); } getSVFilterMetadata(searchTerm: string) { - genomicsApi().getSVGenomicFilterOptions(searchTerm).then( - result => { - result.gene.items.forEach(el => { el.checked = false; }); - this.setState({ svFilterMetadata: result, submittedSVFilterMetadata: { ...result } }); - localStorage.setItem("svOriginalFilterMetadata", JSON.stringify(result)); - } - ).catch(e => { - console.log(e, 'error'); - }); + genomicsApi() + .getSVGenomicFilterOptions(searchTerm) + .then((result) => { + result.gene.items.forEach((el) => { + el.checked = false; + }); + this.setState({ + svFilterMetadata: result, + submittedSVFilterMetadata: { ...result }, + }); + localStorage.setItem( + "svOriginalFilterMetadata", + JSON.stringify(result) + ); + }) + .catch((e) => { + console.log(e, "error"); + }); } getVariantSearch(searchTerm: string) { @@ -518,7 +548,13 @@ export const GenomicViewComponent = withRouteData( } fetchVariantData() { - const { searchTerm, currentPage, sortMetadata, rowCount, filterMetadata } = this.state; + const { + searchTerm, + currentPage, + sortMetadata, + rowCount, + filterMetadata, + } = this.state; const searchRequest: SearchVariantsRequest = { query: searchTerm, @@ -539,13 +575,19 @@ export const GenomicViewComponent = withRouteData( } fetchSVVariantData() { - const { svSearchTerm, currentPage, svSortMetadata, rowCount, svFilterMetadata } = this.state; + const { + svSearchTerm, + currentPage, + svSortMetadata, + rowCount, + svFilterMetadata, + } = this.state; const searchRequest: SearchSVVariantsRequest = { query: svSearchTerm, pageNumber: currentPage, rowCount: rowCount, sortMetadata: svSortMetadata, - filterMetadata: svFilterMetadata + filterMetadata: svFilterMetadata, }; genomicsApi() @@ -558,7 +600,10 @@ export const GenomicViewComponent = withRouteData( }); } - filterGenomics(filteredMetadata: GenomicFilters, sortMetadata: SortMetadata) { + filterGenomics( + filteredMetadata: GenomicFilters, + sortMetadata: SortMetadata + ) { const { searchTerm, rowCount } = this.state; const searchRequest = { query: searchTerm, @@ -571,11 +616,17 @@ export const GenomicViewComponent = withRouteData( genomicsApi() .searchVariants(searchRequest) .then((results) => { - this.setState({ searchResults: results.items,loadingResults:false }); + this.setState({ + searchResults: results.items, + loadingResults: false, + }); }); } - filterSVGenomics(filteredMetadata: SVGenomicFilters, sortMetadata: SortSVMetadata) { + filterSVGenomics( + filteredMetadata: SVGenomicFilters, + sortMetadata: SortSVMetadata + ) { const { svSearchTerm, rowCount } = this.state; const searchRequest = { query: svSearchTerm, @@ -588,9 +639,12 @@ export const GenomicViewComponent = withRouteData( genomicsApi() .searchSVVariants(searchRequest) .then((results) => { - console.log('Am i here with results?'); + console.log("Am i here with results?"); console.log(results); - this.setState({ searchSVResults: results.items,loadingResults:false }); + this.setState({ + searchSVResults: results.items, + loadingResults: false, + }); }); } @@ -606,13 +660,29 @@ export const GenomicViewComponent = withRouteData( handleSearchTerm(searchTerm: string) { if (this.state.searchTerm !== searchTerm) { - this.setState({ filterMetadata: null, searchTerm: searchTerm, loadingResults: true, loadingVariantListSize: true }, () => this.search(searchTerm)); + this.setState( + { + filterMetadata: null, + searchTerm: searchTerm, + loadingResults: true, + loadingVariantListSize: true, + }, + () => this.search(searchTerm) + ); } } handleSVSearchTerm(searchTerm: string) { if (this.state.svSearchTerm !== searchTerm) { - this.setState({ svFilterMetadata: null, svSearchTerm: searchTerm, loadingResults: true, loadingVariantListSize: true }, () => this.svSearch(searchTerm)); + this.setState( + { + svFilterMetadata: null, + svSearchTerm: searchTerm, + loadingResults: true, + loadingVariantListSize: true, + }, + () => this.svSearch(searchTerm) + ); } } @@ -628,10 +698,15 @@ export const GenomicViewComponent = withRouteData( this.getGenomicChartData(); } - handleFilterSubmit(filteredMetadata: GenomicFilters, sortMetadata: SortMetadata) { - if (filteredMetadata['alleleFrequency']['checked']) { - filteredMetadata['alleleFrequency']['maxFreq'] = filteredMetadata['alleleFrequency']['max']; - filteredMetadata['alleleFrequency']['minFreq'] = filteredMetadata['alleleFrequency']['min']; + handleFilterSubmit( + filteredMetadata: GenomicFilters, + sortMetadata: SortMetadata + ) { + if (filteredMetadata.alleleFrequency.checked) { + filteredMetadata.alleleFrequency.maxFreq = + filteredMetadata.alleleFrequency.max; + filteredMetadata.alleleFrequency.minFreq = + filteredMetadata.alleleFrequency.min; } this.setState({ submittedFilterMetadata: { ...filteredMetadata } }); @@ -639,22 +714,35 @@ export const GenomicViewComponent = withRouteData( this.getSearchSize(this.state.searchTerm, true); } - handleSVFilterSubmit(filteredMetadata: SVGenomicFilters, sortMetadata: SortSVMetadata) { - - if (filteredMetadata['alleleFrequency']['checked']) { - filteredMetadata['alleleFrequency']['maxFreq'] = filteredMetadata['alleleFrequency']['max']; - filteredMetadata['alleleFrequency']['minFreq'] = filteredMetadata['alleleFrequency']['min']; + handleSVFilterSubmit( + filteredMetadata: SVGenomicFilters, + sortMetadata: SortSVMetadata + ) { + if (filteredMetadata.alleleFrequency.checked) { + filteredMetadata.alleleFrequency.maxFreq = + filteredMetadata.alleleFrequency.max; + filteredMetadata.alleleFrequency.minFreq = + filteredMetadata.alleleFrequency.min; } this.setState({ submittedSVFilterMetadata: { ...filteredMetadata } }); this.filterSVGenomics(filteredMetadata, sortMetadata); this.getSVSearchSize(this.state.svSearchTerm, true); - } handleScrollBottom() { - this.setState({ currentPage: this.state.currentPage + 1, loadingResults: true, scrollClean: false }) - const { searchTerm, currentPage, sortMetadata, rowCount, filterMetadata } = this.state; + this.setState({ + currentPage: this.state.currentPage + 1, + loadingResults: true, + scrollClean: false, + }); + const { + searchTerm, + currentPage, + sortMetadata, + rowCount, + filterMetadata, + } = this.state; const searchRequest: SearchVariantsRequest = { query: searchTerm, pageNumber: currentPage, @@ -674,8 +762,18 @@ export const GenomicViewComponent = withRouteData( } handleSVScrollBottom() { - this.setState({ currentPage: this.state.currentPage + 1, loadingResults: true, scrollClean: false }) - const { svSearchTerm, currentPage, svSortMetadata, rowCount, svFilterMetadata } = this.state; + this.setState({ + currentPage: this.state.currentPage + 1, + loadingResults: true, + scrollClean: false, + }); + const { + svSearchTerm, + currentPage, + svSortMetadata, + rowCount, + svFilterMetadata, + } = this.state; const searchRequest: SearchVariantsRequest = { query: svSearchTerm, pageNumber: currentPage, @@ -695,137 +793,169 @@ export const GenomicViewComponent = withRouteData( } render() { - const { currentPage, selectionId, loadingVariantListSize, variantListSize, loadingResults, searchResults, searchSVResults, - participantCount, chartData, rowCount, searchTerm, filterMetadata, svFilterMetadata, sortMetadata, svSortMetadata, - submittedFilterMetadata, submittedSVFilterMetadata, - scrollClean } = this.state; - return - -
-
-

{this.title}

-
-
-
- {this.topBarItems.map((item, index) => { - return
-
this.topBarClick(item.id)} - style={{ ...styles.topBarItem, ...selectionId === item.id ? { ...styles.topBarItemSelected,borderBottom:"none" }:{borderBottom:"3px solid #216fb4"} }}> - - {item.label} - -
-
; - }) - } -
-
- -   - + const { + currentPage, + selectionId, + loadingVariantListSize, + variantListSize, + loadingResults, + searchResults, + searchSVResults, + participantCount, + chartData, + rowCount, + searchTerm, + filterMetadata, + svFilterMetadata, + sortMetadata, + svSortMetadata, + submittedFilterMetadata, + submittedSVFilterMetadata, + scrollClean, + } = this.state; + return ( + + +
+
+

{this.title}

+
+
+
+ {this.topBarItems.map((item, index) => { + return ( +
+
this.topBarClick(item.id)} + style={{ + ...styles.topBarItem, + ...(selectionId === item.id + ? { + ...styles.topBarItemSelected, + borderBottom: "none", + } + : { borderBottom: "3px solid #216fb4" }), + }} + > + {item.label} +
+
+ ); + })} +
+
+  
+
-
- {selectionId === 1 && ( -
-

View the self-reported categories, sex assigned at birth, and - age of participants whose genomic data are available within the - Researcher Workbench.{" "}

-
- )} -
{selectionId === 1 && ( - - )} - {(selectionId === 2) && ( - { - this.handleSearchTerm(searchWord); - this.setState({ searchTerm: searchWord }); - }} - onPageChange={(info) => { - this.handlePageChange(info); - }} - onRowCountChange={(info) => { - this.handleRowCountChange(info); - }} - onSortClick={(sortMetadata) => { - this.handleSortClick(sortMetadata); - }} - onFilterSubmit={(filteredMetadata: GenomicFilters, sortMetadata: SortMetadata) => { - this.handleFilterSubmit(filteredMetadata, sortMetadata); - }} - onScrollBottom={() => this.handleScrollBottom()} - currentPage={currentPage} - rowCount={rowCount} - variantListSize={variantListSize} - loadingVariantListSize={loadingVariantListSize} - loadingResults={loadingResults} - searchResults={searchResults} - participantCount={participantCount} - searchTerm={searchTerm} - filterMetadata={filterMetadata} - submittedFilterMetadata={submittedFilterMetadata} - sortMetadata={sortMetadata} - scrollClean={scrollClean} - /> - )} - {(selectionId === 4) && ( - { - this.handleSVSearchTerm(svSearchWord); - this.setState({ svSearchTerm: svSearchWord }); - }} - onPageChange={(info) => { - this.handleSVPageChange(info); - }} - onRowCountChange={(info) => { - this.handleSVRowCountChange(info); - }} - onSortClick={(sortSVMetadata) => { - this.handleSVSortClick(sortSVMetadata); - }} - onFilterSubmit={(filteredMetadata: GenomicFilters, svSortMetadata: SortSVMetadata) => { - this.handleSVFilterSubmit(filteredMetadata, svSortMetadata); - }} - onScrollBottom={() => this.handleSVScrollBottom()} - currentPage={currentPage} - rowCount={rowCount} - variantListSize={variantListSize} - loadingVariantListSize={loadingVariantListSize} - loadingResults={loadingResults} - svResults={searchSVResults} - participantCount={participantCount} - searchTerm={searchTerm} - filterMetadata={svFilterMetadata} - submittedFilterMetadata={submittedSVFilterMetadata} - sortMetadata={svSortMetadata} - scrollClean={scrollClean} - /> - )} - - {selectionId === 3 && ( - this.handleFaqClose()} /> +
+

+ View the self-reported categories, sex assigned at birth, and + age of participants whose genomic data are available within + the Researcher Workbench.{" "} +

+
)} -
-
- Questions about genetic ancestry? - this.topBarClick(3)} - > - Learn More - +
+ {selectionId === 1 && ( + + )} + {selectionId === 2 && ( + { + this.handleSearchTerm(searchWord); + this.setState({ searchTerm: searchWord }); + }} + onPageChange={(info) => { + this.handlePageChange(info); + }} + onRowCountChange={(info) => { + this.handleRowCountChange(info); + }} + onSortClick={(sortMetadata) => { + this.handleSortClick(sortMetadata); + }} + onFilterSubmit={( + filteredMetadata: GenomicFilters, + sortMetadata: SortMetadata + ) => { + this.handleFilterSubmit(filteredMetadata, sortMetadata); + }} + onScrollBottom={() => this.handleScrollBottom()} + currentPage={currentPage} + rowCount={rowCount} + variantListSize={variantListSize} + loadingVariantListSize={loadingVariantListSize} + loadingResults={loadingResults} + searchResults={searchResults} + participantCount={participantCount} + searchTerm={searchTerm} + filterMetadata={filterMetadata} + submittedFilterMetadata={submittedFilterMetadata} + sortMetadata={sortMetadata} + scrollClean={scrollClean} + /> + )} + {selectionId === 4 && ( + { + this.handleSVSearchTerm(svSearchWord); + this.setState({ svSearchTerm: svSearchWord }); + }} + onPageChange={(info) => { + this.handleSVPageChange(info); + }} + onRowCountChange={(info) => { + this.handleSVRowCountChange(info); + }} + onSortClick={(sortSVMetadata) => { + this.handleSVSortClick(sortSVMetadata); + }} + onFilterSubmit={( + filteredMetadata: GenomicFilters, + svSortMetadata: SortSVMetadata + ) => { + this.handleSVFilterSubmit(filteredMetadata, svSortMetadata); + }} + onScrollBottom={() => this.handleSVScrollBottom()} + currentPage={currentPage} + rowCount={rowCount} + variantListSize={variantListSize} + loadingVariantListSize={loadingVariantListSize} + loadingResults={loadingResults} + svResults={searchSVResults} + participantCount={participantCount} + searchTerm={searchTerm} + filterMetadata={svFilterMetadata} + submittedFilterMetadata={submittedSVFilterMetadata} + sortMetadata={svSortMetadata} + scrollClean={scrollClean} + /> + )} + + {selectionId === 3 && ( + this.handleFaqClose()} /> + )} +
+
+ Questions about genetic ancestry? + this.topBarClick(3)} + > + Learn More + +
-
- + + ); } } ); diff --git a/public-ui/src/app/data-browser/views/pm/pm-react.component.tsx b/public-ui/src/app/data-browser/views/pm/pm-react.component.tsx index aea6c87ec..46e782d33 100644 --- a/public-ui/src/app/data-browser/views/pm/pm-react.component.tsx +++ b/public-ui/src/app/data-browser/views/pm/pm-react.component.tsx @@ -29,16 +29,16 @@ const styles = reactStyles({ btnLink: { fontSize: "14px", color: "#0077b7", - textAlign: 'left', - textTransform:'capitalize', - fontFamily:'GothamBook, Arial, sans-serif', - padding: '.5rem', - whiteSpace:'nowrap', - cursor: 'pointer', - margin:0 + textAlign: "left", + textTransform: "capitalize", + fontFamily: "GothamBook, Arial, sans-serif", + padding: ".5rem", + whiteSpace: "nowrap", + cursor: "pointer", + margin: 0, }, btnList: { - width:'14rem', + width: "14rem", }, bsTitle: { paddingTop: "1em", @@ -320,23 +320,27 @@ export const PMReactComponent = withRouteData( } } if (search) { - let filteredConcept; if (isNumeric(search)) { - const filteredGroup = pmGroups.filter(a => a.concepts.some(t => t.conceptId.includes(search))); - if (filteredGroup && filteredGroup.length > 0) { - filteredConcept = filteredGroup[0]; - } + const filteredGroup = pmGroups.filter((a) => + a.concepts.some((t) => t.conceptId.includes(search)) + ); + if (filteredGroup && filteredGroup.length > 0) { + filteredConcept = filteredGroup[0]; + } } else { - const filteredGroup = pmGroups.filter((conceptgroup) => - conceptgroup.groupName.toLowerCase().includes(search.toLowerCase())); - if (filteredGroup && filteredGroup.length > 0) { - filteredConcept = filteredGroup[0]; - } + const filteredGroup = pmGroups.filter((conceptgroup) => + conceptgroup.groupName + .toLowerCase() + .includes(search.toLowerCase()) + ); + if (filteredGroup && filteredGroup.length > 0) { + filteredConcept = filteredGroup[0]; + } } - this.setState({selectedGroup: filteredConcept}); + this.setState({ selectedGroup: filteredConcept }); } else { this.setState({ selectedGroup: pmGroups[0] }); } @@ -508,201 +512,221 @@ export const PMReactComponent = withRouteData(

Browse Program Physical Measurements

- {loading ? ( ) : - - {selectedGroup ? -
- -
-
-
-
- {selectedGroup.groupName} -
- {selectedConcept && - selectedConcept.analyses && - selectedConcept.analyses - .measurementValueGenderAnalysis ? ( -
- Sex Assigned At Birth - -
- ) : null} - {selectedGroup && - selectedGroup.concepts && - selectedGroup.concepts.length > 1 ? ( -
- {selectedGroup.concepts.map((concept, index) => { - const btnClass = - selectedConcept === concept - ? "btn btn-link concept-button active" - : "btn-link btn concept-button"; - return ( - - ); - })} -
- ) : null} -
- {unitNames && unitNames.length > 1 ? ( -
- {unitNames.map((unit, index) => { - const btnClass = - selectedConceptUnit === unit - ? "btn btn-link unit-button active" - : "btn btn-link unit-button"; - return ( - - ); - })} -
- ) : null} - {selectedConcept && - (selectedConcept.conceptId === "903111" || - selectedConcept.conceptId === "903120") ? ( - selectedConcept.analyses.countAnalysis.results[0] - .countValue > 20 ? ( -
- Total Participant count:{" "} - { - selectedConcept.analyses.countAnalysis.results[0] - .countValue - } -
- ) : ( -
- Total Participant count: ≤{" "} - { - selectedConcept.analyses.countAnalysis.results[0] - .countValue - } -
- ) - ) : null} -
- {selectedConcept && - selectedConcept.analyses && - selectedConcept.analyses - .measurementGenderCountAnalysis ? ( - selectedConcept.conceptId !== "903111" && - selectedConcept.conceptId !== "903120" && - selectedConceptUnit ? ( - - {this.getCountAnalysis().map((gender, index) => { - const chartKey = gender.stratum3 + "-" + index; - return ( -
- -
- ); - })} -
- ) : selectedConcept.analyses - .measurementValueGenderAnalysis ? ( -
- -
- ) : null - ) : null} - {selectedConcept.analyses && - selectedConcept.analyses.ageAnalysis ? ( -
-
- Age When Physical Measurement Was Taken - -
- -
- ) : null} -
-
-
-
- :
0 Results
} - -
} + {loading ? ( + + ) : ( + + {selectedGroup ? ( +
+ +
+
+
+
+ {selectedGroup.groupName} +
+ {selectedConcept && + selectedConcept.analyses && + selectedConcept.analyses + .measurementValueGenderAnalysis ? ( +
+ Sex Assigned At Birth + +
+ ) : null} + {selectedGroup && + selectedGroup.concepts && + selectedGroup.concepts.length > 1 ? ( +
+ {selectedGroup.concepts.map((concept, index) => { + const btnClass = + selectedConcept === concept + ? "btn btn-link concept-button active" + : "btn-link btn concept-button"; + return ( + + ); + })} +
+ ) : null} +
+ {unitNames && unitNames.length > 1 ? ( +
+ {unitNames.map((unit, index) => { + const btnClass = + selectedConceptUnit === unit + ? "btn btn-link unit-button active" + : "btn btn-link unit-button"; + return ( + + ); + })} +
+ ) : null} + {selectedConcept && + (selectedConcept.conceptId === "903111" || + selectedConcept.conceptId === "903120") ? ( + selectedConcept.analyses.countAnalysis.results[0] + .countValue > 20 ? ( +
+ Total Participant count:{" "} + { + selectedConcept.analyses.countAnalysis + .results[0].countValue + } +
+ ) : ( +
+ Total Participant count: ≤{" "} + { + selectedConcept.analyses.countAnalysis + .results[0].countValue + } +
+ ) + ) : null} +
+ {selectedConcept && + selectedConcept.analyses && + selectedConcept.analyses + .measurementGenderCountAnalysis ? ( + selectedConcept.conceptId !== "903111" && + selectedConcept.conceptId !== "903120" && + selectedConceptUnit ? ( + + {this.getCountAnalysis().map( + (gender, index) => { + const chartKey = + gender.stratum3 + "-" + index; + return ( +
+ +
+ ); + } + )} +
+ ) : selectedConcept.analyses + .measurementValueGenderAnalysis ? ( +
+ +
+ ) : null + ) : null} + {selectedConcept.analyses && + selectedConcept.analyses.ageAnalysis ? ( +
+
+ Age When Physical Measurement Was Taken + +
+ +
+ ) : null} +
+
+
+
+ ) : ( +
0 Results
+ )} +
+ )}
); diff --git a/public-ui/src/app/data-browser/views/quick-search/genomic-call-to-action-react.component.tsx b/public-ui/src/app/data-browser/views/quick-search/genomic-call-to-action-react.component.tsx index 58a08ff3c..382affc11 100644 --- a/public-ui/src/app/data-browser/views/quick-search/genomic-call-to-action-react.component.tsx +++ b/public-ui/src/app/data-browser/views/quick-search/genomic-call-to-action-react.component.tsx @@ -1,75 +1,87 @@ import * as React from "react"; + import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; const ctaCss = ` .cta-stat-layout { display:grid; -}` +}`; interface Props { wgsSRParticipantCount: number; wgsLRParticipantCount: number; wgsSVParticipantCount: number; microarrayParticipantCount: number; - } -interface State { - -} - - -export class GenomicCallToActionComponent extends React.Component { +interface State {} +export class GenomicCallToActionComponent extends React.Component< + Props, + State +> { render() { - const { wgsSRParticipantCount, + const { + wgsSRParticipantCount, wgsLRParticipantCount, wgsSVParticipantCount, - microarrayParticipantCount } = this.props; - return - -
window.open("https://www.researchallofus.org/register/?utm_source=intra&utm_medium=DataBr&utm_campaign=gen_card")} className="result-box"> -
- - Genomic data only in - Researcher Workbench - - -
- + microarrayParticipantCount, + } = this.props; + return ( + + +
+ window.open( + "https://www.researchallofus.org/register/?utm_source=intra&utm_medium=DataBr&utm_campaign=gen_card" + ) + } + className="result-box" + > +
+ + Genomic data only in Researcher Workbench + +
+ +
-
-
- {wgsLRParticipantCount > 0 && - - - {wgsLRParticipantCount.toLocaleString()}{" "} - participants in the

Long-Read WGS dataset +
+ {wgsLRParticipantCount > 0 && ( + + + {wgsLRParticipantCount.toLocaleString()}{" "} + participants in the

Long-Read WGS dataset +
- } - {wgsSVParticipantCount > 0 && + )} + {wgsSVParticipantCount > 0 && ( + + + {wgsSVParticipantCount.toLocaleString()}{" "} + participants in the

Short-Read WGS Structural

{" "} + Variants dataset +
+
+ )} - {wgsSVParticipantCount.toLocaleString()}{" "} - participants in the

Short-Read WGS Structural

Variants dataset + {microarrayParticipantCount.toLocaleString()}{" "} + participants in the

Genotyping Arrays dataset
-
} - - - {microarrayParticipantCount.toLocaleString()}{" "} - participants in the

Genotyping Arrays dataset
-
-
- +
-
- - + + ); } } diff --git a/public-ui/src/app/data-browser/views/quick-search/home-view-react.component.tsx b/public-ui/src/app/data-browser/views/quick-search/home-view-react.component.tsx index 339f6ff7d..6b18198df 100644 --- a/public-ui/src/app/data-browser/views/quick-search/home-view-react.component.tsx +++ b/public-ui/src/app/data-browser/views/quick-search/home-view-react.component.tsx @@ -1,6 +1,7 @@ -import _ from "lodash"; import * as React from "react"; +import _ from "lodash"; +import { environment } from "environments/environment"; import { withRouteData } from "app/components/app-router"; import { CdrVersionReactComponent } from "app/data-browser/cdr-version/cdr-version-info"; import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; @@ -17,7 +18,7 @@ import { globalStyles } from "app/utils/global-styles"; import { triggerEvent } from "app/utils/google_analytics"; import { NavStore } from "app/utils/navigation"; import { Spinner } from "app/utils/spinner"; -import { environment } from "environments/environment"; + import { GenomicCallToActionComponent } from "./genomic-call-to-action-react.component"; export const homeCss = ` @@ -277,7 +278,6 @@ const styles = reactStyles({ padding: "16px", }, - resultBoxTitle: { color: "#337ab7", fontFamily: "GothamBook,Arial, sans-serif", @@ -289,7 +289,7 @@ const styles = reactStyles({ flexDirection: "row", height: "2rem", justifyContent: "space-between", - alighItems: "center" + alighItems: "center", }, resultBody: { color: "#302c71", @@ -357,7 +357,7 @@ const styles = reactStyles({ lineHeight: "2", fontFamily: "GothamBook, Arial, sans-serif", fontSize: "20px", - textAlign: "center" + textAlign: "center", }, dbSubDesc: { padding: "2rem", @@ -437,16 +437,16 @@ export const ResultLinksComponent = class extends React.Component this.resultClick(this.props)} className="result-box"> -
+
{name}
{(domainType === "ehr" || domainType === "pmw") && ( - + {standardConceptCount.toLocaleString()} )} {domainType === "survey" && ( - + {questionCount.toLocaleString()} @@ -520,45 +520,56 @@ export const ResultLinksComponent = class extends React.Component - {wgsSRParticipantCount.toLocaleString()} -
Participants in Short-Read

+
+ {" "} + Participants in Short-Read

Whole Genome Sequencing

(WGS) dataset
- + {variantListSize.toLocaleString()} - SNV/Indel Variants + + SNV/Indel Variants + - - - )} {searchWord && domainType === "ehr" && ( - matching medical concepts + + matching medical concepts + )} {searchWord && domainType === "survey" && ( - matching survey questions + + matching survey questions + )} {searchWord && name.toLowerCase() === "physical measurements" && ( - matching Physical Measurements + + matching Physical Measurements + )} {searchWord && name.toLowerCase() === "fitbit" && ( - matching Fitbit Measurements + + matching Fitbit Measurements + )} {!searchWord && domainType === "ehr" && ( @@ -573,7 +584,9 @@ export const ResultLinksComponent = class extends React.Component - Physical Measurements + + Physical Measurements + )} {!searchWord && name.toLowerCase() === "fitbit" && ( @@ -583,34 +596,38 @@ export const ResultLinksComponent = class extends React.Component - {participantCount.toLocaleString()}{" "} - participants + {participantCount.toLocaleString()} participants )} - {domainType === "genomics" && searchWord && !loadingVariantListSize && + {domainType === "genomics" && + searchWord && + !loadingVariantListSize && variantListSize > 0 && ( - {wgsSRParticipantCount.toLocaleString()} -
Participants in Short-Read

+
+ {" "} + Participants in Short-Read

Whole Genome Sequencing

(WGS) dataset
- + {variantListSize.toLocaleString()} - SNV/Indel Variants + + SNV/Indel Variants + - - - )} {name.toLowerCase() === "physical measurements" && ( @@ -670,7 +687,7 @@ export const dBHomeComponent = withRouteData( popUp: false, loading: true, winWidth: window.innerWidth, - winHeight: window.innerHeight + winHeight: window.innerHeight, }; } @@ -678,11 +695,13 @@ export const dBHomeComponent = withRouteData( this.typing = true; this.getDomainInfos(); this.getVariantResultSize(); - }, 1000) + }, 1000); handleChange(val) { this.setState({ loading: true }); - this.setState({ searchWord: val }, () => { localStorage.setItem("searchText", val); }); + this.setState({ searchWord: val }, () => { + localStorage.setItem("searchText", val); + }); this.search(val); this.typing = false; } @@ -697,10 +716,10 @@ export const dBHomeComponent = withRouteData( this.getDomainInfos(); this.getVariantResultSize(); this.getGenomicParticipantCounts(); - window.addEventListener('resize', this.handleResize); + window.addEventListener("resize", this.handleResize); } componentWillUnmount() { - window.removeEventListener('resize', this.handleResize); + window.removeEventListener("resize", this.handleResize); } getGenomicParticipantCounts() { @@ -742,7 +761,7 @@ export const dBHomeComponent = withRouteData( .then((result) => { this.setState({ variantListSize: result, - loadingVariantListSize: false + loadingVariantListSize: false, }); }) .catch((e) => { @@ -806,9 +825,9 @@ export const dBHomeComponent = withRouteData( handleResize = () => { this.setState({ winWidth: window.innerWidth, - winHeight: window.innerHeight + winHeight: window.innerHeight, }); - } + }; render() { const { @@ -822,7 +841,7 @@ export const dBHomeComponent = withRouteData( variantListSize, loadingVariantListSize, winHeight, - winWidth + winWidth, } = this.state; const noResults = domainInfo.length === 0 && @@ -840,13 +859,27 @@ export const dBHomeComponent = withRouteData( Data Browser

- Browse aggregate-level data contributed by All of Us research participants. - Data are derived from multiple - data sources. - To protect participant privacy, we have removed personal identifiers, - rounded aggregate data to counts of 20, and only included summary demographic information. - Individual-level data are available for analysis in the - Researcher Workbench. + Browse aggregate-level data contributed by All of Us + research participants. Data are derived from multiple + + data sources + + . To protect participant privacy, we have removed personal + identifiers, rounded aggregate data to counts of 20, and only + included summary demographic information. Individual-level data are + available for analysis in the + + Researcher Workbench + + .

@@ -862,118 +895,156 @@ export const dBHomeComponent = withRouteData(
- {(loading || loadingVariantListSize) ?
: ( + {loading || loadingVariantListSize ? ( +
+ +
+ ) : (
- {(noConceptData && variantListSize === 0) && } - {true &&
0 && searchWord ? 'has-pm' : ''} `}> - {domainInfo.length > 0 && -
- EHR Domains -
} - {domainInfo.length > 0 && ( - -
- {domainInfo.map((domain, index) => { - const key = "domain" + index; - return ( - - ); - })} -
-
- )} -
- {environment.geno && - genomicInfo && - !loadingVariantListSize && - variantListSize > 0 && ( + {noConceptData && variantListSize === 0 && ( + + )} + {true && ( +
0 && searchWord + ? "has-pm" + : "" + } `} + > + {domainInfo.length > 0 && ( +
+ + EHR Domains + +
+ )} + {domainInfo.length > 0 && ( + +
+ {domainInfo.map((domain, index) => { + const key = "domain" + index; + return ( + + ); + })} +
+
+ )} +
+ {environment.geno && + genomicInfo && + !loadingVariantListSize && + variantListSize > 0 && ( +
+
+ + Genomics + +
+
+ + +
+
+ )} + {physicalMeasurementsInfo.length > 0 && (
- Genomics + + Measurements and Wearables +
-
- - - +
+ {physicalMeasurementsInfo.map( + (phyMeasurements, index) => { + const key = "phyMeasurements" + index; + return ( + + ); + } + )}
)} - {physicalMeasurementsInfo.length > 0 && ( -
-
- Measurements and Wearables -
-
- {physicalMeasurementsInfo.map( - (phyMeasurements, index) => { - const key = "phyMeasurements" + index; - return ( - - ); - } - )} -
-
- )} +
-
} + )} {surveyInfo.length > 0 && (
Survey Questions @@ -1007,16 +1078,15 @@ export const dBHomeComponent = withRouteData(
)}
- ) - } + )} {popUp && ( this.closePopUp()} /> )} -
+ ); } } -); \ No newline at end of file +); diff --git a/public-ui/src/app/data-browser/views/survey-view/components/survey-answer-react.component.tsx b/public-ui/src/app/data-browser/views/survey-view/components/survey-answer-react.component.tsx index 0ca24c826..92342c136 100644 --- a/public-ui/src/app/data-browser/views/survey-view/components/survey-answer-react.component.tsx +++ b/public-ui/src/app/data-browser/views/survey-view/components/survey-answer-react.component.tsx @@ -322,7 +322,10 @@ const SurveyAnswerRowComponent = class extends React.Component<
- {sq.conceptName} ({sq.conceptId}) + + {sq.conceptName} + {" "} + ({sq.conceptId})
{/* tslint:disable-next-line: no-use-before-declare */} diff --git a/public-ui/src/app/data-browser/views/survey-view/components/survey-question-react.component.tsx b/public-ui/src/app/data-browser/views/survey-view/components/survey-question-react.component.tsx index d929e206c..7f4dd6f7a 100644 --- a/public-ui/src/app/data-browser/views/survey-view/components/survey-question-react.component.tsx +++ b/public-ui/src/app/data-browser/views/survey-view/components/survey-question-react.component.tsx @@ -36,7 +36,6 @@ const styles = reactStyles({ }, }); - const styleCss = ` .question-conceptid { font-weight: normal !important; @@ -160,7 +159,7 @@ export class SurveyQuestionReactComponent extends React.Component< this.state; return (
- + this.showAnswers()} @@ -169,7 +168,8 @@ export class SurveyQuestionReactComponent extends React.Component< ({question.conceptId}) + />{" "} + ({question.conceptId}) {question.conceptId === 1585852 && ( Note: Active duty does not include training for the Reserves or diff --git a/public-ui/src/app/data-browser/views/survey-view/survey-react-view.component.tsx b/public-ui/src/app/data-browser/views/survey-view/survey-react-view.component.tsx index 115bc6bca..82047a641 100644 --- a/public-ui/src/app/data-browser/views/survey-view/survey-react-view.component.tsx +++ b/public-ui/src/app/data-browser/views/survey-view/survey-react-view.component.tsx @@ -4,8 +4,8 @@ import _ from "lodash"; import { environment } from "environments/environment"; import { withRouteData } from "app/components/app-router"; import { NoResultSearchComponent } from "app/components/db-no-results/no-results-search.component"; -import { SurveyVersionTableReactComponent } from "app/data-browser/components/survey-version-table/survey-version-table-react.component"; import { PfhhSurveyTableReactComponent } from "app/data-browser/components/survey-version-table/pfhh-survey-table-react.component"; +import { SurveyVersionTableReactComponent } from "app/data-browser/components/survey-version-table/survey-version-table-react.component"; import { SearchComponent } from "app/data-browser/search/home-search.component"; import { SurveyQuestionReactComponent } from "app/data-browser/views/survey-view/components/survey-question-react.component"; import { SurveyDescReactComponent } from "app/data-browser/views/survey-view/survey-desc.component"; @@ -26,7 +26,7 @@ const styles = reactStyles({ fontSize: "14px", fontWeight: 500, lineHeight: "14px", - cursor: "pointer" + cursor: "pointer", }, dbCard: { overflowX: "auto", @@ -37,7 +37,7 @@ const styles = reactStyles({ }, surveyHead: { display: "flex", - justifyContent:"space-between", + justifyContent: "space-between", position: "relative", marginBottom: "calc(18px * 4)", }, @@ -62,7 +62,7 @@ const styles = reactStyles({ flexDirection: "column", width: "50%", postion: "relative", - top: "3rem" + top: "3rem", }, versionTable: { width: "50%", @@ -253,9 +253,10 @@ export const SurveyViewReactComponent = withRouteData( api.getDomainTotals(searchWord, 1, 1).then((data: any) => { data.surveyModules.forEach((survey) => { if (survey.conceptId === 43529712) { - - - this.setState({ pfhhQC: survey.questionCount, pfhhPC: survey.participantCount }); + this.setState({ + pfhhQC: survey.questionCount, + pfhhPC: survey.participantCount, + }); } const surveyRoute = survey.conceptId === 43528895 @@ -282,23 +283,23 @@ export const SurveyViewReactComponent = withRouteData( const surveyPdfUrl = surveyConceptId === 43528895 ? "/assets/surveys/" + - "Health Care Access Utilization".split(" ").join("_") + - ".pdf" - : surveyConceptId === 1740639 - ? "/assets/surveys/" + - "Personal_and_Family_Health_History" + + "Health Care Access Utilization".split(" ").join("_") + ".pdf" - : "/assets/surveys/" + survey.name.split(" ").join("_") + ".pdf"; - const surveyPdfUrlSpanish = + : surveyConceptId === 1740639 + ? "/assets/surveys/" + "Personal_and_Family_Health_History" + ".pdf" + : "/assets/surveys/" + survey.name.split(" ").join("_") + ".pdf"; + const surveyPdfUrlSpanish = surveyConceptId === 43528895 ? "/assets/surveys/" + - "Health Care Access Utilization".split(" ").join("_") + - "_Spanish.pdf" + "Health Care Access Utilization".split(" ").join("_") + + "_Spanish.pdf" : surveyConceptId === 1740639 - ? "/assets/surveys/" + + ? "/assets/surveys/" + "Personal_and_Family_Health_History" + "_Spanish.pdf" - : "/assets/surveys/" + survey.name.split(" ").join("_") + "_Spanish.pdf"; + : "/assets/surveys/" + + survey.name.split(" ").join("_") + + "_Spanish.pdf"; const copeFlag = surveyConceptId === 1333342 || surveyConceptId === 765936; const combinedPfhhFlag = surveyConceptId === 43529712; @@ -439,12 +440,14 @@ export const SurveyViewReactComponent = withRouteData( questions, surveyVersions, surveyPdfUrl, - surveyPdfUrlSpanish + surveyPdfUrlSpanish, } = this.state; - const statClass = (isCopeSurvey || isCombinedPfhh) ? "cope-stat-layout" : "stat-layout"; - const statStyle = (isCopeSurvey || isCombinedPfhh) - ? styles.copeStatLayout - : styles.statLayout; + const statClass = + isCopeSurvey || isCombinedPfhh ? "cope-stat-layout" : "stat-layout"; + const statStyle = + isCopeSurvey || isCombinedPfhh + ? styles.copeStatLayout + : styles.statLayout; const matchingQuestions = questions.filter((question) => question.type.toLowerCase().includes("question") ); @@ -539,28 +542,40 @@ export const SurveyViewReactComponent = withRouteData(
) : null}
- {surveyVersions.length > 0 ? -
+ {surveyVersions.length > 0 ? ( +
+ surveyVersions={surveyVersions} + />
- : isCombinedPfhh ? -
- -
- : -
- Download Survey
- English - - | - Spanish - -
- } - - + ) : isCombinedPfhh ? ( +
+ +
+ ) : ( +
+ Download Survey +
+ + {" "} + English + + | + + {" "} + Spanish + +
+ )}
{questions && (
@@ -26,18 +26,20 @@ export default function Surface(props) { width: "100%", height: "100%", left: 0, - top: 0 + top: 0, }} > {children} -
+
Min Max
@@ -65,10 +67,10 @@ Surface.propTypes = { /** * Width and height attributes of the SVG view box. */ - view: PropTypes.array + view: PropTypes.array, }; Surface.defaultProps = { view: [1000, 500], - trbl: [10, 10, 10, 10] -}; \ No newline at end of file + trbl: [10, 10, 10, 10], +}; diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx index 68790a7d3..397c5f1e3 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-components.tsx @@ -1,6 +1,8 @@ import * as React from "react"; + import * as PropTypes from "prop-types"; -import { view, trbl, dims } from "./slider-constants"; + +import { dims, trbl, view } from "./slider-constants"; // ******************************************************* // RAIL // ******************************************************* @@ -40,35 +42,47 @@ SliderRail.propTypes = { // ******************************************************* // HANDLE COMPONENT // ******************************************************* -export function Handle({ handle: { id, percent, value },domain, category, getHandleProps }) { - - value = (category === 'alleleFrequency') ? value.toFixed(2): value; +export function Handle({ + handle: { id, percent, value }, + domain, + category, + getHandleProps, +}) { + value = category === "alleleFrequency" ? value.toFixed(2) : value; const r0 = dims[1] * 0.4; // inner - visible const r1 = dims[1] * 0.6; // outer - invisible w/ events - return ( + return ( - - {value} - - - + + + {value} + + + + ); } @@ -78,14 +92,14 @@ Handle.propTypes = { handle: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, getHandleProps: PropTypes.func.isRequired, - disabled: PropTypes.bool + disabled: PropTypes.bool, }; Handle.defaultProps = { - disabled: false + disabled: false, }; // ******************************************************* @@ -117,19 +131,19 @@ Track.propTypes = { source: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, target: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, getTrackProps: PropTypes.func.isRequired, - disabled: PropTypes.bool + disabled: PropTypes.bool, }; Track.defaultProps = { - disabled: false + disabled: false, }; // ******************************************************* @@ -166,11 +180,11 @@ Tick.propTypes = { tick: PropTypes.shape({ id: PropTypes.string.isRequired, value: PropTypes.number.isRequired, - percent: PropTypes.number.isRequired + percent: PropTypes.number.isRequired, }).isRequired, - format: PropTypes.func.isRequired + format: PropTypes.func.isRequired, }; Tick.defaultProps = { - format: d => d + format: (d) => d, }; diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx index 6f0b3ff31..5a1084596 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/slider-constants.tsx @@ -3,5 +3,5 @@ export const trbl = [20, 50, 20, 50]; // Margins: Top, Right, Bottom, Left export const dims = [ view[0] - trbl[1] - trbl[3], // Adjusted dimensions width - view[1] - trbl[0] - trbl[2] // Adjusted dimensions height + view[1] - trbl[0] - trbl[2], // Adjusted dimensions height ]; diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx index 4ef9e4b41..dd940cb6c 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/slider-filter/sv-variant-filter-slider.component.tsx @@ -1,5 +1,6 @@ import * as React from "react"; import { Handles, Rail, Slider, Tracks } from "react-compound-slider"; + import { Handle, SliderRail, Track } from "./slider-components"; // example render components - source below import { dims, trbl, view } from "./slider-constants"; import Surface from "./Surface"; @@ -24,48 +25,57 @@ interface State { max: Number; } -export const SVVariantFilterSliderComponent = (class extends React.Component { - +export const SVVariantFilterSliderComponent = class extends React.Component< + Props, + State +> { constructor(props: Props) { super(props); this.state = { domain: [this.props.ogFilterItem.min, this.props.ogFilterItem.max], defaultValues: [this.props.filterItem.min, this.props.filterItem.max], min: this.props.filterItem.min, - max: this.props.filterItem.max - } + max: this.props.filterItem.max, + }; } onUpdate(vals) { this.props.onSliderChange(vals); this.setState({ min: vals[0], - max: vals[1] + max: vals[1], }); - } render() { const { domain, defaultValues } = this.state; - const {filterItem, ogFilterItem, category} = this.props; - return
- - { this.onUpdate(e) }} - rootProps={sliderProps} - values={defaultValues}> - - {({ getRailProps }) => } - - {/* + const { filterItem, ogFilterItem, category } = this.props; + return ( +
+ + { + this.onUpdate(e); + }} + rootProps={sliderProps} + values={defaultValues} + > + + {({ getRailProps }) => } + + {/* {({ ticks }) => ( {ticks.map(tick => ( @@ -74,37 +84,38 @@ export const SVVariantFilterSliderComponent = (class extends React.Component )} */} - - {({ tracks, getTrackProps }) => ( - - {tracks.map(({ id, source, target }) => ( - - ))} - - )} - - - {({ handles, getHandleProps }) => ( - - {handles.map(handle => ( - - ))} - - )} - - - -
+ + {({ tracks, getTrackProps }) => ( + + {tracks.map(({ id, source, target }) => ( + + ))} + + )} + + + {({ handles, getHandleProps }) => ( + + {handles.map((handle) => ( + + ))} + + )} + +
+
+
+ ); } -}) +}; diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx index b3d1745b3..29d67392d 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx @@ -1,34 +1,34 @@ import * as React from "react"; -import { reactStyles } from "app/utils"; import { environment } from "environments/environment"; -import { GenomicFilters, SVVariant } from "publicGenerated"; -import { SortMetadata } from "publicGenerated/fetch"; import { SVVariantSearchComponent } from "../../sv-genomic-view/components/sv-variant-search.component"; import { SVVariantTableComponent } from "../../sv-genomic-view/components/sv-variant-table.component"; +import { reactStyles } from "app/utils"; +import { GenomicFilters, SVVariant } from "publicGenerated"; +import { SortMetadata } from "publicGenerated/fetch"; const styles = reactStyles({ border: { - background: 'white', - padding: '2em', - paddingTop: '1em', + background: "white", + padding: "2em", + paddingTop: "1em", }, titleBox: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'flex-start', + display: "flex", + justifyContent: "space-between", + alignItems: "flex-start", }, boxHeading: { margin: 0, - fontFamily: 'GothamBook, Arial, sans-serif', + fontFamily: "GothamBook, Arial, sans-serif", fontWeight: 100, - fontStyle: 'normal', - fontSize: '.8em', - fontStretch: 'normal', - lineHeight: '1.47em', - letterSpacing: 'normal', - textAlign: 'left', - color: '#262262', + fontStyle: "normal", + fontSize: ".8em", + fontStretch: "normal", + lineHeight: "1.47em", + letterSpacing: "normal", + textAlign: "left", + color: "#262262", }, }); @@ -71,7 +71,7 @@ export class SVGenomicSearchComponent extends React.Component { filterMetadata: this.props.filterMetadata, sortMetadata: this.props.sortMetadata, submittedFilterMetadata: this.props.submittedFilterMetadata, - filtered: false + filtered: false, }; } @@ -90,7 +90,10 @@ export class SVGenomicSearchComponent extends React.Component { handlePageChange(info) { this.props.onPageChange(info); - { !environment.infiniteSrcoll && this.scrollDiv.current.scrollIntoView({ behavior: "smooth" }); } + { + !environment.infiniteSrcoll && + this.scrollDiv.current.scrollIntoView({ behavior: "smooth" }); + } } handleRowCountChange(info) { @@ -104,9 +107,9 @@ export class SVGenomicSearchComponent extends React.Component { handleFilterSubmit(filteredMetadata, sortMetadata) { this.props.onFilterSubmit(filteredMetadata, sortMetadata); - this.setState({filtered:true}); + this.setState({ filtered: true }); setTimeout(() => { - this.setState({filtered:false}); + this.setState({ filtered: false }); }, 1000); } @@ -115,43 +118,72 @@ export class SVGenomicSearchComponent extends React.Component { } render() { - const { searchTerm, filterMetadata, sortMetadata, submittedFilterMetadata,filtered } = this.state; - const { currentPage, loadingResults, svResults, variantListSize, loadingVariantListSize, onSearchInput, - rowCount,scrollClean } = this.props; - return -
-

- TBA -

-
- { onSearchInput(searchWord); this.setState({ searchTerm: searchWord }); }} - onFilterSubmit={(filteredMetadata, sortMetadata) => { this.handleFilterSubmit(filteredMetadata, sortMetadata); }} - loadingResults={loadingResults} - loadingVariantListSize={loadingVariantListSize} - searchTerm={searchTerm} - variantListSize={variantListSize} - filterMetadata={filterMetadata} - sortMetadata={sortMetadata} - submittedFilterMetadata={submittedFilterMetadata} - onSortChange={(e) => this.handleSortClick(e)} - scrollClean={scrollClean} /> - { onSearchInput(searchWord); this.setState({ searchTerm: searchWord }); }} - onRowCountChange={(info: any) => this.handleRowCountChange(info)} - onPageChange={(info: any) => this.handlePageChange(info)} - onSortClick={(sortMetadata: any) => this.handleSortClick(sortMetadata)} - onScrollBottom={() => { environment.infiniteSrcoll && this.handleScrollBottom() }} - currentPage={currentPage} - rowCount={rowCount} - sortMetadata={sortMetadata} - filtered = {filtered} - /> -
; + const { + searchTerm, + filterMetadata, + sortMetadata, + submittedFilterMetadata, + filtered, + } = this.state; + const { + currentPage, + loadingResults, + svResults, + variantListSize, + loadingVariantListSize, + onSearchInput, + rowCount, + scrollClean, + } = this.props; + return ( + +
+

+ TBA +

+
+ { + onSearchInput(searchWord); + this.setState({ searchTerm: searchWord }); + }} + onFilterSubmit={(filteredMetadata, sortMetadata) => { + this.handleFilterSubmit(filteredMetadata, sortMetadata); + }} + loadingResults={loadingResults} + loadingVariantListSize={loadingVariantListSize} + searchTerm={searchTerm} + variantListSize={variantListSize} + filterMetadata={filterMetadata} + sortMetadata={sortMetadata} + submittedFilterMetadata={submittedFilterMetadata} + onSortChange={(e) => this.handleSortClick(e)} + scrollClean={scrollClean} + /> + { + onSearchInput(searchWord); + this.setState({ searchTerm: searchWord }); + }} + onRowCountChange={(info: any) => this.handleRowCountChange(info)} + onPageChange={(info: any) => this.handlePageChange(info)} + onSortClick={(sortMetadata: any) => + this.handleSortClick(sortMetadata) + } + onScrollBottom={() => { + environment.infiniteSrcoll && this.handleScrollBottom(); + }} + currentPage={currentPage} + rowCount={rowCount} + sortMetadata={sortMetadata} + filtered={filtered} + /> +
+ ); } } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx index 91b49676b..3af5fc3c7 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-expanded.component.tsx @@ -1,4 +1,6 @@ import * as React from "react"; +import { faCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { PopulationChartReactComponent } from "app/data-browser/views/genomic-view/components/population-chart.component"; import { reactStyles } from "app/utils"; @@ -6,8 +8,6 @@ import { ClrIcon } from "app/utils/clr-icon"; import { prepVariantPopulationDetails } from "app/utils/constants"; import { Spinner } from "app/utils/spinner"; import { SVVariant, SVVariantInfo } from "publicGenerated"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCircle } from '@fortawesome/free-solid-svg-icons'; const css = ` .exit{ @@ -81,7 +81,6 @@ const styles = reactStyles({ // paddingLeft: "1em", zIndex: "9", borderTop: "1px solid rgb(204, 204, 204)", - }, top: { position: "relative", @@ -133,7 +132,7 @@ const styles = reactStyles({ padding: ".5rem", paddingBottom: "0", paddingTop: "0", - textAlign: "center" + textAlign: "center", }, popTableBody: { borderBottom: "1px solid #DDE0E4", @@ -147,7 +146,7 @@ const styles = reactStyles({ padding: ".5rem", display: "flex", flexDirection: "row", - alignItems: "center" + alignItems: "center", }, closeIcon: { cursor: "pointer", @@ -162,16 +161,15 @@ interface Props { loading: boolean; } // tslint:disable-next-line:no-empty-interface -interface State { } +interface State {} export class SVVariantExpandedComponent extends React.Component { constructor(props: Props) { super(props); } handleMouseOver = () => { - this.props.hovered(true) - - } + this.props.hovered(true); + }; render() { const { variantDetails, variant, loading } = this.props; let variantPopulationDetails: any[] = []; @@ -184,12 +182,14 @@ export class SVVariantExpandedComponent extends React.Component {
-
+
Variant ID: {" "} {!loading ? ( - + {variant.variantId} ) : ( @@ -198,7 +198,7 @@ export class SVVariantExpandedComponent extends React.Component {
)}{" "} -
+
this.props.closed()} className="exit" @@ -213,17 +213,27 @@ export class SVVariantExpandedComponent extends React.Component {
Variant Type:
- {variant.variantType ? variant.variantType : "-"} + + {variant.variantType ? variant.variantType : "-"} +
- Consequence(s) + associated gene(s): + + Consequence(s) + associated gene(s): +
- {variantDetails.consequenceGenes ? variantDetails.consequenceGenes.replace(/;/g, '\n') : "-"} + + {variantDetails.consequenceGenes + ? variantDetails.consequenceGenes.replace(/;/g, "\n") + : "-"} +
Position:
- {variant.position ? variant.position : "-"} + + {variant.position ? variant.position : "-"} +
Size: @@ -239,16 +249,28 @@ export class SVVariantExpandedComponent extends React.Component {
- Allele
Count
+ + Allele
+ Count +
- Allele
Number
+ + Allele
+ Number +
- Allele
Frequency
+ + Allele
+ Frequency +
- Homozygote
Count
+ + Homozygote
+ Count +
@@ -259,8 +281,14 @@ export class SVVariantExpandedComponent extends React.Component {
{item.Ancestry !== "Total" ? ( - + {item.Ancestry}{" "} ) : ( diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx index 039e0466d..53cf4b84d 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx @@ -1,164 +1,219 @@ -import { reactStyles } from 'app/utils'; -import { GenomicFilters } from 'publicGenerated'; -import * as React from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faXmark } from '@fortawesome/free-solid-svg-icons'; +import * as React from "react"; +import { faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { reactStyles } from "app/utils"; +import { GenomicFilters } from "publicGenerated"; interface Props { - filteredMetadata: GenomicFilters; - onChipChange: Function; + filteredMetadata: GenomicFilters; + onChipChange: Function; } interface State { - chips: Array; - + chips: Array; } const lables = { - variantId: 'Variant Id', - variantType: 'Variant Type', - consequence: 'Consequence', - position: 'Position', - size: 'Size', - alleleNumber: 'Allele Number', - alleleFrequency: 'Allele Frequency', - alleleCount: 'Allele Count', - homozygoteCount: 'Homozygote Count' + variantId: "Variant Id", + variantType: "Variant Type", + consequence: "Consequence", + position: "Position", + size: "Size", + alleleNumber: "Allele Number", + alleleFrequency: "Allele Frequency", + alleleCount: "Allele Count", + homozygoteCount: "Homozygote Count", }; const styles = reactStyles({ - - chipCat: { - display: 'flex', - flexWrap: 'wrap', - alignItems: 'center', - paddingRight: '0.25rem' - }, - chip: { - display: 'flex', - alignItems: 'center', - border: '1px #216fb4 solid', - color: 'rgb(33, 111, 180)', - padding: '.05rem .5rem', - borderRadius: '15px', - fontFamily: 'GothamBold', - margin: '.25rem .25rem' - }, - chipFormat: { - fontSize: '.8em', - display: 'flex', - flexWrap: 'wrap' - }, - chipLayout: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center' - } + chipCat: { + display: "flex", + flexWrap: "wrap", + alignItems: "center", + paddingRight: "0.25rem", + }, + chip: { + display: "flex", + alignItems: "center", + border: "1px #216fb4 solid", + color: "rgb(33, 111, 180)", + padding: ".05rem .5rem", + borderRadius: "15px", + fontFamily: "GothamBold", + margin: ".25rem .25rem", + }, + chipFormat: { + fontSize: ".8em", + display: "flex", + flexWrap: "wrap", + }, + chipLayout: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + }, }); export class SVVariantFilterChips extends React.Component { - constructor(props: Props) { - super(props); - this.state = { - chips: [] - }; - } - - formatChips(filteredMetadata): Array { - const displayArr = []; - for (const key in filteredMetadata) { - if (Object.prototype.hasOwnProperty.call(filteredMetadata, key) && filteredMetadata[key] !== undefined) { - const allChecked = Array.isArray(filteredMetadata[key]) && filteredMetadata[key].every((t => t.checked)); - if (!allChecked) { - let el = filteredMetadata[key]; - if (!el.hasOwnProperty("filterActive")) { - if (el.min < 1) { - el.min = +el.min.toFixed(2); - } - if (el.max < 1) { - el.max = +el.max.toFixed(2); - } - } - displayArr.push({ cat: key, data: el }); - } + constructor(props: Props) { + super(props); + this.state = { + chips: [], + }; + } + formatChips(filteredMetadata): Array { + const displayArr = []; + for (const key in filteredMetadata) { + if ( + Object.prototype.hasOwnProperty.call(filteredMetadata, key) && + filteredMetadata[key] !== undefined + ) { + const allChecked = + Array.isArray(filteredMetadata[key]) && + filteredMetadata[key].every((t) => t.checked); + if (!allChecked) { + const el = filteredMetadata[key]; + if (!el.hasOwnProperty("filterActive")) { + if (el.min < 1) { + el.min = +el.min.toFixed(2); + } + if (el.max < 1) { + el.max = +el.max.toFixed(2); } + } + displayArr.push({ cat: key, data: el }); } - return displayArr; + } } + return displayArr; + } - componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { - if (prevProps !== this.props) { - this.setState({ chips: this.formatChips(this.props.filteredMetadata) }); - } + componentDidUpdate( + prevProps: Readonly, + prevState: Readonly, + snapshot?: any + ): void { + if (prevProps !== this.props) { + this.setState({ chips: this.formatChips(this.props.filteredMetadata) }); } + } - removeChip(item, cat) { - const {filteredMetadata} = this.props; - if (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { - filteredMetadata[cat.toString()].items = filteredMetadata[cat.toString()].items.filter(el => { - if (item === el) { - item.checked = false; - } - return el; - }); - } else { - filteredMetadata[cat.toString()].checked = false; - try { - let originalFilterMetadata = JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'); - filteredMetadata[cat.toString()]['min'] = originalFilterMetadata[cat.toString()]['min']; - filteredMetadata[cat.toString()]['max'] = originalFilterMetadata[cat.toString()]['max']; - } catch (e) { - console.log('Error') - } - } - const allFalse = (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) && - filteredMetadata[cat.toString()].items.every(t => t.checked === false); - - if (allFalse && filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { - filteredMetadata[cat.toString()].filterActive = false; + removeChip(item, cat) { + const { filteredMetadata } = this.props; + if (filteredMetadata[cat.toString()].hasOwnProperty("filterActive")) { + filteredMetadata[cat.toString()].items = filteredMetadata[ + cat.toString() + ].items.filter((el) => { + if (item === el) { + item.checked = false; } - this.props.onChipChange(filteredMetadata); + return el; + }); + } else { + filteredMetadata[cat.toString()].checked = false; + try { + const originalFilterMetadata = JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + ); + filteredMetadata[cat.toString()].min = + originalFilterMetadata[cat.toString()].min; + filteredMetadata[cat.toString()].max = + originalFilterMetadata[cat.toString()].max; + } catch (e) { + console.log("Error"); + } } + const allFalse = + filteredMetadata[cat.toString()].hasOwnProperty("filterActive") && + filteredMetadata[cat.toString()].items.every((t) => t.checked === false); - render() { - const { chips } = this.state; - return
- {chips.length > 0 && chips.map((el, count) => { - if (el.data.hasOwnProperty("filterActive")) { - return
{el.data.items.some((p) => p.checked) &&
{lables[el.cat.toString()]} - {el.data.items.map((item, i) => { - const chipLabel = item.option ? item.option : '(undefined)'; - return
- {item.checked &&
- {chipLabel.replace(/_/g, " ")} - this.removeChip(item, el.cat)} - icon={faXmark} className="clear-search-icon" - caria-hidden='true'/> -
} -
; - })} -
} -
; - } else { - return
{el.data.checked &&
- {el.data.checked && -
{lables[el.cat.toString()]} -
- Min  - {el.data.min} -  | Max  - {el.data.max} - this.removeChip(el, el.cat)} - icon={faXmark} className="clear-search-icon" - caria-hidden='true'/> -
-
} -
} -
; - } - - })} -
; + if ( + allFalse && + filteredMetadata[cat.toString()].hasOwnProperty("filterActive") + ) { + filteredMetadata[cat.toString()].filterActive = false; } + this.props.onChipChange(filteredMetadata); + } + + render() { + const { chips } = this.state; + return ( +
+ {chips.length > 0 && + chips.map((el, count) => { + if (el.data.hasOwnProperty("filterActive")) { + return ( +
+ {" "} + {el.data.items.some((p) => p.checked) && ( +
+ {lables[el.cat.toString()]} + {el.data.items.map((item, i) => { + const chipLabel = item.option + ? item.option + : "(undefined)"; + return ( +
+ {item.checked && ( +
+ {chipLabel.replace(/_/g, " ")} + this.removeChip(item, el.cat)} + icon={faXmark} + className="clear-search-icon" + caria-hidden="true" + /> +
+ )} +
+ ); + })} +
+ )} +
+ ); + } else { + return ( +
+ {el.data.checked && ( +
+ {el.data.checked && ( +
+ {lables[el.cat.toString()]} +
+ + Min  + + {el.data.min} + +  | Max  + + {el.data.max} + this.removeChip(el, el.cat)} + icon={faXmark} + className="clear-search-icon" + caria-hidden="true" + /> +
+
+ )} +
+ )} +
+ ); + } + })} +
+ ); + } } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx index da65491bf..90f97a5e9 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx @@ -1,9 +1,10 @@ import * as React from "react"; -import { Cat } from "./sv-variant-filter.component"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; -import { SVVariantFilterSliderComponent } from './slider-filter/sv-variant-filter-slider.component' + +import { SVVariantFilterSliderComponent } from "./slider-filter/sv-variant-filter-slider.component"; +import { Cat } from "./sv-variant-filter.component"; const styles = reactStyles({ filterItem: { @@ -17,7 +18,7 @@ const styles = reactStyles({ fontSize: ".8em", letterSpacing: 0, lineHeight: "16px", - cursor: "pointer" + cursor: "pointer", }, filterItemClosed: { transform: "rotate(90deg)", @@ -44,10 +45,10 @@ const styles = reactStyles({ }, filterItemForm: { display: "flex", - overflow: 'hidden', + overflow: "hidden", flexDirection: "column", paddingLeft: "1rem", - paddingTop:".25rem" + paddingTop: ".25rem", }, filterItemOption: { fontSize: ".8em", @@ -60,10 +61,10 @@ const styles = reactStyles({ marginTop: "0.1rem", }, filterItemLabel: { - width: '80%', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow:'hidden' + width: "80%", + whiteSpace: "nowrap", + textOverflow: "ellipsis", + overflow: "hidden", // wordWrap: "break-word", }, filterSlider: { @@ -97,20 +98,28 @@ interface State { ogFilterMetaData: string; } -export class SVVariantFilterItemComponent extends React.Component { +export class SVVariantFilterItemComponent extends React.Component< + Props, + State +> { constructor(props: Props) { super(props); this.state = { filterItemOpen: false, - filterItemState: props.filterItem || '', - filterCheckMap: props.filterItem || '', - ogFilterMetaData: JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}')[this.props.category.field.toString()] - }; + filterItemState: props.filterItem || "", + filterCheckMap: props.filterItem || "", + ogFilterMetaData: JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + )[this.props.category.field.toString()], + }; } componentDidMount(): void { - if (Array.isArray(this.state.filterCheckMap.items) && this.state.filterCheckMap.items.every(t => t.checked)) { - this.state.filterCheckMap.items.forEach(i => i.checked = false); + if ( + Array.isArray(this.state.filterCheckMap.items) && + this.state.filterCheckMap.items.every((t) => t.checked) + ) { + this.state.filterCheckMap.items.forEach((i) => (i.checked = false)); } } @@ -131,27 +140,29 @@ export class SVVariantFilterItemComponent extends React.Component // } // } - handleCheck(filteredItem) { - const {filterItemState, filterCheckMap} = this.state; - let newFilterItemState = { ...filterItemState}; - let newFilterCheckMap = { ...filterCheckMap}; - const filtered = this.state.filterItemState.items.map(el => el === filteredItem ? { ...el, checked: !filteredItem.checked } : el); - const filterCheckedFlag = (filtered.find(x => x.checked === true)) ? true : false; + const { filterItemState, filterCheckMap } = this.state; + const newFilterItemState = { ...filterItemState }; + const newFilterCheckMap = { ...filterCheckMap }; + const filtered = this.state.filterItemState.items.map((el) => + el === filteredItem ? { ...el, checked: !filteredItem.checked } : el + ); + const filterCheckedFlag = filtered.find((x) => x.checked === true) + ? true + : false; newFilterItemState.items = filtered; newFilterItemState.filterActive = filterCheckedFlag; newFilterCheckMap.items = filtered; newFilterCheckMap.filterActive = filterCheckedFlag; this.setState({ filterItemState: newFilterItemState, - filterCheckMap: newFilterCheckMap + filterCheckMap: newFilterCheckMap, }); this.props.onFilterChange(newFilterItemState, this.props.category); } - handleSliderChange(vals, filterItem) { - const updatedFilterItem = {...filterItem}; + const updatedFilterItem = { ...filterItem }; updatedFilterItem.min = vals[0]; updatedFilterItem.max = vals[1]; updatedFilterItem.checked = true; @@ -159,42 +170,72 @@ export class SVVariantFilterItemComponent extends React.Component } render(): React.ReactNode { - const { category,cleared, filterItem } = this.props; + const { category, cleared, filterItem } = this.props; const { filterItemOpen, filterItemState, ogFilterMetaData } = this.state; - return - -
this.filterClick()} style={styles.filterItem}> - {category.display} -
-
- {(cleared && filterItemOpen && Array.isArray(filterItemState.items)) ?
- {/* this.filterBySearch(e)} /> + return ( + + +
this.filterClick()} style={styles.filterItem}> + {category.display} +
+ +
+
+ {cleared && filterItemOpen && Array.isArray(filterItemState.items) ? ( +
+ {/* this.filterBySearch(e)} />
Select |
*/} - {filterItemState.items.map((item: any, index: number) => { - const key = 'option' + index; - const itemLabel = item.option ? item.option : '(undefined)'; - return - this.handleCheck(item)} - id={item.option} - style={styles.filterItemCheck} - type='checkbox' name={item.option} - checked={item.checked} /> - - ; - })} -
: -
{filterItemOpen && - this.handleSliderChange(e, filterItemState)} />} -
} -
; + {filterItemState.items.map((item: any, index: number) => { + const key = "option" + index; + const itemLabel = item.option ? item.option : "(undefined)"; + return ( + + this.handleCheck(item)} + id={item.option} + style={styles.filterItemCheck} + type="checkbox" + name={item.option} + checked={item.checked} + /> + + + ); + })} +
+ ) : ( +
+ {filterItemOpen && ( + + this.handleSliderChange(e, filterItemState) + } + /> + )} +
+ )} +
+ ); } } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx index 379b44370..41979ef3e 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-slider.component.tsx @@ -1,45 +1,48 @@ -import { reactStyles } from 'app/utils'; -import * as React from 'react'; +import * as React from "react"; + +import { reactStyles } from "app/utils"; interface Props { - min: Number; - max: Number; - ogFilterItem: any; - onSliderChange: Function; + min: Number; + max: Number; + ogFilterItem: any; + onSliderChange: Function; } const styles = reactStyles({ - sliderFormat: { display: 'flex' } + sliderFormat: { display: "flex" }, }); export class SVVariantFilterSliderComponent extends React.Component { - constructor(props: Props) { - super(props); - } - - - render(): React.ReactNode { - const { min, max, ogFilterItem } = this.props; - // const {sliderValue} = this.state - console.log(ogFilterItem, 'realy'); + constructor(props: Props) { + super(props); + } - return -
- this.props.onSliderChange(e, false)} /> - this.props.onSliderChange(e, true)} /> -
-
; - } + render(): React.ReactNode { + const { min, max, ogFilterItem } = this.props; + // const {sliderValue} = this.state + console.log(ogFilterItem, "realy"); + return ( + +
+ this.props.onSliderChange(e, false)} + /> + this.props.onSliderChange(e, true)} + /> +
+
+ ); + } } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx index 017704bb9..036eff8b1 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx @@ -1,189 +1,218 @@ import * as React from "react"; -import { SVVariantFilterItemComponent } from "./sv-variant-filter-item.component"; -import { SVVariantSortItemComponent } from "./sv-variant-sort-item.component"; import { reactStyles } from "app/utils"; import { GenomicFilters } from "publicGenerated"; import { SortMetadata } from "publicGenerated/fetch"; +import { SVVariantFilterItemComponent } from "./sv-variant-filter-item.component"; +import { SVVariantSortItemComponent } from "./sv-variant-sort-item.component"; const styles = reactStyles({ - filterBox: { - top: ".5rem", - position: "absolute", - padding: ".25rem", - zIndex: 12, - borderRadius: "0 1px 1px 0", - backgroundColor: "#FFFFFF", - boxShadow: - "0 1px 3px 0 rgba(0,0,0,0.15), 0 0 2px 0 rgba(0,0,0,0.25), 0 2px 2px 0 rgba(0,0,0,0.15)", - width: "264px", - height: "421px", - display: "grid", - gridTemplateRows: "84% 16%", - }, - filterItemHandleClosed: { - transform: "rotate(90deg)", - }, - sortByContainer: { - paddingTop: ".5rem", - paddingRight: ".5rem", - }, - actionBtnContainer: { - position: "absolute", - bottom: ".5rem", - width: "100%", - display: "flex", - justifyContent:"space-around", - fontSize: "1.1em", - }, - clearBtn: { - textTransform: "uppercase", - borderRadius: "2px", - padding: "1rem", - border: "none", - background: "transparent", - width: "45%", - cursor: "pointer", - }, - applyBtn: { - textTransform: "uppercase", - borderRadius: "2px", - padding: "1rem", - border: "none", - background: "#262262", - color: "white", - width: "45%", - cursor: "pointer", - }, - filterItems: { - maxHeight: "340px", - overflowY: "auto", - }, + filterBox: { + top: ".5rem", + position: "absolute", + padding: ".25rem", + zIndex: 12, + borderRadius: "0 1px 1px 0", + backgroundColor: "#FFFFFF", + boxShadow: + "0 1px 3px 0 rgba(0,0,0,0.15), 0 0 2px 0 rgba(0,0,0,0.25), 0 2px 2px 0 rgba(0,0,0,0.15)", + width: "264px", + height: "421px", + display: "grid", + gridTemplateRows: "84% 16%", + }, + filterItemHandleClosed: { + transform: "rotate(90deg)", + }, + sortByContainer: { + paddingTop: ".5rem", + paddingRight: ".5rem", + }, + actionBtnContainer: { + position: "absolute", + bottom: ".5rem", + width: "100%", + display: "flex", + justifyContent: "space-around", + fontSize: "1.1em", + }, + clearBtn: { + textTransform: "uppercase", + borderRadius: "2px", + padding: "1rem", + border: "none", + background: "transparent", + width: "45%", + cursor: "pointer", + }, + applyBtn: { + textTransform: "uppercase", + borderRadius: "2px", + padding: "1rem", + border: "none", + background: "#262262", + color: "white", + width: "45%", + cursor: "pointer", + }, + filterItems: { + maxHeight: "340px", + overflowY: "auto", + }, }); - export interface Cat { - display: String; - field: String; + display: String; + field: String; } interface Props { - filterMetadata: GenomicFilters; - sortMetadata: SortMetadata; - onFilterSubmit: Function; - onSortChange: Function; + filterMetadata: GenomicFilters; + sortMetadata: SortMetadata; + onFilterSubmit: Function; + onSortChange: Function; } interface State { - filterCats: Cat[]; - filteredMetadata: any; - filterMetadata: any; - cleared: Boolean; - ogFilterMetaData: any; - sortMetadata: SortMetadata; + filterCats: Cat[]; + filteredMetadata: any; + filterMetadata: any; + cleared: Boolean; + ogFilterMetaData: any; + sortMetadata: SortMetadata; } export class SVVariantFilterComponent extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + filterCats: [ + { display: "Variant ID", field: "variantId" }, + { display: "Variant Type", field: "variantType" }, + { display: "Consequence", field: "consequence" }, + { display: "Position", field: "position" }, + { display: "Size", field: "size" }, + { display: "Allele Count", field: "alleleCount" }, + { display: "Allele Number", field: "alleleNumber" }, + { display: "Allele Frequency", field: "alleleFrequency" }, + { display: "Homozygote Count", field: "homozygoteCount" }, + ], + filteredMetadata: this.props.filterMetadata, + filterMetadata: this.props.filterMetadata, + cleared: true, + ogFilterMetaData: JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + ), + sortMetadata: this.props.sortMetadata, + }; + } - constructor(props: Props) { - super(props); - this.state = { - filterCats: [ - { display: 'Variant ID', field: 'variantId' }, - { display: 'Variant Type', field: 'variantType' }, - { display: 'Consequence', field: 'consequence' }, - { display: 'Position', field: 'position' }, - { display: 'Size', field: 'size' }, - { display: 'Allele Count', field: 'alleleCount' }, - { display: 'Allele Number', field: 'alleleNumber' }, - { display: 'Allele Frequency', field: 'alleleFrequency' }, - { display: 'Homozygote Count', field: 'homozygoteCount' }, - ], - filteredMetadata: this.props.filterMetadata, - filterMetadata: this.props.filterMetadata, - cleared: true, - ogFilterMetaData: JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'), - sortMetadata: this.props.sortMetadata, - }; - } + handleFilterChange(filteredItem: GenomicFilters, cat: Cat) { + const filterMetadataChange = this.props.filterMetadata; + filterMetadataChange[cat.field.toString()] = filteredItem; + this.setState({ filterMetadata: filterMetadataChange }); + } - handleFilterChange(filteredItem: GenomicFilters, cat: Cat) { - const filterMetadataChange = this.props.filterMetadata; - filterMetadataChange[cat.field.toString()] = filteredItem; - this.setState({ filterMetadata: filterMetadataChange }); - } + handleSortChange(sortedItem: SortMetadata) { + this.setState({ sortMetadata: sortedItem }); + } - handleSortChange(sortedItem: SortMetadata) { - this.setState({ sortMetadata: sortedItem }); - } - - submitFilter(filteredMetadata: GenomicFilters) { - // tslint:disable-next-line: forin - for (const key in filteredMetadata) { - const filterItem = filteredMetadata[key]; - const touched = Array.isArray(filterItem) && filterItem.some((t => t.checked)); - if (Array.isArray(filterItem)) { - if (!touched) { - filteredMetadata[key] = filterItem.forEach((item) => { - item.checked = true; - }); - filteredMetadata[key] = filterItem; - } - } + submitFilter(filteredMetadata: GenomicFilters) { + // tslint:disable-next-line: forin + for (const key in filteredMetadata) { + const filterItem = filteredMetadata[key]; + const touched = + Array.isArray(filterItem) && filterItem.some((t) => t.checked); + if (Array.isArray(filterItem)) { + if (!touched) { + filteredMetadata[key] = filterItem.forEach((item) => { + item.checked = true; + }); + filteredMetadata[key] = filterItem; } - filteredMetadata = this.state.filteredMetadata; - const sortMetadata = this.state.sortMetadata; - this.props.onFilterSubmit(filteredMetadata, sortMetadata); + } } + filteredMetadata = this.state.filteredMetadata; + const sortMetadata = this.state.sortMetadata; + this.props.onFilterSubmit(filteredMetadata, sortMetadata); + } - handleClear() { - const ogFilterMetaData = JSON.parse(localStorage.getItem("originalFilterMetadata") || '{}'); - // tslint:disable-next-line: forin - for (const key in this.state.filteredMetadata) { - this.state.filteredMetadata[key] = ogFilterMetaData[key]; - } - - const { sortMetadata } = this.state; - for (const smKey in sortMetadata) { - sortMetadata[smKey].sortActive = false; - sortMetadata[smKey].sortDirection = "asc"; - } - sortMetadata['variantId'].sortActive = true; - sortMetadata['variantId'].sortDirection = "asc"; + handleClear() { + const ogFilterMetaData = JSON.parse( + localStorage.getItem("originalFilterMetadata") || "{}" + ); + // tslint:disable-next-line: forin + for (const key in this.state.filteredMetadata) { + this.state.filteredMetadata[key] = ogFilterMetaData[key]; + } - this.setState({ cleared: false, filteredMetadata: this.state.filteredMetadata, sortMetadata: sortMetadata }, () => this.setState({ cleared: true })); - this.props.onFilterSubmit(this.state.filteredMetadata, sortMetadata); - + const { sortMetadata } = this.state; + for (const smKey in sortMetadata) { + sortMetadata[smKey].sortActive = false; + sortMetadata[smKey].sortDirection = "asc"; } + sortMetadata.variantId.sortActive = true; + sortMetadata.variantId.sortDirection = "asc"; + + this.setState( + { + cleared: false, + filteredMetadata: this.state.filteredMetadata, + sortMetadata: sortMetadata, + }, + () => this.setState({ cleared: true }) + ); + this.props.onFilterSubmit(this.state.filteredMetadata, sortMetadata); + } - render() { - const { filterMetadata } = this.props; - const { filterCats, filteredMetadata, cleared, sortMetadata } = this.state; - return -
-
- {filterCats.map((cat, index) => { - const key = 'cat' + index; - { - return cleared && filterMetadata && filteredMetadata && - this.handleFilterChange(e, cat)} - key={key} - category={cat} - cleared={cleared} - filterItem={filteredMetadata[cat.field.toString()]} />; - } - }) - } -
- { this.handleSortChange(e)} sortMetadata={sortMetadata} />} -
-
-
- - -
+ render() { + const { filterMetadata } = this.props; + const { filterCats, filteredMetadata, cleared, sortMetadata } = this.state; + return ( + +
+
+ {filterCats.map((cat, index) => { + const key = "cat" + index; + { + return ( + cleared && + filterMetadata && + filteredMetadata && ( + this.handleFilterChange(e, cat)} + key={key} + category={cat} + cleared={cleared} + filterItem={filteredMetadata[cat.field.toString()]} + /> + ) + ); + } + })} +
+ { + this.handleSortChange(e)} + sortMetadata={sortMetadata} + /> + }
- ; - } +
+
+ + +
+
+
+ ); + } } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx index 5402e4c6f..f00350f83 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-row.component.tsx @@ -4,8 +4,8 @@ import { genomicsApi } from "app/services/swagger-fetch-clients"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; import { SVVariant, SVVariantInfo } from "publicGenerated"; -import { SVVariantExpandedComponent } from "./sv-variant-expanded.component"; +import { SVVariantExpandedComponent } from "./sv-variant-expanded.component"; const styles = reactStyles({ variant: { @@ -119,84 +119,72 @@ export class SVVariantRowComponent extends React.Component { this.setState({ svVariantExpanded: !this.state.svVariantExpanded, }); - {} + { + } } - -render() { - const { variant } = this.props; - const { svVariantExpanded, variantDetails, loadingVarDetails } = this.state; - return ( - - - {!loadingVarDetails && svVariantExpanded ? ( - this.handleClick()} - hovered={() => this.state.mouseOverExpanded ? this.props.allowParentScroll(true): this.props.allowParentScroll(false)} - /> - ) : ( -
-
this.handleClick(variant.variantId)} - style={styles.variant} - > + render() { + const { variant } = this.props; + const { svVariantExpanded, variantDetails, loadingVarDetails } = this.state; + return ( + + + {!loadingVarDetails && svVariantExpanded ? ( + this.handleClick()} + hovered={() => + this.state.mouseOverExpanded + ? this.props.allowParentScroll(true) + : this.props.allowParentScroll(false) + } + /> + ) : ( +
this.handleClick(variant.variantId)} + style={styles.variant} > -
- {variant.variantId.length > 40 ? ( - - {variant.variantId.substr(0, 40)} … - - ) : ( - variant.variantId - )} -
-
- { }} - size="lg" - shape="caret" - dir="down" - /> +
+
+ {variant.variantId.length > 40 ? ( + + {variant.variantId.substr(0, 40)} … + + ) : ( + variant.variantId + )} +
+
+ {}} + size="lg" + shape="caret" + dir="down" + /> +
+
{variant.variantType}
+
{variant.consequence}
+
{variant.position}
+
{variant.size}
+
{variant.alleleCount}
+
{variant.alleleNumber}
+
{variant.alleleFrequency}
+
{variant.homozygoteCount}
-
- {variant.variantType} -
-
- {variant.consequence} -
-
- {variant.position} -
-
- {variant.size} -
-
- {variant.alleleCount} -
-
- {variant.alleleNumber} -
-
- {variant.alleleFrequency} -
-
- {variant.homozygoteCount} -
-
)} -
- ); -} + + ); + } } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx index d5ba6bf44..c77b7efce 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-search.component.tsx @@ -1,4 +1,5 @@ import * as React from "react"; + import { environment } from "environments/environment"; import { SearchComponent } from "app/data-browser/search/home-search.component"; import { reactStyles } from "app/utils"; @@ -6,14 +7,15 @@ import { ClrIcon } from "app/utils/clr-icon"; import { Spinner } from "app/utils/spinner"; import { SVGenomicFilters } from "publicGenerated"; import { SortSVMetadata } from "publicGenerated/fetch"; -import { SVVariantFilterChips } from './sv-variant-filter-chips.component' + import { SVVariantFilterComponent } from "./sv-variant-filter.component"; +import { SVVariantFilterChips } from "./sv-variant-filter-chips.component"; const styles = reactStyles({ searchBar: { paddingRight: "2rem", width: "calc(100% - 16rem)", - minWidth: "20rem" + minWidth: "20rem", }, searchHelpText: { paddingTop: "2em", @@ -25,22 +27,22 @@ const styles = reactStyles({ marginLeft: "-1rem", }, resultSize: { - fontSize:"1.2em" + fontSize: "1.2em", }, filterBtn: { fontFamily: "gothamBold", color: "#216FB4", cursor: "Pointer", - width: "fit-content" + width: "fit-content", }, filterContainer: { position: "relative", }, resultInfo: { - display:"grid", + display: "grid", gridTemplateColumns: "11.5rem 1fr", - alignItems: "baseline" - } + alignItems: "baseline", + }, }); const css = ` @@ -93,16 +95,16 @@ export class SVVariantSearchComponent extends React.Component { constructor(props: Props) { super(props); this.state = { - searchWord: '', + searchWord: "", filterShow: false, filteredMetadata: undefined, filteredMetaMap: undefined, filterMetadata: this.props.filterMetadata, submittedFilterMetadata: this.props.submittedFilterMetadata, sortMetadata: this.props.sortMetadata, - scrollClean: this.props.scrollClean + scrollClean: this.props.scrollClean, }; - if (this.state.searchWord !== '') { + if (this.state.searchWord !== "") { this.props.onSearchTerm(this.state.searchWord); } this.filterWrapperRef = React.createRef(); @@ -110,14 +112,24 @@ export class SVVariantSearchComponent extends React.Component { } handleChange(val: string) { - if (val == '') { this.setState({ scrollClean: true }) } + if (val == "") { + this.setState({ scrollClean: true }); + } this.props.onSearchTerm(val); - this.setState({ searchWord: val, filteredMetaMap: null, filterShow: false }); + this.setState({ + searchWord: val, + filteredMetaMap: null, + filterShow: false, + }); } - componentWillUpdate(nextProps: Readonly, nextState: Readonly, nextContext: any): void { + componentWillUpdate( + nextProps: Readonly, + nextState: Readonly, + nextContext: any + ): void { if (this.props.scrollClean != nextProps.scrollClean) { - this.setState({ scrollClean: nextProps.scrollClean }) + this.setState({ scrollClean: nextProps.scrollClean }); } } componentDidMount() { @@ -130,7 +142,10 @@ export class SVVariantSearchComponent extends React.Component { handleClickOutside(event) { const { filterShow } = this.state; - if (this.filterWrapperRef && !this.filterWrapperRef.current.contains(event.target)) { + if ( + this.filterWrapperRef && + !this.filterWrapperRef.current.contains(event.target) + ) { if (filterShow) { this.setState({ filterShow: !this.state.filterShow }); } @@ -155,7 +170,10 @@ export class SVVariantSearchComponent extends React.Component { this.setState({ filterShow: !this.state.filterShow }); } - handleFilterSubmit(filteredMetadata: SVGenomicFilters, sortMetadata: SortSVMetadata) { + handleFilterSubmit( + filteredMetadata: SVGenomicFilters, + sortMetadata: SortSVMetadata + ) { this.setState({ filteredMetadata: filteredMetadata }); this.props.onFilterSubmit(filteredMetadata, sortMetadata); this.setState({ filterShow: false }); @@ -173,53 +191,96 @@ export class SVVariantSearchComponent extends React.Component { } render() { - const { searchWord, filterShow, sortMetadata, submittedFilterMetadata, scrollClean } = this.state; - const { filterMetadata } = this.props - const { variantListSize, loadingResults, loadingVariantListSize } = this.props; - const variantListSizeDisplay = variantListSize ? variantListSize.toLocaleString() : 0; - return - -
-
- this.handleChange(val)} - onClear={() => this.handleChange('')} placeholderText='Search by variant' /> + const { + searchWord, + filterShow, + sortMetadata, + submittedFilterMetadata, + scrollClean, + } = this.state; + const { filterMetadata } = this.props; + const { variantListSize, loadingResults, loadingVariantListSize } = + this.props; + const variantListSizeDisplay = variantListSize + ? variantListSize.toLocaleString() + : 0; + return ( + + +
+
+ this.handleChange(val)} + onClear={() => this.handleChange("")} + placeholderText="Search by variant" + /> +
+
+ Examples by query type:

+ Variant: 1-104946932-0fa1

+
-
- Examples by query type:

- Variant: 1-104946932-0fa1

+ {submittedFilterMetadata && ( + this.handleChipChange(changes)} + /> + )} +
+ {!loadingResults && + !loadingVariantListSize && + variantListSize > 0 && + environment.genoFilters ? ( +
this.showFilter()} style={styles.filterBtn}> + Filter & Sort +
+ ) : scrollClean ? ( +
+ ) : ( +
this.showFilter()} style={styles.filterBtn}> + Filter & Sort +
+ )} + + {!loadingResults && !loadingVariantListSize && searchWord ? ( + + {!loadingResults && !loadingVariantListSize ? ( + variantListSizeDisplay + ) : ( + + + + )}{" "} + variants + + ) : scrollClean ? ( +
+ ) : ( + + {variantListSizeDisplay} variants + + )} +
-
- { - submittedFilterMetadata && - this.handleChipChange(changes)} /> - } -
- {((!loadingResults && !loadingVariantListSize) && (variantListSize > 0) && environment.genoFilters) ?
this.showFilter()} - style={styles.filterBtn}> Filter & Sort
: - scrollClean ?
:
this.showFilter()} - style={styles.filterBtn}> Filter & Sort
} - - { - (!loadingResults && !loadingVariantListSize && searchWord) ? {(!loadingResults && !loadingVariantListSize) ? variantListSizeDisplay : - } variants : - scrollClean ?
: {variantListSizeDisplay} variants - } -
-
- {environment.genoFilters &&
- {filterShow && - this.handleFilterSubmit(filteredMetadata, sortMetadata)} - onSortChange={(e) => this.handleSortChange(e)} - />} -
- } - -
; + {environment.genoFilters && ( +
+ {filterShow && ( + this.handleFilterSubmit(filteredMetadata, sortMetadata)} + onSortChange={(e) => this.handleSortChange(e)} + /> + )} +
+ )} + + ); } } diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx index 2531d5491..dd946525b 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-sort-item.component.tsx @@ -1,11 +1,12 @@ import * as React from "react"; +import { faArrowUp } from "@fortawesome/free-solid-svg-icons"; +import { faArrowDown } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + import { TooltipReactComponent } from "app/data-browser/components/tooltip/tooltip-react.component"; import { reactStyles } from "app/utils"; import { ClrIcon } from "app/utils/clr-icon"; import { SortSVMetadata } from "publicGenerated/fetch"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; -import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; const styles = reactStyles({ sortItem: { @@ -18,7 +19,7 @@ const styles = reactStyles({ color: "#262262", letterSpacing: 0, lineHeight: "16px", - cursor: "pointer" + cursor: "pointer", }, sortItemClosed: { transform: "rotate(90deg)", @@ -29,7 +30,7 @@ const styles = reactStyles({ sortItemForm: { fontSize: "0.8em", display: "flex", - overflow: 'visible', + overflow: "visible", flexDirection: "column", paddingLeft: "1rem", paddingTop: ".25rem", @@ -45,15 +46,15 @@ const styles = reactStyles({ marginTop: "0.1rem", }, sortItemLabel: { - width: '80%', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow: 'hidden' + width: "80%", + whiteSpace: "nowrap", + textOverflow: "ellipsis", + overflow: "hidden", // wordWrap: "break-word", }, activeSort: { - fontFamily: 'gothamBold' - } + fontFamily: "gothamBold", + }, }); const css = ` @@ -91,15 +92,15 @@ const css = ` `; const lables = { - variantId: 'Variant Id', - variantType: 'Variant Type', - consequence: 'Consequence', - position: 'Position', - size: 'Size', - alleleNumber: 'Allele Number', - alleleFrequency: 'Allele Frequency', - alleleCount: 'Allele Count', - homozygoteCount: 'Homozygote Count' + variantId: "Variant Id", + variantType: "Variant Type", + consequence: "Consequence", + position: "Position", + size: "Size", + alleleNumber: "Allele Number", + alleleFrequency: "Allele Frequency", + alleleCount: "Allele Count", + homozygoteCount: "Homozygote Count", }; interface Props { @@ -122,32 +123,31 @@ export class SVVariantSortItemComponent extends React.Component { sortItemOpen: false, sortMetadata: props.sortMetadata, filterCats: [ - { display: 'Variant Type', field: 'variantType' }, - { display: 'Variant ID', field: 'variantId' }, - { display: 'Consequence', field: 'consequence' }, - { display: 'Position', field: 'position' }, - { display: 'Size', field: 'size' }, - { display: 'Allele Count', field: 'alleleCount' }, - { display: 'Allele Number', field: 'alleleNumber' }, - { display: 'Allele Frequency', field: 'alleleFrequency' }, - { display: 'Homozygote Count', field: 'homozygoteCount' }, + { display: "Variant Type", field: "variantType" }, + { display: "Variant ID", field: "variantId" }, + { display: "Consequence", field: "consequence" }, + { display: "Position", field: "position" }, + { display: "Size", field: "size" }, + { display: "Allele Count", field: "alleleCount" }, + { display: "Allele Number", field: "alleleNumber" }, + { display: "Allele Frequency", field: "alleleFrequency" }, + { display: "Homozygote Count", field: "homozygoteCount" }, ], sortCats: [ - { display: 'Variant Type', field: 'variantType' }, - { display: 'Variant ID', field: 'variantId' }, - { display: 'Consequence', field: 'consequence' }, - { display: 'Position', field: 'position' }, - { display: 'Size', field: 'size' }, - { display: 'Allele Count', field: 'alleleCount' }, - { display: 'Allele Number', field: 'alleleNumber' }, - { display: 'Allele Frequency', field: 'alleleFrequency' }, - { display: 'Homozygote Count', field: 'homozygoteCount' }, - ] + { display: "Variant Type", field: "variantType" }, + { display: "Variant ID", field: "variantId" }, + { display: "Consequence", field: "consequence" }, + { display: "Position", field: "position" }, + { display: "Size", field: "size" }, + { display: "Allele Count", field: "alleleCount" }, + { display: "Allele Number", field: "alleleNumber" }, + { display: "Allele Frequency", field: "alleleFrequency" }, + { display: "Homozygote Count", field: "homozygoteCount" }, + ], }; } - componentDidMount(): void { - } + componentDidMount(): void {} sortClick() { this.setState({ sortItemOpen: !this.state.sortItemOpen }); @@ -157,15 +157,15 @@ export class SVVariantSortItemComponent extends React.Component { const { sortMetadata } = this.state; if (sortMetadata[field].sortActive) { - if (sortMetadata[field].sortDirection === 'asc') { - sortMetadata[field].sortDirection = 'desc' + if (sortMetadata[field].sortDirection === "asc") { + sortMetadata[field].sortDirection = "desc"; } else { - sortMetadata[field].sortDirection = 'asc' + sortMetadata[field].sortDirection = "asc"; } for (const item in sortMetadata) { if (item !== field) { sortMetadata[item].sortActive = false; - sortMetadata[item].sortDirection = 'desc'; + sortMetadata[item].sortDirection = "desc"; } } } else { @@ -173,7 +173,7 @@ export class SVVariantSortItemComponent extends React.Component { for (const item in sortMetadata) { if (item !== field) { sortMetadata[item].sortActive = false; - sortMetadata[item].sortDirection = 'asc'; + sortMetadata[item].sortDirection = "asc"; } } } @@ -184,33 +184,80 @@ export class SVVariantSortItemComponent extends React.Component { const { cleared } = this.props; const { sortItemOpen, sortMetadata, filterCats, sortCats } = this.state; - return - -
this.sortClick()} style={styles.sortItem}> - Sort By -
-
- {(cleared && sortItemOpen) && -
- {sortCats && sortCats.map((cat, index) => { - console.log(cat); - console.log(cat.field); - console.log(sortMetadata); - return
this.clickToSort(cat.field)}> - -
{cat.display} - Click to select
ascending or descending
-
- {sortMetadata[cat.field].sortActive && sortMetadata[cat.field].sortDirection === 'asc' && -
-
- })} -
} -
; + return ( + + +
this.sortClick()} style={styles.sortItem}> + Sort By +
+ +
+
+ {cleared && sortItemOpen && ( +
+ {sortCats && + sortCats.map((cat, index) => { + console.log(cat); + console.log(cat.field); + console.log(sortMetadata); + return ( +
this.clickToSort(cat.field)} + > + +
+ {cat.display} + + Click to select
+ ascending or descending +
+
+ {sortMetadata[cat.field].sortActive && + sortMetadata[cat.field].sortDirection === "asc" && ( +
+
+ ); + })} +
+ )} +
+ ); } -} \ No newline at end of file +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx index 60d854862..34ee58f1a 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-table.component.tsx @@ -1,14 +1,16 @@ import * as React from "react"; +import { faArrowUp } from "@fortawesome/free-solid-svg-icons"; +import { faArrowDown } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { environment } from "environments/environment"; import { reactStyles } from "app/utils"; import { Spinner } from "app/utils/spinner"; -import { environment } from "environments/environment"; import { SVVariant } from "publicGenerated"; import { SortSVMetadata } from "publicGenerated/fetch"; -import { TablePaginatorComponent } from "./table-paginator.component"; + import { SVVariantRowComponent } from "./sv-variant-row.component"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; -import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; +import { TablePaginatorComponent } from "./table-paginator.component"; const styles = reactStyles({ tableContainer: { @@ -23,7 +25,7 @@ const styles = reactStyles({ height: environment.infiniteSrcoll ? "30rem" : "", }, noScroll: { - overflowX: "scroll" + overflowX: "scroll", }, tableFrame: { border: "1px solid #CCCCCC", @@ -148,7 +150,6 @@ interface State { svResults: SVVariant[]; sortMetadata: any; allowParentScroll: Boolean; - } export class SVVariantTableComponent extends React.Component { @@ -158,7 +159,7 @@ export class SVVariantTableComponent extends React.Component { loading: props.loadingResults, svResults: props.svResults, sortMetadata: props.sortMetadata, - allowParentScroll: true + allowParentScroll: true, }; } scrollAreaRef: React.RefObject = React.createRef(); @@ -183,26 +184,26 @@ export class SVVariantTableComponent extends React.Component { if (filtered) { this.scrollAreaToTop(); - } if (prevProps.svResults !== svResults) { this.setState({ svResults: svResults, loading: loadingResults }); } - - - } handleScrollEnd = (event) => { clearTimeout(this.debounceTimer); this.debounceTimer = setTimeout(() => { - const scrollArea = document.querySelector('.scroll-area'); + const scrollArea = document.querySelector(".scroll-area"); if (scrollArea) { const scrollTop = scrollArea.scrollTop; const scrollHeight = scrollArea.scrollHeight; // trigger scroll at 35% - const scrolledToBottom = scrollTop / scrollHeight > .35; - if (scrolledToBottom && this.props.currentPage < this.props.variantListSize / this.props.rowCount) { + const scrolledToBottom = scrollTop / scrollHeight > 0.35; + if ( + scrolledToBottom && + this.props.currentPage < + this.props.variantListSize / this.props.rowCount + ) { // Fetch new data and append this.props.onScrollBottom(); } @@ -210,8 +211,6 @@ export class SVVariantTableComponent extends React.Component { }, 150); }; - - handlePageClick = (data) => { const { searchTerm } = this.props; this.setState({ loading: true }); @@ -257,131 +256,112 @@ export class SVVariantTableComponent extends React.Component { setArrowIcon(varName: string) { const { sortMetadata } = this.state; - return sortMetadata[varName].sortDirection === "asc" ? faArrowUp : faArrowDown; + return sortMetadata[varName].sortDirection === "asc" + ? faArrowUp + : faArrowDown; } - - - - render() { - const { loadingVariantListSize, loadingResults, variantListSize, rowCount, currentPage } = - this.props; + const { + loadingVariantListSize, + loadingResults, + variantListSize, + rowCount, + currentPage, + } = this.props; const { loading, svResults, sortMetadata, allowParentScroll } = this.state; - styles.noScroll.overflowX = !allowParentScroll ? "hidden":"scroll"; + styles.noScroll.overflowX = !allowParentScroll ? "hidden" : "scroll"; return ( {!loading && - !loadingVariantListSize && - svResults && - svResults.length ? ( -
+ !loadingVariantListSize && + svResults && + svResults.length ? ( +
- - Variant ID - + Variant ID {sortMetadata.variantId.sortActive && ( )}
- - Variant Type - + Variant Type {sortMetadata.variantType.sortActive && ( )}
- - Consequence - + Consequence {sortMetadata.consequence.sortActive && ( )}
- - Position - + Position {sortMetadata.position.sortActive && ( )}
- - Size - + Size {sortMetadata.size.sortActive && ( )}
- - Allele Count - + Allele Count {sortMetadata.alleleCount.sortActive && ( )}
- - Allele Number - + Allele Number {sortMetadata.alleleNumber.sortActive && ( )}
- - Allele Frequency - + Allele Frequency {sortMetadata.alleleFrequency.sortActive && ( )}
- - Homozygote Count - + Homozygote Count {sortMetadata.homozygoteCount.sortActive && ( )} @@ -394,13 +374,21 @@ export class SVVariantTableComponent extends React.Component { this.setState({allowParentScroll:!this.state.allowParentScroll})} + allowParentScroll={() => + this.setState({ + allowParentScroll: !this.state.allowParentScroll, + }) + } /> ); })} - {environment.infiniteSrcoll &&
- {currentPage < variantListSize / rowCount && loadingResults && } -
} + {environment.infiniteSrcoll && ( +
+ {currentPage < variantListSize / rowCount && loadingResults && ( + + )} +
+ )}
) : (
@@ -409,23 +397,23 @@ export class SVVariantTableComponent extends React.Component { {" "}
)} - {(!svResults || - (svResults && svResults.length === 0)) && ( -
-
- Enter a query in the search bar or get started with an example query: -
-
- Variant:{" "} -
this.searchItem("1-104946932-0fa1")} - style={styles.helpSearchDiv} - > - 1-104946932-0fa1 -
+ {(!svResults || (svResults && svResults.length === 0)) && ( +
+
+ Enter a query in the search bar or get started with an example + query: +
+
+ Variant:{" "} +
this.searchItem("1-104946932-0fa1")} + style={styles.helpSearchDiv} + > + 1-104946932-0fa1
- )} +
+ )}
)} {!loading && @@ -433,7 +421,7 @@ export class SVVariantTableComponent extends React.Component { svResults && variantListSize > rowCount && (
- {!environment.infiniteSrcoll && + {!environment.infiniteSrcoll && ( { this.handleRowCountChange(info); }} /> - } + )}
)} ); } -} \ No newline at end of file +} diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx index 9d5e111b9..f0ab9d943 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/table-paginator.component.tsx @@ -1,7 +1,7 @@ import * as React from "react"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faAngleLeft } from '@fortawesome/free-solid-svg-icons'; -import { faAngleRight } from '@fortawesome/free-solid-svg-icons'; +import { faAngleLeft } from "@fortawesome/free-solid-svg-icons"; +import { faAngleRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { reactStyles } from "app/utils"; @@ -132,7 +132,12 @@ export class TablePaginatorComponent extends React.Component { }); }} > - + Page {currentPage} of {pageCount}
diff --git a/public-ui/src/app/guards/is-safe-guard.service.ts b/public-ui/src/app/guards/is-safe-guard.service.ts index 5b5e46f32..84dedc07f 100644 --- a/public-ui/src/app/guards/is-safe-guard.service.ts +++ b/public-ui/src/app/guards/is-safe-guard.service.ts @@ -53,4 +53,4 @@ export class IsSafeGuard implements CanActivate, CanActivateChild { ): Observable { return this.canActivate(route, state); } -} \ No newline at end of file +} diff --git a/public-ui/src/app/services/server-config.service.ts b/public-ui/src/app/services/server-config.service.ts index 05b37e3c0..ff3816d3f 100644 --- a/public-ui/src/app/services/server-config.service.ts +++ b/public-ui/src/app/services/server-config.service.ts @@ -27,4 +27,4 @@ export class ServerConfigService { } return this.configObs; } -} \ No newline at end of file +} diff --git a/public-ui/src/app/shared/components/breadcrumb/breadcrumb-react.component.tsx b/public-ui/src/app/shared/components/breadcrumb/breadcrumb-react.component.tsx index d510f9d88..864430c23 100644 --- a/public-ui/src/app/shared/components/breadcrumb/breadcrumb-react.component.tsx +++ b/public-ui/src/app/shared/components/breadcrumb/breadcrumb-react.component.tsx @@ -17,16 +17,16 @@ const styles = reactStyles({ color: "#0079b8", marginRight: "0.5em", textDecoration: "none", - cursor: "pointer" + cursor: "pointer", }, crumb: { color: "#2b266d", marginRight: "0.5em", }, crumbContainer: { - paddingLeft:'1rem', + paddingLeft: "1rem", paddingTop: "1rem", - margin: "0 calc(10%)" + margin: "0 calc(10%)", }, separator: { marginRight: "0.5em", diff --git a/public-ui/src/app/shared/components/rh-header/rh-header.tsx b/public-ui/src/app/shared/components/rh-header/rh-header.tsx index d0cc89ade..5a512d015 100644 --- a/public-ui/src/app/shared/components/rh-header/rh-header.tsx +++ b/public-ui/src/app/shared/components/rh-header/rh-header.tsx @@ -27,74 +27,310 @@ export class RhHeader extends React.Component<{}, State> { }; } - render() { - const { menuOpen, searchTerms, submenusOpen1,submenusOpen2,submenusOpen3,submenusOpen4 } = this.state; + const { + menuOpen, + searchTerms, + submenusOpen1, + submenusOpen2, + submenusOpen3, + submenusOpen4, + } = this.state; return ( -
+
-
-
-
+
+ + + +
+
+
+
+
+ + + +
-
-
this.setState({ menuOpen: !menuOpen })} id="nav-mobile" className="nav__mobile" /> +
this.setState({ menuOpen: !menuOpen })} + id="nav-mobile" + className="nav__mobile" + />
diff --git a/public-ui/src/app/shared/services/header-footer.service.ts b/public-ui/src/app/shared/services/header-footer.service.ts index 0f60ce46e..6c7fb2922 100644 --- a/public-ui/src/app/shared/services/header-footer.service.ts +++ b/public-ui/src/app/shared/services/header-footer.service.ts @@ -137,105 +137,105 @@ export class HeaderFooterService { constructor() { this.allOfUsUrl = environment.researchAllOfUsUrl; -// this.menu = [ -// { -// title: "about", -// url: "https://www.researchallofus.org/about-the-research-hub/", -// submenu: true, -// sub0: [ -// { -// title: "About the Research Hub ", -// url: "https://www.researchallofus.org/about-the-research-hub/", -// submenu: false, -// }, -// { -// title: "Researchers as Partners", -// url: "https://www.researchallofus.org/researchers-as-partners/", -// submenu: false, -// }, -// { -// title: "Privacy & Security Protocols", -// url: "https://www.researchallofus.org/privacy-security-protocols/", -// submenu: false, -// }, -// { -// title: "Research Hub Updates", -// url: "https://www.researchallofus.org/research-hub-updates/", -// submenu: false, -// }, -// { -// title: "FAQ", -// url: "https://www.researchallofus.org/frequently-asked-questions/", -// submenu: false, -// }, -// ], -// }, -// { -// title: "Data & Tools", -// url: "https://www.researchallofus.org/data-tools/", -// submenu: true, -// class: "active-menu", -// sub0: [ -// { -// title: "Data Browser", -// url: "https://databrowser.researchallofus.org/", -// submenu: false, -// class: "active-menu", -// }, -// { -// title: "Data Snapshots", -// url: "https://www.researchallofus.org/data-snapshots/", -// submenu: false, -// }, -// { -// title: "Data Access Tiers", -// url: "https://www.researchallofus.org/data-tools/data-access/", -// submenu: false, -// }, -// { -// title: "Data Sources", -// url: "https://www.researchallofus.org/data-tools/data-sources/", -// submenu: false, -// }, -// { -// title: "Data Methods", -// url: "https://www.researchallofus.org/data-tools/methods/", -// submenu: false, -// }, -// { -// title: "Survey Explorer", -// url: "https://www.researchallofus.org/data-sources/survey-explorer/", -// submenu: false, -// }, -// { -// title: "Researcher Workbench", -// url: "https://www.researchallofus.org/workbench/", -// submenu: false, -// }, -// ], -// }, -// { -// title: "Discover", -// url: "https://www.researchallofus.org/research-projects-directory/?section=abc", -// submenu: true, -// sub0: [ -// { -// title: "Researcher Projects Directory", -// url: "https://www.researchallofus.org/research-projects-directory/", -// submenu: false, -// }, -// { -// title: "Publications", -// url: "https://www.researchallofus.org/publications", -// submenu: false, -// }, -// ], -// }, -// { -// title: "FAQ", -// url: this.allOfUsUrl + "/frequently-asked-questions/", -// submenu: false, -// }, -// ]; + // this.menu = [ + // { + // title: "about", + // url: "https://www.researchallofus.org/about-the-research-hub/", + // submenu: true, + // sub0: [ + // { + // title: "About the Research Hub ", + // url: "https://www.researchallofus.org/about-the-research-hub/", + // submenu: false, + // }, + // { + // title: "Researchers as Partners", + // url: "https://www.researchallofus.org/researchers-as-partners/", + // submenu: false, + // }, + // { + // title: "Privacy & Security Protocols", + // url: "https://www.researchallofus.org/privacy-security-protocols/", + // submenu: false, + // }, + // { + // title: "Research Hub Updates", + // url: "https://www.researchallofus.org/research-hub-updates/", + // submenu: false, + // }, + // { + // title: "FAQ", + // url: "https://www.researchallofus.org/frequently-asked-questions/", + // submenu: false, + // }, + // ], + // }, + // { + // title: "Data & Tools", + // url: "https://www.researchallofus.org/data-tools/", + // submenu: true, + // class: "active-menu", + // sub0: [ + // { + // title: "Data Browser", + // url: "https://databrowser.researchallofus.org/", + // submenu: false, + // class: "active-menu", + // }, + // { + // title: "Data Snapshots", + // url: "https://www.researchallofus.org/data-snapshots/", + // submenu: false, + // }, + // { + // title: "Data Access Tiers", + // url: "https://www.researchallofus.org/data-tools/data-access/", + // submenu: false, + // }, + // { + // title: "Data Sources", + // url: "https://www.researchallofus.org/data-tools/data-sources/", + // submenu: false, + // }, + // { + // title: "Data Methods", + // url: "https://www.researchallofus.org/data-tools/methods/", + // submenu: false, + // }, + // { + // title: "Survey Explorer", + // url: "https://www.researchallofus.org/data-sources/survey-explorer/", + // submenu: false, + // }, + // { + // title: "Researcher Workbench", + // url: "https://www.researchallofus.org/workbench/", + // submenu: false, + // }, + // ], + // }, + // { + // title: "Discover", + // url: "https://www.researchallofus.org/research-projects-directory/?section=abc", + // submenu: true, + // sub0: [ + // { + // title: "Researcher Projects Directory", + // url: "https://www.researchallofus.org/research-projects-directory/", + // submenu: false, + // }, + // { + // title: "Publications", + // url: "https://www.researchallofus.org/publications", + // submenu: false, + // }, + // ], + // }, + // { + // title: "FAQ", + // url: this.allOfUsUrl + "/frequently-asked-questions/", + // submenu: false, + // }, + // ]; } } diff --git a/public-ui/src/app/utils/constants.tsx b/public-ui/src/app/utils/constants.tsx index 4fa8a562f..d64f8e583 100644 --- a/public-ui/src/app/utils/constants.tsx +++ b/public-ui/src/app/utils/constants.tsx @@ -53,7 +53,7 @@ export const PM_CONCEPTS = [ "903120", ]; -export let fitbitConcepts = [ +export const fitbitConcepts = [ { id: 1, displayName: "any Fitbit data", @@ -88,25 +88,25 @@ export let fitbitConcepts = [ conceptName: "Activity intraday steps (minute-level)", icon: "faPersonWalking", tooltipKey: "fitbitActivityStepsHelpText", - } + }, ]; if (fitbitUpdateFlag) { -fitbitConcepts.push({ - id: 6, - displayName: "sleep daily summary", - conceptName: "sleep daily summary", - icon: "faBedPulse", - tooltipKey: "sleepDailySummaryHelpText" -}); + fitbitConcepts.push({ + id: 6, + displayName: "sleep daily summary", + conceptName: "sleep daily summary", + icon: "faBedPulse", + tooltipKey: "sleepDailySummaryHelpText", + }); -fitbitConcepts.push({ - id: 7, - displayName: "sleep level (sequence by level)", - conceptName: "Sleep Level (Sequence by level)", - icon: "faBedPulse", - tooltipKey: "sleepLevelHelpText" -}); + fitbitConcepts.push({ + id: 7, + displayName: "sleep level (sequence by level)", + conceptName: "Sleep Level (Sequence by level)", + icon: "faBedPulse", + tooltipKey: "sleepLevelHelpText", + }); } export const VARIANT_POPULATION_DETAILS = [ @@ -166,7 +166,13 @@ export const VARIANT_POPULATION_DETAILS = [ HomozygoteCount: 0, color: "#B2AEAD", }, - { Ancestry: "Total", AlleleCount: 0, AlleleNumber: 0, AlleleFrequency: 0, HomozygoteCount: 0, }, + { + Ancestry: "Total", + AlleleCount: 0, + AlleleNumber: 0, + AlleleFrequency: 0, + HomozygoteCount: 0, + }, ]; export const prepVariantPopulationDetails = (variantDetails) => { @@ -175,48 +181,48 @@ export const prepVariantPopulationDetails = (variantDetails) => { VARIANT_POPULATION_DETAILS[0].AlleleFrequency = variantDetails.afrAlleleFrequency; VARIANT_POPULATION_DETAILS[0].HomozygoteCount = - variantDetails.afrHomozygoteCount; + variantDetails.afrHomozygoteCount; VARIANT_POPULATION_DETAILS[1].AlleleCount = variantDetails.easAlleleCount; VARIANT_POPULATION_DETAILS[1].AlleleNumber = variantDetails.easAlleleNumber; VARIANT_POPULATION_DETAILS[1].AlleleFrequency = variantDetails.easAlleleFrequency; VARIANT_POPULATION_DETAILS[1].HomozygoteCount = - variantDetails.easHomozygoteCount; + variantDetails.easHomozygoteCount; VARIANT_POPULATION_DETAILS[2].AlleleCount = variantDetails.eurAlleleCount; VARIANT_POPULATION_DETAILS[2].AlleleNumber = variantDetails.eurAlleleNumber; VARIANT_POPULATION_DETAILS[2].AlleleFrequency = variantDetails.eurAlleleFrequency; VARIANT_POPULATION_DETAILS[2].HomozygoteCount = - variantDetails.eurHomozygoteCount; + variantDetails.eurHomozygoteCount; VARIANT_POPULATION_DETAILS[3].AlleleCount = variantDetails.amrAlleleCount; VARIANT_POPULATION_DETAILS[3].AlleleNumber = variantDetails.amrAlleleNumber; VARIANT_POPULATION_DETAILS[3].AlleleFrequency = variantDetails.amrAlleleFrequency; VARIANT_POPULATION_DETAILS[3].HomozygoteCount = - variantDetails.amrHomozygoteCount; + variantDetails.amrHomozygoteCount; VARIANT_POPULATION_DETAILS[4].AlleleCount = variantDetails.midAlleleCount; VARIANT_POPULATION_DETAILS[4].AlleleNumber = variantDetails.midAlleleNumber; VARIANT_POPULATION_DETAILS[4].AlleleFrequency = variantDetails.midAlleleFrequency; VARIANT_POPULATION_DETAILS[4].HomozygoteCount = - variantDetails.midHomozygoteCount; + variantDetails.midHomozygoteCount; VARIANT_POPULATION_DETAILS[5].AlleleCount = variantDetails.sasAlleleCount; VARIANT_POPULATION_DETAILS[5].AlleleNumber = variantDetails.sasAlleleNumber; VARIANT_POPULATION_DETAILS[5].AlleleFrequency = variantDetails.sasAlleleFrequency; VARIANT_POPULATION_DETAILS[5].HomozygoteCount = - variantDetails.sasHomozygoteCount; + variantDetails.sasHomozygoteCount; VARIANT_POPULATION_DETAILS[6].AlleleCount = variantDetails.othAlleleCount; VARIANT_POPULATION_DETAILS[6].AlleleNumber = variantDetails.othAlleleNumber; VARIANT_POPULATION_DETAILS[6].AlleleFrequency = variantDetails.othAlleleFrequency; VARIANT_POPULATION_DETAILS[6].HomozygoteCount = - variantDetails.othHomozygoteCount; + variantDetails.othHomozygoteCount; VARIANT_POPULATION_DETAILS[7].AlleleCount = variantDetails.totalAlleleCount; VARIANT_POPULATION_DETAILS[7].AlleleNumber = variantDetails.totalAlleleNumber; VARIANT_POPULATION_DETAILS[7].AlleleFrequency = variantDetails.totalAlleleFrequency; VARIANT_POPULATION_DETAILS[7].HomozygoteCount = - variantDetails.totalHomozygoteCount; + variantDetails.totalHomozygoteCount; return VARIANT_POPULATION_DETAILS; -}; \ No newline at end of file +}; diff --git a/public-ui/src/app/utils/db-config.service.ts b/public-ui/src/app/utils/db-config.service.ts index a0ded333a..401b14514 100644 --- a/public-ui/src/app/utils/db-config.service.ts +++ b/public-ui/src/app/utils/db-config.service.ts @@ -184,7 +184,7 @@ export class DbConfigService { { conceptId: 1586134, conceptName: "The Basics" }, { conceptId: 43529712, conceptName: "Personal Medical History" }, { conceptId: 43528895, conceptName: "Health Care Access and Utilization" }, - { conceptId: 1740639, conceptName: "Personal and Family Health History"}, + { conceptId: 1740639, conceptName: "Personal and Family Health History" }, { conceptId: 43528698, conceptName: "Family Health History" }, { conceptId: 1333342, conceptName: "COVID-19 Participant Experience" }, ]; diff --git a/public-ui/src/app/utils/icons.tsx b/public-ui/src/app/utils/icons.tsx index 7b7b1bb5b..3af925fba 100644 --- a/public-ui/src/app/utils/icons.tsx +++ b/public-ui/src/app/utils/icons.tsx @@ -1,6 +1,5 @@ -import * as React from 'react'; - -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import * as React from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; const Icon = ({ shape, size, style, color, ...props }) => { return ( diff --git a/public-ui/src/app/utils/spinner.tsx b/public-ui/src/app/utils/spinner.tsx index 5e57eed93..3e9f142ea 100644 --- a/public-ui/src/app/utils/spinner.tsx +++ b/public-ui/src/app/utils/spinner.tsx @@ -1,8 +1,5 @@ import * as React from "react"; - - - export const loadingCss = ` a:link,a:visited,a{ color:#2aa3d8; diff --git a/public-ui/src/environments/environment.prod.ts b/public-ui/src/environments/environment.prod.ts index 79550ba15..729c2356f 100644 --- a/public-ui/src/environments/environment.prod.ts +++ b/public-ui/src/environments/environment.prod.ts @@ -12,5 +12,5 @@ export const environment = { geno: true, genoFilters: true, fitbitCDRUpdate: true, - infiniteSrcoll: true + infiniteSrcoll: true, }; diff --git a/public-ui/src/environments/environment.stable.ts b/public-ui/src/environments/environment.stable.ts index a6a5348ed..ad2f97611 100644 --- a/public-ui/src/environments/environment.stable.ts +++ b/public-ui/src/environments/environment.stable.ts @@ -13,5 +13,5 @@ export const environment = { geno: true, genoFilters: true, fitbitCDRUpdate: true, - infiniteSrcoll: true + infiniteSrcoll: true, }; diff --git a/public-ui/src/environments/environment.staging.ts b/public-ui/src/environments/environment.staging.ts index b067ac800..71da8cd56 100644 --- a/public-ui/src/environments/environment.staging.ts +++ b/public-ui/src/environments/environment.staging.ts @@ -13,5 +13,5 @@ export const environment = { geno: true, genoFilters: true, fitbitCDRUpdate: true, - infiniteSrcoll: true + infiniteSrcoll: true, }; diff --git a/public-ui/src/environments/test-env-base.ts b/public-ui/src/environments/test-env-base.ts index 2704d789c..9e62e4a7b 100644 --- a/public-ui/src/environments/test-env-base.ts +++ b/public-ui/src/environments/test-env-base.ts @@ -9,5 +9,5 @@ export const testEnvironmentBase = { geno: true, genoFilters: true, fitbitCDRUpdate: true, - infiniteSrcoll: true + infiniteSrcoll: true, }; diff --git a/public-ui/src/types/index.d.ts b/public-ui/src/types/index.d.ts index 794ef3752..9972e323c 100644 --- a/public-ui/src/types/index.d.ts +++ b/public-ui/src/types/index.d.ts @@ -6,4 +6,4 @@ declare global { setPublicApiUrl: (...args: any[]) => void; dataLayer: any; } -} \ No newline at end of file +} From bd610d12a45f56225eb75b4370a11d435d6acafc Mon Sep 17 00:00:00 2001 From: Srushti Gangireddy Date: Mon, 12 Aug 2024 15:47:34 -0500 Subject: [PATCH 4/7] trying to fix errors --- .../data-browser/views/genomic-view/genomic-view.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx index 7b1593f7d..d23652c74 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import _ from "lodash"; -import { SVGenomicSearchComponent } from "../sv-genomic-view/components/sv-genomic-search.component"; +import { SVGenomicSearchComponent } from "app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component"; import { withRouteData } from "app/components/app-router"; import { GenomicOverviewComponent } from "app/data-browser/views/genomic-view/components/genomic-overview.component"; import { genomicsApi } from "app/services/swagger-fetch-clients"; From f172058074cd1db71d991eb084737d2558d7847c Mon Sep 17 00:00:00 2001 From: Srushti Gangireddy Date: Mon, 12 Aug 2024 15:50:51 -0500 Subject: [PATCH 5/7] trying to fix errors --- .../components/sv-genomic-search.component.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx index 29d67392d..7317947eb 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-genomic-search.component.tsx @@ -1,8 +1,8 @@ import * as React from "react"; import { environment } from "environments/environment"; -import { SVVariantSearchComponent } from "../../sv-genomic-view/components/sv-variant-search.component"; -import { SVVariantTableComponent } from "../../sv-genomic-view/components/sv-variant-table.component"; +import { SVVariantSearchComponent } from "app/data-browser/views/sv-genomic-view/components/sv-variant-search.component"; +import { SVVariantTableComponent } from "app/data-browser/views/sv-genomic-view/components/sv-variant-table.component"; import { reactStyles } from "app/utils"; import { GenomicFilters, SVVariant } from "publicGenerated"; import { SortMetadata } from "publicGenerated/fetch"; From efb17fcbe36bae19b803bd3d53fb188912351e92 Mon Sep 17 00:00:00 2001 From: Srushti Gangireddy Date: Tue, 13 Aug 2024 16:43:04 -0500 Subject: [PATCH 6/7] making size filter work --- .../publicapi/GenomicsController.java | 50 +++++++++++++++++-- public-api/src/main/resources/public-api.yaml | 4 ++ .../genomic-view/genomic-view.component.tsx | 1 + .../sv-variant-filter-chips.component.tsx | 2 +- .../sv-variant-filter-item.component.tsx | 3 +- .../sv-variant-filter.component.tsx | 4 +- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java b/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java index 466e26d5c..6a76fd035 100644 --- a/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java +++ b/public-api/src/main/java/org/pmiops/workbench/publicapi/GenomicsController.java @@ -171,19 +171,27 @@ public class GenomicsController implements GenomicsApiDelegate { "0 as min_count, 0 as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id tj, \n" + " unnest(split(consequence, ', ')) con\n"; - private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT = " group by con),\n" + + + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_SIZE = " group by con),\n" + "e as \n" + + "(select distinct 'Size' as option, '' as genes, '' as variant_type, '' as consequence, \n" + + "min(size) as min_count, max(size) as max_count\n" + + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT = "),\n" + + "f as \n" + "(select distinct 'Allele Count' as option, '' as genes, '' as variant_type, '' as consequence, \n" + "min(allele_count) as min_count, max(allele_count) as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; + + private static final String SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_NUMBER = "),\n" + - "f as \n" + + "g as \n" + "(select distinct 'Allele Number' as option, '' as genes, '' as variant_type, '' as consequence, \n" + "min(allele_number) as min_count, max(allele_number) as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; private static final String SV_FILTER_OPTION_SQL_TEMPLATE_HOMOZYGOTE_COUNT = "),\n" + - "g as \n" + + "h as \n" + "(select distinct 'Homozygote Count' as option, '' as genes, '' as variant_type, '' as consequence, \n" + "min(homozygote_count) as min_count, max(homozygote_count) as max_count\n" + "from ${projectId}.${dataSetId}.selected_sv_fields_db_with_id\n"; @@ -198,7 +206,9 @@ public class GenomicsController implements GenomicsApiDelegate { "union all \n" + "select * from f \n" + "union all \n" + - "select * from g;"; + "select * from g \n" + + "union all \n" + + "select * from h;"; public GenomicsController() {} @@ -466,6 +476,7 @@ public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequ String WHERE_VAR_TYPE_IN = "AND variant_type in ("; String WHERE_CON_NULL = ""; String WHERE_SV_CON_IN = " AND (EXISTS (SELECT con FROM UNNEST (split(consequence, ', ')) as con where con in ( \n"; + String SIZE_FILTER = ""; String ALLELE_COUNT_FILTER = ""; String ALLELE_NUMBER_FILTER = ""; String ALLELE_FREQUENCY_FILTER = ""; @@ -510,6 +521,12 @@ public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequ } } } + SVGenomicFilterOption sizeFilter = filters.getSize(); + if (sizeFilter != null && sizeFilter.isChecked()) { + Long minVal = sizeFilter.getMin(); + Long maxVal = sizeFilter.getMax(); + SIZE_FILTER = " AND size BETWEEN " + minVal + " AND " + maxVal; + } SVGenomicFilterOption acFilter = filters.getAlleleCount(); if (acFilter != null && acFilter.isChecked()) { Long minVal = acFilter.getMin(); @@ -576,6 +593,9 @@ public ResponseEntity getSVVariantSearchResultSize(SVVariantResultSizeRequ finalSql += ") "; } + if (SIZE_FILTER.length() > 0) { + finalSql += SIZE_FILTER; + } if (ALLELE_COUNT_FILTER.length() > 0) { finalSql += ALLELE_COUNT_FILTER; } @@ -748,7 +768,7 @@ public ResponseEntity searchSVVariants(SearchSVVariantsRe String WHERE_CON_NULL = ""; String WHERE_SV_CON_IN = " AND (EXISTS (SELECT con FROM UNNEST (split(consequence, ', ')) as con where con in ( \n"; - + String SIZE_FILTER = ""; String ALLELE_COUNT_FILTER = ""; String ALLELE_NUMBER_FILTER = ""; String ALLELE_FREQUENCY_FILTER = ""; @@ -798,6 +818,13 @@ public ResponseEntity searchSVVariants(SearchSVVariantsRe } } + SVGenomicFilterOption sizeFilter = filters.getSize(); + if (sizeFilter != null && sizeFilter.isChecked()) { + Long minVal = sizeFilter.getMin(); + Long maxVal = sizeFilter.getMax(); + SIZE_FILTER = " AND size BETWEEN " + minVal + " AND " + maxVal; + } + SVGenomicFilterOption acFilter = filters.getAlleleCount(); if (acFilter != null && acFilter.isChecked()) { Long minVal = acFilter.getMin(); @@ -866,6 +893,9 @@ public ResponseEntity searchSVVariants(SearchSVVariantsRe } } + if (SIZE_FILTER.length() > 0) { + finalSql += SIZE_FILTER; + } if (ALLELE_COUNT_FILTER.length() > 0) { finalSql += ALLELE_COUNT_FILTER; } @@ -1447,6 +1477,7 @@ public ResponseEntity getSVGenomicFilterOptions(String variant finalSql = SV_FILTER_OPTION_SQL_TEMPLATE_GENE + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_VAR_TYPE + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_CON + searchSqlQuery + + SV_FILTER_OPTION_SQL_TEMPLATE_SIZE + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_COUNT + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_ALLELE_NUMBER + searchSqlQuery + SV_FILTER_OPTION_SQL_TEMPLATE_HOMOZYGOTE_COUNT + searchSqlQuery @@ -1478,6 +1509,7 @@ public ResponseEntity getSVGenomicFilterOptions(String variant List geneFilters = new ArrayList<>(); List conFilters = new ArrayList<>(); + SVGenomicFilterOption sizeFilter = new SVGenomicFilterOption(); SVGenomicFilterOption alleleCountFilter = new SVGenomicFilterOption(); SVGenomicFilterOption alleleNumberFilter = new SVGenomicFilterOption(); SVGenomicFilterOption homozygoteCountFilter = new SVGenomicFilterOption(); @@ -1508,6 +1540,13 @@ public ResponseEntity getSVGenomicFilterOptions(String variant genomicFilterOption.setMin(0L); genomicFilterOption.setMax(0L); conFilters.add(genomicFilterOption); + } else if (option.equals("Size")) { + genomicFilterOption.setOption(""); + genomicFilterOption.setCount(0L); + genomicFilterOption.setChecked(false); + genomicFilterOption.setMin(minCount); + genomicFilterOption.setMax(maxCount); + sizeFilter = genomicFilterOption; } else if (option.equals("Allele Count")) { genomicFilterOption.setOption(""); genomicFilterOption.setCount(0L); @@ -1551,6 +1590,7 @@ public ResponseEntity getSVGenomicFilterOptions(String variant genomicFilters.consequence(conFilterList); genomicFilters.variantType(varTypeFilterList); genomicFilters.gene(geneFilterList); + genomicFilters.size(sizeFilter); genomicFilters.alleleCount(alleleCountFilter); genomicFilters.alleleNumber(alleleNumberFilter); genomicFilters.alleleFrequency(alleleFrequencyFilter); diff --git a/public-api/src/main/resources/public-api.yaml b/public-api/src/main/resources/public-api.yaml index 0a8283dc2..01b6c5e75 100644 --- a/public-api/src/main/resources/public-api.yaml +++ b/public-api/src/main/resources/public-api.yaml @@ -1780,6 +1780,10 @@ definitions: description: Variant Type Filters type: object $ref: "#/definitions/SVGenomicFilterOptionList" + size: + description: Size Filters + type: object + $ref: "#/definitions/SVGenomicFilterOption" alleleCount: description: Allele Count Range Filter type: object diff --git a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx index d23652c74..d42ffd454 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx @@ -402,6 +402,7 @@ export const GenomicViewComponent = withRouteData( genomicsApi() .getSVGenomicFilterOptions(searchTerm) .then((result) => { + console.log(result); result.gene.items.forEach((el) => { el.checked = false; }); diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx index 53cf4b84d..6a93b9461 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-chips.component.tsx @@ -113,7 +113,7 @@ export class SVVariantFilterChips extends React.Component { filteredMetadata[cat.toString()].checked = false; try { const originalFilterMetadata = JSON.parse( - localStorage.getItem("originalFilterMetadata") || "{}" + localStorage.getItem("svOriginalFilterMetadata") || "{}" ); filteredMetadata[cat.toString()].min = originalFilterMetadata[cat.toString()].min; diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx index 90f97a5e9..97bcd4916 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter-item.component.tsx @@ -109,7 +109,7 @@ export class SVVariantFilterItemComponent extends React.Component< filterItemState: props.filterItem || "", filterCheckMap: props.filterItem || "", ogFilterMetaData: JSON.parse( - localStorage.getItem("originalFilterMetadata") || "{}" + localStorage.getItem("svOriginalFilterMetadata") || "{}" )[this.props.category.field.toString()], }; } @@ -172,7 +172,6 @@ export class SVVariantFilterItemComponent extends React.Component< render(): React.ReactNode { const { category, cleared, filterItem } = this.props; const { filterItemOpen, filterItemState, ogFilterMetaData } = this.state; - return ( diff --git a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx index 036eff8b1..f1961dcae 100644 --- a/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx +++ b/public-ui/src/app/data-browser/views/sv-genomic-view/components/sv-variant-filter.component.tsx @@ -101,7 +101,7 @@ export class SVVariantFilterComponent extends React.Component { filterMetadata: this.props.filterMetadata, cleared: true, ogFilterMetaData: JSON.parse( - localStorage.getItem("originalFilterMetadata") || "{}" + localStorage.getItem("svOriginalFilterMetadata") || "{}" ), sortMetadata: this.props.sortMetadata, }; @@ -139,7 +139,7 @@ export class SVVariantFilterComponent extends React.Component { handleClear() { const ogFilterMetaData = JSON.parse( - localStorage.getItem("originalFilterMetadata") || "{}" + localStorage.getItem("svOriginalFilterMetadata") || "{}" ); // tslint:disable-next-line: forin for (const key in this.state.filteredMetadata) { From 8a78319fb39e86c6110d7a2100d3626df626e7d5 Mon Sep 17 00:00:00 2001 From: Srushti Gangireddy Date: Wed, 14 Aug 2024 11:45:41 -0500 Subject: [PATCH 7/7] making size filter work --- .../views/genomic-view/genomic-view.component.tsx | 8 ++++++-- public-ui/src/environments/environment.prod.ts | 1 + public-ui/src/environments/environment.stable.ts | 1 + public-ui/src/environments/environment.staging.ts | 1 + public-ui/src/environments/test-env-base.ts | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx index d42ffd454..b7c45e1f4 100644 --- a/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx +++ b/public-ui/src/app/data-browser/views/genomic-view/genomic-view.component.tsx @@ -8,6 +8,7 @@ import { genomicsApi } from "app/services/swagger-fetch-clients"; import { reactStyles } from "app/utils"; import { triggerEvent } from "app/utils/google_analytics"; import { urlParamsStore } from "app/utils/navigation"; +import { environment } from "environments/environment"; import { GenomicFilters, SearchSVVariantsRequest, @@ -282,6 +283,8 @@ export const GenomicViewComponent = withRouteData( }; } + svVCFBrowserFlag = environment.svVCFBrowser; + topBarItems = [ { id: 2, @@ -291,11 +294,12 @@ export const GenomicViewComponent = withRouteData( id: 1, label: "Participant Demographics", }, - { + ...(this.svVCFBrowserFlag ? [{ id: 4, label: "SV Variants", - }, + }] : []) ]; + title = "SNV/Indel Variants"; search = _.debounce((searchTerm: string) => { diff --git a/public-ui/src/environments/environment.prod.ts b/public-ui/src/environments/environment.prod.ts index 729c2356f..abf80288d 100644 --- a/public-ui/src/environments/environment.prod.ts +++ b/public-ui/src/environments/environment.prod.ts @@ -13,4 +13,5 @@ export const environment = { genoFilters: true, fitbitCDRUpdate: true, infiniteSrcoll: true, + svVCFBrowser: false, }; diff --git a/public-ui/src/environments/environment.stable.ts b/public-ui/src/environments/environment.stable.ts index ad2f97611..bd0b730fd 100644 --- a/public-ui/src/environments/environment.stable.ts +++ b/public-ui/src/environments/environment.stable.ts @@ -14,4 +14,5 @@ export const environment = { genoFilters: true, fitbitCDRUpdate: true, infiniteSrcoll: true, + svVCFBrowser: false, }; diff --git a/public-ui/src/environments/environment.staging.ts b/public-ui/src/environments/environment.staging.ts index 71da8cd56..502c0a2a3 100644 --- a/public-ui/src/environments/environment.staging.ts +++ b/public-ui/src/environments/environment.staging.ts @@ -14,4 +14,5 @@ export const environment = { genoFilters: true, fitbitCDRUpdate: true, infiniteSrcoll: true, + svVCFBrowser: false, }; diff --git a/public-ui/src/environments/test-env-base.ts b/public-ui/src/environments/test-env-base.ts index 9e62e4a7b..fca3c473b 100644 --- a/public-ui/src/environments/test-env-base.ts +++ b/public-ui/src/environments/test-env-base.ts @@ -10,4 +10,5 @@ export const testEnvironmentBase = { genoFilters: true, fitbitCDRUpdate: true, infiniteSrcoll: true, + svVCFBrowser: true, };