Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions website/src/components/collections/form/CollectionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export function CollectionForm({
onRemove={removeVariant}
canRemove={variants.length > 1}
lineageFields={lineageFields}
lapisUrl={lapisUrl}
/>
))}
<button type='button' className='btn btn-sm w-full' onClick={addVariant}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import { describe, expect, it as itVitest, vi } from 'vitest';
import { render } from 'vitest-browser-react';

import { VariantEditor } from './VariantEditor.tsx';
import { DUMMY_LAPIS_URL } from '../../../../routeMocker.ts';
import { it } from '../../../../test-extend.ts';
import { withQueryProvider } from '../../../backendApi/withQueryProvider.tsx';
import type { VariantUpdate } from '../../../types/Collection.ts';

const VariantEditorWithProvider = withQueryProvider(VariantEditor);

const FILTER_VARIANT: VariantUpdate = { type: 'filterObject', name: 'JN.1*', filterObject: {} };
const QUERY_VARIANT: VariantUpdate = {
type: 'query',
Expand All @@ -17,13 +21,14 @@ describe('VariantEditor', () => {
lapis.mockLapisDown();

const { getByText } = render(
<VariantEditor
<VariantEditorWithProvider
index={0}
variant={FILTER_VARIANT}
onChange={vi.fn()}
onRemove={vi.fn()}
canRemove={false}
lineageFields={[]}
lapisUrl={DUMMY_LAPIS_URL}
/>,
);

Expand All @@ -35,13 +40,14 @@ describe('VariantEditor', () => {
lapis.mockLapisDown();

const { getByRole } = render(
<VariantEditor
<VariantEditorWithProvider
index={0}
variant={FILTER_VARIANT}
onChange={vi.fn()}
onRemove={vi.fn()}
canRemove={false}
lineageFields={[]}
lapisUrl={DUMMY_LAPIS_URL}
/>,
);

Expand All @@ -50,13 +56,14 @@ describe('VariantEditor', () => {

itVitest('"Use advanced query" checkbox is checked for query variant', async () => {
const { getByRole } = render(
<VariantEditor
<VariantEditorWithProvider
index={0}
variant={QUERY_VARIANT}
onChange={vi.fn()}
onRemove={vi.fn()}
canRemove={false}
lineageFields={[]}
lapisUrl={DUMMY_LAPIS_URL}
/>,
);

Expand All @@ -69,13 +76,14 @@ describe('VariantEditor', () => {
const onChange = vi.fn();

const { getByRole } = render(
<VariantEditor
<VariantEditorWithProvider
index={0}
variant={FILTER_VARIANT}
onChange={onChange}
onRemove={vi.fn()}
canRemove={false}
lineageFields={[]}
lapisUrl={DUMMY_LAPIS_URL}
/>,
);

Expand All @@ -88,13 +96,14 @@ describe('VariantEditor', () => {
lapis.mockLapisDown();

const { getByRole } = render(
<VariantEditor
<VariantEditorWithProvider
index={0}
variant={FILTER_VARIANT}
onChange={vi.fn()}
onRemove={vi.fn()}
canRemove={true}
lineageFields={[]}
lapisUrl={DUMMY_LAPIS_URL}
/>,
);

Expand All @@ -107,13 +116,14 @@ describe('VariantEditor', () => {
const onRemove = vi.fn();

const { getByRole } = render(
<VariantEditor
<VariantEditorWithProvider
index={2}
variant={FILTER_VARIANT}
onChange={vi.fn()}
onRemove={onRemove}
canRemove={true}
lineageFields={[]}
lapisUrl={DUMMY_LAPIS_URL}
/>,
);

Expand All @@ -126,13 +136,14 @@ describe('VariantEditor', () => {
const onChange = vi.fn();

const { getByPlaceholder } = render(
<VariantEditor
<VariantEditorWithProvider
index={0}
variant={QUERY_VARIANT}
onChange={onChange}
onRemove={vi.fn()}
canRemove={false}
lineageFields={[]}
lapisUrl={DUMMY_LAPIS_URL}
/>,
);

Expand Down
31 changes: 10 additions & 21 deletions website/src/components/collections/form/VariantEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { memo, useCallback, useRef } from 'react';

import type { FilterObject, VariantUpdate } from '../../../types/Collection.ts';
import { AdvancedQueryFilter } from '../../genspectrum/AdvancedQueryFilter.tsx';
import { GsLineageFilter } from '../../genspectrum/GsLineageFilter.tsx';
import { GsMutationFilter } from '../../genspectrum/GsMutationFilter.tsx';

Expand All @@ -11,6 +12,7 @@ type Props = {
onRemove: (index: number) => void;
canRemove?: boolean;
lineageFields: string[];
lapisUrl: string;
};

// Memoized to prevent re-rendering all variant cards when only one changes in the parent list.
Expand All @@ -21,6 +23,7 @@ export const VariantEditor = memo(function VariantEditor({
onRemove,
canRemove = true,
lineageFields,
lapisUrl,
}: Props) {
const variantRef = useRef(variant);
variantRef.current = variant;
Expand Down Expand Up @@ -75,7 +78,13 @@ export const VariantEditor = memo(function VariantEditor({

<div className='col-span-2 flex flex-col gap-4'>
{variant.type === 'query' ? (
<QueryVariantFields variant={variant} onChange={(v) => onChange(index, v)} />
<AdvancedQueryFilter
enabled
lapisUrl={lapisUrl}
value={variant.countQuery}
allowedFields={lineageFields.length > 0 ? lineageFields : undefined}
onInput={(newValue) => onChange(index, { ...variant, countQuery: newValue ?? '' })}
/>
) : (
<MutationListVariantFields
filterObject={variant.filterObject}
Expand Down Expand Up @@ -109,26 +118,6 @@ export const VariantEditor = memo(function VariantEditor({
);
});

function QueryVariantFields({
variant,
onChange,
}: {
variant: Extract<VariantUpdate, { type: 'query' }>;
onChange: (v: VariantUpdate) => void;
}) {
return (
<div className='flex flex-col'>
<label className='label'>Query</label>
<textarea
className='textarea textarea-bordered w-full max-w-xl font-mono text-sm'
placeholder='LAPIS filter expression for counting sequences matching this variant.'
value={variant.countQuery}
onChange={(e) => onChange({ ...variant, countQuery: e.currentTarget.value })}
/>
</div>
);
}

// Memoized because GsMutationFilter is expensive to re-render. Relies on handleFilterObjectChange
// being stable (via useCallback) in the parent — otherwise memo would have no effect.
const MutationListVariantFields = memo(function MutationListVariantFields({
Expand Down