From e2f807446ddcd34564fec094a67fa842a3812a27 Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 16 Jun 2023 15:19:51 -0400 Subject: [PATCH 01/13] feat: Add no results text when search has no results. Fix error message in certain layouts to behave like no results text. --- src/components/search/context.js | 2 ++ .../concept-search-results.js | 16 ++++++++++++---- .../concepts-grid-layout/concepts-grid-layout.js | 2 -- .../expanded-results-layout.js | 14 ++++++++++++-- .../variable-view-layout/variable-view-layout.js | 15 ++++++++++++--- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/components/search/context.js b/src/components/search/context.js index e782c539..4c97a8d3 100644 --- a/src/components/search/context.js +++ b/src/components/search/context.js @@ -228,6 +228,7 @@ export const HelxSearch = ({ children }) => { useEffect(() => { setConceptPages({}) + setError({}) setTypeFilter(null) setSelectedResult(null) setVariableStudyResults([]) @@ -279,6 +280,7 @@ export const HelxSearch = ({ children }) => { } catch (error) { console.log(error) setError({ message: 'An error occurred!' }) + setTotalConcepts(0) setIsLoadingConcepts(false) analyticsEvents.searchExecuted(query, Date.now() - startTime, 0, error) } diff --git a/src/components/search/results/concepts-grid-layout/concept-search-results.js b/src/components/search/results/concepts-grid-layout/concept-search-results.js index a3a42d77..2c5aa48b 100644 --- a/src/components/search/results/concepts-grid-layout/concept-search-results.js +++ b/src/components/search/results/concepts-grid-layout/concept-search-results.js @@ -1,5 +1,5 @@ import React, { Fragment, useMemo, useCallback } from 'react' -import { Spin, Grid as AntGrid, Typography, Radio, Tooltip } from 'antd' +import { Spin, Grid as AntGrid, Typography, Radio, Tooltip, Empty } from 'antd' import { ConceptCard, useHelxSearch, SearchLayout, ExpandedResultsLayout } from '../../' import InfiniteScroll from 'react-infinite-scroll-component' import { BackTop } from '../../../layout' @@ -9,8 +9,8 @@ const { Text } = Typography const { useBreakpoint } = AntGrid export const ConceptSearchResults = () => { - const { query, conceptPages, perPage, currentPage, pageCount, typeFilter, isLoadingVariableResults, - isLoadingConcepts, error, layout, setCurrentPage, setSelectedResult } = useHelxSearch() + const { query, conceptPages, perPage, currentPage, pageCount, typeFilter, + isLoadingConcepts, error, setCurrentPage, setSelectedResult } = useHelxSearch() const { md } = useBreakpoint(); const concepts = useMemo(() => Object.values(conceptPages).flat(), [conceptPages]) const hasMore = useMemo(() => ( @@ -25,9 +25,17 @@ export const ConceptSearchResults = () => { return ( { - query && !error.message && ( + query && (
+ { error.message ? ( + { error.message } + ) : concepts.length === 0 && !isLoadingConcepts ? ( + No results were found for "{ query }" + } /> + // No results found + ) : null } {concepts.length > 0 && } { - const { error } = useHelxSearch() return ( - { error && { error.message } } ) diff --git a/src/components/search/results/expanded-results-layout/expanded-results-layout.js b/src/components/search/results/expanded-results-layout/expanded-results-layout.js index f349ab16..185856a9 100644 --- a/src/components/search/results/expanded-results-layout/expanded-results-layout.js +++ b/src/components/search/results/expanded-results-layout/expanded-results-layout.js @@ -1,5 +1,5 @@ import { Fragment, useEffect, useState } from 'react' -import { Divider, Grid, Spin } from 'antd' +import { Divider, Grid, Spin, Typography, Empty } from 'antd' import { SearchForm, useHelxSearch } from '../..' import { ExpandedResultsSidebar } from './expanded-results-sidebar' import { ExpandedResultsContent } from './expanded-results-content' @@ -7,10 +7,12 @@ import { ResultsHeader } from '..' import classNames from 'classnames' import './expanded-results-layout.css' + +const { Text } = Typography const { useBreakpoint } = Grid export const ExpandedResultsLayout = () => { - const { selectedResult, setSelectedResult, concepts, totalConcepts, isLoadingConcepts, query } = useHelxSearch() + const { selectedResult, setSelectedResult, concepts, totalConcepts, isLoadingConcepts, query, error } = useHelxSearch() const { md } = useBreakpoint() const [expanded, setExpanded] = useState(true) @@ -52,6 +54,14 @@ export const ExpandedResultsLayout = () => { {isLoadingConcepts && totalConcepts === 0 && ( )} + { error.message ? ( + { error.message } + ) : concepts.length === 0 && !isLoadingConcepts ? ( + No results were found for "{ query }" + } /> + // No results found + ) : null } {totalConcepts > 0 && ( {/* { md && } */} diff --git a/src/components/search/results/variable-view-layout/variable-view-layout.js b/src/components/search/results/variable-view-layout/variable-view-layout.js index 953f5fe4..44e4c759 100644 --- a/src/components/search/results/variable-view-layout/variable-view-layout.js +++ b/src/components/search/results/variable-view-layout/variable-view-layout.js @@ -1,20 +1,29 @@ import { Fragment } from 'react' -import { Spin } from 'antd' +import { Spin, Typography, Empty } from 'antd' import { VariableSearchResults } from './' import { ResultsHeader } from '../' import { useHelxSearch, SearchForm } from '../../' +const { Text } = Typography + export const VariableViewLayout = () => { - const { error, totalVariableResults, isLoadingVariableResults } = useHelxSearch() + const { query, error, totalVariableResults, isLoadingVariableResults } = useHelxSearch() return ( - { error && { error.message } } { isLoadingVariableResults ? ( ) : ( + { error.message ? ( + { error.message } + ) : query && totalVariableResults === 0 ? ( + No results were found for "{ query }" + } /> + // No results found + ) : null } {totalVariableResults > 0 && } From 36019025e8a408d4a018e98e1168e3b13862c637 Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 16 Jun 2023 16:47:17 -0400 Subject: [PATCH 02/13] feat: Add count indicator to studies/cdes in result modal. Add bouncing dots component --- src/components/bouncing-dots.js | 27 +++++++++++++++++++ src/components/index.js | 3 ++- .../search/concept-modal/concept-modal.js | 23 +++++++++++----- 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 src/components/bouncing-dots.js diff --git a/src/components/bouncing-dots.js b/src/components/bouncing-dots.js new file mode 100644 index 00000000..0f5fdacb --- /dev/null +++ b/src/components/bouncing-dots.js @@ -0,0 +1,27 @@ +export const BouncingDots = ({ height=8 }) => { + return ( + // Functionality pulled from antd mobile library + + + + + + + + + + + + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/src/components/index.js b/src/components/index.js index 0fdd9880..54b3e9f1 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,4 +1,5 @@ export * from './workspaces/navigation-tab-group' export * from './link' export * from './notifications' -export * from './debounced-input' \ No newline at end of file +export * from './debounced-input' +export * from './bouncing-dots' \ No newline at end of file diff --git a/src/components/search/concept-modal/concept-modal.js b/src/components/search/concept-modal/concept-modal.js index 76e18f47..700acfbf 100644 --- a/src/components/search/concept-modal/concept-modal.js +++ b/src/components/search/concept-modal/concept-modal.js @@ -13,6 +13,7 @@ import CustomIcon, { } from '@ant-design/icons' import { CdesTab, OverviewTab, StudiesTab, KnowledgeGraphsTab, TranQLTab } from './tabs' import { useHelxSearch } from '../' +import { BouncingDots } from '../../' import { useAnalytics, useEnvironment } from '../../../contexts' import { kgLink } from '../../../utils' import './concept-modal.css' @@ -84,13 +85,23 @@ export const ConceptModalBody = ({ result }) => { const fetchCdesTranqlController = useRef([]) const fetchKgsController = useRef() + const studyTitle = ( +
+ Studies { studies ? `(${ Object.keys(studies).length })` : } +
+ ) + const cdeTitle = ( +
+ CDEs { cdes ? `(${ Object.keys(cdes).length })` : } +
+ ) + const tabs = { - 'overview': { title: 'Overview', icon: , content: , }, - 'studies': { title: 'Studies', icon: , content: , }, - 'cdes': { title: `CDEs`, icon: , content: }, - 'kgs': { title: 'Knowledge Graphs', icon: , content: , }, - 'tranql': { title: 'TranQL', icon: , content: }, - // 'robokop': { title: 'Robokop', icon: , content: } + 'overview': { title: 'Overview', icon: , content: , }, + 'studies': { title: studyTitle, icon: , content: , }, + 'cdes': { title: cdeTitle, icon: , content: }, + 'kgs': { title: 'Knowledge Graphs', icon: , content: , }, + 'tranql': { title: 'TranQL', icon: , content: } } const links = { 'robokop' : { title: 'ROBOKOP', icon: , url: "https://robokop.renci.org/" } From 1044a2ee7d2da84e7ec59e91dda2338272cea61c Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 16 Jun 2023 16:49:58 -0400 Subject: [PATCH 03/13] fix: Code style fix --- src/components/search/concept-modal/concept-modal.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/search/concept-modal/concept-modal.js b/src/components/search/concept-modal/concept-modal.js index 700acfbf..18383ca7 100644 --- a/src/components/search/concept-modal/concept-modal.js +++ b/src/components/search/concept-modal/concept-modal.js @@ -97,11 +97,11 @@ export const ConceptModalBody = ({ result }) => { ) const tabs = { - 'overview': { title: 'Overview', icon: , content: , }, - 'studies': { title: studyTitle, icon: , content: , }, - 'cdes': { title: cdeTitle, icon: , content: }, - 'kgs': { title: 'Knowledge Graphs', icon: , content: , }, - 'tranql': { title: 'TranQL', icon: , content: } + 'overview': { title: 'Overview', icon: , content: , }, + 'studies': { title: studyTitle, icon: , content: , }, + 'cdes': { title: cdeTitle, icon: , content: }, + 'kgs': { title: 'Knowledge Graphs', icon: , content: , }, + 'tranql': { title: 'TranQL', icon: , content: } } const links = { 'robokop' : { title: 'ROBOKOP', icon: , url: "https://robokop.renci.org/" } From 69f3e6c5318c55812e87f2211af6b728b5b88ece Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Tue, 27 Jun 2023 12:50:38 -0400 Subject: [PATCH 04/13] feat: Add study/cde loading indicators to concept card tabs --- src/components/bouncing-dots.js | 4 +-- .../search/concept-card/concept-card.js | 30 +++++++++++++++---- .../search/concept-modal/concept-modal.js | 2 +- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/components/bouncing-dots.js b/src/components/bouncing-dots.js index 0f5fdacb..6f8f28e2 100644 --- a/src/components/bouncing-dots.js +++ b/src/components/bouncing-dots.js @@ -1,7 +1,7 @@ -export const BouncingDots = ({ height=8 }) => { +export const BouncingDots = ({ height=8, style={}, ...props }) => { return ( // Functionality pulled from antd mobile library - + diff --git a/src/components/search/concept-card/concept-card.js b/src/components/search/concept-card/concept-card.js index b6080137..3eb34fad 100644 --- a/src/components/search/concept-card/concept-card.js +++ b/src/components/search/concept-card/concept-card.js @@ -1,11 +1,12 @@ import { Fragment, useState, useEffect, useMemo, forwardRef } from 'react' import PropTypes from 'prop-types' import { Badge, Card, Space, Typography } from 'antd' -import { ExpandOutlined as ViewIcon } from '@ant-design/icons' +import { ExpandOutlined as ViewIcon, LoadingOutlined } from '@ant-design/icons' import { useHelxSearch } from '../' import { OverviewTab } from './overview-tab' import { StudiesTab } from './studies-tab' // import { CdesTab } from './cdes-tab' +import { BouncingDots } from '../../' import { useAnalytics, useEnvironment } from '../../../contexts' import classNames from 'classnames' import './concept-card.css' @@ -23,11 +24,30 @@ export const ConceptCard = forwardRef(({ index, result, openModalHandler, icon=V const { context } = useEnvironment() const { query, fetchVariablesForConceptId, fetchCDEs } = useHelxSearch() + const studyTitle = useMemo(() => ( +
+ { !studies && ( + + ) } + Studies + { studies && ` (${ Object.keys(studies).length })` } +
+ ), [studies]) + const cdeTitle = useMemo(() => ( +
+ { !cdeStudies && ( + + ) } + CDEs + { cdeStudies && ` (${ Object.keys(cdeStudies).length })` } +
+ ), [cdeStudies]) + const tabs = useMemo(() => ({ - 'overview': { title: 'Overview', content: }, - 'studies': { title: `Studies`, content: }, - 'cdes': { title: `CDEs`, content: }, - }), [result, studies, cdeStudies]) + 'overview': { title: 'Overview', content: }, + 'studies': { title: studyTitle, content: }, + 'cdes': { title: cdeTitle, content: }, + }), [result, studies, cdeStudies, studyTitle, cdeTitle]) context.hidden_result_tabs.forEach((tab) => { delete tabs[tab] diff --git a/src/components/search/concept-modal/concept-modal.js b/src/components/search/concept-modal/concept-modal.js index 18383ca7..fd3ea6e5 100644 --- a/src/components/search/concept-modal/concept-modal.js +++ b/src/components/search/concept-modal/concept-modal.js @@ -92,7 +92,7 @@ export const ConceptModalBody = ({ result }) => { ) const cdeTitle = (
- CDEs { cdes ? `(${ Object.keys(cdes).length })` : } + CDEs { cdes ? `(${ Object.keys(cdes).length })` : }
) From a2c9667ac04b2a0ae8b3ce0c3a127c2b08dd0fcf Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Thu, 29 Jun 2023 16:57:25 -0400 Subject: [PATCH 05/13] fix: Remove manifest.json for now. This file is used for installing SPAs locally as desktop applications --- public/{manifest.json => _manifest.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename public/{manifest.json => _manifest.json} (100%) diff --git a/public/manifest.json b/public/_manifest.json similarity index 100% rename from public/manifest.json rename to public/_manifest.json From 9fc85f3b0187310379a82953a857159d1d3b2036 Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Thu, 29 Jun 2023 16:58:56 -0400 Subject: [PATCH 06/13] fix: Readd manifest.json and remove meta reference in index file --- public/{_manifest.json => manifest.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename public/{_manifest.json => manifest.json} (100%) diff --git a/public/_manifest.json b/public/manifest.json similarity index 100% rename from public/_manifest.json rename to public/manifest.json From bfa4f33b4f9ef7971238d85c3e5833ac2229eb56 Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 30 Jun 2023 14:40:40 -0400 Subject: [PATCH 07/13] feat: Add dynamic title/description meta tags --- Dockerfile | 2 +- bin/create_index | 15 +++++++++++++++ bin/start_server | 7 +++++++ public/favicon.ico | Bin 3870 -> 99678 bytes public/index.ejs | 12 +++++++----- webpack.base.config.js | 5 +++-- 6 files changed, 33 insertions(+), 8 deletions(-) create mode 100755 bin/create_index diff --git a/Dockerfile b/Dockerfile index e30f97a2..e59f3463 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ RUN npm run build FROM nginx:latest COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf COPY --from=builder /usr/src/app/build/ /usr/share/nginx/static/ -RUN mv /usr/share/nginx/static/frontend/index.html /usr/share/nginx/html/ +# RUN mv /usr/share/nginx/static/frontend/index.html /usr/share/nginx/html/ WORKDIR /usr/src/app COPY bin /usr/src/app/bin diff --git a/bin/create_index b/bin/create_index new file mode 100755 index 00000000..9030b542 --- /dev/null +++ b/bin/create_index @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if [ -z "$1" ] + then + echo "Usage: create_index /path/to/build" + exit 1 +fi + +title="${META_TITLE:-HeLx UI}" +description="${META_DESCRIPTION:-HeLx UI}" + +cat "$1/index_template.html" | sed \ + -e "s/%META_TITLE%/$title/" \ + -e "s/%META_DESCRIPTION%/$description/" \ + > $1/index.html \ No newline at end of file diff --git a/bin/start_server b/bin/start_server index 010b8136..b9863566 100755 --- a/bin/start_server +++ b/bin/start_server @@ -6,5 +6,12 @@ if [ -z "$1" ] echo "Usage: start_server /path/to/output/env.json" exit 1 fi + +# Create env.json file populate_env $1 +# Generate the index.html file +create_index /usr/share/nginx/static/frontend +# Move into nginx html folder +mv /usr/share/nginx/static/frontend/index.html /usr/share/nginx/html/ + nginx -g "daemon off;" \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico index a11777cc471a4344702741ab1c8a588998b1311a..491d22e5026aae3b034515ebda7546daf35dc9cc 100644 GIT binary patch literal 99678 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%0DEm$m=2ZLGzq0NZ; zKa8Pm)xM9~{?~dFqPmU>aNo?hV($h@S_H;n+q0KTmoj-v8-;pEKPj zYO@HAgna(hiy-9Jy#_-45%Yhvlem`eJ-@Xz`f|`~ps)_c)6cP&1ehb*+I=cSQUj`qq|yeP8gQ zb(drf*nVv|2mJT)m%=7s{;-D7XL0{W-wbE0|EGtsd`I(Fi2Yy3b$%bPygHZ%eXRk{ zlw6Y#54-;h{cp?lJrM2o%&+YGqTPiyLs|p&75eb+L(k0Hzve{$_vw;j~{dzgIZYrC&NY-jwxuo^_YhSmRte*GJ5F2u3>^nF|hg|@DNhYD>6 zd>*LL$C|YF40r*q2elfOH4yqK`hO95UxzvHUrWGMv~{j(R^0#E-3;+vK-Bd2Jecujo+yQ-G!+)NAj}`i;dkxqZ?ZjunlfdUE z-!~l%OZxLXcpQ{qz3F=nuf5%i;p9Q)hYU{OY{C;q6{|Fw2mmt3u4mnHB%rXDByzT<74Liy99U`%LNI%1uz2ceoyI|GN*K z0c{$z@1uUfHf=4gfkmVbu&mw?`tk3?3&H!Qct5D@gL?al^=KRY@Cet=bHx2$HMaBF z6+Q_*iwUNOFed-*1-aQIVgEQe6D1kOE=J(ha zWzJ*R{BOUZPkruvpwF?R4FkoYKl`<>n$Nm}=;NdG5&iETxf~h?YF}MWo2BmmYMX|V z4)YjR{}08E(P$Iq>T^K!zwwLkuECBU`dX^{f3P;!svqXD-1|Sw)AQpqQ1rjoKdhxq ze|e$5m?VOb;eqd&ywPX8UiPB0U$hR4BYy;{Eao%&hH|AYVj7u}-fAL9CHSO|UT?7Ju5 z!94}85C1m29linHlP?DQ@%hYi`t7g}>o~Hq3;#fc|!ze6JHP zaj*~2|Br%e?-|$zh8%1QYT~@?)BT_(BNpr%j({$3%v)haSn5F=0u98TC)(w{RinsC zAz$D6ENR`m-AGO#jIkE+eV}enz+@P5uq`x#>)AeiUfK;tEZ8@AHhWGw=8d4%DfFX& z`he^0B)A)VudMHe9BdmnjM!%<&%P$}Mk?4htO;KP=i&LA+c8pOE-(ECom>N}L4I#p z(z%5`J;UsC#LwX{p8ob3-nEvWpG!KA)IKSY6i5mr1(E_ufuulEASsX(ND3qck^)JA zq(D+2DUcLM3M2)R0!e|SKvEznkQ7J?Bn6TJNr9w5QXnai6i5mr1(E_ufuulEASsX( zND3qck^)JAq(D+2DUcLM3M2)R0!e|SKvEznkQ7J?Bn6TJNr9w5QXnai6i5mr1(E_u zfuulEASsX(ND3qck^)JAq(D+2DNt0PoI+AySyG^*xtBFvS?_d5*4nGQt<)=7Zs?sX zkF82K4v-$(-CBO7yP=$AJv}unDwXFi%09-PY3*(3fpqgC z((Thow`JoEy<#HG4_N?`SCc17bm&@IG zy3|Jc{M>>z%bVLtx94-|AU(e6kEA>DxnNKGMvdL1JDd1N+4Q8bdAivKrkfi~cQm%g zberk+v7M!!@}~BWk#1|~;-A)U>X^smI>AdAecjq6t0o>1egQA)ntO z=1XJK%)hBjs)-KCQlF-BN2#l+yD?9rAL-WaCI+y)Gh@7ta=EE|exB~iD0-<}zNXxs zr;!DF4Atqjs`9Lo?soEBucf?zpK?liLRV|~39gz$GDS&&Wl4em2mLo$_5c6? literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/public/index.ejs b/public/index.ejs index f8a0b7dd..1776298d 100644 --- a/public/index.ejs +++ b/public/index.ejs @@ -2,19 +2,21 @@ - + - + - + - HeLx UI + %META_TITLE% diff --git a/webpack.base.config.js b/webpack.base.config.js index 879f64ca..be8afec8 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -90,12 +90,13 @@ const baseConfig = { { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource', - } + }, ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(paths.public, 'index.ejs'), + filename: "index_template.html", templateParameters: { publicUrl: '/static/frontend/' }, @@ -105,7 +106,7 @@ const baseConfig = { minifyCSS: true, minifyJS: true, minifyURLs: true, - removeAttributeQuotes: true, + removeAttributeQuotes: false, removeComments: true, removeEmptyAttributes: true, removeOptionalTags: true, From b0d59047a63e3085536e92c773dcd75d9d005641 Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 7 Jul 2023 11:28:45 -0400 Subject: [PATCH 08/13] fix: Some minor fixes --- bin/populate_env | 10 +++++++++- webpack.base.config.js | 2 +- webpack.dev.config.js | 6 ++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/bin/populate_env b/bin/populate_env index e88ed36e..d99de9b6 100755 --- a/bin/populate_env +++ b/bin/populate_env @@ -16,6 +16,8 @@ hidden_support_sections="${REACT_APP_HIDDEN_SUPPORT_SECTIONS}" hidden_result_tabs="${REACT_APP_HIDDEN_RESULT_TABS}" deployment_namespace="${REACT_APP_DEPLOYMENT_NAMESPACE}" appstore_asset_branch="${REACT_APP_APPSTORE_ASSET_BRANCH}" +meta_title="${META_TITLE:-HeLx UI}" +meta_description="${META_DESCRIPTION:-HeLx UI}" template='{ @@ -36,7 +38,11 @@ template='{ "hidden_support_sections": "%HIDDEN_SUPPORT_SECTIONS%", "hidden_result_tabs": "%HIDDEN_RESULT_TABS%", "deployment_namespace": "%DEPLOYMENT_NAMESPACE%", - "appstore_asset_branch": "%APPSTORE_ASSET_BRANCH%" + "appstore_asset_branch": "%APPSTORE_ASSET_BRANCH%", + "meta": { + "title": "%META_TITLE%", + "description": "%META_DESCRIPTION%" + } }' echo "$template" | sed \ @@ -50,4 +56,6 @@ echo "$template" | sed \ -e "s/%TRANQL_URL%/$tranql_url/" \ -e "s/%DEPLOYMENT_NAMESPACE%/$deployment_namespace/" \ -e "s/%APPSTORE_ASSET_BRANCH%/$appstore_asset_branch/" \ + -e "s/%META_TITLE%/$meta_title/" \ + -e "s/%META_DESCRIPTION%/$meta_description/" \ > $1 \ No newline at end of file diff --git a/webpack.base.config.js b/webpack.base.config.js index be8afec8..c79f4d6a 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -112,7 +112,7 @@ const baseConfig = { removeOptionalTags: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributese: true, + removeStyleLinkTypeAttributes: true, useShortDoctype: true } }), diff --git a/webpack.dev.config.js b/webpack.dev.config.js index d6f5059c..193936f0 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.js @@ -1,4 +1,3 @@ -const express = require('express') const path = require('path') const webpack = require('webpack') const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin') @@ -6,12 +5,15 @@ const ForkTsCheckerPlugin = require('fork-ts-checker-webpack-plugin') const ESLintPlugin = require('eslint-webpack-plugin') const { merge } = require('webpack-merge') const { baseConfig, paths, createBabelLoader, createCopyPlugin } = require('./webpack.base.config.js') +const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = merge(baseConfig, { mode: 'development', devtool: 'eval-source-map', devServer: { - historyApiFallback: true, + historyApiFallback: { + index: "index_template.html" + }, static: { directory: paths.build, watch: { From dc33a53911b11f01889f22e94dc0c0b60001d535 Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 7 Jul 2023 12:59:11 -0400 Subject: [PATCH 09/13] fix: Fix meta titles/descriptions in development builds --- public/index.ejs | 4 +-- webpack.base.config.js | 59 ++++++++++++++++++++++++++---------------- webpack.dev.config.js | 7 +++-- webpack.prod.config.js | 3 ++- 4 files changed, 43 insertions(+), 30 deletions(-) diff --git a/public/index.ejs b/public/index.ejs index 1776298d..d0c576f1 100644 --- a/public/index.ejs +++ b/public/index.ejs @@ -7,7 +7,7 @@ " /> - %META_TITLE% + <%= isDevelopment ? "HeLx UI" : "%META_TITLE%" %> diff --git a/webpack.base.config.js b/webpack.base.config.js index c79f4d6a..c416511f 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -44,6 +44,40 @@ const createCopyPlugin = ({ ] }) ) +const createHtmlWebpackPlugin = ({ + isDevelopment +}) => { + /** + * In production, we have an extra step where we generate an index template + * which is then turned into our final index file using sed substitutions from + * environment variables. + * + * In development, we just want to directly generate an index.html. + */ + return new HtmlWebpackPlugin({ + template: path.resolve(paths.public, 'index.ejs'), + filename: isDevelopment ? "index.html" : "index_template.html", + templateParameters: { + isDevelopment, + publicUrl: '/static/frontend/' + }, + minify: { + html5: true, + collapseWhitespace: true, + minifyCSS: true, + minifyJS: true, + minifyURLs: true, + removeAttributeQuotes: false, + removeComments: true, + removeEmptyAttributes: true, + removeOptionalTags: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true + } + }) +} const baseConfig = { entry: paths.entryPoint, output: { @@ -94,28 +128,6 @@ const baseConfig = { ] }, plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(paths.public, 'index.ejs'), - filename: "index_template.html", - templateParameters: { - publicUrl: '/static/frontend/' - }, - minify: { - html5: true, - collapseWhitespace: true, - minifyCSS: true, - minifyJS: true, - minifyURLs: true, - removeAttributeQuotes: false, - removeComments: true, - removeEmptyAttributes: true, - removeOptionalTags: true, - removeRedundantAttributes: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true, - useShortDoctype: true - } - }), new MiniCssExtractPlugin({ filename: '[name].[contenthash].css', chunkFilename: '[id].[contenthash].css' @@ -135,5 +147,6 @@ module.exports = { paths, baseConfig, createBabelLoader, - createCopyPlugin + createCopyPlugin, + createHtmlWebpackPlugin } \ No newline at end of file diff --git a/webpack.dev.config.js b/webpack.dev.config.js index 193936f0..9bdf2e06 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.js @@ -4,16 +4,14 @@ const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin') const ForkTsCheckerPlugin = require('fork-ts-checker-webpack-plugin') const ESLintPlugin = require('eslint-webpack-plugin') const { merge } = require('webpack-merge') -const { baseConfig, paths, createBabelLoader, createCopyPlugin } = require('./webpack.base.config.js') +const { baseConfig, paths, createBabelLoader, createCopyPlugin, createHtmlWebpackPlugin } = require('./webpack.base.config.js') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = merge(baseConfig, { mode: 'development', devtool: 'eval-source-map', devServer: { - historyApiFallback: { - index: "index_template.html" - }, + historyApiFallback: true, static: { directory: paths.build, watch: { @@ -36,6 +34,7 @@ module.exports = merge(baseConfig, { rules: [ createBabelLoader({ isDevelopment: true }) ] }, plugins: [ + createHtmlWebpackPlugin({ isDevelopment: true }), new ReactRefreshPlugin(), new ForkTsCheckerPlugin({ async: false diff --git a/webpack.prod.config.js b/webpack.prod.config.js index d1a5add7..865c39d5 100644 --- a/webpack.prod.config.js +++ b/webpack.prod.config.js @@ -2,7 +2,7 @@ const webpack = require('webpack'); const TerserPlugin = require('terser-webpack-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const { merge } = require('webpack-merge') -const { baseConfig, paths, createBabelLoader, createCopyPlugin } = require('./webpack.base.config.js'); +const { baseConfig, paths, createBabelLoader, createCopyPlugin, createHtmlWebpackPlugin } = require('./webpack.base.config.js'); console.log("Creating optimized production build. This may take a while...") module.exports = merge(baseConfig, { @@ -19,6 +19,7 @@ module.exports = merge(baseConfig, { rules: [ createBabelLoader({ isDevelopment: false }) ] }, plugins: [ + createHtmlWebpackPlugin({ isDevelopment: false }), new webpack.EnvironmentPlugin({ 'NODE_ENV': 'production' }), From 65befd659c0bfd3536f223b03199ccbc24c147af Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 7 Jul 2023 13:04:17 -0400 Subject: [PATCH 10/13] feat: Use env.json meta.title to configure page (tab) title final segment --- src/views/view.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/views/view.tsx b/src/views/view.tsx index 053197ac..bfc9d4a3 100644 --- a/src/views/view.tsx +++ b/src/views/view.tsx @@ -1,4 +1,5 @@ -import { ComponentType, useEffect } from 'react' +import { useEffect, useMemo } from 'react' +import { useEnvironment } from '../contexts' interface ViewProps { // Note: a null title is equivalent to an empty string or empty array, it will just display "HeLx UI" as the title. @@ -12,11 +13,15 @@ interface ViewProps { * The "HeLx UI" component is appended to the end of all titles, does not need to be included. */ export const useTitle = (title: string | string[] | null) => { + const { context } = useEnvironment() as any if (title === "") title = [] const titleSegments = Array.isArray(title) ? title : [title] + const websiteTitle = useMemo(() => { + return context?.meta.title ?? "HeLx UI" + }, [context]) useEffect(() => { - if (title !== null) document.title = [...titleSegments, "HeLx UI"].join(" · ") - }, [titleSegments]) + if (title !== null) document.title = [...titleSegments, websiteTitle].join(" · ") + }, [titleSegments, websiteTitle]) } // export function withView( From f99cfa85b8bc8046b17de19196230b97efe27dfe Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 7 Jul 2023 14:37:30 -0400 Subject: [PATCH 11/13] fix: Fix hot module reloading for CSS files --- webpack.base.config.js | 22 +++++++++++++++------- webpack.dev.config.js | 7 ++++++- webpack.prod.config.js | 10 ++++++++-- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/webpack.base.config.js b/webpack.base.config.js index c416511f..1d7ec7a1 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -78,6 +78,18 @@ const createHtmlWebpackPlugin = ({ } }) } +const createMiniCssExtractPlugin = ({ + isDevelopment +}) => ( + // CSS HMR doesn't work with contenthash + isDevelopment ? new MiniCssExtractPlugin({ + filename: '[name].css', + chunkFilename: '[id].css' + }) : new MiniCssExtractPlugin({ + filename: '[name].[contenthash].css', + chunkFilename: '[id].[contenthash].css' + }) +) const baseConfig = { entry: paths.entryPoint, output: { @@ -127,12 +139,7 @@ const baseConfig = { }, ] }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name].[contenthash].css', - chunkFilename: '[id].[contenthash].css' - }) - ], + plugins: [], resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx'] }, @@ -148,5 +155,6 @@ module.exports = { baseConfig, createBabelLoader, createCopyPlugin, - createHtmlWebpackPlugin + createHtmlWebpackPlugin, + createMiniCssExtractPlugin } \ No newline at end of file diff --git a/webpack.dev.config.js b/webpack.dev.config.js index 9bdf2e06..0f91ef45 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.js @@ -4,7 +4,11 @@ const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin') const ForkTsCheckerPlugin = require('fork-ts-checker-webpack-plugin') const ESLintPlugin = require('eslint-webpack-plugin') const { merge } = require('webpack-merge') -const { baseConfig, paths, createBabelLoader, createCopyPlugin, createHtmlWebpackPlugin } = require('./webpack.base.config.js') +const { + baseConfig, paths, + createBabelLoader, createCopyPlugin, createHtmlWebpackPlugin, + createMiniCssExtractPlugin +} = require('./webpack.base.config.js') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = merge(baseConfig, { @@ -34,6 +38,7 @@ module.exports = merge(baseConfig, { rules: [ createBabelLoader({ isDevelopment: true }) ] }, plugins: [ + createMiniCssExtractPlugin({ isDevelopment: true }), createHtmlWebpackPlugin({ isDevelopment: true }), new ReactRefreshPlugin(), new ForkTsCheckerPlugin({ diff --git a/webpack.prod.config.js b/webpack.prod.config.js index 865c39d5..5a8fab32 100644 --- a/webpack.prod.config.js +++ b/webpack.prod.config.js @@ -1,8 +1,13 @@ const webpack = require('webpack'); const TerserPlugin = require('terser-webpack-plugin') -const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const { merge } = require('webpack-merge') -const { baseConfig, paths, createBabelLoader, createCopyPlugin, createHtmlWebpackPlugin } = require('./webpack.base.config.js'); +const { + baseConfig, paths, + createBabelLoader, createCopyPlugin, createHtmlWebpackPlugin, + createMiniCssExtractPlugin +} = require('./webpack.base.config.js') console.log("Creating optimized production build. This may take a while...") module.exports = merge(baseConfig, { @@ -19,6 +24,7 @@ module.exports = merge(baseConfig, { rules: [ createBabelLoader({ isDevelopment: false }) ] }, plugins: [ + createMiniCssExtractPlugin({ isDevelopment: false }), createHtmlWebpackPlugin({ isDevelopment: false }), new webpack.EnvironmentPlugin({ 'NODE_ENV': 'production' From 729be5ecd3c0a6b4958b01e04bfcdd6863176811 Mon Sep 17 00:00:00 2001 From: Griffin Roupe Date: Fri, 7 Jul 2023 14:37:56 -0400 Subject: [PATCH 12/13] feat: Add collapsible button to variable view histogram for more screen space --- .../variable-view-layout/variable-results.css | 9 + .../variable-view-layout/variable-results.js | 241 +++++++++--------- 2 files changed, 136 insertions(+), 114 deletions(-) diff --git a/src/components/search/results/variable-view-layout/variable-results.css b/src/components/search/results/variable-view-layout/variable-results.css index eb230735..bb530f69 100644 --- a/src/components/search/results/variable-view-layout/variable-results.css +++ b/src/components/search/results/variable-view-layout/variable-results.css @@ -1,3 +1,12 @@ +.variable-histogram-collapse-panel > .ant-collapse-header { + padding: 0 !important; + align-items: center !important; + margin-bottom: 16px; +} +.variable-histogram-collapse-panel > .ant-collapse-content > .ant-collapse-content-box { + padding: 0 !important; +} + .histogram-startover-btn { margin-bottom: 10px; } diff --git a/src/components/search/results/variable-view-layout/variable-results.js b/src/components/search/results/variable-view-layout/variable-results.js index 95893d4e..f28e8f7f 100644 --- a/src/components/search/results/variable-view-layout/variable-results.js +++ b/src/components/search/results/variable-view-layout/variable-results.js @@ -1,5 +1,5 @@ -import React, { useEffect, useState, useRef, useMemo, useCallback } from 'react' -import { Typography, Button, Space, Divider, Slider, Tooltip } from 'antd' +import React, { useEffect, useState, useRef, useMemo, useCallback, Fragment } from 'react' +import { Typography, Button, Space, Divider, Slider, Tooltip, Collapse } from 'antd' import { ArrowLeftOutlined, ArrowRightOutlined, InfoCircleFilled } from '@ant-design/icons' import { generate, presetPalettes, red, volcano, orange, gold, yellow, green, cyan, blue, geekblue, purple, magenta } from '@ant-design/colors' import { Column } from '@ant-design/plots'; @@ -13,6 +13,7 @@ import { useDebounce, useDebouncedCallback } from 'use-debounce' import './variable-results.css'; const { Text, Title } = Typography +const { Panel } = Collapse // Between 0-9 const COLOR_INTENSITY = 4 @@ -131,6 +132,7 @@ const HistogramLegend = ({ title: _title, items, style, ...props }) => { export const VariableSearchResults = () => { const { variableResults, variableStudyResults, totalVariableResults } = useHelxSearch() + const [collapseHistogram, setCollapseHistogram] = useState(false) const [page, setPage] = useState(0) /** filteredVariables holds the variables displayed in the histogram */ const [_filteredVariables, _setFilteredVariables] = useState([variableResults]) @@ -360,124 +362,135 @@ export const VariableSearchResults = () => { return (
- {/* The results header has a bottom margin of 16, so the divider shouldn't have a top margin. */} - -
- Variables according to Dug score - -
- Dug score -
-
    -
  • - This is the metric used by Dug to describe how relevant a result is -
  • -
  • - The score is calculated from how closely the search query matches information known about a result -
+ setCollapseHistogram(!collapseHistogram) } + > + + {/* The results header has a bottom margin of 16, so the divider shouldn't have a top margin. */} + +
+ Variables according to Dug score + +
+ Dug score +
+
    +
  • + This is the metric used by Dug to describe how relevant a result is +
  • +
  • + The score is calculated from how closely the search query matches information known about a result +
+
+ }> + +
- }> - - -
-
- { filteredVariables.length < totalVariableResults && ( -
- - Viewing {filteredVariables.length} variables within the {Math.floor(filteredPercentileLower)}-{Math.floor(filteredPercentileUpper)} percentiles - -
- ) } - -
-
- - result.score)) } - max={ Math.max(...variableResults.map((result) => result.score)) } - step={ null } - marks={ variableResults.reduce((acc, cur) => { - acc[cur.score] = { - label: cur.score, - style: { - display: "none" - } - } - return acc - }, {}) } - // Margin to align with the histogram - style={{ marginRight: 0, marginBottom: 4, marginTop: 16, flexGrow: 1 }} - className="histogram-slider" - /> + + + }> + { filteredVariables.length < totalVariableResults && ( +
+ + Viewing {filteredVariables.length} variables within the {Math.floor(filteredPercentileLower)}-{Math.floor(filteredPercentileUpper)} percentiles +
- { - const [minScore, maxScore] = absScoreRange - const startRatio = Math.round(i / GRADIENT_CONSTITUENTS.length * 100) / 100 - const endRatio = Math.round((i + 1) / GRADIENT_CONSTITUENTS.length * 100) / 100 - const startScore = (startRatio * maxScore) - (startRatio * minScore) + minScore - const endScore = (endRatio * maxScore) - (endRatio * minScore) + minScore - const count = variableResults.filter((result) => result.score >= startScore && result.score <= endScore).length - const filteredCount = filteredVariables.filter((result) => result.score >= startScore && result.score <= endScore).length - return { - id: color, - name: { - name: `${ startRatio === 0 ? Math.floor(startScore) : Math.ceil(startScore) } - ${ endRatio === 1 ? Math.ceil(endScore) : Math.floor(endScore) }`, - style: filteredCount === 0 ? { color: "rgba(0, 0, 0, 0.25)" } : undefined - }, - description: { - description: filteredCount < count ? `(${ filteredCount } / ${ count } variables)` : `(${ count } variables)`, - style: filteredCount === 0 ? { color: "rgba(0, 0, 0, 0.25)" } : undefined - }, - marker: { - path: (w, h) => { - const x = 0 - const y = 0 - return `M ${ x },${ y } L ${ x + w }, ${ y } L ${ x + w }, ${ y + h } L ${ x }, ${ y + h } L ${ x },${ y } Z` + ) } + +
+
+ + result.score)) } + max={ Math.max(...variableResults.map((result) => result.score)) } + step={ null } + marks={ variableResults.reduce((acc, cur) => { + acc[cur.score] = { + label: cur.score, + style: { + display: "none" + } + } + return acc + }, {}) } + // Margin to align with the histogram + style={{ marginRight: 0, marginBottom: 4, marginTop: 16, flexGrow: 1 }} + className="histogram-slider" + /> +
+ { + const [minScore, maxScore] = absScoreRange + const startRatio = Math.round(i / GRADIENT_CONSTITUENTS.length * 100) / 100 + const endRatio = Math.round((i + 1) / GRADIENT_CONSTITUENTS.length * 100) / 100 + const startScore = (startRatio * maxScore) - (startRatio * minScore) + minScore + const endScore = (endRatio * maxScore) - (endRatio * minScore) + minScore + const count = variableResults.filter((result) => result.score >= startScore && result.score <= endScore).length + const filteredCount = filteredVariables.filter((result) => result.score >= startScore && result.score <= endScore).length + return { + id: color, + name: { + name: `${ startRatio === 0 ? Math.floor(startScore) : Math.ceil(startScore) } - ${ endRatio === 1 ? Math.ceil(endScore) : Math.floor(endScore) }`, + style: filteredCount === 0 ? { color: "rgba(0, 0, 0, 0.25)" } : undefined + }, + description: { + description: filteredCount < count ? `(${ filteredCount } / ${ count } variables)` : `(${ count } variables)`, + style: filteredCount === 0 ? { color: "rgba(0, 0, 0, 0.25)" } : undefined }, - // path: [["M", x - x_r, y - y_r], ["L", x + x_r, y - y_r], ["L", x + x_r, y + y_r], ["L", x - x_r, y + y_r], ["Z"]], - style: { - fill: filteredCount > 0 ? color : "rgba(0, 0, 0, 0.15)" + marker: { + path: (w, h) => { + const x = 0 + const y = 0 + return `M ${ x },${ y } L ${ x + w }, ${ y } L ${ x + w }, ${ y + h } L ${ x }, ${ y + h } L ${ x },${ y } Z` + }, + // path: [["M", x - x_r, y - y_r], ["L", x + x_r, y - y_r], ["L", x + x_r, y + y_r], ["L", x - x_r, y + y_r], ["Z"]], + style: { + fill: filteredCount > 0 ? color : "rgba(0, 0, 0, 0.15)" + } } } - } - }) } - style={{ marginLeft: 24, marginRight: 8, flexShrink: 0 }} - /> -
-
- - - - - - - -
+
+ + + + + - -
-
+ + + + +
+ + + Studies { studyResultsForDisplay.length < variableStudyResults.length && (
From c92a2b7a7a3a3abd7c35c05cebd44847ce8f0897 Mon Sep 17 00:00:00 2001 From: Gaurav Vaidya Date: Fri, 21 Jul 2023 15:18:58 -0400 Subject: [PATCH 13/13] Updated REACT_APP_HELX_SEARCH_URL (should have /search-api suffix). --- .env.sample | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.sample b/.env.sample index e9e3b31a..1f10957c 100644 --- a/.env.sample +++ b/.env.sample @@ -3,7 +3,7 @@ REACT_APP_WORKSPACES_ENABLED=false # Enable when working with Dug REACT_APP_SEMANTIC_SEARCH_ENABLED='true' # Points the app to the Dug API -REACT_APP_HELX_SEARCH_URL='https:\/\/heal-dev.apps.renci.org' +REACT_APP_HELX_SEARCH_URL='https:\/\/heal-dev.apps.renci.org\/search-api' # Only necessary if working with analytics specifically. # Should be a project token/GA property. Make sure to configure details # in env.json @@ -19,4 +19,4 @@ REACT_APP_HIDDEN_RESULT_TABS='' # Some static assets such as app logo are loaded externally from the dockstore repository (helx-apps). REACT_APP_DOCKSTORE_BRANCH=master # Some static assets such as brand logo are loaded externally from the Appstore repository. -REACT_APP_APPSTORE_ASSET_BRANCH=master \ No newline at end of file +REACT_APP_APPSTORE_ASSET_BRANCH=master