diff --git a/src/components/TreeView.vue b/src/components/TreeView.vue index 36e3baa0..c863cbff 100644 --- a/src/components/TreeView.vue +++ b/src/components/TreeView.vue @@ -20,6 +20,7 @@ @treeNodeClick="(t, e)=>$emit(TreeEvent.Click, t, e)" @treeNodeDblclick="(t, e)=>$emit(TreeEvent.DoubleClick, t, e)" @treeNodeCheckboxChange="(t, e)=>$emit(TreeEvent.CheckboxChange, t, e)" + @treeNodeChildCheckboxChange="(t, c, e)=>$emit(TreeEvent.ChildCheckboxChange, t, c, e)" @treeNodeRadioChange="(t, e)=>$emit(TreeEvent.RadioChange, t, e)" @treeNodeExpandedChange="(t, e)=>$emit(TreeEvent.ExpandedChange, t, e)" @treeNodeChildrenLoad="(t, e)=>$emit(TreeEvent.ChildrenLoad, t, e)" @@ -121,6 +122,7 @@ const emit = defineEmits([ TreeEvent.Add, TreeEvent.CheckboxChange, TreeEvent.ChildrenLoad, + TreeEvent.ChildCheckboxChange, TreeEvent.Click, TreeEvent.Delete, TreeEvent.DoubleClick, @@ -220,7 +222,7 @@ function getCheckedRadioButtons() { * @param matcherFunction {function} A function which takes a node as an argument * and returns a boolean indicating a match for some condition * @param maxMatches {integer} The maximum number of matches to return - * @returns {Array} An array of any nodes matched by the given function + * @returns {Array} An array of any nodes matched by the given function */ function getMatching(matcherFunction, maxMatches = 0) { let matches = []; diff --git a/src/components/TreeViewNode.vue b/src/components/TreeViewNode.vue index 864eb41c..217a0cfc 100644 --- a/src/components/TreeViewNode.vue +++ b/src/components/TreeViewNode.vue @@ -187,7 +187,8 @@ :is-mounted="isMounted" @treeNodeClick="(t, e)=>$emit(TreeEvent.Click, t, e)" @treeNodeDblclick="(t, e)=>$emit(TreeEvent.DoubleClick, t, e)" - @treeNodeCheckboxChange="(t, e)=>$emit(TreeEvent.CheckboxChange, t, e)" + @treeNodeCheckboxChange="handleCheckboxChange" + @treeNodeChildCheckboxChange="(t, c, e)=>$emit(TreeEvent.ChildCheckboxChange, t, c, e)" @treeNodeRadioChange="(t, e)=>$emit(TreeEvent.RadioChange, t, e)" @treeNodeExpandedChange="(t, e)=>$emit(TreeEvent.ExpandedChange, t, e)" @treeNodeChildrenLoad="(t, e)=>$emit(TreeEvent.ChildrenLoad, t, e)" @@ -277,6 +278,7 @@ const emit = defineEmits([ TreeEvent.Add, TreeEvent.Click, TreeEvent.CheckboxChange, + TreeEvent.ChildCheckboxChange, TreeEvent.ChildrenLoad, TreeEvent.Delete, TreeEvent.DoubleClick, @@ -530,6 +532,20 @@ function handleChildDeletion(node, event) { emit(TreeEvent.Delete, node, event); } +/** + * Emits the treeNodeCheckboxChange event, and if the event is for + * a direct child then it also emits the treeNodeChildCheckboxChange event. + * @param {TreeViewNode} node The node on which the checkbox changed + * @param {Event} event The event that triggered the change + */ +function handleCheckboxChange(node, event) { + emit(TreeEvent.CheckboxChange, node, event); + + if (children.value.includes(node)) { + emit(TreeEvent.ChildCheckboxChange, model.value, node, event); + } +} + // CREATION LOGIC // id and label are required; notify the user. Validation is done here instead diff --git a/src/enums/event.js b/src/enums/event.js index e780d9f6..2367d9b4 100644 --- a/src/enums/event.js +++ b/src/enums/event.js @@ -6,6 +6,7 @@ const events = Object.freeze({ Click: 'treeNodeClick', DoubleClick: 'treeNodeDblclick', CheckboxChange: 'treeNodeCheckboxChange', + ChildCheckboxChange: 'treeNodeChildCheckboxChange', RadioChange: 'treeNodeRadioChange', ExpandedChange: 'treeNodeExpandedChange', ChildrenLoad: 'treeNodeChildrenLoad', diff --git a/src/stories/Home.stories.mdx b/src/stories/Home.stories.mdx index cd7a6023..b2070378 100644 --- a/src/stories/Home.stories.mdx +++ b/src/stories/Home.stories.mdx @@ -273,18 +273,19 @@ If specified, the `modelDefaults` property of the TreeView will be merged with n ## Events -| Event | Description | Handler Parameters | -|:---------------------------|:---------------------------------------------------------------|:-----------------------------------------------------------------------| -| treeNodeAdd | Emitted when a node is added | `target` The model of the target (child) node
`parent` The model of the parent node
`event` The original event | -| treeNodeClick | Emitted when a node is clicked | `target` The model of the target node
`event` The original event | -| treeNodeDblclick | Emitted when a node is double clicked | `target` The model of the target node
`event` The original event | -| treeNodeDelete | Emitted when a node is deleted | `target` The model of the target node
`event` The original event | -| treeNodeCheckboxChange | Emitted when a node's checkbox emits a change event | `target` The model of the target node
`event` The original event | -| treeNodeRadioChange | Emitted when a node's radio button emits a change event | `target` The model of the target node
`event` The original event | -| treeNodeExpandedChange | Emitted when a node is expanded or collapsed | `target` The model of the target node
`event` The original event | -| treeNodeSelectedChange | Emitted when a node is selected or deselected | `target` The model of the target node
`event` The original event | -| treeNodeChildrenLoad | Emitted when a node's children are done loading asynchronously | `target` The model of the target node
`event` The original event | -| treeRootNodesLoad | Emitted when the root nodes are done loading asynchronously | | +| Event | Description | Handler Parameters | +|:----------------------------|:---------------------------------------------------------------|:-----------------------------------------------------------------------| +| treeNodeAdd | Emitted when a node is added | `target` The model of the target (child) node
`parent` The model of the parent node
`event` The original event | +| treeNodeClick | Emitted when a node is clicked | `target` The model of the target node
`event` The original event | +| treeNodeDblclick | Emitted when a node is double clicked | `target` The model of the target node
`event` The original event | +| treeNodeDelete | Emitted when a node is deleted | `target` The model of the target node
`event` The original event | +| treeNodeCheckboxChange | Emitted when a node's checkbox emits a change event | `target` The model of the target node
`event` The original event | +| treeNodeChildCheckboxChange | Emitted when a child node's checkbox emits a change event | `target` The model of the target node (the parent of the changed node)
`child` The model of changed node
`event` The original event | +| treeNodeRadioChange | Emitted when a node's radio button emits a change event | `target` The model of the target node
`event` The original event | +| treeNodeExpandedChange | Emitted when a node is expanded or collapsed | `target` The model of the target node
`event` The original event | +| treeNodeSelectedChange | Emitted when a node is selected or deselected | `target` The model of the target node
`event` The original event | +| treeNodeChildrenLoad | Emitted when a node's children are done loading asynchronously | `target` The model of the target node
`event` The original event | +| treeRootNodesLoad | Emitted when the root nodes are done loading asynchronously | | ## CSS Classes diff --git a/tests/unit/TreeView.eventHandling.spec.js b/tests/unit/TreeView.eventHandling.spec.js index a1e81b55..90298a38 100644 --- a/tests/unit/TreeView.eventHandling.spec.js +++ b/tests/unit/TreeView.eventHandling.spec.js @@ -61,6 +61,18 @@ describe('TreeView.vue (event handling)', () => { }); }); + describe('when a node fires a treeNodeChildCheckboxChange event', () => { + + beforeEach(() => { + wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: SelectionMode.Multiple }); + wrapper.findComponent(TreeViewNode).vm.$emit('treeNodeChildCheckboxChange'); + }); + + it('should emit a treeNodeChildCheckboxChange event', () => { + expect(wrapper.emitted('treeNodeChildCheckboxChange').length).to.equal(1); + }); + }); + describe('when a node fires a treeNodeRadioChange event', () => { beforeEach(() => { diff --git a/tests/unit/TreeViewNode.interactions.spec.js b/tests/unit/TreeViewNode.interactions.spec.js index c923f300..18549e19 100644 --- a/tests/unit/TreeViewNode.interactions.spec.js +++ b/tests/unit/TreeViewNode.interactions.spec.js @@ -226,6 +226,31 @@ describe('TreeViewNode.vue (interactions)', () => { }); }); + describe('when a child node\'s checkbox is toggled', () => { + + let checkbox = null; + + beforeEach(() => { + wrapper = createWrapper({ + ariaKeyMap: {}, + initialModel: generateNodes(['es', ['ces']])[0], + modelDefaults: {}, + depth: 0, + treeId: 'tree', + initialRadioGroupValues: {}, + isMounted: false + }); + + const childWrapper = wrapper.find('.grtvn-children').findComponent(TreeViewNode); + checkbox = childWrapper.find('#' + childWrapper.vm.inputId); + }); + + it('should emit the treeNodeChildCheckboxChange event', () => { + checkbox.setChecked(); + expect(wrapper.emitted().treeNodeCheckboxChange.length).to.equal(1); + }); + }); + describe('when the node\'s radiobutton is toggled', () => { let radioButton = null;