Fix loss of tree node state when switching views

This commit fixes an issue where the check state of categories was lost
when toggling between card and tree views. This is solved by immediately
emitting node state changes for all nodes. This ensures consistent view
transitions without any loss of node state information.

Furthermore, this commit includes added unit tests for the modified code
sections.
This commit is contained in:
undergroundwires
2023-09-24 20:34:47 +02:00
parent 0303ef2fd9
commit 8f188acd3c
33 changed files with 1606 additions and 175 deletions

View File

@@ -1,17 +1,15 @@
import { shallowMount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import { TreeNodeStateChangedEmittedEvent } from '@/presentation/components/Scripts/View/Tree/TreeView/Bindings/TreeNodeStateChangedEmittedEvent';
import { shallowMount } from '@vue/test-utils';
import { useCollectionSelectionStateUpdater } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/UseCollectionSelectionStateUpdater';
import { InjectionKeys } from '@/presentation/injectionSymbols';
import { HierarchyAccessStub } from '@tests/unit/shared/Stubs/HierarchyAccessStub';
import { TreeNodeStateAccessStub } from '@tests/unit/shared/Stubs/TreeNodeStateAccessStub';
import { TreeNodeStub } from '@tests/unit/shared/Stubs/TreeNodeStub';
import { UseCollectionStateStub } from '@tests/unit/shared/Stubs/UseCollectionStateStub';
import { TreeNodeStateDescriptorStub } from '@tests/unit/shared/Stubs/TreeNodeStateDescriptorStub';
import { TreeNodeCheckState } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/State/CheckState';
import { NodeStateChangedEventStub } from '@tests/unit/shared/Stubs/NodeStateChangedEventStub';
import { CategoryCollectionStateStub } from '@tests/unit/shared/Stubs/CategoryCollectionStateStub';
import { UserSelectionStub } from '@tests/unit/shared/Stubs/UserSelectionStub';
import { TreeNodeStateChangedEmittedEventStub } from '@tests/unit/shared/Stubs/TreeNodeStateChangedEmittedEventStub';
describe('useCollectionSelectionStateUpdater', () => {
describe('updateNodeSelection', () => {
@@ -19,16 +17,17 @@ describe('useCollectionSelectionStateUpdater', () => {
it('does nothing', () => {
// arrange
const { returnObject, useStateStub } = mountWrapperComponent();
const mockEvent: TreeNodeStateChangedEmittedEvent = {
node: createTreeNodeStub({
isBranch: true,
currentState: TreeNodeCheckState.Checked,
}),
change: new NodeStateChangedEventStub().withCheckStateChange({
const mockEvent = new TreeNodeStateChangedEmittedEventStub()
.withNode(
createTreeNodeStub({
isBranch: true,
currentState: TreeNodeCheckState.Checked,
}),
)
.withCheckStateChange({
oldState: TreeNodeCheckState.Checked,
newState: TreeNodeCheckState.Unchecked,
}),
};
});
// act
returnObject.updateNodeSelection(mockEvent);
// assert
@@ -40,16 +39,17 @@ describe('useCollectionSelectionStateUpdater', () => {
it('does nothing', () => {
// arrange
const { returnObject, useStateStub } = mountWrapperComponent();
const mockEvent: TreeNodeStateChangedEmittedEvent = {
node: createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Checked,
}),
change: new NodeStateChangedEventStub().withCheckStateChange({
const mockEvent = new TreeNodeStateChangedEmittedEventStub()
.withNode(
createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Checked,
}),
)
.withCheckStateChange({
oldState: TreeNodeCheckState.Checked,
newState: TreeNodeCheckState.Checked,
}),
};
});
// act
returnObject.updateNodeSelection(mockEvent);
// assert
@@ -64,19 +64,17 @@ describe('useCollectionSelectionStateUpdater', () => {
const selectionStub = new UserSelectionStub([]);
selectionStub.isSelected = () => false;
useStateStub.withState(new CategoryCollectionStateStub().withSelection(selectionStub));
const mockEvent: TreeNodeStateChangedEmittedEvent = {
node: new TreeNodeStub()
.withHierarchy(new HierarchyAccessStub().withIsBranchNode(false))
.withState(new TreeNodeStateAccessStub().withCurrent(
new TreeNodeStateDescriptorStub().withCheckState(
TreeNodeCheckState.Checked,
),
)),
change: new NodeStateChangedEventStub().withCheckStateChange({
const mockEvent = new TreeNodeStateChangedEmittedEventStub()
.withNode(
createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Checked,
}),
)
.withCheckStateChange({
oldState: TreeNodeCheckState.Unchecked,
newState: TreeNodeCheckState.Checked,
}),
};
});
// act
returnObject.updateNodeSelection(mockEvent);
// assert
@@ -91,16 +89,17 @@ describe('useCollectionSelectionStateUpdater', () => {
const selectionStub = new UserSelectionStub([]);
selectionStub.isSelected = () => true;
useStateStub.withState(new CategoryCollectionStateStub().withSelection(selectionStub));
const mockEvent: TreeNodeStateChangedEmittedEvent = {
node: createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Checked,
}),
change: new NodeStateChangedEventStub().withCheckStateChange({
const mockEvent = new TreeNodeStateChangedEmittedEventStub()
.withNode(
createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Checked,
}),
)
.withCheckStateChange({
oldState: TreeNodeCheckState.Unchecked,
newState: TreeNodeCheckState.Checked,
}),
};
});
// act
returnObject.updateNodeSelection(mockEvent);
// assert
@@ -115,16 +114,17 @@ describe('useCollectionSelectionStateUpdater', () => {
const selectionStub = new UserSelectionStub([]);
selectionStub.isSelected = () => true;
useStateStub.withState(new CategoryCollectionStateStub().withSelection(selectionStub));
const mockEvent: TreeNodeStateChangedEmittedEvent = {
node: createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Unchecked,
}),
change: new NodeStateChangedEventStub().withCheckStateChange({
const mockEvent = new TreeNodeStateChangedEmittedEventStub()
.withNode(
createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Unchecked,
}),
)
.withCheckStateChange({
oldState: TreeNodeCheckState.Checked,
newState: TreeNodeCheckState.Unchecked,
}),
};
});
// act
returnObject.updateNodeSelection(mockEvent);
// assert
@@ -139,16 +139,17 @@ describe('useCollectionSelectionStateUpdater', () => {
const selectionStub = new UserSelectionStub([]);
selectionStub.isSelected = () => false;
useStateStub.withState(new CategoryCollectionStateStub().withSelection(selectionStub));
const mockEvent: TreeNodeStateChangedEmittedEvent = {
node: createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Unchecked,
}),
change: new NodeStateChangedEventStub().withCheckStateChange({
const mockEvent = new TreeNodeStateChangedEmittedEventStub()
.withNode(
createTreeNodeStub({
isBranch: false,
currentState: TreeNodeCheckState.Unchecked,
}),
)
.withCheckStateChange({
oldState: TreeNodeCheckState.Checked,
newState: TreeNodeCheckState.Unchecked,
}),
};
});
// act
returnObject.updateNodeSelection(mockEvent);
// assert
@@ -186,9 +187,5 @@ function createTreeNodeStub(scenario: {
}) {
return new TreeNodeStub()
.withHierarchy(new HierarchyAccessStub().withIsBranchNode(scenario.isBranch))
.withState(new TreeNodeStateAccessStub().withCurrent(
new TreeNodeStateDescriptorStub().withCheckState(
scenario.currentState,
),
));
.withState(new TreeNodeStateAccessStub().withCurrentCheckState(scenario.currentState));
}