11<script setup lang="ts">
22import type { File , Task } from ' @vitest/runner'
33import { useResizeObserver } from ' @vueuse/core'
4-
54import { hideAllPoppers } from ' floating-vue'
6-
75import { computed , ref } from ' vue'
6+
87// @ts-expect-error missing types
98import { RecycleScroller } from ' vue-virtual-scroller'
10-
11- import { config } from ' ~/composables/client'
12-
9+ import { availableProjects , config } from ' ~/composables/client'
1310import { useSearch } from ' ~/composables/explorer/search'
11+ import { ALL_PROJECTS , projectSort } from ' ~/composables/explorer/state'
1412import { activeFileId } from ' ~/composables/params'
1513import DetailsPanel from ' ../DetailsPanel.vue'
1614import FilterStatus from ' ../FilterStatus.vue'
@@ -32,6 +30,8 @@ const emit = defineEmits<{
3230const includeTaskLocation = computed (() => config .value .includeTaskLocation )
3331
3432const searchBox = ref <HTMLInputElement | undefined >()
33+ const selectProjectRef = ref <HTMLSelectElement | undefined >()
34+ const sortProjectRef = ref <HTMLSelectElement | undefined >()
3535
3636const {
3737 initialized,
@@ -47,7 +47,15 @@ const {
4747 filteredFiles,
4848 testsTotal,
4949 uiEntries,
50- } = useSearch (searchBox )
50+ enableProjects,
51+ disableClearProjects,
52+ currentProject,
53+ currentProjectName,
54+ clearProject,
55+ disableProjectSort,
56+ clearProjectSort,
57+ disableClearProjectSort,
58+ } = useSearch (searchBox , selectProjectRef , sortProjectRef )
5159
5260const filterClass = ref <string >(' grid-cols-2' )
5361const filterHeaderClass = ref <string >(' grid-col-span-2' )
@@ -71,6 +79,104 @@ useResizeObserver(() => testExplorerRef.value, ([{ contentRect }]) => {
7179 <div p =" 2" h-10 flex =" ~ gap-2" items-center bg-header border =" b base" >
7280 <slot name =" header" :filtered-files =" isFiltered || isFilteredByStatus ? filteredFiles : undefined" />
7381 </div >
82+ <div
83+ v-if =" enableProjects"
84+ p =" l3 y2 r2"
85+ bg-header
86+ border =" b-2 base"
87+ grid =" ~ cols-[auto_auto_minmax(0,1fr)_auto] gap-x-2 gap-y-1"
88+ items-center
89+ >
90+ <!-- Row 1 -->
91+ <div class =" i-carbon:workspace" flex-shrink-0 />
92+ <label for =" project-select" text-sm >
93+ Projects
94+ </label >
95+ <div class =" relative flex-1" >
96+ <select
97+ id =" project-select"
98+ ref =" selectProjectRef"
99+ v-model =" currentProject"
100+ w-full
101+ appearance-none
102+ bg-base
103+ text-base
104+ border =" ~ base rounded"
105+ pl-2
106+ pr-8
107+ py-1
108+ text-sm
109+ cursor-pointer
110+ hover:bg-active
111+ class =" outline-none"
112+ >
113+ <option :value =" ALL_PROJECTS" class =" text-base bg-base" >
114+ All Projects
115+ </option >
116+ <option
117+ v-for =" project in availableProjects"
118+ :key =" project"
119+ :value =" project"
120+ class =" text-base bg-base"
121+ >
122+ {{ project }}
123+ </option >
124+ </select >
125+ <div class =" i-carbon:chevron-down absolute right-2 top-1/2 op50 -translate-y-1/2 pointer-events-none" />
126+ </div >
127+
128+ <IconButton
129+ v-tooltip.bottom =" 'Clear project filter'"
130+ :disabled =" disableClearProjects"
131+ title =" Clear project filter"
132+ icon =" i-carbon:filter-remove"
133+ @click.passive =" clearProject(true)"
134+ />
135+
136+ <!-- Row 2 -->
137+ <div class =" i-carbon:arrows-vertical" flex-shrink-0 />
138+ <label for =" project-sort" text-sm >
139+ Sort by
140+ </label >
141+ <div class =" relative flex-1" :class =" { 'op-50 cursor-not-allowed': disableProjectSort }" >
142+ <select
143+ id =" project-sort"
144+ ref =" sortProjectRef"
145+ v-model =" projectSort"
146+ w-full
147+ appearance-none
148+ bg-base
149+ text-base
150+ border =" ~ base rounded"
151+ pl-2
152+ pr-8
153+ py-1
154+ text-sm
155+ cursor-pointer
156+ hover:bg-active
157+ class =" outline-none"
158+ :disabled =" disableProjectSort"
159+ >
160+ <option value =" default" class =" text-base bg-base" >
161+ Default
162+ </option >
163+ <option value =" asc" class =" text-base bg-base" >
164+ Project A-Z
165+ </option >
166+ <option value =" desc" class =" text-base bg-base" >
167+ Project Z-A
168+ </option >
169+ </select >
170+ <div class =" i-carbon:chevron-down absolute right-2 top-1/2 op50 -translate-y-1/2 pointer-events-none" />
171+ </div >
172+ <IconButton
173+ v-tooltip.bottom =" 'Reset sort'"
174+ :disabled =" disableClearProjectSort"
175+ title =" Reset sort"
176+ icon =" i-carbon:filter-reset"
177+ @click.passive =" clearProjectSort(true)"
178+ />
179+ </div >
74180 <div
75181 p =" l3 y2 r2"
76182 flex =" ~ gap-2"
@@ -149,7 +255,7 @@ useResizeObserver(() => testExplorerRef.value, ([{ contentRect }]) => {
149255 </div >
150256 </template >
151257 <!-- empty-state -->
152- <template v-if =" (isFiltered || isFilteredByStatus ) && uiEntries .length === 0 " >
258+ <template v-if =" (isFiltered || isFilteredByStatus || !! currentProjectName ) && uiEntries .length === 0 " >
153259 <div v-if =" initialized" flex =" ~ col" items-center p =" x4 y4" font-light >
154260 <div op30 >
155261 No matched test
0 commit comments