diff --git a/packages/libs/multi-blast/config-overrides.js b/packages/libs/multi-blast/config-overrides.js index 501f171fce..c061efdf7e 100644 --- a/packages/libs/multi-blast/config-overrides.js +++ b/packages/libs/multi-blast/config-overrides.js @@ -1,4 +1,4 @@ -const path = require('path'); +const webpack = require('webpack'); /* * The following gives preference to the project's root node_modules directory when loading modules and loaders. @@ -10,18 +10,37 @@ module.exports = function override(config, env) { ...config, resolve: { ...config.resolve, - modules: [ - path.join(__dirname, 'node_modules'), - ...(config.resolve.modules || ['node_modules']), - ], + fallback: { + path: 'path-browserify', + stream: 'stream-browserify', + querystring: 'querystring-es3', + assert: 'assert', + buffer: 'buffer', + fs: false, + }, }, - resolveLoader: { - ...config.resolveLoader, - modules: [ - path.join(__dirname, 'node_modules'), - ...(config.resolveLoader.modules || ['node_modules']), + plugins: [ + ...config.plugins, + new webpack.ProvidePlugin({ + // This is needed by shape2geohash, used by MapVEuMap + process: 'process/browser', + }), + ], + externals: [{ jquery: 'jQuery' }], + module: { + ...config.module, + rules: [ + ...config.module.rules, + { + test: /\.(js|jsx|ts|tsx)$/, + exclude: /node_modules\/(?!(@veupathdb))/, + enforce: 'pre', + use: 'source-map-loader', + }, ], }, - externals: [{ jquery: 'jQuery' }], + snapshot: { + managedPaths: [], + }, }; }; diff --git a/packages/libs/multi-blast/package.json b/packages/libs/multi-blast/package.json index e105dc2848..93a00cf200 100644 --- a/packages/libs/multi-blast/package.json +++ b/packages/libs/multi-blast/package.json @@ -12,7 +12,8 @@ "src/lib" ], "sideEffects": [ - "*.scss" + "*.scss", + "src/globals.js" ], "dependencies": { "@veupathdb/coreui": "workspace:^", @@ -99,9 +100,9 @@ "react-redux": "^7.2.6", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", - "react-scripts": "4.0.1", + "react-scripts": "5.0.1", "read": "^1.0.7", - "recoil": "^0.2.0", + "recoil": "^0.7.7", "redux": "^4.0.0", "sass": "^1.56.2", "script-loader": "^0.7.2", diff --git a/packages/libs/multi-blast/src/index.tsx b/packages/libs/multi-blast/src/index.tsx index a11052b4a0..a783f493bb 100644 --- a/packages/libs/multi-blast/src/index.tsx +++ b/packages/libs/multi-blast/src/index.tsx @@ -1,15 +1,22 @@ import './globals'; + +// eslint-disable-next-line import/no-webpack-loader-syntax +import '!!script-loader!@veupathdb/wdk-client/vendored/jquery'; +// eslint-disable-next-line import/no-webpack-loader-syntax +import '!!script-loader!@veupathdb/wdk-client/vendored/jquery-migrate-1.2.1'; +// eslint-disable-next-line import/no-webpack-loader-syntax +import '!!script-loader!@veupathdb/wdk-client/vendored/jquery-ui'; +// eslint-disable-next-line import/no-webpack-loader-syntax +import '!!script-loader!@veupathdb/wdk-client/vendored/jquery.qtip.min'; + import React, { Suspense } from 'react'; import { Redirect, RouteComponentProps } from 'react-router'; import { RecoilRoot } from 'recoil'; -import { initialize } from '@veupathdb/web-common/lib/bootstrap'; -import { - BlastSummaryViewPlugin, - GenomeSummaryViewPlugin, - ResultTableSummaryViewPlugin, -} from '@veupathdb/wdk-client/lib/Plugins'; +import { initialize } from '@veupathdb/wdk-client'; +import { ResultTableSummaryViewPlugin } from '@veupathdb/wdk-client/lib/Plugins'; import { RouteEntry } from '@veupathdb/wdk-client/lib/Core/RouteEntry'; import { ClientPluginRegistryEntry } from '@veupathdb/wdk-client/lib/Utils/ClientPlugin'; +import BlastSummaryViewPlugin from '@veupathdb/blast-summary-view/lib/Controllers/BlastSummaryViewController'; import { OrganismParam, @@ -28,9 +35,6 @@ import { BlastWorkspaceRouter } from './lib/controllers/BlastWorkspaceRouter'; import { isMultiBlastQuestion } from './lib/utils/pluginConfig'; import { wrapWdkService } from './lib/utils/wdkServiceIntegration'; -import '@veupathdb/wdk-client/lib/Core/Style/index.scss'; -import '@veupathdb/web-common/lib/styles/client.scss'; - import './index.css'; initialize({ @@ -93,11 +97,6 @@ initialize({ name: '_default', component: ResultTableSummaryViewPlugin, }, - { - type: 'summaryView', - name: 'genomic-view', - component: GenomeSummaryViewPlugin, - }, { type: 'summaryView', name: 'blast-view', diff --git a/packages/libs/multi-blast/src/lib/components/BlastForm.scss b/packages/libs/multi-blast/src/lib/components/BlastForm.scss index 46dbcad544..2e2417ea27 100644 --- a/packages/libs/multi-blast/src/lib/components/BlastForm.scss +++ b/packages/libs/multi-blast/src/lib/components/BlastForm.scss @@ -1,12 +1,16 @@ .wdk-QuestionForm { &MultiBlast { + > * + * { + margin-block-start: 1em; + } + .SequenceParam { display: inline-flex; flex-direction: column; gap: 1em; &Instructions { - font-size: 1.2em; + font-size: 1.1em; } textarea { diff --git a/packages/libs/multi-blast/src/lib/components/BlastForm.tsx b/packages/libs/multi-blast/src/lib/components/BlastForm.tsx index 2536fa2a31..1f772281fc 100644 --- a/packages/libs/multi-blast/src/lib/components/BlastForm.tsx +++ b/packages/libs/multi-blast/src/lib/components/BlastForm.tsx @@ -1,5 +1,4 @@ import { - ChangeEvent, FormEvent, useCallback, useContext, @@ -23,11 +22,15 @@ import { TextArea, } from '@veupathdb/wdk-client/lib/Components'; import { WdkDependenciesContext } from '@veupathdb/wdk-client/lib/Hooks/WdkDependenciesEffect'; -import { makeClassNameHelper } from '@veupathdb/wdk-client/lib/Utils/ComponentUtils'; +import { + makeClassNameHelper, + safeHtml, +} from '@veupathdb/wdk-client/lib/Utils/ComponentUtils'; import { scrollIntoView } from '@veupathdb/wdk-client/lib/Utils/DomUtils'; import { Parameter, ParameterGroup, + SelectEnumParam, } from '@veupathdb/wdk-client/lib/Utils/WdkModel'; import DefaultQuestionForm, { ParameterList, @@ -38,7 +41,6 @@ import DefaultQuestionForm, { import { Plugin } from '@veupathdb/wdk-client/lib/Utils/ClientPlugin'; import { makeParamDependenciesUpdating, - useChangeParamValue, useDependentParamsAreUpdating, } from '@veupathdb/wdk-client/lib/Views/Question/Params/Utils'; @@ -74,6 +76,7 @@ import { AdvancedParamGroup } from './AdvancedParamGroup'; import { BlastFormValidationInfo } from './BlastFormValidationInfo'; import './BlastForm.scss'; +import Banner from '@veupathdb/coreui/lib/components/banners/Banner'; export const blastFormCx = makeClassNameHelper('wdk-QuestionForm'); @@ -86,6 +89,10 @@ export interface Props extends DefaultQuestionFormProps { isMultiBlast?: boolean; } +interface TransformedProps extends Props { + originalQuestion: Props['state']['question']; +} + export function BlastForm(props: Props) { const targetType = props.state.paramValues[BLAST_DATABASE_TYPE_PARAM_NAME]; const targetMetadataByDataType = useContext(TargetMetadataByDataType); @@ -101,54 +108,47 @@ export function BlastForm(props: Props) { ); } -function BlastFormWithTransformedQuestion(props: Props) { +function BlastFormWithTransformedQuestion(props: TransformedProps) { const canChangeRecordType = props.canChangeRecordType ?? false; const targetType = props.state.paramValues[BLAST_DATABASE_TYPE_PARAM_NAME]; + const blastAlgorithmParameter = props.state.question.parametersByName[ + BLAST_ALGORITHM_PARAM_NAME + ] as SelectEnumParam; + const selectedBlastAlgorithm = props.state.paramValues[BLAST_ALGORITHM_PARAM_NAME]; + const selectedBlastAlgorithmDisplay = + blastAlgorithmParameter.vocabulary.find( + ([term]) => term === selectedBlastAlgorithm + )?.[1] ?? selectedBlastAlgorithm; + const restrictedAdvancedParamGroup = useMemo(() => { const fullAdvancedParamGroup = props.state.question.groupsByName[ADVANCED_PARAMS_GROUP_NAME]; + if (fullAdvancedParamGroup == null) { + return undefined; + } + return { ...fullAdvancedParamGroup, - displayName: `Advanced ${selectedBlastAlgorithm.toUpperCase()} Parameters`, + displayName: `Advanced ${selectedBlastAlgorithmDisplay.toUpperCase()} Parameters`, parameters: fullAdvancedParamGroup.parameters.filter( (paramName) => !isOmittedParam(props.state.question.parametersByName[paramName]) ), }; }, [ - selectedBlastAlgorithm, + selectedBlastAlgorithmDisplay, props.state.question.groupsByName, props.state.question.parametersByName, ]); const enabledAlgorithms = useEnabledAlgorithms(targetType); - const updateQueryParam = useChangeParamValue( - props.state.question.parametersByName[BLAST_QUERY_SEQUENCE_PARAM_NAME], - props.state, - props.eventHandlers.updateParamValue - ); - - const onQueryFileInputChanged = useCallback( - (event: ChangeEvent) => { - const file = - event.target.files == null || event.target.files.length === 0 - ? null - : event.target.files[0]; - - if (file != null) { - file.text().then(updateQueryParam); - } - }, - [updateQueryParam] - ); - const defaultAdvancedParamsMetadata = useDefaultAdvancedParams( props.state.question ); @@ -208,6 +208,11 @@ function BlastFormWithTransformedQuestion(props: Props) { props.eventHandlers.updateParamValue ); + const enableSequenceTextArea = + !props.state.question.parametersByName[ + BLAST_QUERY_SEQUENCE_PARAM_NAME + ]?.properties?.['multiBlastOptions']?.includes('fileOnly'); + const targetParamElement = (
- {props.isMultiBlast - ? 'Paste one or several sequences, or upload a FASTA file.' - : 'Paste one sequence, or upload a one-sequence FASTA file.'} + {props.originalQuestion.parametersByName[ + BLAST_QUERY_SEQUENCE_PARAM_NAME + ]?.visibleHelp ?? + (enableSequenceTextArea + ? props.isMultiBlast + ? 'Paste one or several sequences, or upload a FASTA file. If both are provided, the file will be used.' + : 'Paste one sequence, or upload a one-sequence FASTA file. If both are provided, the file will be used.' + : props.isMultiBlast + ? 'Upload a FASTA file.' + : 'Upload a one-sequence FASTA file.')}
-