Skip to content
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
25 changes: 17 additions & 8 deletions packages/ui/client/components/explorer/Explorer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const {
currentProject,
currentProjectName,
clearProject,
disableProjectSort,
clearProjectSort,
disableClearProjectSort,
searchMatcher,
Expand Down Expand Up @@ -88,7 +87,6 @@ useResizeObserver(() => testExplorerRef.value, ([{ contentRect }]) => {
grid="~ cols-[auto_auto_minmax(0,1fr)_auto] gap-x-2 gap-y-1"
items-center
>
<!-- Row 1 -->
<div class="i-carbon:workspace" flex-shrink-0 />
<label for="project-select" text-sm>
Projects
Expand Down Expand Up @@ -133,13 +131,19 @@ useResizeObserver(() => testExplorerRef.value, ([{ contentRect }]) => {
icon="i-carbon:filter-remove"
@click.passive="clearProject(true)"
/>

<!-- Row 2 -->
</div>
<div
p="l3 y2 r2"
bg-header
border="b-2 base"
grid="~ cols-[auto_auto_minmax(0,1fr)_auto] gap-x-2"
items-center
>
<div class="i-carbon:arrows-vertical" flex-shrink-0 />
<label for="project-sort" text-sm>
Sort by
</label>
<div class="relative flex-1" :class="{ 'op-50 cursor-not-allowed': disableProjectSort }">
<div class="relative flex-1">
<select
id="project-sort"
ref="sortProjectRef"
Expand All @@ -156,15 +160,20 @@ useResizeObserver(() => testExplorerRef.value, ([{ contentRect }]) => {
cursor-pointer
hover:bg-active
class="outline-none"
:disabled="disableProjectSort"
>
<option value="default" class="text-base bg-base">
Default
</option>
<option value="asc" class="text-base bg-base">
<option value="duration-desc" class="text-base bg-base">
Slowest first
</option>
<option value="duration-asc" class="text-base bg-base">
Fastest first
</option>
<option v-if="enableProjects" value="asc" class="text-base bg-base">
Project A-Z
</option>
<option value="desc" class="text-base bg-base">
<option v-if="enableProjects" value="desc" class="text-base bg-base">
Project Z-A
</option>
</select>
Expand Down
15 changes: 10 additions & 5 deletions packages/ui/client/composables/explorer/search.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Ref } from 'vue'
import type { ProjectSortUIType } from '~/composables/explorer/types'
import type { SortUIType } from '~/composables/explorer/types'
import { debouncedWatch } from '@vueuse/core'
import { computed, ref, watch } from 'vue'
import { explorerTree } from '~/composables/explorer'
Expand Down Expand Up @@ -38,8 +38,14 @@ export function useSearch(

const disableClearSearch = computed(() => search.value === '')
const debouncedSearch = ref(search.value)
const disableProjectSort = computed(() => currentProject.value !== ALL_PROJECTS)
const disableClearProjectSort = computed(() => disableProjectSort.value || projectSort.value === 'default')
const disableClearProjectSort = computed(() => projectSort.value === 'default')

// Reset project-specific sort when multiple projects are no longer available
watch(() => enableProjects.value, (enabled) => {
if (!enabled && (projectSort.value === 'asc' || projectSort.value === 'desc')) {
projectSort.value = 'default'
}
})

debouncedWatch(() => search.value, (value) => {
debouncedSearch.value = value?.trim() ?? ''
Expand Down Expand Up @@ -90,7 +96,7 @@ export function useSearch(
skippedValue: boolean,
onlyTestsValue: boolean,
projectValue: string,
projectSortValue: ProjectSortUIType,
projectSortValue: SortUIType,
) {
if (!initialized.value) {
return
Expand Down Expand Up @@ -157,7 +163,6 @@ export function useSearch(
currentProjectName,
clearProject,
projectSort,
disableProjectSort,
clearProjectSort,
disableClearProjectSort,
}
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/client/composables/explorer/state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { File, Task } from '@vitest/runner'
import type { FileTreeNode, Filter, FilteredTests, ProjectSortUIType, TreeFilterState, UITaskTreeNode } from './types'
import type { FileTreeNode, Filter, FilteredTests, SortUIType, TreeFilterState, UITaskTreeNode } from './types'
import { createTagsFilter } from '@vitest/runner/utils'
import { useLocalStorage } from '@vueuse/core'
import { computed, reactive, ref, shallowRef } from 'vue'
Expand Down Expand Up @@ -30,7 +30,7 @@ export const treeFilter = useLocalStorage<TreeFilterState>(
projectSort: undefined,
},
)
export const projectSort = ref<ProjectSortUIType>(treeFilter.value.projectSort || 'default')
export const projectSort = ref<SortUIType>(treeFilter.value.projectSort || 'default')
export const currentProject = shallowRef(treeFilter.value?.project || ALL_PROJECTS)
export const enableProjects = computed(() => availableProjects.value.length > 1)
export const disableClearProjects = computed(() => currentProject.value === ALL_PROJECTS)
Expand Down
11 changes: 9 additions & 2 deletions packages/ui/client/composables/explorer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,20 @@ export interface Filter {
}

export type ProjectSortType = 'asc' | 'desc'
export type ProjectSortUIType = ProjectSortType | 'default'
export type DurationSortType = 'duration-asc' | 'duration-desc'
export type SortType = ProjectSortType | DurationSortType
export type SortUIType = SortType | 'default'

/**
* @deprecated Use `SortUIType` instead
*/
export type ProjectSortUIType = SortUIType

export interface TreeFilterState extends Filter {
search: string
expandAll?: boolean
project?: string
projectSort?: ProjectSortType
projectSort?: SortType
}

export interface CollectorInfo {
Expand Down
27 changes: 16 additions & 11 deletions packages/ui/client/composables/explorer/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { File, Task } from '@vitest/runner'
import type {
FileTreeNode,
ParentTreeNode,
ProjectSortUIType,
SortUIType,
SuiteTreeNode,
TestTreeNode,
UITaskTreeNode,
Expand Down Expand Up @@ -33,24 +33,29 @@ export function isParentNode(node: UITaskTreeNode): node is FileTreeNode | Suite
return node.type === 'file' || node.type === 'suite'
}

export function getSortedRootTasks(sort: ProjectSortUIType, tasks = explorerTree.root.tasks) {
export function getSortedRootTasks(sort: SortUIType, tasks = explorerTree.root.tasks) {
const sorted = [...tasks]

sorted.sort((a, b) => {
if (sort === 'asc' || sort === 'desc') {
if (sort === 'duration-desc' || sort === 'duration-asc') {
const durationA = a.duration ?? 0
const durationB = b.duration ?? 0
if (durationA !== durationB) {
return sort === 'duration-desc'
? durationB - durationA
: durationA - durationB
}
}
else if (sort === 'asc' || sort === 'desc') {
const projectA = a.projectName || ''
const projectB = b.projectName || ''
if (projectA !== projectB) {
if (sort === 'asc') {
return projectA.localeCompare(projectB)
}
else {
return projectB.localeCompare(projectA)
}
return sort === 'asc'
? projectA.localeCompare(projectB)
: projectB.localeCompare(projectA)
}
}
// Default sort (by filepath, then project) is the fallback for project sort
// and the primary for default sort
// Default sort (by filepath, then project) is the fallback
return `${a.filepath}:${a.projectName}`.localeCompare(`${b.filepath}:${b.projectName}`)
})

Expand Down
Loading