Share artifact toolbar story constants

This commit is contained in:
Dotta committed 2026-06-06 15:14:07 +00:00
1 parent e3d8c2f92b
commit 667bfc089b
2 files changed
+33 -46

No files matched your search

+8 -8
View File
@@ -29,7 +29,7 @@ import { Button } from "@/components/ui/button";
const ARTIFACTS_PAGE_SIZE = 30;
const SEARCH_DEBOUNCE_MS = 250;
const KIND_FILTERS: { value: ArtifactKindFilter; label: string }[] = [
export const ARTIFACT_KIND_FILTERS: { value: ArtifactKindFilter; label: string }[] = [
{ value: "all", label: "All" },
{ value: "image", label: "Images" },
{ value: "video", label: "Videos" },
@@ -38,13 +38,13 @@ const KIND_FILTERS: { value: ArtifactKindFilter; label: string }[] = [
{ value: "file", label: "Files" },
];
const GROUP_OPTIONS: { value: ArtifactGroupBy; label: string }[] = [
export const ARTIFACT_GROUP_OPTIONS: { value: ArtifactGroupBy; label: string }[] = [
{ value: "none", label: "None" },
{ value: "task", label: "Task" },
{ value: "parent_task", label: "Parent task" },
];
const KIND_VALUES = new Set(KIND_FILTERS.map((filter) => filter.value));
const KIND_VALUES = new Set(ARTIFACT_KIND_FILTERS.map((filter) => filter.value));
function parseGroupBy(value: string | null): ArtifactGroupBy {
if (value === "none" || value === "task" || value === "parent_task") return value;
@@ -57,8 +57,8 @@ function parseKind(value: string | null): ArtifactKindFilter {
: "all";
}
function groupByLabel(value: ArtifactGroupBy): string {
return GROUP_OPTIONS.find((option) => option.value === value)?.label ?? "None";
export function artifactGroupByLabel(value: ArtifactGroupBy): string {
return ARTIFACT_GROUP_OPTIONS.find((option) => option.value === value)?.label ?? "None";
}
export function Artifacts() {
@@ -276,7 +276,7 @@ export function Artifacts() {
type="button"
variant="outline"
size="icon"
aria-label={`Group artifacts (currently ${groupByLabel(groupBy)})`}
aria-label={`Group artifacts (currently ${artifactGroupByLabel(groupBy)})`}
title="Group artifacts"
data-testid="artifact-group-control"
data-group-by={groupBy}
@@ -287,7 +287,7 @@ export function Artifacts() {
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-44">
<DropdownMenuLabel>Group by</DropdownMenuLabel>
{GROUP_OPTIONS.map((option) => (
{ARTIFACT_GROUP_OPTIONS.map((option) => (
<DropdownMenuItem
key={option.value}
data-testid={`artifact-group-option-${option.value}`}
@@ -303,7 +303,7 @@ export function Artifacts() {
</DropdownMenu>
<div className="flex flex-wrap items-center gap-1.5" role="tablist" aria-label="Filter artifacts by type">
{KIND_FILTERS.map((filter) => (
{ARTIFACT_KIND_FILTERS.map((filter) => (
<button
key={filter.value}
type="button"
+25 -38
View File
@@ -14,6 +14,11 @@ import {
import { Input } from "@/components/ui/input";
import { cn, formatDate } from "@/lib/utils";
import type { CompanyArtifact } from "@/api/artifacts";
import {
ARTIFACT_GROUP_OPTIONS,
ARTIFACT_KIND_FILTERS,
artifactGroupByLabel,
} from "@/pages/Artifacts";
/**
* Storybook coverage for the company Artifacts page. Covers:
@@ -24,7 +29,8 @@ import type { CompanyArtifact } from "@/api/artifacts";
* screenshots without booting a live backend.
*/
type GroupBy = "none" | "task" | "parent_task";
type StoryArtifactKindFilter = (typeof ARTIFACT_KIND_FILTERS)[number]["value"];
type StoryArtifactGroupBy = (typeof ARTIFACT_GROUP_OPTIONS)[number]["value"];
const SAMPLE_IMAGE =
"data:image/svg+xml;utf8," +
@@ -144,25 +150,6 @@ function ArtifactsGrid({ artifacts }: { artifacts: CompanyArtifact[] }) {
// grouping controls so Storybook stays useful for visual review.
// ---------------------------------------------------------------------------
const KIND_FILTERS = [
{ value: "all", label: "All" },
{ value: "image", label: "Images" },
{ value: "video", label: "Videos" },
{ value: "document", label: "Documents" },
{ value: "text", label: "Text" },
{ value: "file", label: "Files" },
] as const;
const GROUP_OPTIONS: { value: GroupBy; label: string }[] = [
{ value: "none", label: "None" },
{ value: "task", label: "Task" },
{ value: "parent_task", label: "Parent task" },
];
function groupByLabel(value: GroupBy) {
return GROUP_OPTIONS.find((option) => option.value === value)?.label ?? "None";
}
/**
* Toolbar replica matching the existing Artifacts page (search + kind filters)
* with the group-by icon control placed before the filter chips.
@@ -177,10 +164,10 @@ function ArtifactsToolbar({
}: {
query: string;
onQueryChange: (value: string) => void;
kind: (typeof KIND_FILTERS)[number]["value"];
onKindChange: (value: (typeof KIND_FILTERS)[number]["value"]) => void;
groupBy: GroupBy;
onGroupByChange: (value: GroupBy) => void;
kind: StoryArtifactKindFilter;
onKindChange: (value: StoryArtifactKindFilter) => void;
groupBy: StoryArtifactGroupBy;
onGroupByChange: (value: StoryArtifactGroupBy) => void;
}) {
return (
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
@@ -214,14 +201,14 @@ function ArtifactsToolbar({
size="icon"
className={cn("h-8 w-8 shrink-0", groupBy !== "none" && "bg-accent")}
title="Group artifacts"
aria-label={`Group artifacts (currently ${groupByLabel(groupBy)})`}
aria-label={`Group artifacts (currently ${artifactGroupByLabel(groupBy)})`}
>
<Layers className="h-3.5 w-3.5" aria-hidden="true" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-44">
<DropdownMenuLabel>Group by</DropdownMenuLabel>
{GROUP_OPTIONS.map(({ value, label }) => (
{ARTIFACT_GROUP_OPTIONS.map(({ value, label }) => (
<DropdownMenuItem
key={value}
aria-selected={groupBy === value}
@@ -236,7 +223,7 @@ function ArtifactsToolbar({
</DropdownMenu>
<div className="flex flex-wrap items-center gap-1.5" role="tablist" aria-label="Filter artifacts by type">
{KIND_FILTERS.map((filter) => (
{ARTIFACT_KIND_FILTERS.map((filter) => (
<button
key={filter.value}
type="button"
@@ -261,7 +248,7 @@ function ArtifactsToolbar({
interface MockGroup {
id: string;
groupBy: Exclude<GroupBy, "none">;
groupBy: Exclude<StoryArtifactGroupBy, "none">;
issueIdentifier: string;
issueTitle: string;
count: number;
@@ -502,8 +489,8 @@ type Story = StoryObj;
export const Grid: Story = {
render: () => {
const [query, setQuery] = useState("");
const [kind, setKind] = useState<(typeof KIND_FILTERS)[number]["value"]>("all");
const [groupBy, setGroupBy] = useState<GroupBy>("none");
const [kind, setKind] = useState<StoryArtifactKindFilter>("all");
const [groupBy, setGroupBy] = useState<StoryArtifactGroupBy>("none");
return (
<div className="mx-auto w-full max-w-6xl space-y-5 p-6">
<ArtifactsToolbar
@@ -528,8 +515,8 @@ export const Grid: Story = {
export const GroupedByTask: Story = {
render: () => {
const [query, setQuery] = useState("");
const [kind, setKind] = useState<(typeof KIND_FILTERS)[number]["value"]>("all");
const [groupBy, setGroupBy] = useState<GroupBy>("task");
const [kind, setKind] = useState<StoryArtifactKindFilter>("all");
const [groupBy, setGroupBy] = useState<StoryArtifactGroupBy>("task");
return (
<div className="mx-auto w-full max-w-6xl space-y-5 p-6">
<ArtifactsToolbar
@@ -557,8 +544,8 @@ export const GroupedByTask: Story = {
export const GroupedByParentTask: Story = {
render: () => {
const [query, setQuery] = useState("");
const [kind, setKind] = useState<(typeof KIND_FILTERS)[number]["value"]>("all");
const [groupBy, setGroupBy] = useState<GroupBy>("parent_task");
const [kind, setKind] = useState<StoryArtifactKindFilter>("all");
const [groupBy, setGroupBy] = useState<StoryArtifactGroupBy>("parent_task");
return (
<div className="mx-auto w-full max-w-6xl space-y-5 p-6">
<ArtifactsToolbar
@@ -587,8 +574,8 @@ export const GroupedByParentTask: Story = {
export const SelectedStack: Story = {
render: () => {
const [query, setQuery] = useState("");
const [kind, setKind] = useState<(typeof KIND_FILTERS)[number]["value"]>("all");
const [groupBy, setGroupBy] = useState<GroupBy>("task");
const [kind, setKind] = useState<StoryArtifactKindFilter>("all");
const [groupBy, setGroupBy] = useState<StoryArtifactGroupBy>("task");
return (
<div className="mx-auto w-full max-w-6xl space-y-5 p-6">
<ArtifactsToolbar
@@ -637,8 +624,8 @@ export const MobileGrouping: Story = {
parameters: { viewport: { defaultViewport: "mobile" } },
render: () => {
const [query, setQuery] = useState("");
const [kind, setKind] = useState<(typeof KIND_FILTERS)[number]["value"]>("all");
const [groupBy, setGroupBy] = useState<GroupBy>("task");
const [kind, setKind] = useState<StoryArtifactKindFilter>("all");
const [groupBy, setGroupBy] = useState<StoryArtifactGroupBy>("task");
return (
<div className="mx-auto w-full max-w-md space-y-5 p-4">
<ArtifactsToolbar