feat: always show song list header action menu (#1869)

This commit is contained in:
Phan An 2024-10-31 16:50:35 +07:00 committed by GitHub
parent 3848e8b52d
commit 5d9d75111a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 30 additions and 10 deletions

View file

@ -70,7 +70,7 @@ new class extends UnitTestCase {
}, },
global: { global: {
stubs: { stubs: {
SongListSorter: this.stub('song-list-sorter'), ActionMenu: this.stub('song-list-header-action-menu'),
}, },
provide: { provide: {
[<symbol>SelectedPlayablesKey]: [ref(selectedPlayables), (value: Playable[]) => (selectedPlayables = value)], [<symbol>SelectedPlayablesKey]: [ref(selectedPlayables), (value: Playable[]) => (selectedPlayables = value)],

View file

@ -66,8 +66,8 @@
</template> </template>
</span> </span>
<span class="extra"> <span class="extra">
<SongListSorter <ActionMenu
v-if="config.sortable" :sortable="config.sortable"
:field="sortField" :field="sortField"
:has-custom-order-sort="config.hasCustomOrderSort" :has-custom-order-sort="config.hasCustomOrderSort"
:order="sortOrder" :order="sortOrder"
@ -87,10 +87,10 @@ import { PlayableListConfigKey, PlayableListSortFieldKey, SongListSortOrderKey }
import type { getPlayableCollectionContentType } from '@/utils/typeGuards' import type { getPlayableCollectionContentType } from '@/utils/typeGuards'
import { usePlayableListColumnVisibility } from '@/composables/usePlayableListColumnVisibility' import { usePlayableListColumnVisibility } from '@/composables/usePlayableListColumnVisibility'
import SongListSorter from '@/components/song/song-list/SongListSorter.vue' import ActionMenu from '@/components/song/song-list/SongListHeaderActionMenu.vue'
withDefaults(defineProps<{ withDefaults(defineProps<{
contentType?: ReturnType<getPlayableCollectionContentType> contentType?: ReturnType<typeof getPlayableCollectionContentType>
}>(), { }>(), {
contentType: 'songs', contentType: 'songs',
}) })

View file

@ -2,7 +2,7 @@ import { screen } from '@testing-library/vue'
import { expect, it } from 'vitest' import { expect, it } from 'vitest'
import UnitTestCase from '@/__tests__/UnitTestCase' import UnitTestCase from '@/__tests__/UnitTestCase'
import { useLocalStorage } from '@/composables/useLocalStorage' import { useLocalStorage } from '@/composables/useLocalStorage'
import Component from './SongListSorter.vue' import Component from './SongListHeaderActionMenu.vue'
new class extends UnitTestCase { new class extends UnitTestCase {
protected test () { protected test () {
@ -15,6 +15,12 @@ new class extends UnitTestCase {
) )
}) })
it ('emits the sort event when an item is clicked', async () => {
const { emitted } = this.render(Component)
await this.user.click(screen.getByText('Title'))
expect(emitted().sort[0]).toEqual(['title'])
})
it('contains proper items for episode-only lists', () => { it('contains proper items for episode-only lists', () => {
this.render(Component, { this.render(Component, {
props: { props: {
@ -49,6 +55,17 @@ new class extends UnitTestCase {
screen.getByText('Custom Order') screen.getByText('Custom Order')
}) })
it('does not sort if the list is not sortable', async () => {
const { emitted } = this.render(Component, {
props: {
sortable: false,
},
})
await this.user.click(screen.getByText('Title'))
expect(emitted().sort).toBeUndefined()
})
it('has a checkbox to toggle the column visibility', async () => { it('has a checkbox to toggle the column visibility', async () => {
this.be().render(Component) this.be().render(Component)

View file

@ -1,7 +1,8 @@
<template> <template>
<article> <article>
<button ref="button" class="w-full focus:text-k-highlight" title="Sort" @click.stop="trigger"> <button ref="button" class="w-full focus:text-k-highlight" title="Sort" @click.stop="trigger">
<Icon :icon="faSort" /> <Icon v-if="sortable" :icon="faSort" />
<Icon v-else :icon="faEllipsis" />
</button> </button>
<OnClickOutside @trigger="hide"> <OnClickOutside @trigger="hide">
<menu ref="menu" class="context-menu normal-case tracking-normal"> <menu ref="menu" class="context-menu normal-case tracking-normal">
@ -10,7 +11,7 @@
:key="item.label" :key="item.label"
:class="currentlySortedBy(item.field) && 'active'" :class="currentlySortedBy(item.field) && 'active'"
class="cursor-pointer flex justify-between !pl-3 hover:!bg-white/10" class="cursor-pointer flex justify-between !pl-3 hover:!bg-white/10"
@click="sort(item.field)" @click="sortable && sort(item.field)"
> >
<label <label
v-if="shouldShowColumnVisibilityCheckboxes()" v-if="shouldShowColumnVisibilityCheckboxes()"
@ -39,7 +40,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { isEqual } from 'lodash' import { isEqual } from 'lodash'
import { faArrowDown, faArrowUp, faCheck, faSort } from '@fortawesome/free-solid-svg-icons' import { faArrowDown, faArrowUp, faCheck, faEllipsis, faSort } from '@fortawesome/free-solid-svg-icons'
import { OnClickOutside } from '@vueuse/components' import { OnClickOutside } from '@vueuse/components'
import { computed, onBeforeUnmount, onMounted, ref, toRefs } from 'vue' import { computed, onBeforeUnmount, onMounted, ref, toRefs } from 'vue'
import { useFloatingUi } from '@/composables/useFloatingUi' import { useFloatingUi } from '@/composables/useFloatingUi'
@ -48,11 +49,13 @@ import type { getPlayableCollectionContentType } from '@/utils/typeGuards'
import { usePlayableListColumnVisibility } from '@/composables/usePlayableListColumnVisibility' import { usePlayableListColumnVisibility } from '@/composables/usePlayableListColumnVisibility'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
sortable?: boolean
field?: MaybeArray<PlayableListSortField> // the current field(s) being sorted by field?: MaybeArray<PlayableListSortField> // the current field(s) being sorted by
order?: SortOrder order?: SortOrder
hasCustomOrderSort?: boolean // whether to provide "custom order" sort (like for playlists) hasCustomOrderSort?: boolean // whether to provide "custom order" sort (like for playlists)
contentType?: ReturnType<typeof getPlayableCollectionContentType> contentType?: ReturnType<typeof getPlayableCollectionContentType>
}>(), { }>(), {
sortable: true,
field: 'title', field: 'title',
order: 'asc', order: 'asc',
hasCustomOrderSort: false, hasCustomOrderSort: false,

View file

@ -2,6 +2,6 @@
exports[`renders 1`] = ` exports[`renders 1`] = `
<div class="sortable song-list-header flex z-[2] bg-k-bg-secondary pl-5"><span class="track-number" data-testid="header-track-number" role="button" title="Sort by track number"> # <!--v-if--><!--v-if--></span><span class="title-artist" data-testid="header-title" role="button" title="Sort by title"> Title <br data-testid="Icon" icon="[object Object]" class="text-k-highlight"><!--v-if--></span><span title="Sort by album" class="album" data-testid="header-album" role="button">Album<span class="ml-2"><!--v-if--><!--v-if--></span></span> <div class="sortable song-list-header flex z-[2] bg-k-bg-secondary pl-5"><span class="track-number" data-testid="header-track-number" role="button" title="Sort by track number"> # <!--v-if--><!--v-if--></span><span class="title-artist" data-testid="header-title" role="button" title="Sort by title"> Title <br data-testid="Icon" icon="[object Object]" class="text-k-highlight"><!--v-if--></span><span title="Sort by album" class="album" data-testid="header-album" role="button">Album<span class="ml-2"><!--v-if--><!--v-if--></span></span>
<!--v-if--><span class="time" data-testid="header-length" role="button" title="Sort by duration"> Time <!--v-if--><!--v-if--></span><span class="extra"><br data-testid="song-list-sorter" field="title" order="asc" content-type="songs"></span> <!--v-if--><span class="time" data-testid="header-length" role="button" title="Sort by duration"> Time <!--v-if--><!--v-if--></span><span class="extra"><br data-testid="song-list-header-action-menu" sortable="true" field="title" order="asc" content-type="songs"></span>
</div> </div>
`; `;