-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[IndexFilters] Prototype "add as filter" search input UX
[IndexFilters] Remove search from FilterButton [IndexFilters] Add a SearchField subcomponent [IndexFilters] Move search input beside tabs; prototype add as filter UX [IndexFilters] Update stories to change search placeholder tab reference when active tab changes [Storybook][Test Pages] Create a new ProductsPage story for prototype WIP mock saved filter logic (have to fake outside of admin) switch prototype to tabless filterless orders index ignore commas when filtering by query filter Improve order data with David's feedback Implement fully working tabs and filters to flesh out the table [Key dev dependencies] Bumped TS and Storybook packages Ensure special chars like '#' don't break search
- Loading branch information
Showing
25 changed files
with
2,588 additions
and
179 deletions.
There are no files selected for viewing
6 changes: 6 additions & 0 deletions
6
polaris-internal/src/components/IndexFilters/components/FilterButton/FilterButton.module.css
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,6 @@ | ||
.pressed > button { | ||
/* stylelint-disable polaris/conventions/polaris/custom-property-allowed-list -- Filter section activator pressed state */ | ||
background: var(--pc-button-bg_active); | ||
color: var(--pc-button-color_active); | ||
box-shadow: var(--pc-button-box-shadow_active); | ||
} |
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
1 change: 1 addition & 0 deletions
1
polaris-internal/src/components/IndexFilters/components/FilterButton/index.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 @@ | ||
export {FilterButton} from './FilterButton'; |
108 changes: 108 additions & 0 deletions
108
polaris-internal/src/components/IndexFilters/components/SearchField/SearchField.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,108 @@ | ||
import React, {useId, useState} from 'react'; | ||
import {SearchIcon, ReturnIcon} from '@shopify/polaris-icons'; | ||
|
||
import {Box} from '../../../Box'; | ||
import {Icon} from '../../../Icon'; | ||
import {TextField} from '../../../TextField'; | ||
import {useBreakpoints} from '../../../../utilities/breakpoints'; | ||
import {useI18n} from '../../../../utilities/i18n'; | ||
import {InlineStack} from '../../../InlineStack'; | ||
|
||
export interface SearchFieldProps { | ||
focused?: boolean; | ||
value?: string; | ||
placeholder?: string; | ||
disabled?: boolean; | ||
/** Shows a loading spinner to the right of the input */ | ||
loading?: boolean; | ||
onChange: (value: string) => void; | ||
onFocus?: () => void; | ||
onBlur?: () => void; | ||
onClear?: () => void; | ||
onKeyDownEnter?(): void; | ||
} | ||
|
||
export function SearchField({ | ||
focused: forceFocus = false, | ||
value, | ||
placeholder, | ||
disabled, | ||
loading, | ||
onChange, | ||
onClear, | ||
onFocus, | ||
onBlur, | ||
onKeyDownEnter, | ||
}: SearchFieldProps) { | ||
const id = useId(); | ||
const i18n = useI18n(); | ||
const {mdUp} = useBreakpoints(); | ||
const [focused, setFocused] = useState(forceFocus); | ||
|
||
function handleChange(eventValue: string) { | ||
onChange(eventValue ?? value); | ||
} | ||
|
||
function handleClear() { | ||
if (onClear) { | ||
onClear(); | ||
} else { | ||
onChange(''); | ||
} | ||
} | ||
|
||
function handleKeyDown(event: React.KeyboardEvent) { | ||
if (event.key === 'Enter') onKeyDownEnter?.(); | ||
} | ||
|
||
function handleFocus() { | ||
onFocus?.(); | ||
setFocused(true); | ||
} | ||
|
||
function handleBlur() { | ||
onBlur?.(); | ||
setFocused(false); | ||
} | ||
|
||
const addAsFilterText = | ||
value && focused ? ( | ||
<InlineStack gap="200" blockAlign="center" align="end"> | ||
{i18n.translate('Polaris.IndexFilters.SearchField.action.addAsFilter')} | ||
<Box borderRadius="100" background="bg-surface-tertiary-hover"> | ||
<Icon source={ReturnIcon} tone="inherit" /> | ||
</Box> | ||
</InlineStack> | ||
) : undefined; | ||
|
||
return ( | ||
<div | ||
onKeyDown={handleKeyDown} | ||
style={{width: '100%', minWidth: mdUp ? '22.5rem' : 'none'}} | ||
> | ||
<TextField | ||
id={id} | ||
value={value} | ||
onChange={handleChange} | ||
onFocus={handleFocus} | ||
onBlur={handleBlur} | ||
onClearButtonClick={handleClear} | ||
autoComplete="off" | ||
placeholder={placeholder} | ||
disabled={disabled} | ||
variant="borderless" | ||
size="slim" | ||
prefix={mdUp ? <Icon source={SearchIcon} /> : undefined} | ||
focused={focused} | ||
label={ | ||
placeholder ?? | ||
i18n.translate('Polaris.IndexFilters.SearchField.defaultPlaceholder') | ||
} | ||
labelHidden | ||
clearButton | ||
loading={loading} | ||
suffix={addAsFilterText} | ||
/> | ||
</div> | ||
); | ||
} |
1 change: 1 addition & 0 deletions
1
polaris-internal/src/components/IndexFilters/components/SearchField/index.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 @@ | ||
export {SearchField} from './SearchField'; |
87 changes: 87 additions & 0 deletions
87
...is-internal/src/components/IndexFilters/components/SearchField/tests/SearchField.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,87 @@ | ||
import React from 'react'; | ||
import type {ComponentProps} from 'react'; | ||
import {mountWithApp} from 'tests/utilities'; | ||
|
||
import {SearchField} from '..'; | ||
import {TextField} from '../../../../TextField'; | ||
|
||
jest.mock('../../../../../utilities/breakpoints', () => ({ | ||
...(jest.requireActual('../../../../../utilities/breakpoints') as any), | ||
useBreakpoints: jest.fn(), | ||
})); | ||
|
||
function mockUseBreakpoints(mdUp: boolean) { | ||
const useBreakpoints: jest.Mock = jest.requireMock( | ||
'../../../../../utilities/breakpoints', | ||
).useBreakpoints; | ||
|
||
useBreakpoints.mockReturnValue({ | ||
mdUp, | ||
}); | ||
} | ||
|
||
describe('SearchField', () => { | ||
const defaultProps: ComponentProps<typeof SearchField> = { | ||
onChange: jest.fn(), | ||
value: 'foo', | ||
placeholder: 'bar', | ||
}; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
mockUseBreakpoints(false); | ||
}); | ||
|
||
it('will call onChange when changed', () => { | ||
const props = {...defaultProps}; | ||
const spy = jest.fn(); | ||
const wrapper = mountWithApp(<SearchField {...props} onChange={spy} />, {}); | ||
|
||
wrapper.act(() => { | ||
wrapper.find(TextField)!.trigger('onChange'); | ||
}); | ||
|
||
expect(spy).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('will call onChange correctly when clear button clicked', () => { | ||
const props = {...defaultProps}; | ||
const wrapper = mountWithApp(<SearchField {...props} />, {}); | ||
|
||
wrapper.act(() => { | ||
wrapper.find(TextField)?.trigger('onClearButtonClick'); | ||
}); | ||
|
||
expect(props.onChange).toHaveBeenCalledWith(''); | ||
}); | ||
|
||
it('will call onFocus', () => { | ||
const props = {...defaultProps, onFocus: jest.fn()}; | ||
const wrapper = mountWithApp(<SearchField {...props} />); | ||
|
||
wrapper.act(() => { | ||
wrapper.findAll('input')[0]?.trigger('onFocus'); | ||
}); | ||
|
||
expect(props.onFocus).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('will call onBlur', () => { | ||
const props = {...defaultProps, onBlur: jest.fn()}; | ||
const wrapper = mountWithApp(<SearchField {...props} />); | ||
|
||
wrapper.act(() => { | ||
wrapper.findAll('input')[0]?.trigger('onBlur'); | ||
}); | ||
|
||
expect(props.onBlur).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('will pass the placeholder', () => { | ||
const wrapper = mountWithApp(<SearchField {...defaultProps} />); | ||
|
||
expect(wrapper).toContainReactComponent('input', { | ||
placeholder: defaultProps.placeholder, | ||
}); | ||
}); | ||
}); |
3 changes: 2 additions & 1 deletion
3
polaris-react/.storybook/RenderPerformanceProfiler/RenderPerformanceProfiler.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
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
Oops, something went wrong.