Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(website): store previewed sequence ID in URL #3762

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
60 changes: 58 additions & 2 deletions website/src/components/SearchPage/SearchFullUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,66 @@ export const InnerSearchFullUI = ({
return consolidateGroupedFields(metadataSchemaWithExpandedRanges);
}, [metadataSchema]);

const [previewedSeqId, setPreviewedSeqId] = useState<string | null>(null);
const [previewHalfScreen, setPreviewHalfScreen] = useState(false);
const [state, setState] = useQueryAsState(initialQueryDict);

// Initialize previewedSeqId from URL parameter if present
const [previewedSeqId, setPreviewedSeqIdInner] = useState<string | null>(state.selectedSeq || null);
const [previewHalfScreen, setPreviewHalfScreenInner] = useState(state.halfScreen === 'true');

// Function to update both state and URL for half screen preference
const setPreviewHalfScreen = useCallback(
(isHalfScreen: boolean) => {
setPreviewHalfScreenInner(isHalfScreen);
setState((prev: QueryState) => {
if (!isHalfScreen) {
const withoutHalfScreenSet = { ...prev };
delete withoutHalfScreenSet.halfScreen;
return withoutHalfScreenSet;
} else {
return {
...prev,
halfScreen: 'true',
};
}
});
},
[setState],
);

// Function to update both state and URL for selected sequence
const setPreviewedSeqId = useCallback(
(seqId: string | null) => {
setPreviewedSeqIdInner(seqId);
setState((prev: QueryState) => {
if (seqId === null) {
const withoutSeqIdSet = { ...prev };
delete withoutSeqIdSet.selectedSeq;
return withoutSeqIdSet;
} else {
return {
...prev,
selectedSeq: seqId,
};
}
});
},
[setState],
);

// Update local state when URL parameters change
useEffect(() => {
if (state.selectedSeq !== undefined && state.selectedSeq !== previewedSeqId) {
setPreviewedSeqIdInner(state.selectedSeq);
} else if (state.selectedSeq === undefined && previewedSeqId !== null) {
setPreviewedSeqIdInner(null);
}

const halfScreenFromUrl = state.halfScreen === 'true';
if (halfScreenFromUrl !== previewHalfScreen) {
setPreviewHalfScreenInner(halfScreenFromUrl);
}
}, [state.selectedSeq, state.halfScreen, previewedSeqId, previewHalfScreen]);

const searchVisibilities = useMemo(() => {
return getFieldVisibilitiesFromQuery(schema, state);
}, [schema, state]);
Expand Down
57 changes: 57 additions & 0 deletions website/tests/pages/search/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,61 @@ test.describe('The search page', () => {
void expect(page.getByRole('checkbox', { name: 'Pango lineage' })).toBeVisible();
void expect(page.getByRole('checkbox', { name: 'Hidden Field' })).not.toBeVisible();
});

test('should add selected sequence to URL when clicking a sequence', async ({ searchPage, page }) => {
// Go to search page
await searchPage.goto();

// Get accessions using the helper method that returns a Promise
const accessions = await searchPage.getAccessions(1);
const accessionId = accessions[0];

// Find the link with the accession ID
const accessionLink = page.getByRole('link', { name: accessionId });

// Click to show the sequence preview modal
await accessionLink.click();

// Wait for the modal to appear
await expect(page.getByText('Amino acid mutations')).toBeVisible({ timeout: 30000 });

// Verify URL contains the selectedSeq parameter
await expect(page).toHaveURL(new RegExp(`selectedSeq=${accessionId}`));
});

test('should add halfScreen parameter to URL when toggling view mode', async ({ searchPage, page }) => {
// Go to search page and click a sequence
await searchPage.goto();
const firstAccessionLink = page.getByRole('link', { name: /LOC_\d+\.\d+/ });
await firstAccessionLink.click();

// Wait for the modal to appear
await expect(page.getByText('Amino acid mutations')).toBeVisible({ timeout: 30000 });

// Click the dock button (halfScreen toggle)
await page.getByTitle('Dock sequence details view').click();

// Verify URL contains the halfScreen parameter
await expect(page).toHaveURL(/halfScreen=true/);

// Toggle back to full screen
await page.getByTitle('Expand sequence details view').click();

// Verify halfScreen parameter is removed from URL
await expect(page).not.toHaveURL(/halfScreen/);
});

test('should restore state from URL parameters', async ({ searchPage, page }) => {
// Get a valid sequence ID first by using the searchPage fixture
await searchPage.goto();

const accessions = await searchPage.getAccessions(1);

const accessionId = /LOC_\d+\.\d+/.exec(accessions[0])[0];

await page.goto(`${baseUrl}${routes.searchPage(dummyOrganism.key)}?selectedSeq=${accessionId}&halfScreen=true`);

await expect(page.getByText('Amino acid mutations')).toBeVisible({ timeout: 30000 });
await expect(page.getByTitle('Expand sequence details view')).toBeVisible();
});
});
Loading