diff --git a/src/views/domain-workflows/domain-workflows-filters/domain-workflows-filters.styles.ts b/src/views/domain-workflows/domain-workflows-filters/domain-workflows-filters.styles.ts
deleted file mode 100644
index 0959f0ed7..000000000
--- a/src/views/domain-workflows/domain-workflows-filters/domain-workflows-filters.styles.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { styled as createStyled, type Theme } from 'baseui';
-
-export const styled = {
- FiltersContainer: createStyled('div', ({ $theme }: { $theme: Theme }) => ({
- marginTop: $theme.sizing.scale950,
- marginBottom: $theme.sizing.scale900,
- })),
-};
diff --git a/src/views/domain-workflows/domain-workflows-filters/domain-workflows-filters.tsx b/src/views/domain-workflows/domain-workflows-filters/domain-workflows-filters.tsx
deleted file mode 100644
index 4eb2151bb..000000000
--- a/src/views/domain-workflows/domain-workflows-filters/domain-workflows-filters.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-'use client';
-import PageFilters from '@/components/page-filters/page-filters';
-import domainPageQueryParamsConfig from '@/views/domain-page/config/domain-page-query-params.config';
-
-import domainWorkflowsFiltersConfig from '../config/domain-workflows-filters.config';
-
-import { styled } from './domain-workflows-filters.styles';
-
-export default function DomainWorkflowsFilters() {
- return (
-
-
-
- );
-}
diff --git a/src/views/domain-workflows/domain-workflows-header/__tests__/domain-workflows-header.test.tsx b/src/views/domain-workflows/domain-workflows-header/__tests__/domain-workflows-header.test.tsx
new file mode 100644
index 000000000..fc587dcde
--- /dev/null
+++ b/src/views/domain-workflows/domain-workflows-header/__tests__/domain-workflows-header.test.tsx
@@ -0,0 +1,97 @@
+import { render, screen, userEvent } from '@/test-utils/rtl';
+
+import * as usePageFiltersModule from '@/components/page-filters/hooks/use-page-filters';
+import { type Props as PageFiltersToggleProps } from '@/components/page-filters/page-filters-toggle/page-filters-toggle.types';
+
+import { mockDomainWorkflowsQueryParamsValues } from '../../__fixtures__/domain-workflows-query-params';
+import DomainWorkflowsHeader from '../domain-workflows-header';
+
+jest.mock(
+ '@/components/page-filters/page-filters-search/page-filters-search',
+ () => jest.fn(() =>
Filter search
)
+);
+
+jest.mock(
+ '@/components/page-filters/page-filters-fields/page-filters-fields',
+ () => jest.fn(() => Filter fields
)
+);
+
+jest.mock(
+ '@/components/page-filters/page-filters-toggle/page-filters-toggle',
+ () =>
+ jest.fn((props: PageFiltersToggleProps) => (
+
+ ))
+);
+
+jest.mock(
+ '../../domain-workflows-query-input/domain-workflows-query-input',
+ () => jest.fn(() => Query input
)
+);
+
+const mockSetQueryParams = jest.fn();
+const mockResetAllFilters = jest.fn();
+const mockActiveFiltersCount = 2;
+jest.mock('@/components/page-filters/hooks/use-page-filters', () =>
+ jest.fn(() => ({
+ resetAllFilters: mockResetAllFilters,
+ activeFiltersCount: mockActiveFiltersCount,
+ queryParams: mockDomainWorkflowsQueryParamsValues,
+ setQueryParams: mockSetQueryParams,
+ }))
+);
+
+describe(DomainWorkflowsHeader.name, () => {
+ it('renders segmented control', async () => {
+ render();
+
+ expect(await screen.findByText('Search')).toBeInTheDocument();
+ expect(await screen.findByText('Query')).toBeInTheDocument();
+ });
+
+ it('renders page search and filters button when input type is search', async () => {
+ render();
+
+ expect(await screen.findByText('Filter search')).toBeInTheDocument();
+ expect(await screen.findByText('Filter toggle')).toBeInTheDocument();
+ });
+
+ it('renders page filters when filter toggle is clicked', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const filterToggle = await screen.findByText('Filter toggle');
+ await user.click(filterToggle);
+
+ expect(await screen.findByText('Filter fields')).toBeInTheDocument();
+ });
+
+ it('renders query input when input type is query', async () => {
+ jest.spyOn(usePageFiltersModule, 'default').mockReturnValueOnce({
+ resetAllFilters: mockResetAllFilters,
+ activeFiltersCount: mockActiveFiltersCount,
+ queryParams: {
+ ...mockDomainWorkflowsQueryParamsValues,
+ inputType: 'query',
+ },
+ setQueryParams: mockSetQueryParams,
+ });
+
+ render();
+
+ expect(await screen.findByText('Query')).toBeInTheDocument();
+ });
+
+ it('toggles input type when segmented control is used', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const queryButton = await screen.findByText('Search');
+ await user.click(queryButton);
+
+ expect(mockSetQueryParams).toHaveBeenCalledWith(
+ { inputType: 'search' },
+ { pageRerender: true, replace: false }
+ );
+ });
+});
diff --git a/src/views/domain-workflows/domain-workflows-header/domain-workflows-header.styles.ts b/src/views/domain-workflows/domain-workflows-header/domain-workflows-header.styles.ts
new file mode 100644
index 000000000..f651cce16
--- /dev/null
+++ b/src/views/domain-workflows/domain-workflows-header/domain-workflows-header.styles.ts
@@ -0,0 +1,60 @@
+import { styled as createStyled, type Theme } from 'baseui';
+import {
+ type SegmentOverrides,
+ type SegmentedControlOverrides,
+} from 'baseui/segmented-control';
+import { type StyleObject } from 'styletron-react';
+
+export const styled = {
+ HeaderContainer: createStyled('div', ({ $theme }: { $theme: Theme }) => ({
+ marginTop: $theme.sizing.scale950,
+ marginBottom: $theme.sizing.scale900,
+ })),
+ InputContainer: createStyled('div', ({ $theme }: { $theme: Theme }) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: $theme.sizing.scale500,
+ marginBottom: $theme.sizing.scale500,
+ [$theme.mediaQuery.medium]: {
+ flexDirection: 'row',
+ },
+ })),
+};
+
+export const overrides = {
+ inputToggle: {
+ Root: {
+ style: ({ $theme }: { $theme: Theme }): StyleObject => ({
+ flex: '1 0 auto',
+ height: $theme.sizing.scale950,
+ padding: $theme.sizing.scale0,
+ borderRadius: $theme.borders.radius300,
+ width: '100%',
+ ...$theme.typography.ParagraphSmall,
+ [$theme.mediaQuery.medium]: {
+ width: 'auto',
+ },
+ }),
+ },
+ SegmentList: {
+ style: ({ $theme }: { $theme: Theme }): StyleObject => ({
+ height: $theme.sizing.scale950,
+ ...$theme.typography.ParagraphSmall,
+ }),
+ },
+ Active: {
+ style: ({ $theme }: { $theme: Theme }): StyleObject => ({
+ height: $theme.sizing.scale900,
+ top: 0,
+ }),
+ },
+ } satisfies SegmentedControlOverrides,
+ inputToggleSegment: {
+ Segment: {
+ style: ({ $theme }: { $theme: Theme }): StyleObject => ({
+ height: $theme.sizing.scale900,
+ whiteSpace: 'nowrap',
+ }),
+ },
+ } satisfies SegmentOverrides,
+};
diff --git a/src/views/domain-workflows/domain-workflows-header/domain-workflows-header.tsx b/src/views/domain-workflows/domain-workflows-header/domain-workflows-header.tsx
new file mode 100644
index 000000000..847fe3b29
--- /dev/null
+++ b/src/views/domain-workflows/domain-workflows-header/domain-workflows-header.tsx
@@ -0,0 +1,85 @@
+'use client';
+import { useState } from 'react';
+
+import { Segment, SegmentedControl } from 'baseui/segmented-control';
+
+import usePageFilters from '@/components/page-filters/hooks/use-page-filters';
+import PageFiltersFields from '@/components/page-filters/page-filters-fields/page-filters-fields';
+import PageFiltersSearch from '@/components/page-filters/page-filters-search/page-filters-search';
+import PageFiltersToggle from '@/components/page-filters/page-filters-toggle/page-filters-toggle';
+import domainPageQueryParamsConfig from '@/views/domain-page/config/domain-page-query-params.config';
+
+import domainWorkflowsFiltersConfig from '../config/domain-workflows-filters.config';
+import DomainWorkflowsQueryInput from '../domain-workflows-query-input/domain-workflows-query-input';
+
+import { overrides, styled } from './domain-workflows-header.styles';
+
+export default function DomainWorkflowsHeader() {
+ const [areFiltersShown, setAreFiltersShown] = useState(false);
+
+ const { resetAllFilters, activeFiltersCount, queryParams, setQueryParams } =
+ usePageFilters({
+ pageFiltersConfig: domainWorkflowsFiltersConfig,
+ pageQueryParamsConfig: domainPageQueryParamsConfig,
+ });
+
+ return (
+
+
+ {
+ setQueryParams(
+ {
+ inputType: activeKey === 'query' ? 'query' : 'search',
+ },
+ { replace: false, pageRerender: true }
+ );
+ }}
+ overrides={overrides.inputToggle}
+ >
+
+
+
+ {queryParams.inputType === 'query' ? (
+ setQueryParams({ query: v })}
+ />
+ ) : (
+ <>
+
+ {
+ setAreFiltersShown((value) => !value);
+ }}
+ activeFiltersCount={activeFiltersCount}
+ />
+ >
+ )}
+
+ {queryParams.inputType === 'search' && areFiltersShown && (
+
+ )}
+
+ );
+}
diff --git a/src/views/domain-workflows/domain-workflows-query-input/__tests__/domain-workflows-query-input.test.tsx b/src/views/domain-workflows/domain-workflows-query-input/__tests__/domain-workflows-query-input.test.tsx
new file mode 100644
index 000000000..99bb0a7a7
--- /dev/null
+++ b/src/views/domain-workflows/domain-workflows-query-input/__tests__/domain-workflows-query-input.test.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+
+import { render, screen, userEvent, waitFor } from '@/test-utils/rtl';
+
+import DomainWorkflowsQueryInput from '../domain-workflows-query-input';
+
+describe(DomainWorkflowsQueryInput.name, () => {
+ it('renders as expected', async () => {
+ setup({});
+
+ expect(await screen.findByRole('textbox')).toBeInTheDocument();
+ expect(await screen.findByText('Run Query')).toBeInTheDocument();
+ });
+
+ it('renders as expected when loaded with a start value', async () => {
+ setup({ startValue: 'test_query' });
+
+ const textbox = await screen.findByRole('textbox');
+ await waitFor(() => expect(textbox).toHaveValue('test_query'));
+ expect(await screen.findByText('Rerun Query')).toBeInTheDocument();
+ });
+
+ it('calls setValue and changes text when the Run Query button is clicked', async () => {
+ const { mockSetValue, user } = setup({});
+
+ const textbox = await screen.findByRole('textbox');
+ await user.type(textbox, 'mock_query');
+ await user.click(await screen.findByText('Run Query'));
+
+ expect(mockSetValue).toHaveBeenCalledWith('mock_query');
+ });
+});
+
+function setup({ startValue }: { startValue?: string }) {
+ const mockSetValue = jest.fn();
+ const user = userEvent.setup();
+ render(
+
+ );
+
+ return { mockSetValue, user };
+}
diff --git a/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.styles.ts b/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.styles.ts
new file mode 100644
index 000000000..40aebdcef
--- /dev/null
+++ b/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.styles.ts
@@ -0,0 +1,28 @@
+import { type Theme } from 'baseui';
+import { type ButtonOverrides } from 'baseui/button';
+import { type InputOverrides } from 'baseui/input';
+import { type StyleObject } from 'styletron-react';
+
+export const overrides = {
+ input: {
+ Root: {
+ style: ({ $theme }: { $theme: Theme }): StyleObject => ({
+ height: $theme.sizing.scale950,
+ }),
+ },
+ Input: {
+ style: ({ $theme }: { $theme: Theme }): StyleObject => ({
+ ...$theme.typography.MonoParagraphXSmall,
+ }),
+ },
+ } satisfies InputOverrides,
+ runButton: {
+ Root: {
+ style: ({ $theme }: { $theme: Theme }): StyleObject => ({
+ whiteSpace: 'nowrap',
+ height: $theme.sizing.scale950,
+ ...$theme.typography.LabelSmall,
+ }),
+ },
+ } satisfies ButtonOverrides,
+};
diff --git a/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.tsx b/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.tsx
new file mode 100644
index 000000000..3066aadd5
--- /dev/null
+++ b/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.tsx
@@ -0,0 +1,41 @@
+import React, { useEffect, useState } from 'react';
+
+import { Button } from 'baseui/button';
+import { Input } from 'baseui/input';
+import { MdPlayArrow, MdCode, MdRefresh } from 'react-icons/md';
+
+import { overrides } from './domain-workflows-query-input.styles';
+import { type Props } from './domain-workflows-query-input.types';
+
+export default function DomainWorkflowsQueryInput({ value, setValue }: Props) {
+ const [queryText, setQueryText] = useState('');
+
+ useEffect(() => {
+ setQueryText(value);
+ }, [value]);
+
+ const isQueryUnchanged = value && value === queryText;
+
+ return (
+ <>
+ {
+ setQueryText(event.target.value);
+ }}
+ startEnhancer={() => }
+ overrides={overrides.input}
+ placeholder="Filter workflows using a custom query"
+ clearable
+ clearOnEscape
+ />
+
+ >
+ );
+}
diff --git a/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.types.ts b/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.types.ts
new file mode 100644
index 000000000..99094d144
--- /dev/null
+++ b/src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.types.ts
@@ -0,0 +1,4 @@
+export type Props = {
+ value: string;
+ setValue: (v: string | undefined) => void;
+};
diff --git a/src/views/domain-workflows/domain-workflows.tsx b/src/views/domain-workflows/domain-workflows.tsx
index 01c176bd3..dbf5c5e7d 100644
--- a/src/views/domain-workflows/domain-workflows.tsx
+++ b/src/views/domain-workflows/domain-workflows.tsx
@@ -2,13 +2,13 @@ import React from 'react';
import { type DomainPageTabContentProps } from '@/views/domain-page/domain-page-content/domain-page-content.types';
-import DomainWorkflowsFilters from './domain-workflows-filters/domain-workflows-filters';
+import DomainWorkflowsHeader from './domain-workflows-header/domain-workflows-header';
import DomainWorkflowsTable from './domain-workflows-table/domain-workflows-table';
export default function DomainWorkflows(props: DomainPageTabContentProps) {
return (
<>
-
+
>
);