-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
96059f5
commit 46f0df0
Showing
8 changed files
with
377 additions
and
2 deletions.
There are no files selected for viewing
102 changes: 102 additions & 0 deletions
102
...views/domain-workflows/domain-workflows-header/__tests__/domain-workflows-header.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
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(() => <div>Filter search</div>) | ||
); | ||
|
||
jest.mock( | ||
'@/components/page-filters/page-filters-fields/page-filters-fields', | ||
() => jest.fn(() => <div>Filter fields</div>) | ||
); | ||
|
||
jest.mock( | ||
'@/components/page-filters/page-filters-toggle/page-filters-toggle', | ||
() => | ||
jest.fn((props: PageFiltersToggleProps) => ( | ||
<button onClick={props.onClick}>Filter toggle</button> | ||
)) | ||
); | ||
|
||
jest.mock( | ||
'../../domain-workflows-query-input/domain-workflows-query-input', | ||
() => jest.fn(() => <div>Query input</div>) | ||
); | ||
|
||
jest.mock( | ||
'../../domain-workflows-query-label/domain-workflows-query-label', | ||
() => jest.fn(() => <div>Query label</div>) | ||
); | ||
|
||
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(<DomainWorkflowsHeader />); | ||
|
||
expect(await screen.findByText('Search')).toBeInTheDocument(); | ||
expect(await screen.findByText('Query label')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders page search and filters button when input type is search', async () => { | ||
render(<DomainWorkflowsHeader />); | ||
|
||
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(<DomainWorkflowsHeader />); | ||
|
||
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(<DomainWorkflowsHeader />); | ||
|
||
expect(await screen.findByText('Query input')).toBeInTheDocument(); | ||
}); | ||
|
||
it('toggles input type when segmented control is used', async () => { | ||
const user = userEvent.setup(); | ||
render(<DomainWorkflowsHeader />); | ||
|
||
const queryButton = await screen.findByText('Search'); | ||
await user.click(queryButton); | ||
|
||
expect(mockSetQueryParams).toHaveBeenCalledWith( | ||
{ inputType: 'search' }, | ||
{ pageRerender: true, replace: false } | ||
); | ||
}); | ||
}); |
60 changes: 60 additions & 0 deletions
60
src/views/domain-workflows/domain-workflows-header/domain-workflows-header.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
}; |
85 changes: 85 additions & 0 deletions
85
src/views/domain-workflows/domain-workflows-header/domain-workflows-header.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<styled.HeaderContainer> | ||
<styled.InputContainer> | ||
<SegmentedControl | ||
activeKey={queryParams.inputType} | ||
onChange={({ activeKey }) => { | ||
setQueryParams( | ||
{ | ||
inputType: activeKey === 'query' ? 'query' : 'search', | ||
}, | ||
{ replace: false, pageRerender: true } | ||
); | ||
}} | ||
overrides={overrides.inputToggle} | ||
> | ||
<Segment | ||
overrides={overrides.inputToggleSegment} | ||
key="search" | ||
label="Search" | ||
/> | ||
<Segment | ||
overrides={overrides.inputToggleSegment} | ||
key="query" | ||
// TODO @adhitya.mamallan - replace this with the label tooltip component | ||
label="Query" | ||
/> | ||
</SegmentedControl> | ||
{queryParams.inputType === 'query' ? ( | ||
<DomainWorkflowsQueryInput | ||
value={queryParams.query} | ||
setValue={(v) => setQueryParams({ query: v })} | ||
/> | ||
) : ( | ||
<> | ||
<PageFiltersSearch | ||
pageQueryParamsConfig={domainPageQueryParamsConfig} | ||
searchQueryParamKey="search" | ||
searchPlaceholder="Search for Workflow ID, Run ID, or Workflow Type" | ||
/> | ||
<PageFiltersToggle | ||
isActive={areFiltersShown} | ||
onClick={() => { | ||
setAreFiltersShown((value) => !value); | ||
}} | ||
activeFiltersCount={activeFiltersCount} | ||
/> | ||
</> | ||
)} | ||
</styled.InputContainer> | ||
{queryParams.inputType === 'search' && areFiltersShown && ( | ||
<PageFiltersFields | ||
pageFiltersConfig={domainWorkflowsFiltersConfig} | ||
resetAllFilters={resetAllFilters} | ||
queryParams={queryParams} | ||
setQueryParams={setQueryParams} | ||
/> | ||
)} | ||
</styled.HeaderContainer> | ||
); | ||
} |
54 changes: 54 additions & 0 deletions
54
...in-workflows/domain-workflows-query-input/__tests__/domain-workflows-query-input.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
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('Run Query')).toBeInTheDocument(); | ||
}); | ||
|
||
it('calls setValue 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'); | ||
}); | ||
|
||
it('calls setValue with undefined when the input is cleared', async () => { | ||
const { mockSetValue, user } = setup({ startValue: 'test_query' }); | ||
|
||
const textbox = await screen.findByRole('textbox'); | ||
await user.clear(textbox); | ||
|
||
expect(mockSetValue).toHaveBeenCalledWith(undefined); | ||
}); | ||
}); | ||
|
||
function setup({ startValue }: { startValue?: string }) { | ||
const mockSetValue = jest.fn(); | ||
const user = userEvent.setup(); | ||
render( | ||
<DomainWorkflowsQueryInput | ||
value={startValue ?? ''} | ||
setValue={mockSetValue} | ||
/> | ||
); | ||
|
||
return { mockSetValue, user }; | ||
} |
28 changes: 28 additions & 0 deletions
28
...iews/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
}; |
42 changes: 42 additions & 0 deletions
42
src/views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
|
||
import { Button } from 'baseui/button'; | ||
import { Input } from 'baseui/input'; | ||
import { MdPlayArrow, MdCode } 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<string>(''); | ||
|
||
useEffect(() => { | ||
value && setQueryText(value); | ||
}, [value]); | ||
|
||
return ( | ||
<> | ||
<Input | ||
value={queryText} | ||
onChange={(event) => { | ||
setQueryText(event.target.value); | ||
if (!event.target.value) { | ||
setValue(undefined); | ||
} | ||
}} | ||
startEnhancer={() => <MdCode />} | ||
overrides={overrides.input} | ||
placeholder="Write a custom query for workflows" | ||
clearable | ||
clearOnEscape | ||
/> | ||
<Button | ||
onClick={() => queryText && setValue(queryText)} | ||
overrides={overrides.runButton} | ||
startEnhancer={<MdPlayArrow />} | ||
> | ||
Run Query | ||
</Button> | ||
</> | ||
); | ||
} |
4 changes: 4 additions & 0 deletions
4
...views/domain-workflows/domain-workflows-query-input/domain-workflows-query-input.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export type Props = { | ||
value: string; | ||
setValue: (v: string | undefined) => void; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters