From 77528515f82716b92e1c6515c2b68083faedc190 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Thu, 26 Sep 2024 23:35:22 -0400 Subject: [PATCH 1/4] [IndexFilters] Prototype search beside filters on one line --- polaris-react/playground/OrdersPage.tsx | 51 ++--- .../src/components/Filters/Filters.module.css | 100 ++++++--- .../src/components/Filters/Filters.tsx | 118 ++--------- .../FilterPill/FilterPill.module.css | 46 ++-- .../components/FilterPill/FilterPill.tsx | 5 +- .../components/FiltersBar/FiltersBar.tsx | 163 +++++++-------- .../components/SearchField/SearchField.tsx | 104 ++++----- .../SearchField/tests/SearchField.test.tsx | 10 +- .../IndexFilters/IndexFilters.module.css | 6 +- .../components/IndexFilters/IndexFilters.tsx | 197 +++--------------- .../components/SearchField/SearchField.tsx | 11 +- .../SortButton/SortButton.module.css | 2 +- .../components/SortButton/SortButton.tsx | 4 - .../SortButton/tests/SortButton.test.tsx | 8 - .../UpdateButtons/UpdateButtons.tsx | 115 +++++----- .../useIsSticky/tests/useIsSticky.test.tsx | 3 +- .../hooks/useIsSticky/useIsSticky.ts | 9 +- .../IndexFilters/tests/IndexFilters.test.tsx | 69 ------ .../src/components/Tabs/Tabs.module.css | 1 - .../components/TextField/TextField.module.css | 42 +++- .../src/components/TextField/TextField.tsx | 27 +-- polaris-tokens/src/themes/base/color.ts | 2 +- .../index-filters-with-no-filters.tsx | 1 - 23 files changed, 416 insertions(+), 678 deletions(-) diff --git a/polaris-react/playground/OrdersPage.tsx b/polaris-react/playground/OrdersPage.tsx index 9c843ba3065..9b119d252ed 100644 --- a/polaris-react/playground/OrdersPage.tsx +++ b/polaris-react/playground/OrdersPage.tsx @@ -465,7 +465,7 @@ function OrdersIndexTableWithFilters( 'order desc', ]); - const {mode, setMode} = useSetIndexFiltersMode(IndexFiltersMode.Default); + const {mode, setMode} = useSetIndexFiltersMode(); const preProcessText = (input: string) => { // Insert a space between numbers and letters if they are adjacent @@ -555,24 +555,6 @@ function OrdersIndexTableWithFilters( fulfillmentStatusSet, ); - // if ( - // matchesQueryValue && - // matchesPaymentStatus && - // matchesFulfillmentStatus && - // matchesStatus - // ) { - // console.log( - // ` - // nextFilters: `, - // nextFilters, - // ` - // matchesQueryValue: ${matchesQueryValue}, - // matchesPaymentStatus: ${matchesPaymentStatus}, - // matchesFulfillmentStatus: ${matchesFulfillmentStatus}, - // matchesStatus: ${matchesStatus} - // `, - // ); - // } setLoading(false); return ( matchesQueryValue && @@ -764,12 +746,6 @@ function OrdersIndexTableWithFilters( // ---- Filters const filters: FilterInterface[] = [ - { - key: 'queryValue', - label: handlers.queryValue.label, - hidden: true, - filter: null, - }, { key: 'paymentStatus', value: paymentStatus, @@ -833,7 +809,6 @@ function OrdersIndexTableWithFilters( const appliedFilters: AppliedFilterInterface[] = []; Object.entries({ - queryValue, status, paymentStatus, fulfillmentStatus, @@ -873,16 +848,32 @@ function OrdersIndexTableWithFilters( } }; + const appliedFiltersWithQuery = [ + ...appliedFilters, + { + key: 'queryValue', + value: queryValue, + unsavedChanges: + queryValue && selectedView === 0 + ? true + : queryValue && + queryValue !== + savedViewFilters[selectedView]?.find( + ({key}) => key === 'queryValue', + )?.value, + }, + ]; + const hasUnsavedSortChange = (selectedView === 0 && sortSelected[0] !== 'order desc') || sortSelected[0] !== savedSortSelected[selectedView]; const hasUnsavedFilterChange = - (selectedView === 0 && appliedFilters.length > 0) || + (selectedView === 0 && appliedFiltersWithQuery.length > 0) || (!savedViewFilters[selectedView] && appliedFilters.length > 0) || - (appliedFilters.length === 0 && + (appliedFiltersWithQuery.length === 0 && savedViewFilters[selectedView]?.length > 0) || - !appliedFilters.every(appliedFilterMatchesSavedFilter); + !appliedFiltersWithQuery.every(appliedFilterMatchesSavedFilter); const hasUnsavedChanges = hasUnsavedSortChange || hasUnsavedFilterChange; @@ -914,7 +905,6 @@ function OrdersIndexTableWithFilters( } setQueryValue(''); - setMode(IndexFiltersMode.Default); setSelectedView(view); setLoading(true); handleResetToSavedFilters(view); @@ -1032,7 +1022,6 @@ function OrdersIndexTableWithFilters( }; const handleSaveViewAs = async (index: number, name: string) => { - setMode(IndexFiltersMode.Default); setViewNames((names) => [...names, name]); setSelectedView(index); const nextFilters = getFiltersToSave(); diff --git a/polaris-react/src/components/Filters/Filters.module.css b/polaris-react/src/components/Filters/Filters.module.css index 5b149b96482..a014a2f69cd 100644 --- a/polaris-react/src/components/Filters/Filters.module.css +++ b/polaris-react/src/components/Filters/Filters.module.css @@ -19,50 +19,81 @@ flex: 1; } -.FiltersWrapper { - border-bottom: var(--p-border-width-025) solid var(--p-color-border-secondary); - height: 53px; - overflow: hidden; +.FiltersInner { + max-width: 90%; + display: flex; + justify-content: flex-start; + align-items: center; + flex-wrap: nowrap; + white-space: nowrap; + padding-inline: var(--p-space-100); - @media (--p-breakpoints-sm-down) { - background: var(--p-color-bg-surface); + > :first-child { + padding-left: var(--p-space-100); } +} - @media (--p-breakpoints-md-up) { - height: auto; - overflow: visible; +.hideQueryField .FiltersInner { + flex: 1; + padding: var(--p-space-300); +} + +.SearchFieldWrapper { + position: relative; + padding: var(--p-space-150) var(--p-space-100) var(--p-space-150) 0; + + &::after { + content: ''; + z-index: var(--p-z-index-1); + position: absolute; + top: 0; + right: calc(-1 * var(--p-space-300)); + width: var(--p-width-200); + height: 100%; + pointer-events: none; + /* stylelint-disable-next-line polaris/color/function-disallowed-list -- Overflow shadow for applied filters */ + background: linear-gradient( + to left, + rgba(255, 255, 255, 0), + var(--p-color-bg-surface) + ); } } -.hideQueryField .FiltersWrapper { +.FilterActionWrapper { + position: relative; display: flex; align-items: center; -} + gap: var(--p-space-100); -.FiltersInner { - overflow: auto; - white-space: nowrap; - padding: var(--p-space-300) var(--p-space-200) var(--p-space-500); + &::before { + content: ''; + z-index: var(--p-z-index-1); + position: absolute; + top: 0; + left: calc(-1 * var(--p-space-200)); + width: var(--p-width-200); + height: 100%; + pointer-events: none; + /* stylelint-disable-next-line polaris/color/function-disallowed-list -- Overflow shadow for applied filters */ + background: linear-gradient( + to right, + rgba(255, 255, 255, 0), + var(--p-color-bg-surface) + ); + } } -.hideQueryField .FiltersInner { +.AppliedFilters { flex: 1; - padding: var(--p-space-300); -} - -@media (--p-breakpoints-md-up) { - .FiltersInner { - overflow: visible; - flex-wrap: wrap; - gap: var(--p-space-200); - /* stylelint-disable-next-line -- No 6px space token */ - padding: 0.375rem var(--p-space-200); - } + display: flex; + align-items: center; + gap: var(--p-space-100); + flex-wrap: nowrap; + min-height: 40px; - .hideQueryField .FiltersInner { - flex: 1; - /* stylelint-disable-next-line -- No 6px space token */ - padding: 0.375rem var(--p-space-200); + > :last-child { + margin-right: var(--p-space-100); } } @@ -70,7 +101,8 @@ background: var(--p-color-bg-surface); border-radius: var(--p-border-radius-200); border: var(--p-color-border) dashed var(--p-border-width-025); - padding: 0 var(--p-space-200) 0 var(--p-space-300); + padding: 0 var(--p-space-300) 0 var(--p-space-200); + margin-left: var(--p-space-100); height: 28px; cursor: pointer; display: flex; @@ -86,8 +118,7 @@ @media (--p-breakpoints-md-up) { height: 24px; - /* stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY */ - padding: 0 0.375rem 0 var(--p-space-200); + padding: 0 var(--p-space-200) 0 var(--p-space-100); } &:hover, @@ -201,6 +232,7 @@ .FiltersStickyArea { position: relative; + width: 100%; display: flex; gap: var(--p-space-100); flex-wrap: nowrap; diff --git a/polaris-react/src/components/Filters/Filters.tsx b/polaris-react/src/components/Filters/Filters.tsx index b540eec3268..257a55c977e 100644 --- a/polaris-react/src/components/Filters/Filters.tsx +++ b/polaris-react/src/components/Filters/Filters.tsx @@ -1,60 +1,12 @@ import React from 'react'; import type {ReactNode} from 'react'; -import type {TransitionStatus} from 'react-transition-group'; import {classNames} from '../../utilities/css'; import type {AppliedFilterInterface, FilterInterface} from '../../types'; -import {InlineStack} from '../InlineStack'; -import {Box} from '../Box'; import {FiltersBar, SearchField} from './components'; import styles from './Filters.module.css'; -const TRANSITION_DURATION = 'var(--p-motion-duration-150)'; -const TRANSITION_MARGIN = '-36px'; - -const defaultStyle = { - transition: `opacity ${TRANSITION_DURATION} var(--p-motion-ease)`, - opacity: 0, -}; - -const transitionStyles = { - entering: {opacity: 1}, - entered: {opacity: 1}, - exiting: {opacity: 0}, - exited: {opacity: 0}, - unmounted: {opacity: 0}, -}; - -const defaultFilterStyles = { - transition: `opacity ${TRANSITION_DURATION} var(--p-motion-ease), margin ${TRANSITION_DURATION} var(--p-motion-ease)`, - opacity: 0, - marginTop: TRANSITION_MARGIN, -}; - -const transitionFilterStyles = { - entering: { - opacity: 1, - marginTop: 0, - }, - entered: { - opacity: 1, - marginTop: 0, - }, - exiting: { - opacity: 0, - marginTop: TRANSITION_MARGIN, - }, - exited: { - opacity: 0, - marginTop: TRANSITION_MARGIN, - }, - unmounted: { - opacity: 0, - marginTop: TRANSITION_MARGIN, - }, -}; - export interface FiltersProps { /** Currently entered text in the query field */ queryValue?: string; @@ -88,17 +40,12 @@ export interface FiltersProps { disableQueryField?: boolean; /** Disable the filters */ disableFilters?: boolean; - /** Whether the text field should be borderless. Should be true when used as part of the IndexFilters component. */ - borderlessQueryField?: boolean; /** Whether an asyncronous task is currently being run. */ loading?: boolean; - mountedState?: TransitionStatus; /** Callback when the add filter button is clicked. */ onAddFilterClick?: () => void; /** Whether the filter should close when clicking inside another Popover. */ closeOnChildOverlayClick?: boolean; - /** @deprecated The name of the currently selected view */ - selectedViewName?: string; } export function Filters({ @@ -117,76 +64,36 @@ export function Filters({ hideFilters, hideQueryField, disableQueryField, - borderlessQueryField, loading, disableFilters, - mountedState, onAddFilterClick, closeOnChildOverlayClick, - selectedViewName, }: FiltersProps) { const hideFilterBar = hideFilters || filters.length === 0; const queryFieldMarkup = hideQueryField ? null : ( -
- - -
- -
- {children} -
-
-
+ ); - const mountedStateStyles = - mountedState && !hideQueryField - ? { - ...defaultFilterStyles, - ...transitionFilterStyles[mountedState], - } - : undefined; - const filtersMarkup = hideFilterBar ? null : ( {children} @@ -199,7 +106,6 @@ export function Filters({ hideQueryField && styles.hideQueryField, )} > - {queryFieldMarkup} {filtersMarkup} ); diff --git a/polaris-react/src/components/Filters/components/FilterPill/FilterPill.module.css b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.module.css index 61a6666e695..9db64fce836 100644 --- a/polaris-react/src/components/Filters/components/FilterPill/FilterPill.module.css +++ b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.module.css @@ -18,10 +18,6 @@ &:active { background: var(--p-color-bg-surface-hover); border-color: var(--p-color-border); - - path { - fill: var(--p-color-icon-hover); - } } &:hover, @@ -88,6 +84,7 @@ padding-right: 0; } + /* stylelint-disable-next-line selector-max-class -- Custom styles necessary */ &.locked { padding-right: calc(var(--p-space-050) + var(--p-space-300)); @@ -99,22 +96,47 @@ .clearButton { /* stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY */ - @mixin focus-ring base, 0, base; - margin-right: var(--p-space-200); + @mixin unstyled-button; + display: block; + flex-shrink: 0; + height: 20px; + width: 20px; + margin: var(--p-space-025); + margin-left: var(--p-space-050); + /* stylelint-disable-next-line polaris/border/declaration-property-unit-disallowed-list -- override border-radius */ + border-radius: 7px; - @media (--p-breakpoints-md-up) { - margin-right: var(--p-space-100); + svg { + fill: var(--p-color-icon-secondary); + } + + &:hover { + background: var(--p-color-bg-fill-transparent-hover); + outline: var(--p-border-width-025) solid transparent; + + svg { + fill: var(--p-color-icon-hover); + } + } + + /* stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY */ + @mixin focus-ring wide; + + &:focus-visible { + background: var(--p-color-bg-fill-transparent-hover); + + svg { + fill: var(--p-color-icon-hover); + } } &:focus-visible:not(:active) { /* stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY */ @mixin focus-ring base, 0, focused; } -} -.IconWrapper { - @media (--p-breakpoints-md-up) { - scale: 0.8; + &:active { + background: var(--p-color-bg-fill-transparent-active); } } diff --git a/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx index 4b846fe29be..af27d70797a 100644 --- a/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx +++ b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx @@ -108,10 +108,7 @@ export function FilterPill({ focused && styles.focusedFilterButton, ); - const clearButtonClassNames = classNames( - styles.PlainButton, - styles.clearButton, - ); + const clearButtonClassNames = classNames(styles.clearButton); const toggleButtonClassNames = classNames( styles.PlainButton, diff --git a/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx b/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx index 89483e1d7dd..3cffb963207 100644 --- a/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx +++ b/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx @@ -1,4 +1,4 @@ -import type {PropsWithChildren} from 'react'; +import type {PropsWithChildren, ReactNode} from 'react'; import React, {useState, useRef, useEffect} from 'react'; import {PlusIcon} from '@shopify/polaris-icons'; import type {TransitionStatus} from 'react-transition-group'; @@ -9,23 +9,22 @@ import {Popover} from '../../../Popover'; import {ActionList} from '../../../ActionList'; import {Text} from '../../../Text'; import {UnstyledButton} from '../../../UnstyledButton'; -import {classNames} from '../../../../utilities/css'; import type { ActionListItemDescriptor, AppliedFilterInterface, FilterInterface, } from '../../../../types'; import {InlineStack} from '../../../InlineStack'; +import {InlineGrid} from '../../../InlineGrid'; import {Box} from '../../../Box'; import {Button} from '../../../Button'; import {FilterPill} from '../FilterPill'; import styles from '../../Filters.module.css'; +import {Scrollable} from '../../../Scrollable'; export interface FiltersBarProps { - /** Currently entered text in the query field */ - queryValue?: string; - /** Placeholder text for the query field. */ - queryPlaceholder?: string; + /** The query field markup to render left of the filters */ + queryField?: ReactNode; /** Whether the query field is focused. */ focused?: boolean; /** Available filters added to the filter bar. Shortcut filters are pinned to the front of the bar. */ @@ -45,7 +44,6 @@ export interface FiltersBarProps { onAddFilterClick?: () => void; /** Whether the filter should close when clicking inside another Popover. */ closeOnChildOverlayClick?: boolean; - mountedStateStyles?: any; } export function FiltersBar({ @@ -53,9 +51,8 @@ export function FiltersBar({ appliedFilters, onClearAll, disabled, - hideQueryField, + queryField, disableFilters, - mountedStateStyles, onAddFilterClick, closeOnChildOverlayClick, children, @@ -161,8 +158,6 @@ export function FiltersBar({ }[], ); - const hasOneOrMorePinnedFilters = pinnedFilters.length >= 1; - const addFilterActivator = (
+ {i18n.translate('Polaris.Filters.addFilter')}{' '} -
); @@ -192,48 +187,53 @@ export function FiltersBar({ filters.some((filter) => !filter.pinned) || filters.length !== localPinnedFilters.length; - const pinnedFiltersMarkup = pinnedFilters.map( - ({key: filterKey, ...pinnedFilter}) => { - const appliedFilter = appliedFilters?.find(({key}) => key === filterKey); - const handleFilterPillRemove = () => { - setLocalPinnedFilters((currentLocalPinnedFilters) => - currentLocalPinnedFilters.filter((key) => { - const isMatchedFilters = key === filterKey; - const isPinnedFilterFromProps = pinnedFromPropsKeys.includes(key); - return !isMatchedFilters || isPinnedFilterFromProps; - }), + const pinnedFiltersMarkup = ( + + {pinnedFilters.map(({key: filterKey, ...pinnedFilter}) => { + const appliedFilter = appliedFilters?.find( + ({key}) => key === filterKey, ); - appliedFilter?.onRemove?.(filterKey); - }; + const handleFilterPillRemove = () => { + setLocalPinnedFilters((currentLocalPinnedFilters) => + currentLocalPinnedFilters.filter((key) => { + const isMatchedFilters = key === filterKey; + const isPinnedFilterFromProps = pinnedFromPropsKeys.includes(key); + return !isMatchedFilters || isPinnedFilterFromProps; + }), + ); + appliedFilter?.onRemove?.(filterKey); + }; - return ( - - ); - }, + return ( + + ); + })} + ); const addButton = shouldShowAddButton ? ( -
+
+
+ + + ) : null; const handleNumberChange = useCallback( diff --git a/polaris-tokens/src/themes/base/color.ts b/polaris-tokens/src/themes/base/color.ts index 16ffb3707ac..a21d52e8f2c 100644 --- a/polaris-tokens/src/themes/base/color.ts +++ b/polaris-tokens/src/themes/base/color.ts @@ -945,7 +945,7 @@ export const color: { 'The border color on any element. Pair with bg-surface-tertiary or bg-fill-tertiary.', }, 'color-border-focus': { - value: colors.blue[13], + value: colors.gray[16], description: 'The focus ring for any interactive element in a focused state.', }, diff --git a/polaris.shopify.com/pages/examples/index-filters-with-no-filters.tsx b/polaris.shopify.com/pages/examples/index-filters-with-no-filters.tsx index ebb61769c0f..627b93a3d56 100644 --- a/polaris.shopify.com/pages/examples/index-filters-with-no-filters.tsx +++ b/polaris.shopify.com/pages/examples/index-filters-with-no-filters.tsx @@ -234,7 +234,6 @@ function IndexFiltersWithNoFiltersExample() { mode={mode} setMode={setMode} hideFilters - filteringAccessibilityTooltip="Search (F)" /> Date: Mon, 30 Sep 2024 16:00:36 -0400 Subject: [PATCH 2/4] IFS Proto Side by Side: Includes filter separate --- polaris-react/playground/OrdersPage.tsx | 142 +++++++++++++----- .../components/FiltersBar/FiltersBar.tsx | 29 +--- .../components/SearchField/SearchField.tsx | 2 +- .../IndexFilters/IndexFilters.module.css | 22 +++ .../components/IndexFilters/IndexFilters.tsx | 20 +-- .../components/SearchField/SearchField.tsx | 2 +- .../components/TextField/TextField.module.css | 6 +- 7 files changed, 146 insertions(+), 77 deletions(-) diff --git a/polaris-react/playground/OrdersPage.tsx b/polaris-react/playground/OrdersPage.tsx index 9b119d252ed..60b416365be 100644 --- a/polaris-react/playground/OrdersPage.tsx +++ b/polaris-react/playground/OrdersPage.tsx @@ -45,6 +45,7 @@ import { IndexFiltersMode, Badge, Text, + TextField, } from '../src'; import {orders} from './orders'; @@ -408,11 +409,20 @@ function OrdersIndexTableWithFilters( const [selectedView, setSelectedView] = useState(0); const [sortSelected, setSortSelected] = useState(['order desc']); const [queryValue, setQueryValue] = useState(''); + const [contains, setContains] = useState(''); const [status, setStatus] = useState([]); const [paymentStatus, setPaymentStatus] = useState([]); const [fulfillmentStatus, setFulfillmentStatus] = useState([]); const [loading, setLoading] = useState(false); const [filteredOrders, setFilteredOrders] = useState(orders); + const [tempPersistedSearch, setTempPersistedSearch] = useState([ + '', + '', + '', + '', + '', + ]); + const [savedViewFilters, setSavedViewFilters] = useState( [ [], @@ -512,6 +522,7 @@ function OrdersIndexTableWithFilters( }; const handleFilterOrders = (nextFilters: { + contains?: string; queryValue?: string; paymentStatus?: string[]; fulfillmentStatus?: string[]; @@ -523,6 +534,8 @@ function OrdersIndexTableWithFilters( ? nextFilters.queryValue : queryValue; + const nextContains = + nextFilters.contains !== undefined ? nextFilters.contains : contains; const nextStatus = nextFilters.status !== undefined ? nextFilters.status : status; const nextPaymentStatus = @@ -534,11 +547,16 @@ 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]), @@ -558,6 +576,7 @@ function OrdersIndexTableWithFilters( setLoading(false); return ( matchesQueryValue && + matchesContains && matchesPaymentStatus && matchesFulfillmentStatus && matchesStatus @@ -580,6 +599,16 @@ 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}); @@ -616,6 +645,13 @@ function OrdersIndexTableWithFilters( change: handleQueryValueChange, remove: handleQueryValueRemove, emptyValue: '', + label: 'Search', + }, + contains: { + set: setContains, + change: handleContainsChange, + remove: handleContainsRemove, + emptyValue: '', label: 'Include', }, status: { @@ -668,6 +704,7 @@ function OrdersIndexTableWithFilters( }; const handleChangeFilters = (nextFilterValues: { + contains?: string; queryValue?: string; paymentStatus?: string[]; fulfillmentStatus?: string[]; @@ -684,11 +721,13 @@ function OrdersIndexTableWithFilters( const handleResetToSavedFilters = (view: number) => { const nextFilters: { + contains: string; queryValue: string; paymentStatus: string[]; fulfillmentStatus: string[]; status: string[]; } = { + contains: '', queryValue: '', paymentStatus: [], fulfillmentStatus: [], @@ -713,6 +752,7 @@ function OrdersIndexTableWithFilters( const handleClearFilters = () => { handleChangeFilters({ + contains: '', queryValue: '', paymentStatus: [], fulfillmentStatus: [], @@ -746,6 +786,21 @@ function OrdersIndexTableWithFilters( // ---- Filters const filters: FilterInterface[] = [ + { + key: 'contains', + label: handlers.contains.label, + filter: ( + + ), + }, { key: 'paymentStatus', value: paymentStatus, @@ -809,11 +864,14 @@ function OrdersIndexTableWithFilters( const appliedFilters: AppliedFilterInterface[] = []; Object.entries({ + contains, status, paymentStatus, fulfillmentStatus, }).forEach(([key, value]) => { - if (isEmpty(value)) return; + if (isEmpty(value)) { + return; + } const savedValue = savedViewFilters[selectedView]?.find( (filter) => filter.key === key, @@ -824,14 +882,18 @@ function OrdersIndexTableWithFilters( value, label: getHumanReadableValue(handlers[key].label, value), unsavedChanges: selectedView === 0 ? true : isUnsaved(value, savedValue), - onRemove: key === 'queryValue' ? undefined : handlers[key].remove, + onRemove: handlers[key].remove, }); }); + const savedViewFiltersWithoutQuery = savedViewFilters[selectedView]?.filter( + ({key}) => key !== 'queryValue', + ); + const appliedFilterMatchesSavedFilter = ( appliedFilter: AppliedFilterInterface, ) => { - const savedFilter = savedViewFilters[selectedView].find( + const savedFilter = savedViewFiltersWithoutQuery?.find( (savedFilter) => savedFilter.key === appliedFilter.key, ); @@ -839,41 +901,36 @@ function OrdersIndexTableWithFilters( return false; } else if (typeof appliedFilter.value === 'string') { return appliedFilter.value === savedFilter.value; - } else { + } else if (Array.isArray(appliedFilter.value)) { const hasSameArrayValue = new Set(savedFilter.value).difference(new Set(appliedFilter.value)) .size === 0; - return hasSameArrayValue; + } else { + return true; } }; - const appliedFiltersWithQuery = [ - ...appliedFilters, - { - key: 'queryValue', - value: queryValue, - unsavedChanges: - queryValue && selectedView === 0 - ? true - : queryValue && - queryValue !== - savedViewFilters[selectedView]?.find( - ({key}) => key === 'queryValue', - )?.value, - }, - ]; - const hasUnsavedSortChange = (selectedView === 0 && sortSelected[0] !== 'order desc') || - sortSelected[0] !== savedSortSelected[selectedView]; + (savedSortSelected[selectedView] !== undefined && + savedSortSelected[selectedView] !== sortSelected[0]); + + const isAllViewAndFiltersAreApplied = + selectedView === 0 && appliedFilters.length > 0; + + const appliedFilterCountDoesNotEqualSavedFilterCount = + appliedFilters.length !== savedViewFiltersWithoutQuery?.length; + + const appliedFiltersDoNotMatchSavedFilters = !appliedFilters.every( + appliedFilterMatchesSavedFilter, + ); const hasUnsavedFilterChange = - (selectedView === 0 && appliedFiltersWithQuery.length > 0) || - (!savedViewFilters[selectedView] && appliedFilters.length > 0) || - (appliedFiltersWithQuery.length === 0 && - savedViewFilters[selectedView]?.length > 0) || - !appliedFiltersWithQuery.every(appliedFilterMatchesSavedFilter); + isAllViewAndFiltersAreApplied || + (selectedView > 0 && + (appliedFilterCountDoesNotEqualSavedFilterCount || + appliedFiltersDoNotMatchSavedFilters)); const hasUnsavedChanges = hasUnsavedSortChange || hasUnsavedFilterChange; @@ -885,11 +942,12 @@ function OrdersIndexTableWithFilters( const getFiltersToSave = () => { return Object.entries({ queryValue, - fulfillmentStatus, - paymentStatus, + contains, status, + paymentStatus, + fulfillmentStatus, }) - .filter(([, value]) => value !== undefined) + .filter(([, value]) => !isEmpty(value)) .map(([key, value]) => { return {key, value, label: handlers[key].label}; }); @@ -900,7 +958,7 @@ function OrdersIndexTableWithFilters( const previousSortSelected = sortSelected[0]; let nextFilters; - if (previousView === 0 && hasUnsavedChanges) { + if ((previousView === 0 && hasUnsavedChanges) || queryValue) { nextFilters = getFiltersToSave(); } @@ -920,8 +978,10 @@ function OrdersIndexTableWithFilters( const handleDeleteView = (index: number) => async () => { const nextViewNames = [...viewNames]; + const nextTempPersistedSearch = [...tempPersistedSearch]; const nextSavedViewFilters = [...savedViewFilters]; nextViewNames.splice(index, 1); + nextTempPersistedSearch.splice(index, 1); nextSavedViewFilters.splice(index, 1); setSavedViewFilters(nextSavedViewFilters); setViewNames(nextViewNames); @@ -945,6 +1005,7 @@ function OrdersIndexTableWithFilters( setSavedViewFilters((filters) => [...filters, duplicateViewFilters]); const nextAppliedFilters = { queryValue: '', + contains: '', status: [], paymentStatus: [], fulfillmentStatus: [], @@ -953,7 +1014,12 @@ function OrdersIndexTableWithFilters( duplicateViewFilters.forEach(({key, value}) => { nextAppliedFilters[key] = value; }); + setViewNames((names) => [...names, name]); + setSavedSortSelected((currentSavedSortSelected) => [ + ...currentSavedSortSelected, + 'order desc', + ]); await sleep(250); setSelectedView(duplicateViewIndex); setLoading(false); @@ -961,16 +1027,16 @@ function OrdersIndexTableWithFilters( }; const handleSaveViewFilters = async ( - index: number, + view: number, nextFilters?: SavedViewFilter[], nextSortSelected?: string, ) => { const nextSavedFilters = [...savedViewFilters]; const nextSavedSortSelected = [...savedSortSelected]; - nextSavedSortSelected[index] = nextSortSelected + nextSavedSortSelected[view] = nextSortSelected ? nextSortSelected : sortSelected[0]; - nextSavedFilters[index] = nextFilters + nextSavedFilters[view] = nextFilters ? nextFilters : appliedFilters.map(({key, value, label}) => ({ key, @@ -996,8 +1062,9 @@ function OrdersIndexTableWithFilters( sortSelected[0] !== savedSortSelected[0]); const nextFilters = shouldSaveAppliedFiltersAsNew - ? {queryValue, status, paymentStatus, fulfillmentStatus} + ? getFiltersToSave() : { + contains: '', queryValue: '', status: [], paymentStatus: [], @@ -1005,7 +1072,7 @@ function OrdersIndexTableWithFilters( }; if (shouldSaveAppliedFiltersAsNew) { - handleSaveViewFilters(newViewIndex); + handleSaveViewFilters(newViewIndex, nextFilters); } else { handleClearFilters(); setSavedViewFilters((filters) => [...filters, []]); @@ -1024,7 +1091,7 @@ function OrdersIndexTableWithFilters( const handleSaveViewAs = async (index: number, name: string) => { setViewNames((names) => [...names, name]); setSelectedView(index); - const nextFilters = getFiltersToSave(); + const nextFilters = getFiltersToSave(true); const nextSortSelected = sortSelected[0]; await handleSaveViewFilters(0, []); return handleSaveViewFilters(index, nextFilters, nextSortSelected); @@ -1097,6 +1164,7 @@ function OrdersIndexTableWithFilters( const cancelAction: IndexFiltersProps['cancelAction'] = { onAction: handleCancel, + disabled: !hasUnsavedChanges, }; const queryPlaceholder = `Search ${viewNames[selectedView]?.toLowerCase()}`; diff --git a/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx b/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx index 3cffb963207..3732a9808fa 100644 --- a/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx +++ b/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx @@ -17,7 +17,6 @@ import type { import {InlineStack} from '../../../InlineStack'; import {InlineGrid} from '../../../InlineGrid'; import {Box} from '../../../Box'; -import {Button} from '../../../Button'; import {FilterPill} from '../FilterPill'; import styles from '../../Filters.module.css'; import {Scrollable} from '../../../Scrollable'; @@ -49,7 +48,6 @@ export interface FiltersBarProps { export function FiltersBar({ filters, appliedFilters, - onClearAll, disabled, queryField, disableFilters, @@ -179,13 +177,7 @@ export function FiltersBar({
); - const handleClearAllFilters = () => { - setLocalPinnedFilters(pinnedFromPropsKeys); - onClearAll?.(); - }; - const shouldShowAddButton = - filters.some((filter) => !filter.pinned) || - filters.length !== localPinnedFilters.length; + const shouldShowAddButton = filters.length !== localPinnedFilters?.length; const pinnedFiltersMarkup = ( ) : null; - const clearAllMarkup = appliedFilters?.length ? ( -
- -
- ) : null; - const filterMarkup = (
{pinnedFiltersMarkup} -
- {addButton} - {clearAllMarkup} -
+
{addButton}
); return ( diff --git a/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx b/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx index 5469662544f..3a5ad2479a2 100644 --- a/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx +++ b/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx @@ -60,7 +60,7 @@ export function SearchField({ } return ( - + { - return primaryAction + return primaryAction && !primaryAction.disabled ? { ...primaryAction, onAction: onExecutedPrimaryAction, @@ -167,7 +167,7 @@ export function IndexFilters({ }, [onExecutedPrimaryAction, primaryAction]); const enhancedCancelAction = useMemo(() => { - return cancelAction + return cancelAction && !cancelAction.disabled ? { ...cancelAction, onAction: onExecutedCancelAction, @@ -186,12 +186,14 @@ export function IndexFilters({ const updateButtonsMarkup = useMemo( () => enhancedCancelAction || enhancedPrimaryAction ? ( - +
+ +
) : null, [enhancedPrimaryAction, enhancedCancelAction, disabled, viewNames], ); @@ -336,7 +338,7 @@ export function IndexFilters({ loading={loading || isActionLoading} closeOnChildOverlayClick={closeOnChildOverlayClick} > -
{updateButtonsMarkup}
+ {updateButtonsMarkup}
diff --git a/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx b/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx index 0c79a97635e..63e23ea65a3 100644 --- a/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx +++ b/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx @@ -60,7 +60,7 @@ export function SearchField({ } return ( - + Date: Tue, 1 Oct 2024 09:59:11 -0400 Subject: [PATCH 3/4] IFS Proto Side by Side: Includes filter separate above --- .../src/components/Filters/Filters.module.css | 3 +- .../src/components/Filters/Filters.tsx | 12 ++----- .../components/FiltersBar/FiltersBar.tsx | 31 ++++++++++++++----- .../components/IndexFilters/IndexFilters.tsx | 12 +++++-- .../src/components/InlineGrid/InlineGrid.tsx | 11 ++++++- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/polaris-react/src/components/Filters/Filters.module.css b/polaris-react/src/components/Filters/Filters.module.css index a014a2f69cd..c31c1878e99 100644 --- a/polaris-react/src/components/Filters/Filters.module.css +++ b/polaris-react/src/components/Filters/Filters.module.css @@ -20,7 +20,7 @@ } .FiltersInner { - max-width: 90%; + /* max-width: 90%; */ display: flex; justify-content: flex-start; align-items: center; @@ -85,7 +85,6 @@ } .AppliedFilters { - flex: 1; display: flex; align-items: center; gap: var(--p-space-100); diff --git a/polaris-react/src/components/Filters/Filters.tsx b/polaris-react/src/components/Filters/Filters.tsx index 257a55c977e..b3c30ad1730 100644 --- a/polaris-react/src/components/Filters/Filters.tsx +++ b/polaris-react/src/components/Filters/Filters.tsx @@ -93,20 +93,12 @@ export function Filters({ queryField={queryFieldMarkup} disableFilters={disableFilters} onAddFilterClick={onAddFilterClick} + hideQueryField={hideQueryField} closeOnChildOverlayClick={closeOnChildOverlayClick} > {children} ); - return ( -
- {filtersMarkup} -
- ); + return
{filtersMarkup}
; } diff --git a/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx b/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx index 3732a9808fa..21c8dd4b41b 100644 --- a/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx +++ b/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx @@ -3,6 +3,7 @@ import React, {useState, useRef, useEffect} from 'react'; import {PlusIcon} from '@shopify/polaris-icons'; import type {TransitionStatus} from 'react-transition-group'; +import {classNames} from '../../../../utilities/css'; import {useI18n} from '../../../../utilities/i18n'; import {useOnValueChange} from '../../../../utilities/use-on-value-change'; import {Popover} from '../../../Popover'; @@ -49,6 +50,7 @@ export function FiltersBar({ filters, appliedFilters, disabled, + hideQueryField, queryField, disableFilters, onAddFilterClick, @@ -185,7 +187,7 @@ export function FiltersBar({ hint vertical={false} scrollbarWidth="none" - className={styles.AppliedFilters} + className={classNames(styles.AppliedFilters)} > {pinnedFilters.map(({key: filterKey, ...pinnedFilter}) => { const appliedFilter = appliedFilters?.find( @@ -240,6 +242,10 @@ export function FiltersBar({ ) : null; + const searchFieldMarkup = hideQueryField ? null : ( +
{queryField}
+ ); + const filterMarkup = (
{pinnedFiltersMarkup} @@ -247,16 +253,27 @@ export function FiltersBar({
); + const searchAndFilterMarkup = searchFieldMarkup ? ( + + {searchFieldMarkup} + {filterMarkup} + + ) : ( + filterMarkup + ); + return ( - + - -
{queryField}
- {filterMarkup} -
+ {searchAndFilterMarkup} {children}
diff --git a/polaris-react/src/components/IndexFilters/IndexFilters.tsx b/polaris-react/src/components/IndexFilters/IndexFilters.tsx index 2c40b73b51d..327b6e5a04e 100644 --- a/polaris-react/src/components/IndexFilters/IndexFilters.tsx +++ b/polaris-react/src/components/IndexFilters/IndexFilters.tsx @@ -12,6 +12,7 @@ import {useBreakpoints} from '../../utilities/breakpoints'; import {useIsSticky} from './hooks'; import { Container, + SearchField, SortButton, UpdateButtons, EditColumnsButton, @@ -116,7 +117,6 @@ export function IndexFilters({ isFlushWhenSticky = false, canCreateNewView = true, onCreateNewView, - hideQueryField, closeOnChildOverlayClick, showEditColumnsButton, }: IndexFiltersProps) { @@ -310,6 +310,14 @@ export function IndexFilters({ {isLoading ? : null} )} + {editColumnsMarkup} {sortMarkup} {mode === IndexFiltersMode.EditingColumns @@ -322,7 +330,7 @@ export function IndexFilters({
; type Gap = ResponsiveProp; @@ -95,6 +100,10 @@ function getColumnValue(columns?: ColumnsType) { return 'minmax(0, 1fr)'; case 'twoThirds': return 'minmax(0, 2fr)'; + case 'oneQuarter': + return 'minmax(0, 1fr)'; + case 'threeQuarters': + return 'minmax(0, 3fr)'; } }) .join(' '); From 9b6c85422429f7854b6aa52d82c883ac55814301 Mon Sep 17 00:00:00 2001 From: Chloe Rice Date: Tue, 1 Oct 2024 10:14:00 -0400 Subject: [PATCH 4/4] Ensure query value is unsaved when query is cleared --- polaris-react/playground/OrdersPage.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/polaris-react/playground/OrdersPage.tsx b/polaris-react/playground/OrdersPage.tsx index 60b416365be..c51686f675f 100644 --- a/polaris-react/playground/OrdersPage.tsx +++ b/polaris-react/playground/OrdersPage.tsx @@ -596,7 +596,12 @@ function OrdersIndexTableWithFilters( const handleQueryValueRemove = () => { setQueryValue(''); + const nextSavedViewFilters = [...savedViewFilters]; + nextSavedViewFilters[selectedView] = nextSavedViewFilters[ + selectedView + ].filter(({key}) => key !== 'queryValue'); handleFilterOrders({queryValue: ''}); + setSavedViewFilters(nextSavedViewFilters); }; const handleContainsChange = (value: string) => {