Skip to content

Commit 6c7d54a

Browse files
Improve editor, allow deletion of MSA
1 parent abe8ea7 commit 6c7d54a

11 files changed

Lines changed: 713 additions & 63 deletions

File tree

src/__tests__/components/AnalysisStore.test.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
useGloss,
1313
useGlossDispatch,
1414
useMorphemeBreakdownDispatch,
15+
useMorphemeDeleteDispatch,
1516
useMorphemeGlossDispatch,
1617
useMorphemes,
1718
usePhraseLinkByIdMap,
@@ -864,6 +865,22 @@ function MorphemeWriter({
864865
);
865866
}
866867

868+
/**
869+
* Renders a button that dispatches a morpheme breakdown deletion, used to test
870+
* `useMorphemeDeleteDispatch`.
871+
*
872+
* @param props.tokenRef - Token ref whose breakdown to delete.
873+
* @returns JSX element suitable for passing to `render`.
874+
*/
875+
function MorphemeDeleter({ tokenRef }: Readonly<{ tokenRef: string }>) {
876+
const dispatch = useMorphemeDeleteDispatch();
877+
return (
878+
<button onClick={() => dispatch(tokenRef)} type="button">
879+
delete-morphemes
880+
</button>
881+
);
882+
}
883+
867884
/**
868885
* Renders a button that dispatches a morpheme gloss, used to test `useMorphemeGlossDispatch`.
869886
*
@@ -925,6 +942,16 @@ function MorphemeGlossDispatchUser() {
925942
return undefined;
926943
}
927944

945+
/**
946+
* Renders a component that calls `useMorphemeDeleteDispatch` without a provider.
947+
*
948+
* @returns Nothing — only mounted to trigger the throw.
949+
*/
950+
function MorphemeDeleteDispatchUser() {
951+
useMorphemeDeleteDispatch();
952+
return undefined;
953+
}
954+
928955
describe('useMorphemes', () => {
929956
it('returns empty array when no morphemes exist', () => {
930957
render(
@@ -1018,6 +1045,55 @@ describe('useMorphemeBreakdownDispatch', () => {
10181045
});
10191046
});
10201047

1048+
describe('useMorphemeDeleteDispatch', () => {
1049+
it('removes the morpheme breakdown and calls onSave', async () => {
1050+
const onSave = jest.fn();
1051+
const ta: TokenAnalysis = {
1052+
id: 'ta-1',
1053+
surfaceText: 'cat',
1054+
morphemes: [
1055+
{ id: 'm-1', form: 'ca', writingSystem: 'und' },
1056+
{ id: 'm-2', form: '-t', writingSystem: 'und' },
1057+
],
1058+
};
1059+
const link: TokenAnalysisLink = {
1060+
analysisId: 'ta-1',
1061+
status: 'approved',
1062+
token: { tokenRef: 'tok-1', surfaceText: 'cat' },
1063+
};
1064+
const analysis: TextAnalysis = {
1065+
segmentAnalyses: [],
1066+
segmentAnalysisLinks: [],
1067+
tokenAnalyses: [ta],
1068+
tokenAnalysisLinks: [link],
1069+
phraseAnalyses: [],
1070+
phraseAnalysisLinks: [],
1071+
};
1072+
render(
1073+
<AnalysisStoreProvider initialAnalysis={analysis} analysisLanguage="und" onSave={onSave}>
1074+
<MorphemeDeleter tokenRef="tok-1" />
1075+
<MorphemeReader tokenRef="tok-1" />
1076+
</AnalysisStoreProvider>,
1077+
);
1078+
1079+
await userEvent.click(screen.getByRole('button', { name: 'delete-morphemes' }));
1080+
1081+
expect(screen.getByTestId('morphemes')).toHaveTextContent('');
1082+
expect(onSave).toHaveBeenCalledTimes(1);
1083+
const saved: TextAnalysis = onSave.mock.calls[0][0];
1084+
// The analysis carried no gloss, so the now-empty record and its link are removed entirely.
1085+
expect(saved.tokenAnalyses).toHaveLength(0);
1086+
expect(saved.tokenAnalysisLinks).toHaveLength(0);
1087+
});
1088+
1089+
it('throws when called outside an AnalysisStoreProvider', () => {
1090+
jest.spyOn(console, 'error').mockImplementation(() => {});
1091+
expect(() => render(<MorphemeDeleteDispatchUser />)).toThrow(
1092+
'useMorphemeDeleteDispatch must be used inside an AnalysisStoreProvider',
1093+
);
1094+
});
1095+
});
1096+
10211097
describe('useMorphemeGlossDispatch', () => {
10221098
it('writes a morpheme gloss and calls onSave', async () => {
10231099
const onSave = jest.fn();

0 commit comments

Comments
 (0)