From 0575e7927d7032b98fa07a758111a49e8a5cc3e8 Mon Sep 17 00:00:00 2001 From: Gregg Rapoza Date: Fri, 6 Sep 2024 11:16:46 -0400 Subject: [PATCH] Add treeNodeActivate event --- package.json | 2 +- src/components/TreeView.spec.js | 13 +++++++++++++ src/components/TreeView.vue | 2 ++ src/components/TreeViewNode.spec.js | 8 ++++---- src/components/TreeViewNode.vue | 5 +++++ src/enums/event.js | 1 + src/stories/Home.stories.mdx | 27 ++++++++++++++------------- 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 4ca9250b..cb480756 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@grapoza/vue-tree", - "version": "6.0.0", + "version": "6.1.0", "description": "Tree components for Vue 3", "author": "Gregg Rapoza ", "license": "MIT", diff --git a/src/components/TreeView.spec.js b/src/components/TreeView.spec.js index f7f4cfa6..b2273ac3 100644 --- a/src/components/TreeView.spec.js +++ b/src/components/TreeView.spec.js @@ -648,6 +648,19 @@ describe('TreeView.vue', () => { }); }); + describe('when a node fires a treeNodeActivate event', () => { + + beforeEach(async () => { + const { nodes, modelDefaults } = generateNodes(['es']); + wrapper = await createWrapper({ modelValue: nodes, modelDefaults, selectionMode: SelectionMode.Multiple }); + wrapper.findComponent(TreeViewNode).vm.$emit("treeNodeActivate", wrapper.vm.metaModel[0]); + }); + + it('should emit a treeNodeActivate event', () => { + expect(wrapper.emitted('treeNodeActivate').length).to.equal(1); + }); + }); + describe('when created with nodes that do not contain an explicit id property', () => { it('should fall back to the id property (note: this test is testing implementation [v-for iteration key for nodes] and not functionality)', async () => { diff --git a/src/components/TreeView.vue b/src/components/TreeView.vue index 29fb2b5c..f829341e 100644 --- a/src/components/TreeView.vue +++ b/src/components/TreeView.vue @@ -25,6 +25,7 @@ @treeNodeExpandedChange="(t)=>$emit(TreeEvent.ExpandedChange, t)" @treeNodeChildrenLoad="(t)=>$emit(TreeEvent.ChildrenLoad, t)" @treeNodeSelectedChange="handleNodeSelectedChange" + @treeNodeActivate="(t)=>$emit(TreeEvent.Activate, t)" @treeNodeAdd="(t, p)=>$emit(TreeEvent.Add, t, p)" @treeNodeDelete="handleChildDeletion" @treeNodeAriaFocusableChange="handleFocusableChange" @@ -129,6 +130,7 @@ const model = defineModel({ type: Array, required: true }); // EMITS const emit = defineEmits([ + TreeEvent.Activate, TreeEvent.Add, TreeEvent.CheckboxChange, TreeEvent.ChildrenLoad, diff --git a/src/components/TreeViewNode.spec.js b/src/components/TreeViewNode.spec.js index 861921af..6304c62a 100644 --- a/src/components/TreeViewNode.spec.js +++ b/src/components/TreeViewNode.spec.js @@ -1762,7 +1762,7 @@ describe('TreeViewNode.vue', () => { }); }); - describe('and the node does not have an input', () => { + describe('always', () => { beforeEach(async () => { let defaultProps = getDefaultPropsData(); @@ -1770,9 +1770,9 @@ describe('TreeViewNode.vue', () => { await triggerKeydown(wrapper, wrapper.vm.ariaKeyMap.activateItem[0]); }); - it('should not fire any custom events', () => { - expect(Object.getOwnPropertyNames(wrapper.emitted()).length).to.equal(1); - expect(Object.getOwnPropertyNames(wrapper.emitted())[0]).to.equal('keydown'); + it('should emit a treeNodeActivate event', () => { + expect(wrapper.emitted().treeNodeActivate).to.be.an("array").that.has.length(1); + expect(wrapper.emitted().treeNodeActivate[0][0]).to.equal(wrapper.vm.metaModel); }); }); }); diff --git a/src/components/TreeViewNode.vue b/src/components/TreeViewNode.vue index df7f81b8..4a22d685 100644 --- a/src/components/TreeViewNode.vue +++ b/src/components/TreeViewNode.vue @@ -204,6 +204,7 @@ @treeNodeExpandedChange="(t)=>$emit(TreeEvent.ExpandedChange, t)" @treeNodeChildrenLoad="(t)=>$emit(TreeEvent.ChildrenLoad, t)" @treeNodeSelectedChange="(t)=>$emit(TreeEvent.SelectedChange, t)" + @treeNodeActivate="(t)=>$emit(TreeEvent.Activate, t)" @treeNodeAdd="(t, p)=>$emit(TreeEvent.Add, t, p)" @treeNodeDelete="handleChildDeletion" @treeNodeAriaFocusableChange="(t)=>$emit(TreeEvent.FocusableChange, t)" @@ -290,6 +291,7 @@ const props = defineProps({ // EMITS const emit = defineEmits([ + TreeEvent.Activate, TreeEvent.Add, TreeEvent.Click, TreeEvent.CheckboxChange, @@ -524,6 +526,9 @@ function onKeyDown(event) { target.dispatchEvent(clickEvent); } } + + // Bubble activation out so users can apply handling for any kind of node + emit(TreeEvent.Activate, metaModel.value); } else if (props.ariaKeyMap.selectItem.includes(event.keyCode)) { // Toggles selection for the focused node. diff --git a/src/enums/event.js b/src/enums/event.js index 2367d9b4..9ed1f42c 100644 --- a/src/enums/event.js +++ b/src/enums/event.js @@ -11,6 +11,7 @@ const events = Object.freeze({ ExpandedChange: 'treeNodeExpandedChange', ChildrenLoad: 'treeNodeChildrenLoad', SelectedChange: 'treeNodeSelectedChange', + Activate: 'treeNodeActivate', // Focus FocusableChange: 'treeNodeAriaFocusableChange', diff --git a/src/stories/Home.stories.mdx b/src/stories/Home.stories.mdx index 8f3a1b88..cb825056 100644 --- a/src/stories/Home.stories.mdx +++ b/src/stories/Home.stories.mdx @@ -294,19 +294,20 @@ The `modelDefaults` property's return value contains any data about the node's c ## Events -| Event | Description | Handler Parameters | -|:----------------------------|:---------------------------------------------------------------|:----------------------------------------------------------------------------| -| treeNodeAdd | Emitted when a node is added | `target` The meta model of the target (child) node
`parent` The model of the parent node | -| treeNodeClick | Emitted when a node is clicked | `target` The meta model of the target node
`event` The original event | -| treeNodeDblclick | Emitted when a node is double clicked | `target` The meta model of the target node
`event` The original event | -| treeNodeDelete | Emitted when a node is deleted | `target` The meta model of the target node | -| treeNodeCheckboxChange | Emitted when a node's checkbox emits a change event | `target` The meta model of the target node
`event` The original event | -| treeNodeChildCheckboxChange | Emitted when a child node's checkbox emits a change event | `target` The meta 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 meta model of the target node
`event` The original event | -| treeNodeExpandedChange | Emitted when a node is expanded or collapsed | `target` The meta model of the target node | -| treeNodeSelectedChange | Emitted when a node is selected or deselected | `target` The meta model of the target node | -| treeNodeChildrenLoad | Emitted when a node's children are done loading asynchronously | `target` The meta model of the target node | -| treeRootNodesLoad | Emitted when the root nodes are done loading asynchronously | | +| Event | Description | Handler Parameters | +|:----------------------------|:-----------------------------------------------------------------|:----------------------------------------------------------------------------| +| treeNodeAdd | Emitted when a node is added | `target` The meta model of the target (child) node
`parent` The model of the parent node | +| treeNodeClick | Emitted when a node is clicked | `target` The meta model of the target node
`event` The original event | +| treeNodeDblclick | Emitted when a node is double clicked | `target` The meta model of the target node
`event` The original event | +| treeNodeDelete | Emitted when a node is deleted | `target` The meta model of the target node | +| treeNodeCheckboxChange | Emitted when a node's checkbox emits a change event | `target` The meta model of the target node
`event` The original event | +| treeNodeChildCheckboxChange | Emitted when a child node's checkbox emits a change event | `target` The meta 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 meta model of the target node
`event` The original event | +| treeNodeExpandedChange | Emitted when a node is expanded or collapsed | `target` The meta model of the target node | +| treeNodeSelectedChange | Emitted when a node is selected or deselected | `target` The meta model of the target node | +| treeNodeActivate | Emitted when a node is focused and the activation key is pressed | `target` The meta model of the target node | +| treeNodeChildrenLoad | Emitted when a node's children are done loading asynchronously | `target` The meta model of the target node | +| treeRootNodesLoad | Emitted when the root nodes are done loading asynchronously | | ## CSS Classes