Skip to content

Commit

Permalink
Merge pull request #210 from bento-platform/feat/overview/dataset-card
Browse files Browse the repository at this point in the history
feat: add dataset card + show datasets in project overview
  • Loading branch information
davidlougheed authored Nov 6, 2024
2 parents 6c47d5a + f9de45a commit 47d4f7e
Show file tree
Hide file tree
Showing 53 changed files with 415 additions and 266 deletions.
7 changes: 3 additions & 4 deletions src/js/components/Beacon/BeaconCommon/AssemblyIdSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { useTranslation } from 'react-i18next';
import { Form, Select } from 'antd';
import { useTranslationFn } from '@/hooks';
import type { FormField, BeaconAssemblyIds } from '@/types/beacon';
import { DEFAULT_TRANSLATION } from '@/constants/configConstants';

const AssemblyIdSelect = ({ field, beaconAssemblyIds, disabled }: AssemblyIdSelectProps) => {
const { t: td } = useTranslation(DEFAULT_TRANSLATION);
const t = useTranslationFn();
const assemblyIdOptions = beaconAssemblyIds.map((assembly) => (
<Select.Option key={assembly} value={assembly}>
{assembly}
</Select.Option>
));

return (
<Form.Item name={field.name} label={td(field.name)} rules={field.rules}>
<Form.Item name={field.name} label={t(field.name)} rules={field.rules}>
<Select style={{ width: '100%' }} disabled={disabled}>
{assemblyIdOptions}
</Select>
Expand Down
24 changes: 12 additions & 12 deletions src/js/components/Beacon/BeaconCommon/BeaconQueryFormUi.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Button, Card, Col, Form, Row } from 'antd';
import { useIsAuthenticated } from 'bento-auth-js';
import { useAppDispatch, useTranslationDefault, useQueryWithAuthIfAllowed } from '@/hooks';
import { useAppDispatch, useQueryWithAuthIfAllowed, useTranslationFn } from '@/hooks';
import VariantsForm from './VariantsForm';
import Filters from './Filters';
import SearchToolTip from './ToolTips/SearchToolTip';
Expand Down Expand Up @@ -43,7 +43,7 @@ const BeaconQueryFormUi = ({
launchQuery,
apiErrorMessage,
}: BeaconQueryFormUiProps) => {
const td = useTranslationDefault();
const t = useTranslationFn();
const [form] = Form.useForm();
const [filters, setFilters] = useState<FormFilter[]>([STARTER_FILTER]);
const [hasVariants, setHasVariants] = useState<boolean>(false);
Expand Down Expand Up @@ -157,7 +157,7 @@ const BeaconQueryFormUi = ({
if (!variantsFormValid(formValues)) {
setHasFormError(true);
setErrorAlertClosed(false);
setFormErrorMessage(td(VARIANTS_FORM_ERROR_MESSAGE));
setFormErrorMessage(t(VARIANTS_FORM_ERROR_MESSAGE));
return;
}

Expand All @@ -167,7 +167,7 @@ const BeaconQueryFormUi = ({

dispatch(launchQuery(jsonPayload));
},
[dispatch, td, launchQuery, packageBeaconJSON]
[dispatch, t, launchQuery, packageBeaconJSON]
);

const handleClearForm = () => {
Expand Down Expand Up @@ -218,22 +218,22 @@ const BeaconQueryFormUi = ({
return empty || rangeQuery || sequenceQuery;
};

const searchButtonText = isNetworkQuery ? 'Search Network' : 'Search Beacon';
const searchButtonText = t(`beacon.${isNetworkQuery ? 'search_network' : 'search_beacon'}`);

return (
<div style={{ paddingBottom: 8, display: 'flex', justifyContent: 'center', width: '100%' }}>
<Card
title={td('Search')}
title={t('Search')}
style={{ borderRadius: '10px', maxWidth: '1200px', width: '100%', ...BOX_SHADOW }}
styles={CARD_STYLES}
>
<p style={{ margin: '-8px 0 8px 0', padding: '0', color: 'grey' }}>{td(uiInstructions)}</p>
<p style={{ margin: '-8px 0 8px 0', padding: '0', color: 'grey' }}>{t(uiInstructions)}</p>
<Form form={form} onFinish={handleFinish} layout="vertical" onValuesChange={handleValuesChange}>
<Row gutter={FORM_ROW_GUTTERS}>
{hasVariants && (
<Col xs={24} lg={12}>
<Card
title={td('entities.Variants')}
title={t('entities.Variants')}
style={CARD_STYLE}
styles={CARD_STYLES}
extra={
Expand All @@ -250,7 +250,7 @@ const BeaconQueryFormUi = ({
<Card
title={
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<>{td('Metadata')}</>
<>{t('Metadata')}</>
</div>
}
style={CARD_STYLE}
Expand All @@ -275,16 +275,16 @@ const BeaconQueryFormUi = ({
<Col span={24}>
{showError && (
<BeaconErrorMessage
message={`${td('Beacon error')}: ${errorMessage}`}
message={`${t('beacon.beacon_error')}: ${errorMessage}`}
onClose={() => setErrorAlertClosed(true)}
/>
)}
<div style={BUTTON_AREA_STYLE}>
<Button type="primary" htmlType="submit" loading={isFetchingQueryResponse} style={BUTTON_STYLE}>
{td(searchButtonText)}
{searchButtonText}
</Button>
<Button onClick={handleClearForm} style={BUTTON_STYLE}>
{td('Clear Form')}
{t('Clear Form')}
</Button>
</div>
</Col>
Expand Down
11 changes: 5 additions & 6 deletions src/js/components/Beacon/BeaconCommon/Filter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useTranslationCustom, useTranslationDefault } from '@/hooks';
import { useTranslationFn } from '@/hooks';
import { Button, Form, Select, Space } from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import type { FormInstance } from 'antd/es/form';
Expand All @@ -22,8 +22,7 @@ const FILTER_FORM_ITEM_STYLE = { flex: 1, marginInlineEnd: -1 };
const FILTER_FORM_ITEM_INNER_STYLE = { width: '100%' };

const Filter = ({ filter, form, querySections, removeFilter, isRequired }: FilterProps) => {
const t = useTranslationCustom();
const td = useTranslationDefault();
const t = useTranslationFn();

const [valueOptions, setValueOptions] = useState([{ label: '', value: '' }]);

Expand Down Expand Up @@ -68,19 +67,19 @@ const Filter = ({ filter, form, querySections, removeFilter, isRequired }: Filte
<Space.Compact>
<Form.Item
name={`filterId${filter.index}`}
rules={[{ required: isRequired, message: td('search field required') }]}
rules={[{ required: isRequired, message: t('search field required') }]}
style={FILTER_FORM_ITEM_STYLE}
>
<Select
placeholder={td('select a search field')}
placeholder={t('select a search field')}
style={FILTER_FORM_ITEM_INNER_STYLE}
onSelect={handleSelectKey}
options={searchKeyOptions(querySections)}
/>
</Form.Item>
<Form.Item
name={`filterValue${filter.index}`}
rules={[{ required: isRequired, message: td('value required') }]}
rules={[{ required: isRequired, message: t('value required') }]}
style={FILTER_FORM_ITEM_STYLE}
>
<Select
Expand Down
15 changes: 8 additions & 7 deletions src/js/components/Beacon/BeaconCommon/Filters.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import type { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Form, Space, Switch, Tooltip } from 'antd';
import type { FormInstance } from 'antd/es/form';

import { DEFAULT_TRANSLATION } from '@/constants/configConstants';
import { useBeaconNetwork } from '@/features/beacon/hooks';
import { toggleQuerySectionsUnionOrIntersection } from '@/features/beacon/network.store';
import { useAppSelector, useAppDispatch } from '@/hooks';
import { useAppSelector, useAppDispatch, useTranslationFn } from '@/hooks';
import type { FormFilter } from '@/types/beacon';
import type { SearchFieldResponse } from '@/types/search';

import Filter from './Filter';

const NetworkFilterToggle = () => {
const t = useTranslationFn();
const dispatch = useAppDispatch();
const { isQuerySectionsUnion } = useBeaconNetwork();

Expand All @@ -24,7 +23,9 @@ const NetworkFilterToggle = () => {
checked={isQuerySectionsUnion}
style={{ margin: '5px' }}
/>
<p style={{ margin: '5px' }}>{isQuerySectionsUnion ? 'show all filters' : 'common filters only'}</p>
<p style={{ margin: '5px' }}>
{t(`beacon.${isQuerySectionsUnion ? 'show_all_filters' : 'common_filters_only'}`)}
</p>
</div>
</Tooltip>
);
Expand All @@ -36,7 +37,7 @@ const NetworkFilterToggle = () => {
const BUTTON_STYLE = { margin: '10px 0' };

const Filters = ({ filters, setFilters, form, querySections, isNetworkQuery }: FiltersProps) => {
const { t: td } = useTranslation(DEFAULT_TRANSLATION);
const t = useTranslationFn();

const maxFilters = useAppSelector((state) => state.config.maxQueryParameters);
const maxQueryParametersRequired = useAppSelector((state) => state.config.maxQueryParametersRequired);
Expand Down Expand Up @@ -65,9 +66,9 @@ const Filters = ({ filters, setFilters, form, querySections, isNetworkQuery }: F
return (
<Form.Item>
<Space style={{ display: 'flex', padding: 0 }}>
<Tooltip title={hasMaxFilters ? `${td('maximum of')} ${maxFilters} ${td('filters permitted')}` : null}>
<Tooltip title={hasMaxFilters ? `${t('maximum of')} ${maxFilters} ${t('filters permitted')}` : null}>
<Button style={BUTTON_STYLE} onClick={handleAddFilter} disabled={hasMaxFilters}>
{td('Add Filter')}
{t('Add Filter')}
</Button>
</Tooltip>
{isNetworkQuery && <NetworkFilterToggle />}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ToolTipText } from './ToolTipText';
import { useTranslationDefault } from '@/hooks';
import { useTranslationFn } from '@/hooks';

const METADATA_INSTRUCTIONS = 'Search over clinical or phenotypic properties.';

export const MetadataInstructions = () => {
const td = useTranslationDefault();
return <ToolTipText>{td(METADATA_INSTRUCTIONS)}</ToolTipText>;
const t = useTranslationFn();
return <ToolTipText>{t(METADATA_INSTRUCTIONS)}</ToolTipText>;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ToolTipText } from './ToolTipText';
import { useTranslationDefault } from '@/hooks';
import { useTranslationFn } from '@/hooks';
import { Space, Typography } from 'antd';
const { Title } = Typography;

Expand All @@ -18,16 +18,16 @@ const VARIANTS_INSTRUCTIONS_LINE4a = 'Coordinates are one-based.';
const VARIANTS_INSTRUCTIONS_LINE4b = 'Leave this form blank to search by metadata only.';

const VariantsInstructions = () => {
const td = useTranslationDefault();
const t = useTranslationFn();
return (
<Space direction="vertical" style={{ minWidth: '510px' }}>
<Title level={4} style={{ color: 'white', marginTop: '10px' }}>
{VARIANTS_INSTRUCTIONS_TITLE}
</Title>
<ToolTipText>{td(VARIANTS_INSTRUCTIONS_LINE1a) + ' ' + td(VARIANTS_INSTRUCTIONS_LINE1b)}</ToolTipText>
<ToolTipText>{td(VARIANTS_INSTRUCTIONS_LINE2a) + ' ' + td(VARIANTS_INSTRUCTIONS_LINE2b)}</ToolTipText>
<ToolTipText>{td(VARIANTS_INSTRUCTIONS_LINE3)}</ToolTipText>
<ToolTipText>{td(VARIANTS_INSTRUCTIONS_LINE4a) + ' ' + td(VARIANTS_INSTRUCTIONS_LINE4b)}</ToolTipText>
<ToolTipText>{t(VARIANTS_INSTRUCTIONS_LINE1a) + ' ' + t(VARIANTS_INSTRUCTIONS_LINE1b)}</ToolTipText>
<ToolTipText>{t(VARIANTS_INSTRUCTIONS_LINE2a) + ' ' + t(VARIANTS_INSTRUCTIONS_LINE2b)}</ToolTipText>
<ToolTipText>{t(VARIANTS_INSTRUCTIONS_LINE3)}</ToolTipText>
<ToolTipText>{t(VARIANTS_INSTRUCTIONS_LINE4a) + ' ' + t(VARIANTS_INSTRUCTIONS_LINE4b)}</ToolTipText>
</Space>
);
};
Expand Down
7 changes: 3 additions & 4 deletions src/js/components/Beacon/BeaconCommon/VariantInput.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useTranslation } from 'react-i18next';
import { Form, Input } from 'antd';
import { useTranslationFn } from '@/hooks';
import type { FormField } from '@/types/beacon';
import { DEFAULT_TRANSLATION } from '@/constants/configConstants';

const VariantInput = ({ field, disabled }: VariantInputProps) => {
const { t: td } = useTranslation(DEFAULT_TRANSLATION);
const t = useTranslationFn();
return (
<div>
<Form.Item name={field.name} label={td(field.name)} rules={field.rules}>
<Form.Item name={field.name} label={t(field.name)} rules={field.rules}>
<Input placeholder={field.placeholder} disabled={disabled} />
</Form.Item>
</div>
Expand Down
23 changes: 11 additions & 12 deletions src/js/components/Beacon/BeaconCommon/VariantsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { CSSProperties } from 'react';
import { useTranslation } from 'react-i18next';
import { Col, Row } from 'antd';
import VariantInput from './VariantInput';
import AssemblyIdSelect from './AssemblyIdSelect';
import { useTranslationFn } from '@/hooks';
import type { BeaconAssemblyIds } from '@/types/beacon';
import { DEFAULT_TRANSLATION } from '@/constants/configConstants';

// form state has to be one of these:
// empty (except for autofilled assemblyID)
Expand All @@ -27,36 +26,36 @@ const FORM_STYLE: CSSProperties = {
const FORM_ROW_GUTTER: [number, number] = [12, 0];

const VariantsForm = ({ beaconAssemblyIds }: VariantsFormProps) => {
const { t: td } = useTranslation(DEFAULT_TRANSLATION);
const t = useTranslationFn();
const formFields = {
referenceName: {
name: 'Chromosome',
rules: [{ pattern: CHROMOSOME_REGEX, message: td('Enter a chromosome name, e.g.: "17" or "X"') }],
rules: [{ pattern: CHROMOSOME_REGEX, message: t('Enter a chromosome name, e.g.: "17" or "X"') }],
placeholder: '1-22, X, Y, M',
initialValue: '',
},
start: {
name: 'Variant start',
rules: [{ pattern: DIGITS_REGEX, message: td('Enter a position number, e.g. "100"') }],
placeholder: `${td('e.g.')} 100`,
rules: [{ pattern: DIGITS_REGEX, message: t('Enter a position number, e.g. "100"') }],
placeholder: `${t('e.g.')} 100`,
initialValue: '',
},
end: {
name: 'Variant end',
rules: [{ pattern: DIGITS_REGEX, message: td('Enter a position number, e.g. "200"') }],
placeholder: `${td('e.g.')} 200`,
rules: [{ pattern: DIGITS_REGEX, message: t('Enter a position number, e.g. "200"') }],
placeholder: `${t('e.g.')} 200`,
initialValue: '',
},
referenceBases: {
name: 'Reference base(s)',
rules: [{ pattern: NUCLEOTIDES_REGEX, message: td('Enter any combination of A, C, G, T, or N') }],
placeholder: `A, C, G, T ${td('or')} N`,
rules: [{ pattern: NUCLEOTIDES_REGEX, message: t('Enter any combination of A, C, G, T, or N') }],
placeholder: `A, C, G, T ${t('or')} N`,
initialValue: '',
},
alternateBases: {
name: 'Alternate base(s)',
rules: [{ pattern: NUCLEOTIDES_REGEX, message: td('Enter any combination of A, C, G, T, or N') }],
placeholder: `A, C, G, T ${td('or')} N`,
rules: [{ pattern: NUCLEOTIDES_REGEX, message: t('Enter any combination of A, C, G, T, or N') }],
placeholder: `A, C, G, T ${t('or')} N`,
initialValue: '',
},
assemblyId: { name: 'Assembly ID', placeholder: '', initialValue: '' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { NetworkBeacon } from '@/types/beaconNetwork';
import type { FlattenedBeaconResponse } from '@/types/beacon';
import { BOX_SHADOW } from '@/constants/overviewConstants';
import NodeCountsDisplay from './NodeCountsDisplay';
import { useTranslationFn } from '@/hooks';

const { Link } = Typography;

Expand All @@ -20,6 +21,8 @@ const ApiErrorTag = ({ errorMessage }: { errorMessage: string }) => (
);

const NodeDetails = ({ beacon, response }: NodeDetailsProps) => {
const t = useTranslationFn();

const { apiUrl, organization, overview } = beacon;
const { variants } = overview;
const assemblies = Object.keys(variants);
Expand Down Expand Up @@ -67,7 +70,7 @@ const NodeDetails = ({ beacon, response }: NodeDetailsProps) => {
actions={[
<Link key="homepage" href={organization.welcomeUrl} target="_blank" style={LINK_STYLE}>
<LinkOutlined style={{ marginRight: '5px' }} />
Home Page
{t('beacon.home_page')}
</Link>,
<Link key="bentolink" href={bentoUrl} target="_blank" style={LINK_STYLE}>
<LinkOutlined style={{ marginRight: '5px' }} />
Expand Down
11 changes: 6 additions & 5 deletions src/js/components/Beacon/BeaconNetwork/NetworkSearchResults.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useMemo } from 'react';

import { Space, Spin, Tag } from 'antd';
import { Loading3QuartersOutlined } from '@ant-design/icons';

import SearchResultsPane from '@/components/Search/SearchResultsPane';
import { useBeaconNetwork } from '@/features/beacon/hooks';
import { useTranslationDefault } from '@/hooks';
import { Loading3QuartersOutlined } from '@ant-design/icons';
import { useTranslationFn } from '@/hooks';

const NetworkSearchResults = () => {
const td = useTranslationDefault();
const t = useTranslationFn();

const { hasBeaconNetworkError } = useBeaconNetwork();
const { networkResults, beaconResponses } = useBeaconNetwork();
Expand All @@ -31,7 +32,7 @@ const NetworkSearchResults = () => {
// show number of non-zero responses
const numResultsText = (n: number) => {
if (numNonErrorResponses) {
return `${td('Results from')} ${n} beacon${n == 1 ? '' : 's'}`;
return `${t('beacon.results_from')} ${n} beacon${n == 1 ? '' : 's'}`;
}
return '';
};
Expand All @@ -54,7 +55,7 @@ const NetworkSearchResults = () => {
<SearchResultsPane
isFetchingData={noResponsesYet}
results={networkResults}
resultsTitle="Network Search Results"
resultsTitle={t('beacon.network_search_results')}
resultsExtra={resultsExtra}
/>
);
Expand Down
Loading

0 comments on commit 47d4f7e

Please sign in to comment.