-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Filters - Filter Menu - menu header #2951
base: main
Are you sure you want to change the base?
Changes from 2 commits
6b9fcd0
3679250
270993d
198c0f5
9f706f9
ef3b28e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@commercetools-uikit/filters': minor | ||
--- | ||
|
||
Implement FilterMenu header component. | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,78 @@ | ||
import { screen, render } from '../../../../../../test/test-utils'; | ||
import { render, screen, fireEvent } from '../../../../../../test/test-utils'; | ||
import Header from './header'; | ||
|
||
/** | ||
* THIS IS A PLACEHOLDER, PLEASE UPDATE IT | ||
*/ | ||
const operandOptions = [ | ||
{ value: 'is', label: 'IS' }, | ||
{ value: 'isNot', label: 'IS NOT' }, | ||
]; | ||
|
||
const FilterSetup = () => ( | ||
<Header | ||
label="Filter Label" | ||
operandOptions={operandOptions} | ||
onSelectOperand={jest.fn()} | ||
onSort={jest.fn()} | ||
/> | ||
); | ||
|
||
describe('FilterMenu Header', () => { | ||
it('should render the header', async () => { | ||
await render(<Header />); | ||
await screen.findByText('header'); | ||
it('should render the header label', async () => { | ||
render(<FilterSetup />); | ||
|
||
expect(screen.getByText('Filter Label')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should conditionally render the SelectInput when operandOptions are provided', async () => { | ||
render(<FilterSetup />); | ||
|
||
expect(screen.getByRole('combobox')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should not render SelectInput when operandOptions are not provided', () => { | ||
render( | ||
<Header | ||
label="Filter Label" | ||
onSelectOperand={jest.fn()} | ||
onSort={jest.fn()} | ||
/> | ||
); | ||
|
||
expect(screen.queryByRole('combobox')).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('should conditionally render the IconButton when onSort is provided', async () => { | ||
render(<FilterSetup />); | ||
|
||
expect(screen.getByLabelText('Sort')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should not render IconButton when onSort is not provided', () => { | ||
render( | ||
<Header | ||
label="Filter Label" | ||
onSelectOperand={jest.fn()} | ||
operandOptions={operandOptions} | ||
/> | ||
); | ||
|
||
expect(screen.queryByLabelText('Sort')).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('should toggle isActive and call onSort when IconButton is clicked', async () => { | ||
const onSortMock = jest.fn(); | ||
render( | ||
<Header | ||
label="Filter Label" | ||
operandOptions={operandOptions} | ||
onSelectOperand={jest.fn()} | ||
onSort={onSortMock} | ||
/> | ||
); | ||
|
||
const sortButton = screen.getByLabelText('Sort'); | ||
|
||
fireEvent.click(sortButton); | ||
|
||
expect(onSortMock).toHaveBeenCalled(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,81 @@ | ||
function Header() { | ||
return <div>header</div>; | ||
} | ||
import SelectInput from '@commercetools-uikit/select-input'; | ||
import IconButton from '@commercetools-uikit/icon-button'; | ||
import Spacings from '@commercetools-uikit/spacings'; | ||
import { SortingIcon } from '@commercetools-uikit/icons'; | ||
import { designTokens } from '@commercetools-uikit/design-system'; | ||
import { css } from '@emotion/react'; | ||
import { useState } from 'react'; | ||
|
||
type TOption = { | ||
value: string; | ||
label: string; | ||
}; | ||
|
||
type THeaderProps = { | ||
label: string; | ||
operandOptions?: Array<TOption>; | ||
renderOperandsInput?: boolean; | ||
onSelectOperand: (value: string) => void; | ||
onSort?: () => void; | ||
menuHeaderWidth?: string; | ||
}; | ||
|
||
const headerContainerStyles = css` | ||
display: flex; | ||
align-items: center; | ||
padding-bottom: ${designTokens.spacing10}; | ||
border-bottom: 1px solid ${designTokens.colorNeutral90}; | ||
`; | ||
|
||
const getSelectInputStyles = (props: THeaderProps) => css` | ||
width: ${props.menuHeaderWidth}; | ||
margin: ${designTokens.spacing20}; | ||
`; | ||
|
||
const Header = (props: THeaderProps) => { | ||
const [headerSelectOptions, setHeaderSelectOptions] = useState<string>(); | ||
const [isActive, setIsActive] = useState(false); | ||
|
||
return ( | ||
<Spacings.Inline> | ||
<div css={headerContainerStyles}> | ||
<div>{props.label}</div> | ||
{props.operandOptions && ( | ||
<div css={getSelectInputStyles(props)}> | ||
<SelectInput | ||
appearance="quiet" | ||
value={ | ||
headerSelectOptions | ||
? headerSelectOptions | ||
: props.operandOptions[0].value | ||
} | ||
isCondensed={true} | ||
isSearchable={false} | ||
options={props.operandOptions} | ||
onChange={(event) => { | ||
setHeaderSelectOptions(event.target.value as string); | ||
props.onSelectOperand(event.target.value as string); | ||
}} | ||
/> | ||
</div> | ||
)} | ||
|
||
{props.onSort && ( | ||
<IconButton | ||
size="20" | ||
theme={isActive ? 'info' : 'default'} | ||
label="Sort" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might notice a slight change between the IconButton when Active. This is because "round" prop is deprecated and I have checked in with Filip to confirm we are using the new prop design(square). |
||
icon={<SortingIcon />} | ||
isToggleButton={true} | ||
onClick={() => { | ||
setIsActive(!isActive); | ||
return props.onSort && props.onSort(); | ||
}} | ||
/> | ||
)} | ||
</div> | ||
</Spacings.Inline> | ||
); | ||
}; | ||
|
||
export default Header; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import type { Meta, StoryFn } from '@storybook/react'; | ||
import Filters from './filters'; | ||
import FilterMenu from './filter-menu'; | ||
|
||
const meta: Meta<typeof Filters> = { | ||
title: 'components/Filters', | ||
|
@@ -16,5 +17,6 @@ export default meta; | |
type Story = StoryFn<typeof Filters>; | ||
|
||
export const BasicExample: Story = () => { | ||
return <Filters label={'test'} />; | ||
// return <Filters label={'test'} />; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only for testing purposes, I will return back to its state before merging |
||
return <FilterMenu label={'Size'} />; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need a changeset here, since we aren't publishing
filters
for public consumption until the entire feature is doneThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated here: 270993d
Thank you