Skip to content

Commit

Permalink
feat: improve editing of exclusion zones filter options
Browse files Browse the repository at this point in the history
test: do not toggle open the processings panel
  • Loading branch information
hamed-musallam committed Sep 25, 2024
1 parent a39c498 commit b02b413
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -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<Column<ExclusionZone>> = useMemo(
() => [
{
Header: '#',
style: { width: '30px' },
accessor: (_, index) => index + 1,
},
{
Header: 'from',
Cell: ({ row }) => (
<NumberInput2Controller
control={control}
name={`zones.${row.index}.from`}
fill
noShadowBox
style={{ backgroundColor: 'transparent' }}
/>
),
},
{
Header: 'To',
Cell: ({ row }) => (
<NumberInput2Controller
control={control}
name={`zones.${row.index}.to`}
fill
noShadowBox
style={{ backgroundColor: 'transparent' }}
/>
),
},

{
Header: '',
style: { width: '30px' },
id: 'actions',
Cell: ({ row }) => {
return (
<Button
small
outlined
intent="danger"
onClick={() => handleDelete(row.index)}
>
<FaRegTrashAlt className={Classes.ICON} />
</Button>
);
},
},
],
[control, handleDelete],
);

function handleApplyFilter(values) {
dispatch({
type: 'APPLY_EXCLUSION_ZONE',
payload: values,
});
}

function handleCancelFilter() {
dispatch({
type: 'RESET_SELECTED_TOOL',
});
}

return (
<>
<StickyHeader>
<HeaderContainer>
<div />
<FilterActionButtons
onConfirm={() => handleSubmit(handleApplyFilter)()}
onCancel={handleCancelFilter}
disabledConfirm={!isDirty}
disabledCancel={!isDirty}
/>
</HeaderContainer>
</StickyHeader>
<Sections.Body>
<ReactTable<ExclusionZone>
columns={exclusionsZonesColumns}
data={exclusionsZones}
emptyDataRowText="No Zones"
/>
</Sections.Body>
</>
);
}
25 changes: 22 additions & 3 deletions src/component/panels/filtersPanel/Filters/FilterActionButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div style={{ display: 'flex', flexShrink: 0 }}>
<Button minimal intent="danger" onClick={onCancel} small>
<Button
minimal
intent="danger"
onClick={onCancel}
small
disabled={disabledCancel}
>
Cancel
</Button>

<Button outlined intent="success" onClick={onConfirm} small>
<Button
outlined
intent="success"
onClick={onConfirm}
small
disabled={disabledConfirm}
>
Save
</Button>
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/component/panels/filtersPanel/Filters/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -18,6 +19,7 @@ const {
shiftX,
shift2DX,
shift2DY,
exclusionZones,
} = Filters;
export const filterOptionPanels = {
[apodization.id]: ApodizationOptionsPanel,
Expand All @@ -28,6 +30,7 @@ export const filterOptionPanels = {
[shiftX.id]: ShiftOptionsPanel,
[shift2DX.id]: ShiftOptionsPanel,
[shift2DY.id]: ShiftOptionsPanel,
[exclusionZones.id]: ExclusionZonesOptionsPanel,
};

export const formLabelStyle: LabelStyle = {
Expand Down
2 changes: 2 additions & 0 deletions src/component/reducer/Reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ function innerSpectrumReducer(draft: Draft<State>, 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':
Expand Down
30 changes: 30 additions & 0 deletions src/component/reducer/actions/FiltersActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -176,6 +180,7 @@ export type FiltersActions =
| DeleteFilterAction
| DeleteSpectraFilterAction
| SetFilterSnapshotAction
| ExclusionZoneFilterAction
| AddExclusionZoneAction
| DeleteExclusionZoneAction
| ApplySignalProcessingAction
Expand Down Expand Up @@ -1343,6 +1348,30 @@ function handleSignalProcessingFilter(
setDomain(draft, { updateXDomain, updateYDomain, domainSpectraScope: 'all' });
}

//action
function handleApplyExclusionZone(
draft: Draft<State>,
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<State>,
Expand Down Expand Up @@ -1649,4 +1678,5 @@ export {
handleCalculateManualTwoDimensionPhaseCorrection,
handleToggleAddTracesToBothDirections,
handleApplyAutoPhaseCorrectionTwoDimensionsFilter,
handleApplyExclusionZone,
};
2 changes: 1 addition & 1 deletion src/component/toolbar/ToolTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export const options: RecordOptions = {
id: 'exclusionZones',
label: 'Exclusion zones',
hasOptionPanel: false,
isFilter: false,
isFilter: true,
mode: '1D',
spectraOptions: [
{
Expand Down
4 changes: 1 addition & 3 deletions test-e2e/panels/filters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit b02b413

Please sign in to comment.