From ec8262cfb61cf7166813e9014960927c59c92fdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 Jun 2024 14:16:37 +0000 Subject: [PATCH 01/14] chore(deps): bump @headlessui/react from 1.7.19 to 2.1.1 in /website Bumps [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) from 1.7.19 to 2.1.1. - [Release notes](https://github.com/tailwindlabs/headlessui/releases) - [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v2.1.1/packages/@headlessui-react) --- updated-dependencies: - dependency-name: "@headlessui/react" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- website/package-lock.json | 122 +++++++++++++++++++++++++++++++------- website/package.json | 2 +- 2 files changed, 103 insertions(+), 21 deletions(-) diff --git a/website/package-lock.json b/website/package-lock.json index 67a4a3e244..ef587dc959 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -11,7 +11,7 @@ "@astrojs/mdx": "^3.1.2", "@astrojs/node": "^8.3.2", "@emotion/react": "^11.11.4", - "@headlessui/react": "^1.7.19", + "@headlessui/react": "^2.1.1", "@mui/material": "~5.14.20", "@mui/x-date-pickers": "^6.19.7", "@svgr/core": "^8.1.0", @@ -1911,19 +1911,21 @@ "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, "node_modules/@headlessui/react": { - "version": "1.7.19", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", - "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.1.tgz", + "integrity": "sha512-808gVNUbRDbDR3GMNPHy+ON0uvR8b9H7IA+Q2UbhOsNCIjgwuwb2Iuv8VPT/1AW0UzLX8g10tN6LhF15GaUJCQ==", "dependencies": { - "@tanstack/react-virtual": "^3.0.0-beta.60", - "client-only": "^0.0.1" + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.17.1", + "@react-aria/interactions": "^3.21.3", + "@tanstack/react-virtual": "3.5.0" }, "engines": { "node": ">=10" }, "peerDependencies": { - "react": "^16 || ^17 || ^18", - "react-dom": "^16 || ^17 || ^18" + "react": "^18", + "react-dom": "^18" } }, "node_modules/@humanwhocodes/config-array": { @@ -3133,6 +3135,83 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-aria/focus": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.17.1.tgz", + "integrity": "sha512-FLTySoSNqX++u0nWZJPPN5etXY0WBxaIe/YuL/GTEeuqUIuC/2bJSaw5hlsM6T2yjy6Y/VAxBcKSdAFUlU6njQ==", + "dependencies": { + "@react-aria/interactions": "^3.21.3", + "@react-aria/utils": "^3.24.1", + "@react-types/shared": "^3.23.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.21.3", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.21.3.tgz", + "integrity": "sha512-BWIuf4qCs5FreDJ9AguawLVS0lV9UU+sK4CCnbCNNmYqOWY+1+gRXCsnOM32K+oMESBxilAjdHW5n1hsMqYMpA==", + "dependencies": { + "@react-aria/ssr": "^3.9.4", + "@react-aria/utils": "^3.24.1", + "@react-types/shared": "^3.23.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.4.tgz", + "integrity": "sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.24.1.tgz", + "integrity": "sha512-O3s9qhPMd6n42x9sKeJ3lhu5V1Tlnzhu6Yk8QOvDuXf7UGuUjXf9mzfHJt1dYzID4l9Fwm8toczBzPM9t0jc8Q==", + "dependencies": { + "@react-aria/ssr": "^3.9.4", + "@react-stately/utils": "^3.10.1", + "@react-types/shared": "^3.23.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.1.tgz", + "integrity": "sha512-VS/EHRyicef25zDZcM/ClpzYMC5i2YGN6uegOeQawmgfGjb02yaCX0F0zR69Pod9m2Hr3wunTbtpgVXvYbZItg==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-types/shared": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.1.tgz", + "integrity": "sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", @@ -3558,6 +3637,14 @@ "@svgr/core": "*" } }, + "node_modules/@swc/helpers": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz", + "integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@tailwindcss/forms": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", @@ -3644,11 +3731,11 @@ } }, "node_modules/@tanstack/react-virtual": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.1.2.tgz", - "integrity": "sha512-qibmxtctgOZo2I+3Rw5GR9kXgaa15U5r3/idDY1ItUKW15UK7GhCfyIfE6qYuJ1fxQF6dJDsD8SbpPyuJgpxuA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.5.0.tgz", + "integrity": "sha512-rtvo7KwuIvqK9zb0VZ5IL7fiJAEnG+0EiFZz8FUOs+2mhGqdGmjKIaT1XU7Zq0eFqL0jonLlhbayJI/J2SA/Bw==", "dependencies": { - "@tanstack/virtual-core": "3.1.2" + "@tanstack/virtual-core": "3.5.0" }, "funding": { "type": "github", @@ -3660,9 +3747,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.1.2.tgz", - "integrity": "sha512-DATZJs8iejkIUqXZe6ruDAnjFo78BKnIIgqQZrc7CmEFqfLEN/TPD91n4hRfo6hpRB6xC00bwKxv7vdjFNEmOg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.5.0.tgz", + "integrity": "sha512-KnPRCkQTyqhanNC0K63GBG3wA8I+D1fQuVnAvcBF8f13akOKeQp1gSbu6f77zCxhEk727iV5oQnbHLYzHrECLg==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -6098,11 +6185,6 @@ "node": ">= 12" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", diff --git a/website/package.json b/website/package.json index 15c127082f..9f6cb04422 100644 --- a/website/package.json +++ b/website/package.json @@ -23,7 +23,7 @@ "@astrojs/mdx": "^3.1.2", "@astrojs/node": "^8.3.2", "@emotion/react": "^11.11.4", - "@headlessui/react": "^1.7.19", + "@headlessui/react": "^2.1.1", "@mui/material": "~5.14.20", "@mui/x-date-pickers": "^6.19.7", "@svgr/core": "^8.1.0", From 52437b7408cba40028ed122f2d6cad2cbdb0f3fc Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 17:46:13 +0200 Subject: [PATCH 02/14] chore(website): migrate headless ui to use DialogPanel instead of Dialog.Overlay See https://github.com/tailwindlabs/headlessui/releases/tag/%40headlessui%2Freact%40v1.6.0 for migration guide --- website/src/components/SearchPage/CustomizeModal.tsx | 12 ++++++------ .../src/components/SearchPage/SeqPreviewModal.tsx | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/website/src/components/SearchPage/CustomizeModal.tsx b/website/src/components/SearchPage/CustomizeModal.tsx index fc2313fd25..fad67075c9 100644 --- a/website/src/components/SearchPage/CustomizeModal.tsx +++ b/website/src/components/SearchPage/CustomizeModal.tsx @@ -1,4 +1,4 @@ -import { Dialog, Transition } from '@headlessui/react'; +import { Dialog, DialogPanel, DialogTitle, Transition } from '@headlessui/react'; const titleCaseWords = (str: string) => { return str @@ -52,16 +52,16 @@ export const CustomizeModal: React.FC = ({
- +
-
- + + Customize {titleCaseWords(thingToCustomize)}s - +
Toggle the visibility of {thingToCustomize}s
@@ -91,7 +91,7 @@ export const CustomizeModal: React.FC = ({ Close
-
+
diff --git a/website/src/components/SearchPage/SeqPreviewModal.tsx b/website/src/components/SearchPage/SeqPreviewModal.tsx index e4b3cbef3d..23b7c20492 100644 --- a/website/src/components/SearchPage/SeqPreviewModal.tsx +++ b/website/src/components/SearchPage/SeqPreviewModal.tsx @@ -1,4 +1,4 @@ -import { Dialog, Transition } from '@headlessui/react'; +import { Dialog, DialogPanel, Transition } from '@headlessui/react'; import React, { useEffect, useState } from 'react'; import { routes } from '../../routes/routes'; @@ -124,11 +124,11 @@ export const SeqPreviewModal: React.FC = ({ ) : (
- -
+
+ {controls} {content} -
+
)} From d9bb4328f865ae67597d35c35c31712c425a5e0c Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 18:12:57 +0200 Subject: [PATCH 03/14] chore(website): address Combobox nullable breaking change for headless-ui v2 see https://github.com/tailwindlabs/headlessui/pull/3064 --- website/src/components/SearchPage/SearchFullUI.tsx | 10 ++++++++-- .../components/SearchPage/fields/AutoCompleteField.tsx | 2 +- website/src/types/config.ts | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/website/src/components/SearchPage/SearchFullUI.tsx b/website/src/components/SearchPage/SearchFullUI.tsx index 78b35bd921..7fe012739f 100644 --- a/website/src/components/SearchPage/SearchFullUI.tsx +++ b/website/src/components/SearchPage/SearchFullUI.tsx @@ -15,7 +15,13 @@ import { getLapisUrl } from '../../config.ts'; import { lapisClientHooks } from '../../services/serviceHooks.ts'; import { pageSize } from '../../settings'; import type { Group } from '../../types/backend.ts'; -import { type MetadataFilter, type Schema, type GroupedMetadataFilter, type FieldValues } from '../../types/config.ts'; +import { + type MetadataFilter, + type Schema, + type GroupedMetadataFilter, + type FieldValues, + type SetAFieldValue, +} from '../../types/config.ts'; import { type OrderBy } from '../../types/lapis.ts'; import type { ReferenceGenomesSequenceNames } from '../../types/referencesGenomes.ts'; import type { ClientConfig } from '../../types/runtimeConfig.ts'; @@ -160,7 +166,7 @@ export const InnerSearchFullUI = ({ return values; }, [state, hiddenFieldValues]); - const setAFieldValue = (fieldName: string, value: string | number) => { + const setAFieldValue: SetAFieldValue = (fieldName, value) => { setState((prev: any) => { const newState = { ...prev, diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.tsx index 25b7a12999..2532c72743 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.tsx @@ -10,7 +10,7 @@ type AutoCompleteFieldProps = { field: MetadataFilter | GroupedMetadataFilter; setAFieldValue: SetAFieldValue; lapisUrl: string; - fieldValue?: string | number; + fieldValue?: string | number | null; lapisSearchParameters: Record; }; diff --git a/website/src/types/config.ts b/website/src/types/config.ts index 1f746d4426..46b87161d3 100644 --- a/website/src/types/config.ts +++ b/website/src/types/config.ts @@ -117,5 +117,5 @@ export const websiteConfig = z.object({ }); export type WebsiteConfig = z.infer; -export type FieldValues = Record; -export type SetAFieldValue = (fieldName: string, value: string | number) => void; +export type FieldValues = Record; +export type SetAFieldValue = (fieldName: string, value: string | number | null) => void; From 7b5fb91d47208d525a53164dd426d8dfb3cbac53 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 18:26:12 +0200 Subject: [PATCH 04/14] chore(website): appease typescript for headlessui v2 upgrade for nullable combobox Headlessui v2 makes it necessary to handle null input for combobox. See https://github.com/tailwindlabs/headlessui/pull/3064 --- website/src/components/SearchPage/fields/MutationField.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/src/components/SearchPage/fields/MutationField.tsx b/website/src/components/SearchPage/fields/MutationField.tsx index 46f6b17a72..60e142d4ef 100644 --- a/website/src/components/SearchPage/fields/MutationField.tsx +++ b/website/src/components/SearchPage/fields/MutationField.tsx @@ -168,10 +168,14 @@ export const MutationField: FC = ({ referenceGenomesSequence setOptions(newOptions); }; - const handleOptionClick = (option: MutationQuery[] | MutationQuery) => { + const handleOptionClick = (option: MutationQuery[] | MutationQuery | null) => { if (Array.isArray(option)) { option = option[0]; } + // Unclear how to handle null here, necessary since headlessui v2 + if (!option) { + return; + } const newSelectedOptions = [...selectedOptions, option]; onChange(serializeMutationQueries(newSelectedOptions)); setInputValue(''); From a30a948488743de9620ea2ba245d95d81d6e065f Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 18:32:22 +0200 Subject: [PATCH 05/14] chore(website): address headlessui v2 deprecation warnings due to renamings --- website/src/components/Navigation/DocsMenu.tsx | 10 +++++----- .../src/components/ReviewPage/ReviewPage.tsx | 18 +++++++++--------- .../SearchPage/fields/AutoCompleteField.tsx | 16 ++++++++-------- .../SearchPage/fields/MutationField.tsx | 12 ++++++------ 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/website/src/components/Navigation/DocsMenu.tsx b/website/src/components/Navigation/DocsMenu.tsx index a823064a86..b5e28e701f 100644 --- a/website/src/components/Navigation/DocsMenu.tsx +++ b/website/src/components/Navigation/DocsMenu.tsx @@ -1,4 +1,4 @@ -import { Disclosure } from '@headlessui/react'; +import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'; import type { MDXInstance } from 'astro'; import React from 'react'; @@ -53,17 +53,17 @@ const DocsMenu: React.FC = ({ docsPages, currentPageUrl }) => {
Documentation
- + {open ? ( +
- +
    {Object.entries(groupedPages).map(([dir, pages]) => (
  • @@ -87,7 +87,7 @@ const DocsMenu: React.FC = ({ docsPages, currentPageUrl }) => {
  • ))}
-
+
    diff --git a/website/src/components/ReviewPage/ReviewPage.tsx b/website/src/components/ReviewPage/ReviewPage.tsx index c298a83b07..136fb68cea 100644 --- a/website/src/components/ReviewPage/ReviewPage.tsx +++ b/website/src/components/ReviewPage/ReviewPage.tsx @@ -1,4 +1,4 @@ -import { Menu } from '@headlessui/react'; +import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'; import Pagination from '@mui/material/Pagination'; import { type ChangeEvent, type FC, useState } from 'react'; @@ -220,15 +220,15 @@ const InnerReviewPage: FC = ({ clientConfig, organism, group, a
    {finishedCount > 0 && ( - + Discard sequences - - + +
    {errorCount > 0 && showErrors && ( - + - + )} - + - +
    -
    +
    )} {processedCount > 0 && ( diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.tsx index 2532c72743..99bb297a5e 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.tsx @@ -1,4 +1,4 @@ -import { Combobox } from '@headlessui/react'; +import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'; import { useEffect, useMemo, useState, useRef, forwardRef } from 'react'; import { TextField } from './TextField.tsx'; @@ -87,7 +87,7 @@ export const AutoCompleteField = ({ return ( setAFieldValue(field.name, value)}>
    - - + - No options available
    ) : ( filteredOptions.map((option) => ( - `relative cursor-default select-none py-2 pl-10 pr-4 ${ @@ -168,10 +168,10 @@ export const AutoCompleteField = ({ )} )} - + )) )} - +
    ); diff --git a/website/src/components/SearchPage/fields/MutationField.tsx b/website/src/components/SearchPage/fields/MutationField.tsx index 60e142d4ef..536b8bea7e 100644 --- a/website/src/components/SearchPage/fields/MutationField.tsx +++ b/website/src/components/SearchPage/fields/MutationField.tsx @@ -1,4 +1,4 @@ -import { Combobox, Transition } from '@headlessui/react'; +import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions, Transition } from '@headlessui/react'; import { type FC, Fragment, useMemo, useState } from 'react'; import * as React from 'react'; @@ -226,7 +226,7 @@ export const MutationField: FC = ({ referenceGenomesSequence > Mutations - setHasFocus(true)} onBlur={() => setHasFocus(false)} placeholder={hasFocus ? '' : selectedOptions.length === 0 ? 'Mutations' : 'Enter mutation'} @@ -246,9 +246,9 @@ export const MutationField: FC = ({ referenceGenomesSequence leaveFrom='opacity-100' leaveTo='opacity-0' > - + {options.map((option, index) => ( - @@ -260,9 +260,9 @@ export const MutationField: FC = ({ referenceGenomesSequence {option.text} )} - + ))} - +
From daaa5f73f957b85746e5e335853d404d36330018 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 18:37:43 +0200 Subject: [PATCH 06/14] chore(website): use focus instead of deprecated active, headlessui v2 migration see https://github.com/tailwindlabs/headlessui/releases/tag/%40headlessui%2Freact%40v2.0.0#user-content-upgrading-from-v1 --- .../components/SearchPage/fields/AutoCompleteField.tsx | 8 ++++---- .../src/components/SearchPage/fields/MutationField.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.tsx index 99bb297a5e..dea4b692aa 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.tsx @@ -138,14 +138,14 @@ export const AutoCompleteField = ({ filteredOptions.map((option) => ( + className={({ focus }) => `relative cursor-default select-none py-2 pl-10 pr-4 ${ - active ? 'bg-blue-500 text-white' : 'text-gray-900' + focus ? 'bg-blue-500 text-white' : 'text-gray-900' }` } value={option.option} > - {({ selected, active }) => ( + {({ selected, focus }) => ( <> {option.option} @@ -154,7 +154,7 @@ export const AutoCompleteField = ({ {selected && ( diff --git a/website/src/components/SearchPage/fields/MutationField.tsx b/website/src/components/SearchPage/fields/MutationField.tsx index 536b8bea7e..bccffc3a34 100644 --- a/website/src/components/SearchPage/fields/MutationField.tsx +++ b/website/src/components/SearchPage/fields/MutationField.tsx @@ -251,8 +251,8 @@ export const MutationField: FC = ({ referenceGenomesSequence - `${active ? 'text-white bg-blue-600' : 'text-gray-900'} cursor-default select-none relative py-2 pl-10 pr-4` + className={({ focus }) => + `${focus ? 'text-white bg-blue-600' : 'text-gray-900'} cursor-default select-none relative py-2 pl-10 pr-4` } > {({ selected }) => ( From bdfe36eb7770c47a04e85b406c9bf93a464145c6 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 19:10:29 +0200 Subject: [PATCH 07/14] chore(website): mock ResizeObserver, neccesary for headlessui v2 see https://github.com/tailwindlabs/headlessui/issues/3268 --- website/package-lock.json | 8 ++++++++ website/package.json | 1 + website/vitest.setup.ts | 7 ++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/website/package-lock.json b/website/package-lock.json index ef587dc959..21422e7879 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -78,6 +78,7 @@ "msw": "^2.3.1", "prettier": "3.3.2", "prettier-plugin-astro": "^0.14.0", + "resize-observer-polyfill": "^1.5.1", "sass": "^1.77.6", "tailwindcss": "^3.4.4", "typescript": "^5.5.2", @@ -12668,6 +12669,13 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", diff --git a/website/package.json b/website/package.json index 9f6cb04422..d3d0cbc9af 100644 --- a/website/package.json +++ b/website/package.json @@ -90,6 +90,7 @@ "msw": "^2.3.1", "prettier": "3.3.2", "prettier-plugin-astro": "^0.14.0", + "resize-observer-polyfill": "^1.5.1", "sass": "^1.77.6", "tailwindcss": "^3.4.4", "typescript": "^5.5.2", diff --git a/website/vitest.setup.ts b/website/vitest.setup.ts index dcf83fa662..50748b02c5 100755 --- a/website/vitest.setup.ts +++ b/website/vitest.setup.ts @@ -4,7 +4,8 @@ import '@testing-library/jest-dom'; import { HttpStatusCode } from 'axios'; import { http } from 'msw'; import { setupServer } from 'msw/node'; -import { afterAll, afterEach, beforeAll, beforeEach } from 'vitest'; +import ResizeObserver from 'resize-observer-polyfill'; +import { afterAll, afterEach, beforeAll, beforeEach, vi } from 'vitest'; import type { GetSequencesResponse, Group, SequenceEntryToEdit, SubmissionIdMapping } from './src/types/backend.ts'; import type { DetailsResponse, InsertionsResponse, LapisError, MutationsResponse } from './src/types/lapis.ts'; @@ -33,6 +34,10 @@ export const testConfig = { backendKeycloakClientSecret: 'dummy', } as RuntimeConfig; +// Stubbing necessary since headlessui v2 +// See https://github.com/tailwindlabs/headlessui/issues/3268 +vi.stubGlobal('ResizeObserver', ResizeObserver); + export const metadataKey = 'originalMetaDataField'; export const editableEntry = 'originalMetaDataValue'; export const defaultReviewData: SequenceEntryToEdit = { From fcd277b41782acbfcf0073c4353a130e508d2c28 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 23:04:09 +0200 Subject: [PATCH 08/14] Use new `immediate` feature of headlessui v2 --- .../SearchPage/DownloadDialog/ActiveDownloadFilters.tsx | 3 +-- .../SearchPage/fields/AutoCompleteField.spec.tsx | 2 +- .../src/components/SearchPage/fields/AutoCompleteField.tsx | 7 ++----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/website/src/components/SearchPage/DownloadDialog/ActiveDownloadFilters.tsx b/website/src/components/SearchPage/DownloadDialog/ActiveDownloadFilters.tsx index cfc635f04c..f8db7c1418 100644 --- a/website/src/components/SearchPage/DownloadDialog/ActiveDownloadFilters.tsx +++ b/website/src/components/SearchPage/DownloadDialog/ActiveDownloadFilters.tsx @@ -11,10 +11,9 @@ export const ActiveDownloadFilters: FC = ({ lapisSea let filterValues = Object.entries(lapisSearchParameters) .filter((vals) => vals[1] !== undefined && vals[1] !== '') .filter(([name, val]) => !(Object.keys(hiddenFieldValues).includes(name) && hiddenFieldValues[name] === val)) - .map(([name, filterValue]) => ({ name, filterValue })); + .map(([name, filterValue]) => ({ name, filterValue: filterValue !== null ? filterValue : '' })); filterValues = filterValues.filter(({ filterValue }) => filterValue.length > 0); - if (filterValues.length === 0) { return null; } diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx index 1510ea6e6e..dedb680bb8 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx @@ -35,7 +35,7 @@ describe('AutoCompleteField', () => { setAFieldValue.mockClear(); }); - it('renders input and shows all all options on empty input', async () => { + it('renders input and shows all options on empty input', async () => { mockUseAggregated.mockReturnValue({ data: { data: [ diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.tsx index dea4b692aa..4b644fbe27 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.tsx @@ -62,9 +62,6 @@ export const AutoCompleteField = ({ }); mutate({ fields: [field.name], ...otherFields }); - if (buttonRef.current) { - buttonRef.current.click(); - } }; const options = useMemo( @@ -85,7 +82,7 @@ export const AutoCompleteField = ({ ); return ( - setAFieldValue(field.name, value)}> + setAFieldValue(field.name, value)}>
{ From e4cfd585620d58588f28b52913effe750d381ff5 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 23:23:23 +0200 Subject: [PATCH 09/14] Remove overly specific unit test that does not seem necessary --- .../fields/AutoCompleteField.spec.tsx | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx index dedb680bb8..469b1a7ccd 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx @@ -145,36 +145,6 @@ describe('AutoCompleteField', () => { expect(screen.getByText('No options available')).toBeInTheDocument(); }); - it('calls setAFieldValue, when an option is selected', async () => { - mockUseAggregated.mockReturnValue({ - data: { - data: [ - { testField: 'Option 1', count: 10 }, - { testField: 'Option 2', count: 20 }, - ], - }, - isLoading: false, - error: null, - mutate: vi.fn(), - }); - render( - , - ); - - const input = screen.getByLabelText('Test Field'); - fireEvent.focus(input); - - const options = await screen.findAllByRole('option'); - fireEvent.click(options[0]); - - expect(setAFieldValue).toHaveBeenCalledWith('testField', 'Option 1'); - }); - it('clears input value on clear button click', async () => { mockUseAggregated.mockReturnValue({ data: { From 8e972fc59d47973c8d021375e0a7c74eb671279b Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 29 Jun 2024 23:35:20 +0200 Subject: [PATCH 10/14] Fix flaky test by waiting for modal to be removed --- website/src/components/SearchPage/SearchFullUI.spec.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/src/components/SearchPage/SearchFullUI.spec.tsx b/website/src/components/SearchPage/SearchFullUI.spec.tsx index d4721e59d2..f7bdbf2b22 100644 --- a/website/src/components/SearchPage/SearchFullUI.spec.tsx +++ b/website/src/components/SearchPage/SearchFullUI.spec.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/explicit-member-accessibility */ /* eslint-disable @typescript-eslint/no-empty-function */ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { render, screen, waitFor } from '@testing-library/react'; +import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { beforeEach, describe, expect, it, vi } from 'vitest'; @@ -225,6 +225,7 @@ describe('SearchFullUI', () => { await userEvent.click(field1Checkbox); const closeButton = await screen.findByRole('button', { name: 'Close' }); await userEvent.click(closeButton); + await waitForElementToBeRemoved(() => screen.queryByText('Toggle the visibility of search fields')); expect(screen.queryByLabelText('Field 1')).not.toBeInTheDocument(); }); From f1ef8becebc8d4d8e85808d396be04d146d6d6d5 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 30 Jun 2024 01:30:56 +0200 Subject: [PATCH 11/14] Revert "Remove overly specific unit test that does not seem necessary" This reverts commit c056aa8fcafa30a19a21ae356b2a07ab913c4b6f. --- .../fields/AutoCompleteField.spec.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx index 469b1a7ccd..dedb680bb8 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx @@ -145,6 +145,36 @@ describe('AutoCompleteField', () => { expect(screen.getByText('No options available')).toBeInTheDocument(); }); + it('calls setAFieldValue, when an option is selected', async () => { + mockUseAggregated.mockReturnValue({ + data: { + data: [ + { testField: 'Option 1', count: 10 }, + { testField: 'Option 2', count: 20 }, + ], + }, + isLoading: false, + error: null, + mutate: vi.fn(), + }); + render( + , + ); + + const input = screen.getByLabelText('Test Field'); + fireEvent.focus(input); + + const options = await screen.findAllByRole('option'); + fireEvent.click(options[0]); + + expect(setAFieldValue).toHaveBeenCalledWith('testField', 'Option 1'); + }); + it('clears input value on clear button click', async () => { mockUseAggregated.mockReturnValue({ data: { From 248acc89be923e47e0860773e42fcdf4a1810dbb Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 30 Jun 2024 01:37:09 +0200 Subject: [PATCH 12/14] fix failing test by using userEvent.click instead of fireEvent.click --- .../SearchPage/fields/AutoCompleteField.spec.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx index dedb680bb8..02332f4ac6 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx @@ -1,9 +1,10 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent, act } from '@testing-library/react'; import { describe, it, expect, beforeEach, vi } from 'vitest'; import { AutoCompleteField } from './AutoCompleteField'; import { lapisClientHooks } from '../../../services/serviceHooks.ts'; import { type MetadataFilter } from '../../../types/config.ts'; +import userEvent from '@testing-library/user-event'; vi.mock('../../../services/serviceHooks.ts'); vi.mock('../../../clientLogger.ts', () => ({ @@ -167,10 +168,10 @@ describe('AutoCompleteField', () => { ); const input = screen.getByLabelText('Test Field'); - fireEvent.focus(input); + await userEvent.click(input); const options = await screen.findAllByRole('option'); - fireEvent.click(options[0]); + await userEvent.click(options[0]); expect(setAFieldValue).toHaveBeenCalledWith('testField', 'Option 1'); }); From 4c9371c268cecd781053a6e9f477d8f8408d51d1 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 30 Jun 2024 01:49:29 +0200 Subject: [PATCH 13/14] fix format --- .../components/SearchPage/fields/AutoCompleteField.spec.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx index 02332f4ac6..5d2386a660 100644 --- a/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx +++ b/website/src/components/SearchPage/fields/AutoCompleteField.spec.tsx @@ -1,10 +1,10 @@ -import { render, screen, fireEvent, act } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { describe, it, expect, beforeEach, vi } from 'vitest'; import { AutoCompleteField } from './AutoCompleteField'; import { lapisClientHooks } from '../../../services/serviceHooks.ts'; import { type MetadataFilter } from '../../../types/config.ts'; -import userEvent from '@testing-library/user-event'; vi.mock('../../../services/serviceHooks.ts'); vi.mock('../../../clientLogger.ts', () => ({ From 09d2fd126c6e85cb6911fab126472ef617c03e72 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 30 Jun 2024 02:16:21 +0200 Subject: [PATCH 14/14] chore: address warnings/lints --- website/commitlint.config.js | 2 +- .../SequenceDetailsPage/SequenceContainer.spec.tsx | 3 +-- website/src/pages/[organism]/index.astro | 2 +- website/tests/pages/edit/edit.page.ts | 2 -- website/tests/pages/search/index.spec.ts | 6 +++--- website/tests/pages/search/search.page.ts | 2 +- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/website/commitlint.config.js b/website/commitlint.config.js index 422b19445b..3f5e287f9e 100644 --- a/website/commitlint.config.js +++ b/website/commitlint.config.js @@ -1 +1 @@ -module.exports = { extends: ['@commitlint/config-conventional'] }; +export default { extends: ['@commitlint/config-conventional'] }; diff --git a/website/src/components/SequenceDetailsPage/SequenceContainer.spec.tsx b/website/src/components/SequenceDetailsPage/SequenceContainer.spec.tsx index 4f1e160c37..0d975b3365 100644 --- a/website/src/components/SequenceDetailsPage/SequenceContainer.spec.tsx +++ b/website/src/components/SequenceDetailsPage/SequenceContainer.spec.tsx @@ -1,7 +1,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { render, screen, waitFor } from '@testing-library/react'; -import React from 'react'; -import { act } from 'react-dom/test-utils'; +import React, { act } from 'react'; import { beforeEach, describe, expect, test, vi } from 'vitest'; import { SequencesContainer } from './SequencesContainer.tsx'; diff --git a/website/src/pages/[organism]/index.astro b/website/src/pages/[organism]/index.astro index 8fd0c23b72..46379c6107 100644 --- a/website/src/pages/[organism]/index.astro +++ b/website/src/pages/[organism]/index.astro @@ -2,5 +2,5 @@ import { cleanOrganism } from '../../components/Navigation/cleanOrganism'; const { organism } = cleanOrganism(Astro.params.organism); -return Astro.redirect(`/${organism.key}/search`); +return Astro.redirect(`/${organism!.key}/search`); --- diff --git a/website/tests/pages/edit/edit.page.ts b/website/tests/pages/edit/edit.page.ts index dd03aafbd3..c0ab4e113e 100644 --- a/website/tests/pages/edit/edit.page.ts +++ b/website/tests/pages/edit/edit.page.ts @@ -6,11 +6,9 @@ import { baseUrl, dummyOrganism, expect } from '../../e2e.fixture'; export class EditPage { private readonly submitButton; - private readonly downloadButton; constructor(public readonly page: Page) { this.submitButton = this.page.getByRole('button', { name: 'Submit' }); - this.downloadButton = this.page.getByRole('button', { name: 'Download', exact: false }); } public async goto(accessionVersion: AccessionVersion) { diff --git a/website/tests/pages/search/index.spec.ts b/website/tests/pages/search/index.spec.ts index 436e4b3298..751ae6be5c 100644 --- a/website/tests/pages/search/index.spec.ts +++ b/website/tests/pages/search/index.spec.ts @@ -26,7 +26,7 @@ test.describe('The search page', () => { `${baseUrl}${routes.searchPage(dummyOrganism.key)}?accession=${testAccessionVersion}`, ); const accessionLink = searchPage.page.getByRole('link', { name: testAccessionVersion }); - await searchPage.page.getByText('Search returned 1 sequence'); + searchPage.page.getByText('Search returned 1 sequence'); await expect(accessionLink).toBeVisible(); const rowLocator = searchPage.page.locator('tr'); @@ -77,9 +77,9 @@ test.describe('The search page', () => { await searchPage.searchFor([{ name: 'country', filterValue: 'Switzerland' }]); await searchPage.page.locator('tr').first().waitFor(); - const rowLocator = await searchPage.page.locator('tr').getByText('Switzerland'); + const rowLocator = searchPage.page.locator('tr').getByText('Switzerland'); const rowCount = await rowLocator.count(); - await expect(rowCount).toBeGreaterThan(0); + expect(rowCount).toBeGreaterThan(0); }); test('should reset the search', async ({ searchPage }) => { diff --git a/website/tests/pages/search/search.page.ts b/website/tests/pages/search/search.page.ts index 39a9ab7c7c..10c5bce942 100644 --- a/website/tests/pages/search/search.page.ts +++ b/website/tests/pages/search/search.page.ts @@ -39,7 +39,7 @@ export class SearchPage { const accessions = []; for (let index = 1; index < 1 + elementsCount; index++) { - const element = await rowLocator.nth(index); + const element = rowLocator.nth(index); const innerText = await element.innerText(); accessions.push(innerText.split(' ')[0]); }