From 6094140274317ee908e48d20a3bb0cc21204c8da Mon Sep 17 00:00:00 2001 From: Dylan Audius Date: Tue, 16 Jun 2026 17:32:39 -0700 Subject: [PATCH] fix(search): show freeform genre as a selected filter chip Clicking a freeform/custom genre (e.g. "Hyperpop Fusion") on a track page navigates to search and the genre IS passed to the search API, but the genre filter chip showed no indication of what was being filtered. The FilterButton resolves its active label by matching the URL genre value against its options, which are built from the ~51 hardcoded GENRES. A freeform genre matches nothing, so selectedLabel is undefined and the chip falls back to the generic "Genre" text. Add the current genre as an option when it isn't in the predefined list so it renders as a selected chip on both web and mobile. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../screens/search-screen/SearchFilters.tsx | 17 +++++++++++-- .../src/pages/search-page/SearchFilters.tsx | 25 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/mobile/src/screens/search-screen/SearchFilters.tsx b/packages/mobile/src/screens/search-screen/SearchFilters.tsx index 863c1591a12..e40d4760ca3 100644 --- a/packages/mobile/src/screens/search-screen/SearchFilters.tsx +++ b/packages/mobile/src/screens/search-screen/SearchFilters.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useMemo, useState, useEffect } from 'react' import type { Genre } from '@audius/common/utils' import { @@ -48,12 +48,25 @@ export const GenreFilter = () => { setGenre(value) } + const options = useMemo(() => { + // Freeform/custom genres (e.g. "Hyperpop Fusion") aren't in the predefined + // GENRES list. Without a matching option the FilterButton can't resolve a + // label and the active chip falls back to the generic "Genre" text, leaving + // no indication of what's actually being filtered. Add the current genre as + // an option so it renders as a selected chip. + if (genre && !genreOptions.some((option) => option.value === genre)) { + return [{ label: genre, value: genre }, ...genreOptions] + } + + return genreOptions + }, [genre]) + return ( ) diff --git a/packages/web/src/pages/search-page/SearchFilters.tsx b/packages/web/src/pages/search-page/SearchFilters.tsx index 4a1a4936f1c..835d7247e90 100644 --- a/packages/web/src/pages/search-page/SearchFilters.tsx +++ b/packages/web/src/pages/search-page/SearchFilters.tsx @@ -1,4 +1,4 @@ -import { ReactElement, useCallback, useState } from 'react' +import { ReactElement, useCallback, useMemo, useState } from 'react' import { GENRES, @@ -55,6 +55,24 @@ const GenreFilter = () => { updateGenreParams(value) } + const options = useMemo(() => { + const genreOptions = GENRES.map((genre) => ({ + label: genre, + value: convertGenreLabelToValue(genre) + })) + + // Freeform/custom genres (e.g. "Hyperpop Fusion") aren't in the predefined + // GENRES list. Without a matching option the FilterButton can't resolve a + // label and the active chip falls back to the generic "Genre" text, leaving + // no indication of what's actually being filtered. Add the current genre as + // an option so it renders as a selected chip. + if (genre && !genreOptions.some((option) => option.value === genre)) { + return [{ label: genre, value: genre }, ...genreOptions] + } + + return genreOptions + }, [genre]) + return ( { }} value={genre} onChange={handleGenreChange} - options={GENRES.map((genre) => ({ - label: genre, - value: convertGenreLabelToValue(genre) - }))} + options={options} showFilterInput filterInputProps={{ label: messages.genreFilterLabel }} />