diff --git a/frontend/src/components/VMediaInfo/VMediaTags.vue b/frontend/src/components/VMediaInfo/VMediaTags.vue index 8218862bebf..30e8b41c7b3 100644 --- a/frontend/src/components/VMediaInfo/VMediaTags.vue +++ b/frontend/src/components/VMediaInfo/VMediaTags.vue @@ -1,32 +1,77 @@ diff --git a/frontend/src/locales/scripts/en.json5 b/frontend/src/locales/scripts/en.json5 index bd548d490a1..dd674ad2028 100644 --- a/frontend/src/locales/scripts/en.json5 +++ b/frontend/src/locales/scripts/en.json5 @@ -498,6 +498,11 @@ }, imageInfo: "Image information", audioInfo: "Audio information", + tags: { + title: "Tags", + showMore: "Show more", + showLess: "Show less", + }, contentReport: { short: "Report", long: "Report this content", diff --git a/frontend/src/types/analytics.ts b/frontend/src/types/analytics.ts index f37f81c7026..eb2ba2f3db2 100644 --- a/frontend/src/types/analytics.ts +++ b/frontend/src/types/analytics.ts @@ -283,7 +283,7 @@ export type Events = { /** Pagination depth */ resultPage: number } - /* + /** * Description: Whenever the user clicks the load more button * Questions: * - On what page do users typically find a result? @@ -301,7 +301,7 @@ export type Events = { * *before* loading more results.. */ resultPage: number } - /* + /** * Description: Whenever the user sets a filter. Filter category and key are the values used in code, not the user-facing filter labels. * Questions: * - Do most users filter their searches? @@ -412,6 +412,17 @@ export type Events = { /** the reasons for why this result is considered sensitive */ sensitivities: string } + /** + * Description: The user expands collapsed tags or collapses the expanded ones. + * + * Questions: + * - Are the extra tags useful to users? + * - Do users ever collapse expanded tags? + */ + TOGGLE_TAG_EXPANSION: { + /** The state of the tags after the user interaction. */ + toState: "expanded" | "collapsed" + } } /** diff --git a/frontend/src/utils/focus-management.ts b/frontend/src/utils/focus-management.ts index b9db344ea18..89c9431baf1 100644 --- a/frontend/src/utils/focus-management.ts +++ b/frontend/src/utils/focus-management.ts @@ -88,8 +88,10 @@ export function isFocusableElement(element: HTMLElement) { return element.matches(focusableSelector) } -export function focusElement(element: HTMLElement | null) { - element?.focus() +export function focusElement(element: HTMLElement | Element | null) { + if (element instanceof HTMLElement) { + element.focus() + } } // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select diff --git a/frontend/test/locales/ar.json b/frontend/test/locales/ar.json index 0f7677be5c4..6c616d9d410 100644 --- a/frontend/test/locales/ar.json +++ b/frontend/test/locales/ar.json @@ -191,6 +191,11 @@ "relatedError": "خطأ في جلب الوسائط ذات الصلة", "imageInfo": "معلومات الصورة", "audioInfo": "معلومات صوتية", + "tags": { + "title": "العلامات", + "showMore": "أظهر المزيد", + "showLess": "تظهر أقل" + }, "contentReport": { "short": "تقرير", "long": "الإبلاغ عن هذا المحتوى", diff --git a/frontend/test/playwright/e2e/collections.spec.ts b/frontend/test/playwright/e2e/collections.spec.ts index 6e97c8ce3b5..65d61544abd 100644 --- a/frontend/test/playwright/e2e/collections.spec.ts +++ b/frontend/test/playwright/e2e/collections.spec.ts @@ -4,7 +4,7 @@ * introduced in https://github.com/WordPress/openverse/pull/3831 */ -import { test, expect } from "@playwright/test" +import { test, expect, Page } from "@playwright/test" import { preparePageForTests } from "~~/test/playwright/utils/navigation" import { @@ -12,6 +12,11 @@ import { getH1, // getLoadMoreButton, } from "~~/test/playwright/utils/components" +import { t } from "~~/test/playwright/utils/i18n" +import { + collectAnalyticsEvents, + expectEventPayloadToMatch, +} from "~~/test/playwright/utils/analytics" test.describe.configure({ mode: "parallel" }) @@ -61,3 +66,43 @@ test.describe("collections", () => { // expect(await page.locator("figure").count()).toEqual(20) }) }) + +const COLLAPSE_BUTTON = (page: Page) => + page.getByRole("button", { name: t("mediaDetails.tags.showLess") }) +const EXPAND_BUTTON = (page: Page) => + page.getByRole("button", { name: t("mediaDetails.tags.showMore") }) + +test("some tags are hidden if there are more than 3 rows", async ({ page }) => { + await preparePageForTests(page, "xl", { + features: { additional_search_views: "on" }, + }) + await page.goto("/image/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750") + + const tags = page.getByRole("list", { name: t("mediaDetails.tags.title") }) + await expect(tags).toBeVisible() + const tagsCount = await tags.locator("li").count() + + await EXPAND_BUTTON(page).click() + expect(await tags.locator("li").count()).toBeGreaterThan(tagsCount) +}) + +test("sends analytics events when tags are toggled", async ({ + context, + page, +}) => { + await preparePageForTests(page, "xl", { + features: { additional_search_views: "on" }, + }) + const analyticsEvents = collectAnalyticsEvents(context) + await page.goto("/image/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750") + + await EXPAND_BUTTON(page).click() + await COLLAPSE_BUTTON(page).click() + + const toggleEvents = analyticsEvents.filter( + (event) => event.n === "TOGGLE_TAG_EXPANSION" + ) + expect(toggleEvents).toHaveLength(2) + expectEventPayloadToMatch(toggleEvents[0], { toState: "expanded" }) + expectEventPayloadToMatch(toggleEvents[1], { toState: "collapsed" }) +}) diff --git a/frontend/test/tapes/detail/images/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750_keep-alive.json5 b/frontend/test/tapes/detail/images/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750_keep-alive.json5 new file mode 100644 index 00000000000..bcaf7538e20 --- /dev/null +++ b/frontend/test/tapes/detail/images/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750_keep-alive.json5 @@ -0,0 +1,257 @@ +{ + meta: { + createdAt: '2024-02-19T17:03:23.360Z', + host: 'https://api.openverse.engineering', + resHumanReadable: true, + resUncompressed: true, + }, + req: { + headers: { + connection: 'keep-alive', + }, + url: '/v1/images/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750/', + method: 'GET', + body: '', + }, + res: { + status: 200, + headers: { + date: [ + 'Mon, 19 Feb 2024 17:03:23 GMT', + ], + 'content-type': [ + 'application/json', + ], + 'transfer-encoding': [ + 'chunked', + ], + connection: [ + 'keep-alive', + ], + vary: [ + 'Accept-Encoding, Accept, Authorization, origin', + ], + allow: [ + 'GET, HEAD, OPTIONS', + ], + 'x-ratelimit-limit-anon_burst': [ + '20/min', + ], + 'x-ratelimit-available-anon_burst': [ + '19', + ], + 'x-ratelimit-limit-anon_sustained': [ + '200/day', + ], + 'x-ratelimit-available-anon_sustained': [ + '199', + ], + 'x-frame-options': [ + 'DENY', + ], + 'x-content-type-options': [ + 'nosniff', + ], + 'referrer-policy': [ + 'same-origin', + ], + 'cross-origin-opener-policy': [ + 'same-origin', + ], + 'x-request-id': [ + 'f3cf70767a3e4dc3bf40b854158123ea', + ], + 'cache-control': [ + 'max-age=14400', + ], + 'cf-cache-status': [ + 'HIT', + ], + age: [ + '27135', + ], + 'last-modified': [ + 'Mon, 19 Feb 2024 09:31:08 GMT', + ], + 'strict-transport-security': [ + 'max-age=15552000; includeSubDomains; preload', + ], + server: [ + 'cloudflare', + ], + 'cf-ray': [ + '8580153e2f57692b-FRA', + ], + 'content-encoding': [ + 'br', + ], + 'alt-svc': [ + 'h3=":443"; ma=86400', + ], + }, + body: { + id: '2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750', + title: 'August Great Horned Owl', + indexed_on: '2020-04-25T07:58:25.731612Z', + foreign_landing_url: 'https://www.flickr.com/photos/7327719@N06/3823784992', + url: 'https://live.staticflickr.com/3486/3823784992_a1dcf4a1d1_b.jpg', + creator: "Vicki's Nature", + creator_url: 'https://www.flickr.com/photos/7327719@N06', + license: 'by-nc-nd', + license_version: '2.0', + license_url: 'https://creativecommons.org/licenses/by-nc-nd/2.0/', + provider: 'flickr', + source: 'flickr', + category: null, + filesize: null, + filetype: null, + tags: [ + { + name: '100commentgroup', + accuracy: null, + }, + { + name: 'aas2010', + accuracy: null, + }, + { + name: 'beautifulworldchallenges', + accuracy: null, + }, + { + name: 'bird', + accuracy: null, + }, + { + name: 'bwcgg', + accuracy: null, + }, + { + name: 'canon', + accuracy: null, + }, + { + name: 'cnc', + accuracy: null, + }, + { + name: 'faves3539', + accuracy: null, + }, + { + name: 'favescontestfavored', + accuracy: null, + }, + { + name: 'favescontestwinner', + accuracy: null, + }, + { + name: 'favesfavoredchallenge', + accuracy: null, + }, + { + name: 'favesfavoredwinner', + accuracy: null, + }, + { + name: 'favesrunnerupchallenge', + accuracy: null, + }, + { + name: 'favesrunnerupwinner', + accuracy: null, + }, + { + name: 'favestopseedchallenge', + accuracy: null, + }, + { + name: 'favestopseedchallengex2', + accuracy: null, + }, + { + name: 'faveswinner', + accuracy: null, + }, + { + name: 'gameshotbefore2013bigbirdportraits', + accuracy: null, + }, + { + name: 'gamesweep', + accuracy: null, + }, + { + name: 'gamewinner', + accuracy: null, + }, + { + name: 'georgia', + accuracy: null, + }, + { + name: 'goldstaraward', + accuracy: null, + }, + { + name: 'goldstarawardcontest78', + accuracy: null, + }, + { + name: 'greathornedowl', + accuracy: null, + }, + { + name: 'motherbirds', + accuracy: null, + }, + { + name: 'natureoutpost', + accuracy: null, + }, + { + name: 'natureselegantshots', + accuracy: null, + }, + { + name: 'owl', + accuracy: null, + }, + { + name: 'readygamex2', + accuracy: null, + }, + { + name: 'return', + accuracy: null, + }, + { + name: 's5', + accuracy: null, + }, + { + name: 'specanimal', + accuracy: null, + }, + { + name: 'sweep', + accuracy: null, + }, + { + name: 'vickisnature', + accuracy: null, + }, + ], + attribution: '"August Great Horned Owl" by Vicki\'s Nature is licensed under CC BY-NC-ND 2.0. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-nd/2.0/.', + fields_matched: [], + mature: false, + height: 1024, + width: 1024, + thumbnail: 'http://localhost:49153/v1/images/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750/thumb/', + detail_url: 'http://localhost:49153/v1/images/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750/', + related_url: 'http://localhost:49153/v1/images/2bc7dde0-5aad-4cf7-b91d-7f0e3bd06750/related/', + unstable__sensitivity: [], + }, + }, +} \ No newline at end of file