From 87ce0b3bc9212895ba3161d9c776be899989e548 Mon Sep 17 00:00:00 2001 From: Olga Bulat Date: Mon, 16 Dec 2024 00:06:00 +0300 Subject: [PATCH] Standardize the image and audio result components (#5272) * Standardize the image and audio result components * "ar" locale started using Western numbers * Fix image result query missing --- frontend/shared/utils/query-utils.ts | 19 +++++- .../components/VAudioTrack/VAudioTrack.vue | 60 ++++++++---------- .../VImageResult.vue} | 5 +- .../meta/VImageResult.stories.ts} | 25 +++----- .../VSearchResultsGrid/VAllResultsGrid.vue | 4 +- .../VSearchResultsGrid/VAudioResult.vue | 11 +++- .../VSearchResultsGrid/VImageCollection.vue | 4 +- ...ge-cell.spec.ts => v-image-result.spec.ts} | 12 ++-- ...trinsic-focused-hovered-xl-dark-linux.png} | Bin ...rinsic-focused-hovered-xl-light-linux.png} | Bin ...trinsic-focused-hovered-xs-dark-linux.png} | Bin ...rinsic-focused-hovered-xs-light-linux.png} | Bin ...esult-intrinsic-focused-xl-dark-linux.png} | Bin ...sult-intrinsic-focused-xl-light-linux.png} | Bin ...esult-intrinsic-focused-xs-dark-linux.png} | Bin ...sult-intrinsic-focused-xs-light-linux.png} | Bin ...esult-intrinsic-hovered-xl-dark-linux.png} | Bin ...sult-intrinsic-hovered-xl-light-linux.png} | Bin ...esult-intrinsic-hovered-xs-dark-linux.png} | Bin ...sult-intrinsic-hovered-xs-light-linux.png} | Bin ...result-intrinsic-loaded-xl-dark-linux.png} | Bin ...esult-intrinsic-loaded-xl-light-linux.png} | Bin ...result-intrinsic-loaded-xs-dark-linux.png} | Bin ...esult-intrinsic-loaded-xs-light-linux.png} | Bin ...-square-focused-hovered-xl-dark-linux.png} | Bin ...square-focused-hovered-xl-light-linux.png} | Bin ...-square-focused-hovered-xs-dark-linux.png} | Bin ...square-focused-hovered-xs-light-linux.png} | Bin ...e-result-square-focused-xl-dark-linux.png} | Bin ...-result-square-focused-xl-light-linux.png} | Bin ...e-result-square-focused-xs-dark-linux.png} | Bin ...-result-square-focused-xs-light-linux.png} | Bin ...e-result-square-hovered-xl-dark-linux.png} | Bin ...-result-square-hovered-xl-light-linux.png} | Bin ...e-result-square-hovered-xs-dark-linux.png} | Bin ...-result-square-hovered-xs-light-linux.png} | Bin ...ge-result-square-loaded-xl-dark-linux.png} | Bin ...e-result-square-loaded-xl-light-linux.png} | Bin ...ge-result-square-loaded-xs-dark-linux.png} | Bin ...e-result-square-loaded-xs-light-linux.png} | Bin ...ge-cell.spec.js => v-image-result.spec.js} | 8 +-- .../use-get-locale-formatted-number.spec.js | 2 +- 42 files changed, 78 insertions(+), 72 deletions(-) rename frontend/src/components/{VImageCell/VImageCell.vue => VImageResult/VImageResult.vue} (98%) rename frontend/src/components/{VImageCell/meta/VImageCell.stories.ts => VImageResult/meta/VImageResult.stories.ts} (58%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts => v-image-result.spec.ts} (86%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-hovered-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-hovered-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-hovered-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-hovered-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-hovered-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-hovered-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-hovered-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-hovered-xs-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-focused-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-focused-xs-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-hovered-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-hovered-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-hovered-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-hovered-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-hovered-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-hovered-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-hovered-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-hovered-xs-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-loaded-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-loaded-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-loaded-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-loaded-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-loaded-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-loaded-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-intrinsic-loaded-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-intrinsic-loaded-xs-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-hovered-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-hovered-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-hovered-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-hovered-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-hovered-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-hovered-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-hovered-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-hovered-xs-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-focused-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-focused-xs-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-hovered-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-hovered-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-hovered-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-hovered-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-hovered-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-hovered-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-hovered-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-hovered-xs-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-loaded-xl-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-loaded-xl-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-loaded-xl-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-loaded-xl-light-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-loaded-xs-dark-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-loaded-xs-dark-linux.png} (100%) rename frontend/test/storybook/visual-regression/{v-image-cell.spec.ts-snapshots/v-image-cell-square-loaded-xs-light-linux.png => v-image-result.spec.ts-snapshots/v-image-result-square-loaded-xs-light-linux.png} (100%) rename frontend/test/unit/specs/components/{v-image-cell.spec.js => v-image-result.spec.js} (83%) diff --git a/frontend/shared/utils/query-utils.ts b/frontend/shared/utils/query-utils.ts index 5e26059d4cc..931cf0ceffa 100644 --- a/frontend/shared/utils/query-utils.ts +++ b/frontend/shared/utils/query-utils.ts @@ -1,4 +1,4 @@ -import { SupportedMediaType } from "#shared/constants/media" +import type { SupportedMediaType } from "#shared/constants/media" import type { LocationQueryValue } from "vue-router" @@ -22,3 +22,20 @@ export const validateUUID = (id: string | undefined | null) => { export const mediaSlug = (mediaType: SupportedMediaType) => mediaType === "image" ? "images" : "audio" + +/** + * Create a query string for a single result page, e.g. `/search?q=term&p=1`. + */ +export const singleResultQuery = (searchTerm?: string, position?: number) => { + if (!searchTerm && !position) { + return "" + } + const query = new URLSearchParams() + if (searchTerm) { + query.set("q", searchTerm) + } + if (position) { + query.set("p", position.toString()) + } + return `?${query.toString()}` +} diff --git a/frontend/src/components/VAudioTrack/VAudioTrack.vue b/frontend/src/components/VAudioTrack/VAudioTrack.vue index 4a35904356a..f39b1834794 100644 --- a/frontend/src/components/VAudioTrack/VAudioTrack.vue +++ b/frontend/src/components/VAudioTrack/VAudioTrack.vue @@ -55,14 +55,14 @@ const props = defineProps<{ */ size?: AudioSize /** - * The search term that was used to find this track. This is used - * in the link to the track's detail page. + * The URL for the single result page. Can include search term and position + * in the search results. */ - searchTerm?: string + href?: string }>() const emit = defineEmits<{ "shift-tab": [KeyboardEvent] - interacted: [Omit] + interacted: [Pick] mousedown: [AudioTrackClickEvent] focus: [FocusEvent] }>() @@ -415,34 +415,15 @@ const handleMousedown = (event: MouseEvent) => { emit("mousedown", { event, inWaveform }) } -/** - * These layout-conditional props and listeners allow us - * to set properties on the parent element depending on - * the layout in use. - */ const isComposite = computed(() => ["box", "row"].includes(props.layout)) -const layoutBasedProps = computed(() => - isComposite.value - ? { - href: `/audio/${props.audio.id}/${ - props.searchTerm ? "?q=" + props.searchTerm : "" - }`, - class: [ - "cursor-pointer", - { - "focus-visible:focus-bold-filled": props.layout === "box", - "focus-slim-tx": props.layout === "row", - }, - ], - } - : {} -) + const ariaLabel = computed(() => - isComposite.value - ? t("audioTrack.ariaLabelInteractiveSeekable", { - title: props.audio.title, - }) - : t("audioTrack.ariaLabel", { title: props.audio.title }) + t( + isComposite.value + ? "audioTrack.ariaLabelInteractiveSeekable" + : "audioTrack.ariaLabel", + { title: props.audio.title } + ) ) const togglePlayback = () => { @@ -462,11 +443,15 @@ const handleKeydown = (event: KeyboardEvent) => { seekable.listeners.keydown(event) } -const containerAttributes = computed(() => ({ +/** + * These layout-conditional props and listeners allow us + * to set properties on the parent element depending on + * the layout in use. + */ +const containerAttributes = computed(() => { // ARIA slider attributes are only added when interactive - ...(isComposite.value ? seekable.attributes.value : {}), - ...layoutBasedProps.value, -})) + return isComposite.value ? seekable.attributes.value : {} +}) const snackbar = useAudioSnackbar() @@ -496,9 +481,14 @@ const handleWaveformBlur = () => { { }) const imageLink = computed(() => { - return `/image/${props.image.id}/${ - props.searchTerm ? "?q=" + props.searchTerm : "" - }` + return `/image/${props.image.id}/${singleResultQuery(props.searchTerm)}` }) /** diff --git a/frontend/src/components/VImageCell/meta/VImageCell.stories.ts b/frontend/src/components/VImageResult/meta/VImageResult.stories.ts similarity index 58% rename from frontend/src/components/VImageCell/meta/VImageCell.stories.ts rename to frontend/src/components/VImageResult/meta/VImageResult.stories.ts index 0e25a55faa1..3cce2e82907 100644 --- a/frontend/src/components/VImageCell/meta/VImageCell.stories.ts +++ b/frontend/src/components/VImageResult/meta/VImageResult.stories.ts @@ -2,45 +2,40 @@ import { h } from "vue" import { image } from "~~/test/unit/fixtures/image" -import VImageCell from "~/components/VImageCell/VImageCell.vue" +import VImageResult from "~/components/VImageResult/VImageResult.vue" import type { Meta, StoryObj } from "@storybook/vue3" const meta = { - title: "Components/VImageCell", - component: VImageCell, -} satisfies Meta + title: "Components/VImageResult", + component: VImageResult, +} satisfies Meta export default meta type Story = StoryObj export const Default: Story = { render: (args) => ({ - components: { VImageCell }, + components: { VImageResult }, setup() { return () => h("div", { class: "p-4 image-wrapper max-w-80" }, [ - h("ol", { class: "flex flex-wrap gap-4" }, [h(VImageCell, args)]), + h("ol", { class: "flex flex-wrap gap-4" }, [h(VImageResult, args)]), ]) }, }), - name: "VImageCell", + name: "VImageResult", parameters: { - viewport: { - defaultViewport: "sm", - }, + viewport: { defaultViewport: "sm" }, }, argTypes: { aspectRatio: { options: ["square", "intrinsic"], - control: { type: "radio" }, - }, - - image: { - control: { type: "object" }, + control: { type: "select" }, }, + image: { control: { type: "object" } }, }, args: { diff --git a/frontend/src/components/VSearchResultsGrid/VAllResultsGrid.vue b/frontend/src/components/VSearchResultsGrid/VAllResultsGrid.vue index 8b656ac11d3..932f28077c6 100644 --- a/frontend/src/components/VSearchResultsGrid/VAllResultsGrid.vue +++ b/frontend/src/components/VSearchResultsGrid/VAllResultsGrid.vue @@ -10,7 +10,7 @@ import { } from "#shared/types/media" import { useUiStore } from "~/stores/ui" -import VImageCell from "~/components/VImageCell/VImageCell.vue" +import VImageResult from "~/components/VImageResult/VImageResult.vue" import VAudioResult from "~/components/VSearchResultsGrid/VAudioResult.vue" import VAudioInstructions from "~/components/VSearchResultsGrid/VAudioInstructions.vue" @@ -39,7 +39,7 @@ const isSm = computed(() => uiStore.isBreakpoint("sm")) :aria-label="collectionLabel" >