From d09eaa4d8abc19635da8dedde86402b13fbed57a Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Tue, 24 Sep 2024 21:40:50 +0200 Subject: [PATCH] feat: improve editing of exclusion zones filter options test: do not toggle open the processings panel --- .../Filters/ExclusionZonesOptionsPanel.tsx | 169 ++++++++++++++++++ .../Filters/FilterActionButtons.tsx | 25 ++- .../panels/filtersPanel/Filters/index.tsx | 3 + src/component/reducer/Reducer.ts | 2 + .../reducer/actions/FiltersActions.ts | 30 ++++ src/component/toolbar/ToolTypes.ts | 2 +- test-e2e/panels/filters.test.ts | 4 +- 7 files changed, 228 insertions(+), 7 deletions(-) create mode 100644 src/component/panels/filtersPanel/Filters/ExclusionZonesOptionsPanel.tsx diff --git a/src/component/panels/filtersPanel/Filters/ExclusionZonesOptionsPanel.tsx b/src/component/panels/filtersPanel/Filters/ExclusionZonesOptionsPanel.tsx new file mode 100644 index 000000000..843ec7923 --- /dev/null +++ b/src/component/panels/filtersPanel/Filters/ExclusionZonesOptionsPanel.tsx @@ -0,0 +1,169 @@ +import { Classes } from '@blueprintjs/core'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { Filter } from 'nmr-processing'; +import { useCallback, useEffect, useMemo } from 'react'; +import { useForm, useWatch } from 'react-hook-form'; +import { FaRegTrashAlt } from 'react-icons/fa'; +import { Button } from 'react-science/ui'; +import * as Yup from 'yup'; + +import { ExclusionZone } from '../../../../data/types/data1d/ExclusionZone'; +import { useChartData } from '../../../context/ChartContext'; +import { useDispatch } from '../../../context/DispatchContext'; +import { NumberInput2Controller } from '../../../elements/NumberInput2Controller'; +import ReactTable, { Column } from '../../../elements/ReactTable/ReactTable'; +import { Sections } from '../../../elements/Sections'; + +import { FilterActionButtons } from './FilterActionButtons'; +import { HeaderContainer, StickyHeader } from './InnerFilterHeader'; + +interface ExclusionZonesOptionsPanelProps { + filter: Filter; +} + +const validationSchema = (min: number, max: number) => + Yup.object().shape({ + zones: Yup.array() + .of( + Yup.object({ + id: Yup.string().required(), + from: Yup.number().min(min).max(max).required(), + to: Yup.number().min(min).max(max).required(), + }), + ) + .required(), + }); + +export default function ExclusionZonesOptionsPanel( + options: ExclusionZonesOptionsPanelProps, +) { + const { filter } = options; + const dispatch = useDispatch(); + const { + originDomain: { + xDomain: [min, max], + }, + } = useChartData(); + const { + handleSubmit, + setValue, + control, + reset, + getValues, + formState: { isDirty }, + } = useForm<{ zones: ExclusionZone[] }>({ + defaultValues: { zones: [] }, + resolver: yupResolver(validationSchema(min, max)), + }); + + const exclusionsZones = useWatch({ + name: 'zones', + control, + defaultValue: [], + }); + + useEffect(() => { + if (Array.isArray(filter.value)) { + reset({ zones: filter.value }); + } + }, [filter.value, reset]); + + const handleDelete = useCallback( + (index) => { + setValue( + 'zones', + getValues('zones').filter((_, i) => i !== index), + ); + }, + [getValues, setValue], + ); + + const exclusionsZonesColumns: Array> = useMemo( + () => [ + { + Header: '#', + style: { width: '30px' }, + accessor: (_, index) => index + 1, + }, + { + Header: 'from', + Cell: ({ row }) => ( + + ), + }, + { + Header: 'To', + Cell: ({ row }) => ( + + ), + }, + + { + Header: '', + style: { width: '30px' }, + id: 'actions', + Cell: ({ row }) => { + return ( + + ); + }, + }, + ], + [control, handleDelete], + ); + + function handleApplyFilter(values) { + dispatch({ + type: 'APPLY_EXCLUSION_ZONE', + payload: values, + }); + } + + function handleCancelFilter() { + dispatch({ + type: 'RESET_SELECTED_TOOL', + }); + } + + return ( + <> + + +
+ handleSubmit(handleApplyFilter)()} + onCancel={handleCancelFilter} + disabledConfirm={!isDirty} + disabledCancel={!isDirty} + /> + + + + + columns={exclusionsZonesColumns} + data={exclusionsZones} + emptyDataRowText="No Zones" + /> + + + ); +} diff --git a/src/component/panels/filtersPanel/Filters/FilterActionButtons.tsx b/src/component/panels/filtersPanel/Filters/FilterActionButtons.tsx index 10cf6cbf4..1b64ae7aa 100644 --- a/src/component/panels/filtersPanel/Filters/FilterActionButtons.tsx +++ b/src/component/panels/filtersPanel/Filters/FilterActionButtons.tsx @@ -2,19 +2,38 @@ import { Button, ButtonProps } from '@blueprintjs/core'; interface FilterActionButtonsProps { onConfirm: ButtonProps['onClick']; + disabledConfirm?: boolean; onCancel: ButtonProps['onClick']; + disabledCancel?: boolean; } export function FilterActionButtons(props: FilterActionButtonsProps) { - const { onConfirm, onCancel } = props; + const { + onConfirm, + onCancel, + disabledConfirm = false, + disabledCancel = false, + } = props; return (
- -
diff --git a/src/component/panels/filtersPanel/Filters/index.tsx b/src/component/panels/filtersPanel/Filters/index.tsx index 3c51763eb..723580600 100644 --- a/src/component/panels/filtersPanel/Filters/index.tsx +++ b/src/component/panels/filtersPanel/Filters/index.tsx @@ -4,6 +4,7 @@ import { LabelStyle } from '../../../elements/Label'; import ApodizationOptionsPanel from './ApodizationOptionsPanel'; import BaseLineCorrectionOptionsPanel from './BaseLineCorrectionOptionsPanel'; +import ExclusionZonesOptionsPanel from './ExclusionZonesOptionsPanel'; import PhaseCorrectionOptionsPanel from './PhaseCorrectionOptionsPanel'; import PhaseCorrectionTwoDimensionsOptionsPanel from './PhaseCorrectionTwoDimensionsOptionsPanel'; import ShiftOptionsPanel from './ShiftOptionsPanel'; @@ -18,6 +19,7 @@ const { shiftX, shift2DX, shift2DY, + exclusionZones, } = Filters; export const filterOptionPanels = { [apodization.id]: ApodizationOptionsPanel, @@ -28,6 +30,7 @@ export const filterOptionPanels = { [shiftX.id]: ShiftOptionsPanel, [shift2DX.id]: ShiftOptionsPanel, [shift2DY.id]: ShiftOptionsPanel, + [exclusionZones.id]: ExclusionZonesOptionsPanel, }; export const formLabelStyle: LabelStyle = { diff --git a/src/component/reducer/Reducer.ts b/src/component/reducer/Reducer.ts index b828ace13..009a46279 100644 --- a/src/component/reducer/Reducer.ts +++ b/src/component/reducer/Reducer.ts @@ -499,6 +499,8 @@ function innerSpectrumReducer(draft: Draft, action: Action) { return FiltersActions.handleSetFilterSnapshotHandler(draft, action); case 'APPLY_SIGNAL_PROCESSING_FILTER': return FiltersActions.handleSignalProcessingFilter(draft, action); + case 'APPLY_EXCLUSION_ZONE': + return FiltersActions.handleApplyExclusionZone(draft, action); case 'ADD_EXCLUSION_ZONE': return FiltersActions.handleAddExclusionZone(draft, action); case 'DELETE_EXCLUSION_ZONE': diff --git a/src/component/reducer/actions/FiltersActions.ts b/src/component/reducer/actions/FiltersActions.ts index 8642f01e9..469c23d7c 100644 --- a/src/component/reducer/actions/FiltersActions.ts +++ b/src/component/reducer/actions/FiltersActions.ts @@ -129,6 +129,10 @@ type SetFilterSnapshotAction = ActionType< 'SET_FILTER_SNAPSHOT', { name: string; id: string } >; +type ExclusionZoneFilterAction = ActionType< + 'APPLY_EXCLUSION_ZONE', + { zones: ExclusionZone[] } +>; type AddExclusionZoneAction = ActionType< 'ADD_EXCLUSION_ZONE', { startX: number; endX: number } @@ -176,6 +180,7 @@ export type FiltersActions = | DeleteFilterAction | DeleteSpectraFilterAction | SetFilterSnapshotAction + | ExclusionZoneFilterAction | AddExclusionZoneAction | DeleteExclusionZoneAction | ApplySignalProcessingAction @@ -1343,6 +1348,30 @@ function handleSignalProcessingFilter( setDomain(draft, { updateXDomain, updateYDomain, domainSpectraScope: 'all' }); } +//action +function handleApplyExclusionZone( + draft: Draft, + action: ExclusionZoneFilterAction, +) { + const { zones } = action.payload; + + const activeSpectrum = getActiveSpectrum(draft); + + if (!activeSpectrum) { + return; + } + + FiltersManager.applyFilter(draft.data[activeSpectrum.index], [ + { + name: exclusionZones.id, + value: zones, + }, + ]); + + const { updateXDomain, updateYDomain } = exclusionZones.DOMAIN_UPDATE_RULES; + + setDomain(draft, { updateXDomain, updateYDomain }); +} //action function handleAddExclusionZone( draft: Draft, @@ -1649,4 +1678,5 @@ export { handleCalculateManualTwoDimensionPhaseCorrection, handleToggleAddTracesToBothDirections, handleApplyAutoPhaseCorrectionTwoDimensionsFilter, + handleApplyExclusionZone, }; diff --git a/src/component/toolbar/ToolTypes.ts b/src/component/toolbar/ToolTypes.ts index 63b3bb314..3f1d399be 100644 --- a/src/component/toolbar/ToolTypes.ts +++ b/src/component/toolbar/ToolTypes.ts @@ -239,7 +239,7 @@ export const options: RecordOptions = { id: 'exclusionZones', label: 'Exclusion zones', hasOptionPanel: false, - isFilter: false, + isFilter: true, mode: '1D', spectraOptions: [ { diff --git a/test-e2e/panels/filters.test.ts b/test-e2e/panels/filters.test.ts index 2603a50fb..3ede2dc93 100644 --- a/test-e2e/panels/filters.test.ts +++ b/test-e2e/panels/filters.test.ts @@ -190,7 +190,7 @@ test('Processed spectra filters', async ({ page }) => { await nmrium.page.click('li >> text=Cytisine'); await nmrium.page.click('li >> text=Processed 13C FID'); - // wait specturm to load + // wait spectrum to load await expect(nmrium.page.locator('#nmrSVG')).toBeVisible(); }); await test.step('Check filters panel', async () => { @@ -250,8 +250,6 @@ test('Exclusion zones', async ({ page }) => { const spectraTable = nmrium.page.locator('_react=SpectraTable'); await spectraTable.locator(`_react=[role="row"]`).nth(2).click(); - // Open processings panel - await nmrium.clickPanel('Processings'); const filters = nmrium.page.locator('_react=FiltersSectionsPanel'); await expect(filters.locator('text=Exclusion zones')).toBeVisible();