refactor(ui): shared FilterChipGroup; unify feed filter chips (R13 S2c step 2)
New single-select FilterChipGroup primitive on the canonical rounded-full "Filters bar" chip look (pill + shadow-xs + press feedback), plus an exported filterChipClass so multi-select siblings share ONE geometry source of truth. Migrations: - feed watch-state chips (F1, Feed.tsx) -> FilterChipGroup, testIds preserved - feed content-type toggles restyled to filterChipClass (stay multi-select) so the whole toolbar row is one chip look; wrapped in their own labelled group - feed date presets + Custom (F3, Sidebar.tsx) -> FilterChipGroup with a behaviour-preserving dispatch (preset toggle-off + Custom editor intact) Adds aria-label i18n (feed.showLabel/contentLabel, sidebar.datePresetLabel; HU+EN) and FilterChipGroup.dom.test.tsx (6 tests). Self-E2E'd: watch switch, date preset on/off toggle, Custom opens the range editor. gate green (vitest 136, tsc/eslint-0/knip/prettier). Branch not merged.
This commit is contained in:
@@ -16,6 +16,7 @@ import SidePanel from "./SidePanel";
|
||||
import PanelGroups, { type PanelItem } from "./PanelGroups";
|
||||
import ChannelPicker from "./ChannelPicker";
|
||||
import SegmentedToggle from "./SegmentedToggle";
|
||||
import FilterChipGroup from "./FilterChipGroup";
|
||||
import { useFeedFilters, useFeedFiltersActions } from "./FeedFiltersProvider";
|
||||
import { useChannelsActions } from "./ChannelsProvider";
|
||||
import { useMe } from "../lib/useMe";
|
||||
@@ -150,42 +151,47 @@ export default function Sidebar({
|
||||
return <SavedViewsWidget filters={filters} onApply={setFilters} />;
|
||||
case "channel":
|
||||
return <ChannelPicker filters={filters} setFilters={setFilters} />;
|
||||
case "date":
|
||||
case "date": {
|
||||
// Single-select across the presets + Custom: a preset is active only when its maxAge matches
|
||||
// and no custom range is open; otherwise Custom owns the selection (or none).
|
||||
const activePresetDays = DATE_PRESETS.find(
|
||||
(p) => filters.maxAgeDays === p.days && !customDates,
|
||||
)?.days;
|
||||
const dateKey =
|
||||
customDates || filters.dateFrom || filters.dateTo
|
||||
? "custom"
|
||||
: activePresetDays != null
|
||||
? String(activePresetDays)
|
||||
: "";
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{DATE_PRESETS.map((p) => {
|
||||
const activePreset = filters.maxAgeDays === p.days && !customDates;
|
||||
return (
|
||||
<button
|
||||
key={p.days}
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
...filters,
|
||||
maxAgeDays: activePreset ? undefined : p.days,
|
||||
dateFrom: undefined,
|
||||
dateTo: undefined,
|
||||
})
|
||||
}
|
||||
aria-pressed={activePreset}
|
||||
className="pill text-xs px-2.5 py-1 rounded-full border shadow-xs active:translate-y-px"
|
||||
>
|
||||
{t("sidebar.datePreset." + p.key)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
onClick={() => {
|
||||
<FilterChipGroup
|
||||
ariaLabel={t("sidebar.datePresetLabel")}
|
||||
value={dateKey}
|
||||
options={[
|
||||
...DATE_PRESETS.map((p) => ({
|
||||
value: String(p.days),
|
||||
label: t("sidebar.datePreset." + p.key),
|
||||
})),
|
||||
{ value: "custom", label: t("sidebar.custom") },
|
||||
]}
|
||||
onChange={(key) => {
|
||||
if (key === "custom") {
|
||||
const next = !customDates;
|
||||
setCustomDates(next);
|
||||
if (next) setFilters({ ...filters, maxAgeDays: undefined });
|
||||
}}
|
||||
aria-pressed={!!(customDates || filters.dateFrom || filters.dateTo)}
|
||||
className="pill text-xs px-2.5 py-1 rounded-full border shadow-xs active:translate-y-px"
|
||||
>
|
||||
{t("sidebar.custom")}
|
||||
</button>
|
||||
</div>
|
||||
return;
|
||||
}
|
||||
const days = Number(key);
|
||||
const activePreset = filters.maxAgeDays === days && !customDates;
|
||||
setFilters({
|
||||
...filters,
|
||||
maxAgeDays: activePreset ? undefined : days,
|
||||
dateFrom: undefined,
|
||||
dateTo: undefined,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
{(customDates || filters.dateFrom || filters.dateTo) && (
|
||||
<div className="flex flex-col gap-1.5 mt-2">
|
||||
@@ -233,6 +239,7 @@ export default function Sidebar({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "language": {
|
||||
const chips = visibleChips(languages);
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user