Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(desktop): support expand and collapse all tree nodes #1775

Merged
merged 4 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions src/components/widgets/TreeView.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
<template>
<div class="tree-view">
<div class="tree-view-header">
<el-input :placeholder="$t('viewer.filterDesc')" size="small" v-model="filterText" class="mb-3" clearable />
<div class="tree-view-header mb-3">
<el-input :placeholder="$t('viewer.filterDesc')" size="small" v-model="filterText" clearable class="mr-3" />
<div class="tree-view-header-actions">
<el-tooltip
:effect="currentTheme !== 'light' ? 'light' : 'dark'"
placement="top"
:content="$t('viewer.expendAll')"
:open-delay="500"
>
<el-button size="small" @click="expandAll" icon="el-icon-arrow-down"> </el-button>
</el-tooltip>
<el-tooltip
:effect="currentTheme !== 'light' ? 'light' : 'dark'"
placement="top"
:content="$t('viewer.collapseAll')"
:open-delay="500"
>
<el-button size="small" @click="collapseAll" icon="el-icon-arrow-up"> </el-button>
</el-tooltip>
</div>
</div>
<el-tree
ref="tree"
Expand Down Expand Up @@ -38,12 +56,16 @@

<script lang="ts">
import { Component, Vue, Watch, Prop } from 'vue-property-decorator'
import { Getter } from 'vuex-class'
import { Tree } from 'element-ui'
import { getAllLabels } from '@/utils/topicTree'

@Component
export default class TreeView extends Vue {
@Prop({ default: () => [] }) public data!: TopicTreeData[]

@Getter('currentTheme') private currentTheme!: Theme

private expandedKeys: string[] = []
private filterText = ''

Expand Down Expand Up @@ -102,6 +124,13 @@ export default class TreeView extends Vue {
}
}

private expandAll() {
this.expandedKeys = getAllLabels(this.data)
}

private collapseAll() {
this.expandedKeys = []
}
mounted() {
// Keep the filter text when data changes
this.$nextTick(() => {
Expand All @@ -115,6 +144,25 @@ export default class TreeView extends Vue {

<style lang="scss">
.tree-view {
.tree-view-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
.tree-view-header-actions {
display: flex;
align-items: center;
justify-content: space-between;
.el-button {
border: 1px solid var(--color-border-default);
background: transparent;
&:hover {
border: 1px solid var(--color-main-green);
}
}
}
}

.el-tree-node__content {
border-radius: 8px;
height: 32px;
Expand Down
17 changes: 17 additions & 0 deletions src/utils/topicTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,20 @@ export function findFullTopicPath(treeData: TopicTreeData[], targetLabel: string
}
return null
}

/**
* Retrieves all labels from the given topic tree nodes and their children.
*
* @param nodes - An array of TopicTreeData representing the topic tree nodes.
* @returns An array of strings containing all labels from the nodes and their children.
*/
export function getAllLabels(nodes: TopicTreeData[]): string[] {
let labels: string[] = []
for (const node of nodes) {
labels.push(node.label)
if (node.children && node.children.length > 0) {
labels = labels.concat(getAllLabels(node.children))
}
}
return labels
}
112 changes: 112 additions & 0 deletions tests/unit/utils/globalEventBus.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { expect } from 'chai'
import { globalEventBus } from '@/utils/globalEventBus'

describe('globalEventBus', () => {
it('should emit and listen to events', () => {
const testData = { message: 'Hello, World!' }
let receivedData: any = null

globalEventBus.on('testEvent', (data) => {
receivedData = data
})

globalEventBus.emit('testEvent', testData)

expect(receivedData).to.deep.equal(testData)
})

it('should remove event listener', () => {
let counter = 0
const incrementCounter = () => {
counter++
}

globalEventBus.on('incrementEvent', incrementCounter)

globalEventBus.emit('incrementEvent')
expect(counter).to.equal(1)

globalEventBus.removeListener('incrementEvent', incrementCounter)

globalEventBus.emit('incrementEvent')
expect(counter).to.equal(1)
})

it('should handle multiple listeners for the same event', () => {
let count1 = 0
let count2 = 0
const listener1 = () => {
count1++
}
const listener2 = () => {
count2++
}

globalEventBus.on('multipleListeners', listener1)
globalEventBus.on('multipleListeners', listener2)

globalEventBus.emit('multipleListeners')

expect(count1).to.equal(1)
expect(count2).to.equal(1)
})

it('should not throw error when emitting event with no listeners', () => {
expect(() => {
globalEventBus.emit('nonExistentEvent')
}).to.not.throw()
})

it('should handle removal of non-existent listener', () => {
const listener = () => {}
expect(() => {
globalEventBus.removeListener('someEvent', listener)
}).to.not.throw()
})

it('should handle multiple removals of the same listener', () => {
const listener = () => {}
globalEventBus.on('multiRemoval', listener)

globalEventBus.removeListener('multiRemoval', listener)
globalEventBus.removeListener('multiRemoval', listener) // Second removal

// This should not throw an error
expect(() => {
globalEventBus.emit('multiRemoval')
}).to.not.throw()
})

it('should correctly handle listeners that throw errors', () => {
const errorListener = () => {
throw new Error('Test error')
}
globalEventBus.on('errorEvent', errorListener)

expect(() => {
globalEventBus.emit('errorEvent')
}).to.throw('Test error')

// Clean up
globalEventBus.removeListener('errorEvent', errorListener)
})

it('should maintain separate listener lists for different events', () => {
let count1 = 0
let count2 = 0
globalEventBus.on('event1', () => {
count1++
})
globalEventBus.on('event2', () => {
count2++
})

globalEventBus.emit('event1')
expect(count1).to.equal(1)
expect(count2).to.equal(0)

globalEventBus.emit('event2')
expect(count1).to.equal(1)
expect(count2).to.equal(1)
})
})
Loading
Loading