diff --git a/polaris-react/playground/OrdersPage.tsx b/polaris-react/playground/OrdersPage.tsx index 8531fe64675..bd05fde8f76 100644 --- a/polaris-react/playground/OrdersPage.tsx +++ b/polaris-react/playground/OrdersPage.tsx @@ -416,7 +416,6 @@ function OrdersIndexTableWithFilters( const [queryValue, setQueryValue] = useState(''); const [status, setStatus] = useState([]); const [paymentStatus, setPaymentStatus] = useState([]); - const [contains, setContains] = useState(''); const [fulfillmentStatus, setFulfillmentStatus] = useState([]); const [loading, setLoading] = useState(false); const [filteredOrders, setFilteredOrders] = useState(orders); @@ -464,9 +463,15 @@ function OrdersIndexTableWithFilters( ], ); - const {mode, setMode} = useSetIndexFiltersMode( - props?.withFilteringByDefault ? IndexFiltersMode.Filtering : undefined, - ); + const [savedSortSelected, setSavedSortSelected] = useState([ + 'order asc', + 'order asc', + 'order asc', + 'order asc', + 'order asc', + ]); + + const {mode, setMode} = useSetIndexFiltersMode(IndexFiltersMode.Default); const preProcessText = (input: string) => { // Insert a space between numbers and letters if they are adjacent @@ -513,7 +518,6 @@ function OrdersIndexTableWithFilters( }; const handleFilterOrders = (nextFilters: { - contains?: string; queryValue?: string; paymentStatus?: string[]; fulfillmentStatus?: string[]; @@ -525,8 +529,6 @@ function OrdersIndexTableWithFilters( ? nextFilters.queryValue : queryValue; - const nextContains = - nextFilters.contains !== undefined ? nextFilters.contains : contains; const nextStatus = nextFilters.status !== undefined ? nextFilters.status : status; const nextPaymentStatus = @@ -538,16 +540,11 @@ function OrdersIndexTableWithFilters( ? nextFilters.fulfillmentStatus : fulfillmentStatus; - const containsSet = new Set(nextContains); const statusSet = new Set(nextStatus); const paymentStatusSet = new Set(nextPaymentStatus); const fulfillmentStatusSet = new Set(nextFulfillmentStatus); const result = orders.filter((order) => { const matchesQueryValue = hasTextValueMatches(nextQueryValue, order); - const matchesContains = hasTextValueMatches( - preProcessText(nextContains), - order, - ); const matchesStatus = hasArrayValueMatches( new Set([order.status]), @@ -566,7 +563,6 @@ function OrdersIndexTableWithFilters( // if ( // matchesQueryValue && - // matchesContains && // matchesPaymentStatus && // matchesFulfillmentStatus && // matchesStatus @@ -577,7 +573,6 @@ function OrdersIndexTableWithFilters( // nextFilters, // ` // matchesQueryValue: ${matchesQueryValue}, - // matchesContains: ${matchesContains}, // matchesPaymentStatus: ${matchesPaymentStatus}, // matchesFulfillmentStatus: ${matchesFulfillmentStatus}, // matchesStatus: ${matchesStatus} @@ -587,7 +582,6 @@ function OrdersIndexTableWithFilters( setLoading(false); return ( matchesQueryValue && - matchesContains && matchesPaymentStatus && matchesFulfillmentStatus && matchesStatus @@ -621,16 +615,6 @@ function OrdersIndexTableWithFilters( handleFilterOrders({queryValue: ''}); }; - const handleContainsChange = (value: string) => { - setContains(value); - handleFilterOrders({contains: value}); - }; - - const handleContainsRemove = (value: string[]) => { - setContains(''); - handleFilterOrders({contains: ''}); - }; - const handlePaymentStatusChange = (value: string[]) => { setPaymentStatus(value); handleFilterOrders({paymentStatus: value}); @@ -691,13 +675,6 @@ function OrdersIndexTableWithFilters( change: handleQueryValueChange, remove: handleQueryValueRemove, emptyValue: '', - label: 'Search', - }, - contains: { - set: setContains, - change: handleContainsChange, - remove: handleContainsRemove, - emptyValue: '', label: 'Include', }, status: { @@ -724,7 +701,6 @@ function OrdersIndexTableWithFilters( }; const handleChangeFilters = (nextFilterValues: { - contains?: string; queryValue?: string; paymentStatus?: string[]; fulfillmentStatus?: string[]; @@ -742,13 +718,11 @@ function OrdersIndexTableWithFilters( // ---- Applied filter event handlers ---- const handleResetToSavedFilters = (view: number) => { const nextFilters: { - contains: string; queryValue: string; paymentStatus: string[]; fulfillmentStatus: string[]; status: string[]; } = { - contains: '', queryValue: '', paymentStatus: [], fulfillmentStatus: [], @@ -765,12 +739,13 @@ function OrdersIndexTableWithFilters( nextFilters, ); + setSortSelected([savedSortSelected[view]]); + return handleChangeFilters(nextFilters); }; const handleClearFilters = () => { handleChangeFilters({ - contains: '', queryValue: '', paymentStatus: [], fulfillmentStatus: [], @@ -804,19 +779,10 @@ function OrdersIndexTableWithFilters( const filters: FilterInterface[] = [ { - key: 'contains', - label: handlers.contains.label, - filter: ( - - ), + key: 'queryValue', + label: handlers.queryValue.label, + hidden: true, + filter: null, }, { key: 'paymentStatus', @@ -881,7 +847,7 @@ function OrdersIndexTableWithFilters( const appliedFilters: AppliedFilterInterface[] = []; Object.entries({ - contains, + queryValue, status, paymentStatus, fulfillmentStatus, @@ -901,16 +867,6 @@ function OrdersIndexTableWithFilters( }); }); - const appliedFiltersWithQueryValue = [ - ...appliedFilters, - { - key: 'queryValue', - value: queryValue, - label: getHumanReadableValue(handlers.queryValue.label, queryValue), - onRemove: handlers.queryValue.remove, - }, - ]; - const appliedFilterMatchesSavedFilter = ( appliedFilter: AppliedFilterInterface, ) => { @@ -932,11 +888,20 @@ function OrdersIndexTableWithFilters( }; const hasUnsavedChanges = + sortSelected[0] !== savedSortSelected[selectedView] || (!savedViewFilters[selectedView] && appliedFilters.length > 0) || (appliedFilters.length === 0 && savedViewFilters[selectedView]?.length > 0) || !appliedFilters.every(appliedFilterMatchesSavedFilter); + console.log( + hasUnsavedChanges, + savedViewFilters[selectedView], + appliedFilters, + savedSortSelected[selectedView], + sortSelected, + ); + // ---- View event handlers const sleep = (ms: number) => { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -948,7 +913,6 @@ function OrdersIndexTableWithFilters( fulfillmentStatus, paymentStatus, status, - contains, }) .filter(([key, value]) => value !== undefined) .map(([key, value]) => { @@ -1027,19 +991,22 @@ function OrdersIndexTableWithFilters( const handleSaveViewFilters = useCallback( async (index: number, nextFilters?: SavedViewFilter[]) => { const nextSavedFilters = [...savedViewFilters]; + const nextSavedSortSelected = [...savedSortSelected]; + nextSavedSortSelected[index] = sortSelected[0]; nextSavedFilters[index] = nextFilters ? nextFilters - : appliedFiltersWithQueryValue.map(({key, value, label}) => ({ + : appliedFilters.map(({key, value, label}) => ({ key, value, label, })); + setSavedSortSelected(nextSavedSortSelected); setSavedViewFilters(nextSavedFilters); await sleep(300); return true; }, - [appliedFiltersWithQueryValue, savedViewFilters], + [appliedFilters, sortSelected, savedSortSelected, savedViewFilters], ); const handleCreateNewView = async (name: string) => { @@ -1050,12 +1017,12 @@ function OrdersIndexTableWithFilters( (queryValue || paymentStatus.length > 0 || fulfillmentStatus.length > 0 || - status.length > 0); + status.length > 0 || + sortSelected[0] !== savedSortSelected[0]); const nextFilters = shouldSaveAppliedFiltersAsNew - ? {contains, queryValue, status, paymentStatus, fulfillmentStatus} + ? {queryValue, status, paymentStatus, fulfillmentStatus} : { - contains: '', queryValue: '', status: [], paymentStatus: [], @@ -1067,6 +1034,10 @@ function OrdersIndexTableWithFilters( } else { handleClearFilters(); setSavedViewFilters((filters) => [...filters, []]); + setSavedSortSelected((currentSavedSortSelected) => [ + ...currentSavedSortSelected, + 'order asc', + ]); } handleFilterOrders(nextFilters); @@ -1079,7 +1050,7 @@ function OrdersIndexTableWithFilters( setMode(IndexFiltersMode.Default); setViewNames((names) => [...names, name]); setSelectedView(index); - const saved = await handleSaveViewFilters(index); + const saved = await handleSaveViewFilters(index, getFiltersToSave()); await handleSaveViewFilters(0, []); return saved; }; @@ -1139,9 +1110,7 @@ function OrdersIndexTableWithFilters( const primaryAction: IndexFiltersProps['primaryAction'] = { type: selectedView === 0 ? 'save-as' : 'save', onAction: handleSave, - disabled: - (!hasUnsavedChanges && selectedView !== 0) || - (selectedView === 0 && appliedFilters.length === 0), + disabled: !hasUnsavedChanges, loading: false, }; @@ -1176,6 +1145,7 @@ function OrdersIndexTableWithFilters( onClearAll={handleClearFilters} mode={mode} setMode={setMode} + sortUnsaved={sortSelected[0] !== savedSortSelected[selectedView]} /> diff --git a/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx index 5d9b098b3d5..4b846fe29be 100644 --- a/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx +++ b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx @@ -135,16 +135,16 @@ export function FilterPill({ ); - // const unsavedPip = unsavedChanges ? ( - // - // - // - // ) : null; + const unsavedPip = unsavedChanges ? ( + + + + ) : null; const removeFilterButtonMarkup = selected && onRemove !== undefined ? ( @@ -176,7 +176,7 @@ export function FilterPill({ } > - {/* {unsavedPip} */} + {unsavedPip} {labelMarkup} {disclosureMarkup} diff --git a/polaris-react/src/components/IndexFilters/IndexFilters.tsx b/polaris-react/src/components/IndexFilters/IndexFilters.tsx index 4c2b180582c..41d1d80f518 100644 --- a/polaris-react/src/components/IndexFilters/IndexFilters.tsx +++ b/polaris-react/src/components/IndexFilters/IndexFilters.tsx @@ -65,6 +65,10 @@ export interface IndexFiltersProps sortOptions?: SortButtonChoice[]; /** The currently selected sort choice. Required if using sorting */ sortSelected?: string[]; + /** Whether or not the current sort order has been saved. An indicator of unsaved changes renders when sort is unsaved. + * @default false + */ + sortUnsaved?: boolean; /** Optional callback invoked when a merchant changes the sort order. Required if using sorting */ onSort?: (value: string[]) => void; /** Optional callback when using saved views and changing the sort key */ @@ -120,6 +124,7 @@ export function IndexFilters({ onSortKeyChange, onSortDirectionChange, onAddFilterClick, + sortUnsaved, sortOptions, sortSelected, queryValue = '', @@ -275,17 +280,23 @@ export function IndexFilters({ { + setMode(IndexFiltersMode.Filtering); + }} onChange={handleChangeSortButton} onChangeKey={onSortKeyChange} onChangeDirection={onSortDirectionChange} - disabled={disabled} - disclosureZIndexOverride={disclosureZIndexOverride} /> ); }, [ + setMode, handleChangeSortButton, onSortDirectionChange, onSortKeyChange, + sortUnsaved, sortOptions, sortSelected, disabled, @@ -356,6 +367,7 @@ export function IndexFilters({ function handleQueryFocus() { setFiltersFocused(); + setMode(IndexFiltersMode.Filtering); onQueryFocus?.(); } @@ -439,9 +451,9 @@ export function IndexFilters({ disabled={disabled} pressed={mode === IndexFiltersMode.Filtering} disclosureZIndexOverride={disclosureZIndexOverride} - hasAppliedFilters={ - appliedFilters && appliedFilters?.length > 0 - } + // hasAppliedFilters={ + // appliedFilters && appliedFilters?.length > 0 + // } onClick={handleClickFilterButton} /> )} diff --git a/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.module.css b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.module.css new file mode 100644 index 00000000000..c0243248d66 --- /dev/null +++ b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.module.css @@ -0,0 +1,14 @@ +.FilterButton { + position: relative; +} + +.AppliedFilterIndicator { + position: absolute; + top: var(--p-space-300); + right: var(--p-space-300); + background: var(--p-color-bg-fill-emphasis); + border-radius: var(--p-border-radius-full); + border: var(--p-border-width-025) solid var(--p-color-bg-surface); + width: var(--p-width-200); + height: var(--p-height-200); +} diff --git a/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx index fe8188afc35..e852027b5ad 100644 --- a/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx +++ b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx @@ -9,8 +9,10 @@ import {Tooltip} from '../../../Tooltip'; import {Box} from '../../../Box'; import type {SortButtonChoice} from '../../types'; import {Button} from '../../../Button'; +import {classNames} from '../../../../utilities/css'; import {DirectionButton} from './components'; +import styles from './SortButton.module.css'; export enum SortButtonDirection { Asc = 'asc', @@ -22,6 +24,8 @@ export interface SortButtonProps { selected: ChoiceListProps['selected']; disabled?: boolean; disclosureZIndexOverride?: number; + hasUnsavedChanges?: boolean; + onClick(): void; onChange: (selected: string[]) => void; onChangeKey?: (key: string) => void; onChangeDirection?: (direction: string) => void; @@ -32,6 +36,8 @@ export function SortButton({ selected, disabled, disclosureZIndexOverride, + hasUnsavedChanges, + onClick, onChange, onChangeKey, onChangeDirection, @@ -42,6 +48,7 @@ export function SortButton({ const [selectedValueKey, selectedDirection] = selected[0].split(' '); function handleClick() { + if (!active) onClick(); setActive((pastActive) => !pastActive); } @@ -96,6 +103,12 @@ export function SortButton({ return currentKey === selectedValueKey; }); + const className = classNames(styles.SortButton); + + const unsavedChangeIndicator = hasUnsavedChanges ? ( +
+ ) : null; + const sortButton = ( -
); diff --git a/polaris-react/src/components/IndexFilters/components/SortButton/tests/SortButton.test.tsx b/polaris-react/src/components/IndexFilters/components/SortButton/tests/SortButton.test.tsx index 5b43661e216..0007b54e076 100644 --- a/polaris-react/src/components/IndexFilters/components/SortButton/tests/SortButton.test.tsx +++ b/polaris-react/src/components/IndexFilters/components/SortButton/tests/SortButton.test.tsx @@ -58,6 +58,7 @@ describe('SortButton', () => { it('shows the popover on click and hides it on click again', () => { const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number asc'], }; @@ -84,6 +85,7 @@ describe('SortButton', () => { const disclosureZIndexOverride = 517; const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number asc'], disclosureZIndexOverride, @@ -99,6 +101,7 @@ describe('SortButton', () => { const disclosureZIndexOverride = 517; const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number asc'], disclosureZIndexOverride, @@ -113,6 +116,7 @@ describe('SortButton', () => { it('fires the onChange handler when the ChoiceList changes', () => { const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number asc'], }; @@ -132,6 +136,7 @@ describe('SortButton', () => { it('selects ascending', () => { const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number asc'], }; @@ -150,6 +155,7 @@ describe('SortButton', () => { it('selects descending', () => { const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number desc'], }; @@ -168,6 +174,7 @@ describe('SortButton', () => { it('invokes onChange when clicking the ascending button', () => { const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number desc'], }; @@ -190,6 +197,7 @@ describe('SortButton', () => { it('invokes onChange when clicking the descending button', () => { const props: SortButtonProps = { onChange: jest.fn(), + onClick: jest.fn(), choices, selected: ['order-number asc'], };