diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/index.ts b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/index.ts
index 48603fa9448..ab44bb4bf54 100644
--- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/index.ts
@@ -1,3 +1,4 @@
+export { ResourceFilterPanel, ResourceFilterSection } from './resource-filter-panel'
export type {
ColumnOption,
FilterConfig,
diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-filter-panel.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-filter-panel.tsx
new file mode 100644
index 00000000000..94b2bb0a6bb
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-filter-panel.tsx
@@ -0,0 +1,33 @@
+'use client'
+
+import type { ReactNode } from 'react'
+import { FILTER_SECTION_LABEL_CLASS } from '@/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options'
+
+interface ResourceFilterPanelProps {
+ children: ReactNode
+}
+
+/** The filter content frame shared by resource lists and document chunks. */
+export function ResourceFilterPanel({ children }: ResourceFilterPanelProps) {
+ return
{children}
+}
+
+interface ResourceFilterSectionProps {
+ label: string
+ children: ReactNode
+ /** A distinct label treatment, such as the document chunk status label. */
+ labelClassName?: string
+}
+
+export function ResourceFilterSection({
+ label,
+ children,
+ labelClassName = FILTER_SECTION_LABEL_CLASS,
+}: ResourceFilterSectionProps) {
+ return (
+
+ {label}
+ {children}
+
+ )
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.test.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.test.tsx
index 079c0677838..8a92069fe63 100644
--- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.test.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.test.tsx
@@ -4,7 +4,14 @@
import { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
-import { SortDropdown } from '@/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options'
+import {
+ ResourceFilterPanel,
+ ResourceFilterSection,
+} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-filter-panel'
+import {
+ ResourceOptions,
+ SortDropdown,
+} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options'
const LONG_COLUMN_LABEL = 'highest_current_champion_role_across_the_entire_company'
@@ -125,3 +132,47 @@ describe('SortDropdown', () => {
expect(onOpenChange).toHaveBeenCalledWith(false)
})
})
+
+describe('ResourceOptions filter content', () => {
+ it('keeps section controls focusable and clear actions working in the filter popover', () => {
+ const onClear = vi.fn()
+ act(() => {
+ root.render(
+
+
+
+
+
+
+ ),
+ }}
+ />
+ )
+ })
+
+ const trigger = [...document.querySelectorAll('button')].find(
+ (button) => button.textContent === 'Filter'
+ )
+ expect(trigger).toBeDefined()
+ act(() => trigger?.click())
+
+ const choice = [...document.querySelectorAll('button')].find(
+ (button) => button.textContent === 'Choose status'
+ )
+ expect(choice).toBeDefined()
+ expect(document.body).toHaveTextContent('Status')
+ act(() => choice?.focus())
+ expect(document.activeElement).toBe(choice)
+
+ const clear = [...document.querySelectorAll('button')].find(
+ (button) => button.textContent === 'Clear all filters'
+ )
+ act(() => clear?.click())
+ expect(onClear).toHaveBeenCalledOnce()
+ })
+})
diff --git a/apps/sim/app/workspace/[workspaceId]/files/files.tsx b/apps/sim/app/workspace/[workspaceId]/files/files.tsx
index 9a05428538a..9cb16f9a489 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/files.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/files/files.tsx
@@ -94,7 +94,10 @@ import type {
SearchConfig,
SortConfig,
} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
-import { FILTER_SECTION_LABEL_CLASS } from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
+import {
+ ResourceFilterPanel,
+ ResourceFilterSection,
+} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
import { timeCell } from '@/app/workspace/[workspaceId]/components/resource/components/time-cell'
import { resourceListState } from '@/app/workspace/[workspaceId]/components/resource/is-resource-list-empty'
import type {
@@ -2004,9 +2007,8 @@ function FilesContent() {
: `${uploadedByFilter.length} members`
return (
-
-
- File Type
+
+
-
-
- Size
+
+
-
+
{memberOptions.length > 0 && (
-
- Uploaded By
+
-
+
)}
{hasActiveFilters && (
+
)
}, [
typeFilter,
diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx
index 96cea520580..9de2b6c4cb5 100644
--- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx
@@ -39,6 +39,10 @@ import type {
SearchConfig,
SortConfig,
} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
+import {
+ ResourceFilterPanel,
+ ResourceFilterSection,
+} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
import type {
PaginationConfig,
ResourceColumn,
@@ -737,9 +741,11 @@ export function Document({
const filterContent = useMemo(
() => (
-
-
- Status
+
+
-
+
{enabledFilter.length > 0 && (
+
),
[enabledFilter, setEnabledFilter]
)
diff --git a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
index a9b4257b2fd..199bfd9591d 100644
--- a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
@@ -53,7 +53,10 @@ import type {
SearchConfig,
SortConfig,
} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
-import { FILTER_SECTION_LABEL_CLASS } from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
+import {
+ ResourceFilterPanel,
+ ResourceFilterSection,
+} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options'
import { timeCell } from '@/app/workspace/[workspaceId]/components/resource/components/time-cell'
import { resourceListState } from '@/app/workspace/[workspaceId]/components/resource/is-resource-list-empty'
import type {
@@ -727,9 +730,8 @@ function TablesContent() {
const filterContent = useMemo(
() => (
-
-
- Row Count
+
+
-
+
{memberOptions.length > 0 && (
-
- Owner
+
-
+
)}
{hasActiveFilters && (
+
),
[
rowCountFilter,