diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3d7cd3457..42872cfd4 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -8,7 +8,7 @@ on: jobs: e2e: - runs-on: macos-latest + runs-on: ubuntu-latest strategy: matrix: project: [chromium, firefox, webkit] diff --git a/package-lock.json b/package-lock.json index 08331a9e8..f80f7a062 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@hookform/resolvers": "^5.2.2", + "@tanstack/react-form": "^1.23.8", "@zakodium/nmr-types": "^0.5.0", "@zakodium/nmrium-core": "^0.5.0", "@zakodium/nmrium-core-plugins": "^0.6.18", @@ -58,10 +59,11 @@ "react-ocl-nmr": "^4.1.1", "react-plot": "^3.1.2", "react-rnd": "^10.5.2", - "react-science": "^19.3.1", + "react-science": "^19.4.0", "react-table": "^7.8.0", "smart-array-filter": "^5.0.0", - "yup": "^1.7.1" + "yup": "^1.7.1", + "zod": "^4.1.12" }, "devDependencies": { "@blueprintjs/core": "^6.3.4", @@ -10643,9 +10645,9 @@ } }, "node_modules/react-science": { - "version": "19.3.1", - "resolved": "https://registry.npmjs.org/react-science/-/react-science-19.3.1.tgz", - "integrity": "sha512-qgkf0mdoUMFLIVfNWMN6sijdGlnEagd/s9mqXJPvSu/iJbL0f6Zvzn1cC8H2vbyHr44JkTT2cFKOERQMjDwmyg==", + "version": "19.4.0", + "resolved": "https://registry.npmjs.org/react-science/-/react-science-19.4.0.tgz", + "integrity": "sha512-VHsyk3dY5sTaZ1FYViauQ6VZtrx3Ksnbo00zUr0fmd2uZ2cLJa5qDgiYkWAzdZ59AAMvccWuxcSH1ba8Q+QpPg==", "license": "MIT", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.7.7", diff --git a/package.json b/package.json index 867c10eff..0f37e66ee 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@hookform/resolvers": "^5.2.2", + "@tanstack/react-form": "^1.23.8", "@zakodium/nmr-types": "^0.5.0", "@zakodium/nmrium-core": "^0.5.0", "@zakodium/nmrium-core-plugins": "^0.6.18", @@ -112,10 +113,11 @@ "react-ocl-nmr": "^4.1.1", "react-plot": "^3.1.2", "react-rnd": "^10.5.2", - "react-science": "^19.3.1", + "react-science": "^19.4.0", "react-table": "^7.8.0", "smart-array-filter": "^5.0.0", - "yup": "^1.7.1" + "yup": "^1.7.1", + "zod": "^4.1.12" }, "devDependencies": { "@blueprintjs/core": "^6.3.4", diff --git a/src/component/hooks/useSaveSettings.tsx b/src/component/hooks/useSaveSettings.tsx index ef9ed40fb..5f8923158 100644 --- a/src/component/hooks/useSaveSettings.tsx +++ b/src/component/hooks/useSaveSettings.tsx @@ -1,124 +1,111 @@ -import { Dialog, DialogBody, DialogFooter } from '@blueprintjs/core'; -import styled from '@emotion/styled'; -import { yupResolver } from '@hookform/resolvers/yup'; +import { Button, Dialog, DialogBody, DialogFooter } from '@blueprintjs/core'; +import { revalidateLogic } from '@tanstack/react-form'; import type { Workspace } from '@zakodium/nmrium-core'; import { useRef } from 'react'; -import { useForm } from 'react-hook-form'; -import { useOnOff } from 'react-science/ui'; -import * as Yup from 'yup'; +import { useForm as useReactScienceForm, useOnOff } from 'react-science/ui'; +import { z } from 'zod/v4'; import { usePreferences } from '../context/PreferencesContext.js'; import { useToaster } from '../context/ToasterContext.js'; -import ActionButtons from '../elements/ActionButtons.js'; -import { Input2Controller } from '../elements/Input2Controller.js'; import { useWorkspaceAction } from './useWorkspaceAction.js'; -const schema = Yup.object().shape({ - workspaceName: Yup.string().required(), +const schema = z.object({ + workspaceName: z + .string() + .trim() + .min(1, { error: 'Workspace name must have at least one character' }), }); -function WorkspaceAddForm(props: any) { - const { className, message, control, onEnter } = props; - - return ( -
-

{message}

- { - if (event.key === 'Enter') { - onEnter(); - } - }} - /> -
- ); -} - export function useSaveSettings() { const toaster = useToaster(); const [isOpenDialog, openDialog, closeDialog] = useOnOff(false); const settingsRef = useRef(); const { current } = usePreferences(); - const { handleSubmit, control, reset } = useForm({ - defaultValues: { workspaceName: '' }, - resolver: yupResolver(schema), - }); + const { saveWorkspace, addNewWorkspace } = useWorkspaceAction(); - function handleAddNewWorkspace({ workspaceName }: { workspaceName: string }) { - addNewWorkspace(workspaceName, settingsRef.current); - closeDialog(); - toaster.show({ - message: 'Preferences saved successfully', - intent: 'success', - }); - } + const form = useReactScienceForm({ + defaultValues: { workspaceName: '' }, + validationLogic: revalidateLogic({ modeAfterSubmission: 'change' }), + validators: { onDynamic: schema }, + onSubmit: ({ value }) => { + const { workspaceName } = value; + addNewWorkspace(workspaceName, settingsRef.current); + closeDialog(); + + toaster.show({ + message: 'Preferences saved successfully', + intent: 'success', + }); + }, + }); function saveSettings(values?: Partial) { settingsRef.current = values as Workspace; if (current.source !== 'user') { - reset({ workspaceName: '' }); + form.reset(); openDialog(); } else { saveWorkspace(values); - closeDialog(); } } + return { saveSettings, SaveSettingsModal: () => { return ( - - - Please enter a new user workspace name in order to save your - changes locally - - { - void handleSubmit(handleAddNewWorkspace)(); - }} - control={control} - /> - - - void handleSubmit(handleAddNewWorkspace)()} +
{ + event.preventDefault(); + void form.handleSubmit(); + }} + > + + + {(field) => ( + + )} + + + + + + + + Save workspace + + + + } /> - +
); }, }; } - -const DialogContent = styled(DialogBody)` - background-color: white; - text-align: center; -`; - -const Title = styled.p` - font-size: 1em; - font-weight: bold; - padding: 0 30px; - text-align: left; -`; diff --git a/test-e2e/panels/spectra.test.ts b/test-e2e/panels/spectra.test.ts index 8482ccb9b..045e6fac3 100644 --- a/test-e2e/panels/spectra.test.ts +++ b/test-e2e/panels/spectra.test.ts @@ -29,7 +29,7 @@ test('Check if the color picker is visible after click on the ColorIndicator', a const nmrium = await NmriumPage.create(page); await nmrium.open2D(); await nmrium.page.click('_react=SpectraTabs >> _react=Tab[tabid="1H,1H"]'); - const sketchPicker = nmrium.page.locator('_react=ColorPicker'); + const sketchPicker = nmrium.page.locator('_react=Saturation'); await expect(sketchPicker).toHaveCount(0); await nmrium.page.click('_react=ColorIndicator >> nth=0'); @@ -70,7 +70,7 @@ test('Check change spectrum color, Should be white', async ({ page }) => { await nmrium.page.click('_react=ColorIndicator'); // Click on the top-left of the color picker (white) - await nmrium.page.click('_react=ColorPicker >> div >> nth=0', { + await nmrium.page.click('_react=Saturation', { position: { x: 0, y: 0 }, }); @@ -128,7 +128,7 @@ test('2d spectrum', async ({ page }) => { await nmrium.page.click('_react=ColorIndicator'); // change the color to #ddb1c9ff - await nmrium.page.click('_react=ColorPicker >> div >> nth=0', { + await nmrium.page.click('_react=Saturation', { position: { x: 40, y: 20 }, }); @@ -145,7 +145,7 @@ test('2d spectrum', async ({ page }) => { // Close color picker await nmrium.viewer.locator.click({ force: true }); - await expect(nmrium.page.locator('_react=ColorPicker')).toBeHidden(); + await expect(nmrium.page.locator('_react=Saturation')).toBeHidden(); }); await test.step('Change H1,H1 spectrum', async () => { await nmrium.page.click('_react=SpectraTabs >> _react=Tab[tabid="1H,1H"]'); @@ -194,15 +194,15 @@ test('2d spectrum', async ({ page }) => { .click(); await nmrium.page.click( - '_react=ColorPicker >> _react=SketchPresetColors >> nth=0 >> div >> nth=0', + '_react=SketchPresetColors >> nth=0 >> div >> nth=0', ); // Change colors await nmrium.page.click( - '_react=ColorPicker >> _react=SketchPresetColors >> nth=0 >> div >> nth=0', + '_react=SketchPresetColors >> nth=0 >> div >> nth=0', ); await nmrium.page.click( - '_react=ColorPicker >> _react=SketchPresetColors >> nth=1 >> div >> nth=5', + '_react=SketchPresetColors >> nth=1 >> div >> nth=5', ); // Check that ColorIndicator color changed