Skip to content

Commit

Permalink
improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
theosanderson committed Feb 25, 2025
1 parent db91646 commit 7e91312
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('DownloadDialog', () => {
let [path, query] = getDownloadHref()?.split('?') ?? [];
expect(path).toBe(`${defaultLapisUrl}/sample/details`);
expect(query).toMatch(
/downloadAsFile=true&downloadFileBasename=ebola_metadata_\d{4}-\d{2}-\d{2}T\d{4}&versionStatus=LATEST_VERSION&isRevocation=false&dataUseTerms=OPEN&dataFormat=tsv&fields=field1%2Cfield2&accession=accession1&accession=accession2&field1=value1/,
/downloadAsFile=true&downloadFileBasename=ebola_metadata_\d{4}-\d{2}-\d{2}T\d{4}&versionStatus=LATEST_VERSION&isRevocation=false&dataUseTerms=OPEN&dataFormat=tsv&fields=accessionVersion%2Cfield1%2Cfield2&accession=accession1&accession=accession2&field1=value1/,
);

await userEvent.click(screen.getByLabelText(olderVersionsLabel));
Expand Down Expand Up @@ -134,7 +134,7 @@ describe('DownloadDialog', () => {
let [path, query] = getDownloadHref()?.split('?') ?? [];
expect(path).toBe(`${defaultLapisUrl}/sample/details`);
expect(query).toMatch(
/downloadAsFile=true&downloadFileBasename=ebola_metadata_\d{4}-\d{2}-\d{2}T\d{4}&versionStatus=LATEST_VERSION&isRevocation=false&dataUseTerms=OPEN&dataFormat=tsv&fields=field1%2Cfield2&accessionVersion=SEQID1&accessionVersion=SEQID2/,
/downloadAsFile=true&downloadFileBasename=ebola_metadata_\d{4}-\d{2}-\d{2}T\d{4}&versionStatus=LATEST_VERSION&isRevocation=false&dataUseTerms=OPEN&dataFormat=tsv&fields=accessionVersion%2Cfield1%2Cfield2&accessionVersion=SEQID1&accessionVersion=SEQID2/,
);

await userEvent.click(screen.getByLabelText(olderVersionsLabel));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DownloadDialogButton } from './DowloadDialogButton.tsx';
import { DownloadButton } from './DownloadButton.tsx';
import { DownloadForm } from './DownloadForm.tsx';
import { type DownloadUrlGenerator, type DownloadOption } from './DownloadUrlGenerator.ts';
import { getDefaultSelectedFields } from './FieldSelector/FieldSelectorModal.tsx';
import type { SequenceFilter } from './SequenceFilters.tsx';
import { routes } from '../../../routes/routes.ts';
import type { Metadata } from '../../../types/config.ts';
Expand Down Expand Up @@ -35,6 +36,7 @@ export const DownloadDialog: FC<DownloadDialogProps> = ({

const [downloadOption, setDownloadOption] = useState<DownloadOption | undefined>();
const [agreedToDataUseTerms, setAgreedToDataUseTerms] = useState(dataUseTermsEnabled ? false : true);
const [selectedFields, setSelectedFields] = useState<string[]>(getDefaultSelectedFields(metadata));

return (
<>
Expand All @@ -48,6 +50,8 @@ export const DownloadDialog: FC<DownloadDialogProps> = ({
allowSubmissionOfConsensusSequences={allowSubmissionOfConsensusSequences}
dataUseTermsEnabled={dataUseTermsEnabled}
metadata={metadata}
selectedFields={selectedFields}
onSelectedFieldsChange={setSelectedFields}
/>
{dataUseTermsEnabled && (
<div className='mb-4 py-4'>
Expand Down
51 changes: 32 additions & 19 deletions website/src/components/SearchPage/DownloadDialog/DownloadForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { type FC, useEffect, useState } from 'react';
import type { DownloadDataType } from './DownloadDataType.ts';
import type { DownloadOption } from './DownloadUrlGenerator.ts';
import { FieldSelectorButton } from './FieldSelector/FieldSelectorButton.tsx';
import { FieldSelectorModal, getDefaultSelectedFields } from './FieldSelector/FieldSelectorModal.tsx';
import { FieldSelectorModal } from './FieldSelector/FieldSelectorModal.tsx';
import { DropdownOptionBlock, RadioOptionBlock } from './OptionBlock.tsx';
import { routes } from '../../../routes/routes.ts';
import { ACCESSION_VERSION_FIELD } from '../../../settings.ts';
import type { Metadata } from '../../../types/config.ts';
import type { ReferenceGenomesSequenceNames } from '../../../types/referencesGenomes.ts';

Expand All @@ -15,14 +16,24 @@ type DownloadFormProps = {
allowSubmissionOfConsensusSequences: boolean;
dataUseTermsEnabled: boolean;
metadata: Metadata[];
selectedFields: string[];
onSelectedFieldsChange: (fields: string[]) => void;
};

// Helper function to ensure accessionVersion is always the first field
function ensureAccessionVersionField(fields: string[]): string[] {
const fieldsWithoutAccessionVersion = fields.filter((field) => field !== ACCESSION_VERSION_FIELD);
return [ACCESSION_VERSION_FIELD, ...fieldsWithoutAccessionVersion];
}

export const DownloadForm: FC<DownloadFormProps> = ({
referenceGenomesSequenceNames,
onChange,
allowSubmissionOfConsensusSequences,
dataUseTermsEnabled,
metadata,
selectedFields,
onSelectedFieldsChange,
}) => {
const [includeRestricted, setIncludeRestricted] = useState(0);
const [includeOldData, setIncludeOldData] = useState(0);
Expand All @@ -33,7 +44,6 @@ export const DownloadForm: FC<DownloadFormProps> = ({
const [alignedAminoAcidSequence, setAlignedAminoAcidSequence] = useState(0);

const [isFieldSelectorOpen, setIsFieldSelectorOpen] = useState(false);
const [selectedFields, setSelectedFields] = useState<string[]>(getDefaultSelectedFields(metadata));

const isMultiSegmented = referenceGenomesSequenceNames.nucleotideSequences.length > 1;

Expand Down Expand Up @@ -74,7 +84,7 @@ export const DownloadForm: FC<DownloadFormProps> = ({
includeOldData: includeOldData === 1,
includeRestricted: includeRestricted === 1,
compression: compressionOptions[compression],
fields: dataType === 0 ? selectedFields : undefined, // Only include fields for metadata downloads
fields: dataType === 0 ? ensureAccessionVersionField(selectedFields) : undefined, // Always include accessionVersion as first field
});
}, [
includeRestricted,
Expand All @@ -91,7 +101,18 @@ export const DownloadForm: FC<DownloadFormProps> = ({
selectedFields,
]);

const metadataOption = { label: <>Metadata</> };
const metadataOption = {
label: (
<div className='flex items-center gap-3'>
<span>Metadata</span>
<FieldSelectorButton
onClick={() => setIsFieldSelectorOpen(true)}
selectedFieldsCount={selectedFields.length}
disabled={dataType !== 0}
/>
</div>
),
};
const dataTypeOptions = allowSubmissionOfConsensusSequences
? [
metadataOption,
Expand Down Expand Up @@ -196,21 +217,13 @@ export const DownloadForm: FC<DownloadFormProps> = ({
onSelect={setCompression}
/>

{dataType === 0 && (
<div className='mt-4 flex justify-center'>
<FieldSelectorButton
onClick={() => setIsFieldSelectorOpen(true)}
selectedFieldsCount={selectedFields.length}
/>
<FieldSelectorModal
isOpen={isFieldSelectorOpen}
onClose={() => setIsFieldSelectorOpen(false)}
metadata={metadata}
initialSelectedFields={selectedFields}
onSave={setSelectedFields}
/>
</div>
)}
<FieldSelectorModal
isOpen={isFieldSelectorOpen}
onClose={() => setIsFieldSelectorOpen(false)}
metadata={metadata}
initialSelectedFields={selectedFields}
onSave={onSelectedFieldsChange}
/>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,27 @@ import { type FC } from 'react';
type FieldSelectorButtonProps = {
onClick: () => void;
selectedFieldsCount: number;
disabled?: boolean;
};

export const FieldSelectorButton: FC<FieldSelectorButtonProps> = ({ onClick, selectedFieldsCount }) => {
export const FieldSelectorButton: FC<FieldSelectorButtonProps> = ({
onClick,
selectedFieldsCount,
disabled = false,
}) => {
return (
<button
type='button'
onClick={onClick}
className='inline-flex items-center px-3 py-2 text-sm font-medium border border-gray-300 rounded-md shadow-sm bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500'
disabled={disabled}
className={`inline-flex items-center px-2 py-1 text-xs font-medium border border-gray-300 rounded-md shadow-sm
${
disabled
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
: 'bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500'
}`}
>
<span>Customize fields ({selectedFieldsCount})</span>
<span>Choose fields ({selectedFieldsCount})</span>
</button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,18 @@ describe('FieldSelectorModal', () => {
render(<FieldSelectorModal isOpen={true} onClose={() => {}} metadata={mockMetadata} onSave={() => {}} />);

// Check that fields with includeInDownloadsByDefault=true are checked
const field1Checkbox = screen.getByLabelText('Field 1');
const field2Checkbox = screen.getByLabelText('Field 2');
const field3Checkbox = screen.getByLabelText('Field 3');

expect(field1Checkbox.checked).toBe(true);
expect(field2Checkbox.checked).toBe(false);
expect(field3Checkbox.checked).toBe(true);
const field1Checkbox = screen.getByLabelText('Field 1') as Element;
const field2Checkbox = screen.getByLabelText('Field 2') as Element;
const field3Checkbox = screen.getByLabelText('Field 3') as Element;

// Adding type assertion to properly access the checked property
const input1 = field1Checkbox as unknown as HTMLInputElement;
const input2 = field2Checkbox as unknown as HTMLInputElement;
const input3 = field3Checkbox as unknown as HTMLInputElement;

expect(input1.checked).toBe(true);
expect(input2.checked).toBe(false);
expect(input3.checked).toBe(true);
});

it('calls onSave immediately when a field is toggled', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,17 @@ export const FieldSelectorModal: FC<FieldSelectorProps> = ({
<div className='grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2'>
{fieldsByHeader[header]
.filter((field) => !field.hideOnSequenceDetailsPage)
.sort((a, b) => a.name.localeCompare(b.name))
.sort((a, b) => {
// Sort by order property if available, otherwise alphabetically by name
if (a.order !== undefined && b.order !== undefined) {
return a.order - b.order;
} else if (a.order !== undefined) {
return -1; // a has order, b doesn't, so a comes first
} else if (b.order !== undefined) {
return 1; // b has order, a doesn't, so b comes first
}
return a.name.localeCompare(b.name);
})
.map((field) => (
<div key={field.name} className='flex items-center'>
<input
Expand Down

0 comments on commit 7e91312

Please sign in to comment.