diff --git a/polaris-react/playground/OrdersPage.tsx b/polaris-react/playground/OrdersPage.tsx index 9c843ba3065..c51686f675f 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( [ [], @@ -465,7 +475,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 @@ -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]), @@ -555,27 +573,10 @@ function OrdersIndexTableWithFilters( fulfillmentStatusSet, ); - // if ( - // matchesQueryValue && - // matchesPaymentStatus && - // matchesFulfillmentStatus && - // matchesStatus - // ) { - // console.log( - // ` - // nextFilters: `, - // nextFilters, - // ` - // matchesQueryValue: ${matchesQueryValue}, - // matchesPaymentStatus: ${matchesPaymentStatus}, - // matchesFulfillmentStatus: ${matchesFulfillmentStatus}, - // matchesStatus: ${matchesStatus} - // `, - // ); - // } setLoading(false); return ( matchesQueryValue && + matchesContains && matchesPaymentStatus && matchesFulfillmentStatus && matchesStatus @@ -595,7 +596,22 @@ function OrdersIndexTableWithFilters( const handleQueryValueRemove = () => { setQueryValue(''); + const nextSavedViewFilters = [...savedViewFilters]; + nextSavedViewFilters[selectedView] = nextSavedViewFilters[ + selectedView + ].filter(({key}) => key !== 'queryValue'); handleFilterOrders({queryValue: ''}); + setSavedViewFilters(nextSavedViewFilters); + }; + + const handleContainsChange = (value: string) => { + setContains(value); + handleFilterOrders({contains: value}); + }; + + const handleContainsRemove = (value: string[]) => { + setContains(''); + handleFilterOrders({contains: ''}); }; const handlePaymentStatusChange = (value: string[]) => { @@ -634,6 +650,13 @@ function OrdersIndexTableWithFilters( change: handleQueryValueChange, remove: handleQueryValueRemove, emptyValue: '', + label: 'Search', + }, + contains: { + set: setContains, + change: handleContainsChange, + remove: handleContainsRemove, + emptyValue: '', label: 'Include', }, status: { @@ -686,6 +709,7 @@ function OrdersIndexTableWithFilters( }; const handleChangeFilters = (nextFilterValues: { + contains?: string; queryValue?: string; paymentStatus?: string[]; fulfillmentStatus?: string[]; @@ -702,11 +726,13 @@ function OrdersIndexTableWithFilters( const handleResetToSavedFilters = (view: number) => { const nextFilters: { + contains: string; queryValue: string; paymentStatus: string[]; fulfillmentStatus: string[]; status: string[]; } = { + contains: '', queryValue: '', paymentStatus: [], fulfillmentStatus: [], @@ -731,6 +757,7 @@ function OrdersIndexTableWithFilters( const handleClearFilters = () => { handleChangeFilters({ + contains: '', queryValue: '', paymentStatus: [], fulfillmentStatus: [], @@ -765,10 +792,19 @@ function OrdersIndexTableWithFilters( const filters: FilterInterface[] = [ { - key: 'queryValue', - label: handlers.queryValue.label, - hidden: true, - filter: null, + key: 'contains', + label: handlers.contains.label, + filter: ( + + ), }, { key: 'paymentStatus', @@ -833,12 +869,14 @@ function OrdersIndexTableWithFilters( const appliedFilters: AppliedFilterInterface[] = []; Object.entries({ - queryValue, + contains, status, paymentStatus, fulfillmentStatus, }).forEach(([key, value]) => { - if (isEmpty(value)) return; + if (isEmpty(value)) { + return; + } const savedValue = savedViewFilters[selectedView]?.find( (filter) => filter.key === key, @@ -849,14 +887,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, ); @@ -864,25 +906,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 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 && appliedFilters.length > 0) || - (!savedViewFilters[selectedView] && appliedFilters.length > 0) || - (appliedFilters.length === 0 && - savedViewFilters[selectedView]?.length > 0) || - !appliedFilters.every(appliedFilterMatchesSavedFilter); + isAllViewAndFiltersAreApplied || + (selectedView > 0 && + (appliedFilterCountDoesNotEqualSavedFilterCount || + appliedFiltersDoNotMatchSavedFilters)); const hasUnsavedChanges = hasUnsavedSortChange || hasUnsavedFilterChange; @@ -894,11 +947,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}; }); @@ -909,12 +963,11 @@ function OrdersIndexTableWithFilters( const previousSortSelected = sortSelected[0]; let nextFilters; - if (previousView === 0 && hasUnsavedChanges) { + if ((previousView === 0 && hasUnsavedChanges) || queryValue) { nextFilters = getFiltersToSave(); } setQueryValue(''); - setMode(IndexFiltersMode.Default); setSelectedView(view); setLoading(true); handleResetToSavedFilters(view); @@ -930,8 +983,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); @@ -955,6 +1010,7 @@ function OrdersIndexTableWithFilters( setSavedViewFilters((filters) => [...filters, duplicateViewFilters]); const nextAppliedFilters = { queryValue: '', + contains: '', status: [], paymentStatus: [], fulfillmentStatus: [], @@ -963,7 +1019,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); @@ -971,16 +1032,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, @@ -1006,8 +1067,9 @@ function OrdersIndexTableWithFilters( sortSelected[0] !== savedSortSelected[0]); const nextFilters = shouldSaveAppliedFiltersAsNew - ? {queryValue, status, paymentStatus, fulfillmentStatus} + ? getFiltersToSave() : { + contains: '', queryValue: '', status: [], paymentStatus: [], @@ -1015,7 +1077,7 @@ function OrdersIndexTableWithFilters( }; if (shouldSaveAppliedFiltersAsNew) { - handleSaveViewFilters(newViewIndex); + handleSaveViewFilters(newViewIndex, nextFilters); } else { handleClearFilters(); setSavedViewFilters((filters) => [...filters, []]); @@ -1032,10 +1094,9 @@ function OrdersIndexTableWithFilters( }; const handleSaveViewAs = async (index: number, name: string) => { - setMode(IndexFiltersMode.Default); 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); @@ -1108,6 +1169,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/Filters.module.css b/polaris-react/src/components/Filters/Filters.module.css index 5b149b96482..c31c1878e99 100644 --- a/polaris-react/src/components/Filters/Filters.module.css +++ b/polaris-react/src/components/Filters/Filters.module.css @@ -19,30 +19,18 @@ flex: 1; } -.FiltersWrapper { - border-bottom: var(--p-border-width-025) solid var(--p-color-border-secondary); - height: 53px; - overflow: hidden; - - @media (--p-breakpoints-sm-down) { - background: var(--p-color-bg-surface); - } - - @media (--p-breakpoints-md-up) { - height: auto; - overflow: visible; - } -} - -.hideQueryField .FiltersWrapper { +.FiltersInner { + /* max-width: 90%; */ display: flex; + justify-content: flex-start; align-items: center; -} - -.FiltersInner { - overflow: auto; + flex-wrap: nowrap; white-space: nowrap; - padding: var(--p-space-300) var(--p-space-200) var(--p-space-500); + padding-inline: var(--p-space-100); + + > :first-child { + padding-left: var(--p-space-100); + } } .hideQueryField .FiltersInner { @@ -50,19 +38,61 @@ 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); +.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) + ); + } +} + +.FilterActionWrapper { + position: relative; + display: flex; + align-items: center; + gap: var(--p-space-100); + + &::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) + ); } +} + +.AppliedFilters { + 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 +100,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 +117,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 +231,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..b3c30ad1730 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,90 +64,41 @@ 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} ); - return ( -
- {queryFieldMarkup} - {filtersMarkup} -
- ); + return
{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..21c8dd4b41b 100644 --- a/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx +++ b/polaris-react/src/components/Filters/components/FiltersBar/FiltersBar.tsx @@ -1,31 +1,30 @@ -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'; +import {classNames} from '../../../../utilities/css'; import {useI18n} from '../../../../utilities/i18n'; import {useOnValueChange} from '../../../../utilities/use-on-value-change'; 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,17 +44,15 @@ export interface FiltersBarProps { onAddFilterClick?: () => void; /** Whether the filter should close when clicking inside another Popover. */ closeOnChildOverlayClick?: boolean; - mountedStateStyles?: any; } export function FiltersBar({ filters, 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')}{' '} -
); - const handleClearAllFilters = () => { - setLocalPinnedFilters(pinnedFromPropsKeys); - onClearAll?.(); - }; - const shouldShowAddButton = - filters.some((filter) => !filter.pinned) || - filters.length !== localPinnedFilters.length; + const shouldShowAddButton = 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 clearAllMarkup = appliedFilters?.length ? ( -
- + const searchFieldMarkup = hideQueryField ? null : ( +
{queryField}
+ ); + + const filterMarkup = ( +
+ {pinnedFiltersMarkup} +
{addButton}
- ) : null; + ); + + const searchAndFilterMarkup = searchFieldMarkup ? ( + + {searchFieldMarkup} + {filterMarkup} + + ) : ( + filterMarkup + ); return ( -
-
-
- {pinnedFiltersMarkup} - {addButton} - {clearAllMarkup} -
-
- {hideQueryField ? ( - - - {children} - - - ) : null} -
+ + {searchAndFilterMarkup} + {children} + + ); } diff --git a/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx b/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx index 3608811eaea..3a5ad2479a2 100644 --- a/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx +++ b/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx @@ -1,53 +1,41 @@ -import React, {useId} from 'react'; +import React, {useId, useState} from 'react'; import {SearchIcon} from '@shopify/polaris-icons'; +import {Bleed} from '../../../Bleed'; +import {Box} from '../../../Box'; import {Icon} from '../../../Icon'; import {TextField} from '../../../TextField'; -import {Text} from '../../../Text'; import {useBreakpoints} from '../../../../utilities/breakpoints'; import {useI18n} from '../../../../utilities/i18n'; export interface SearchFieldProps { - onChange: (value: string) => void; - onFocus?: () => void; - onBlur?: () => void; - onClear?: () => void; focused?: boolean; value?: string; placeholder?: string; disabled?: boolean; - borderlessQueryField?: boolean; - /** Show a loading spinner to the right of the input */ + /** Shows a loading spinner to the right of the input */ loading?: boolean; - /** @deprecated If present, will show as a suffix in the text field when entering a search term */ - selectedViewName?: string; + onChange: (value: string) => void; + onFocus?: () => void; + onBlur?: () => void; + onClear?: () => void; } export function SearchField({ - onChange, - onClear, - onFocus, - onBlur, - focused, + focused: forceFocus = false, value, placeholder, disabled, - borderlessQueryField, loading, - selectedViewName, + onChange, + onClear, + onFocus, + onBlur, }: SearchFieldProps) { - const i18n = useI18n(); const id = useId(); + const i18n = useI18n(); const {mdUp} = useBreakpoints(); - - const suffix = - value && selectedViewName && mdUp ? ( - - {i18n.translate('Polaris.Filters.searchInView', { - viewName: selectedViewName, - })} - - ) : null; + const [focused, setFocused] = useState(forceFocus); function handleChange(eventValue: string) { onChange(eventValue ?? value); @@ -61,27 +49,47 @@ export function SearchField({ } } + function handleFocus() { + onFocus?.(); + setFocused(true); + } + + function handleBlur() { + onBlur?.(); + setFocused(false); + } + return ( - : undefined} - suffix={suffix} - focused={focused} - label={placeholder} - labelHidden - clearButton - autoSize={Boolean(suffix)} - loading={loading} - /> + + + + + ) : undefined + } + focused={focused} + label={ + placeholder ?? + i18n.translate('Polaris.IndexFilters.SearchField.defaultPlaceholder') + } + labelHidden + clearButton + loading={loading} + /> + ); } diff --git a/polaris-react/src/components/Filters/components/SearchField/tests/SearchField.test.tsx b/polaris-react/src/components/Filters/components/SearchField/tests/SearchField.test.tsx index 1930f362683..99d1e53146d 100644 --- a/polaris-react/src/components/Filters/components/SearchField/tests/SearchField.test.tsx +++ b/polaris-react/src/components/Filters/components/SearchField/tests/SearchField.test.tsx @@ -87,9 +87,7 @@ describe('SearchField', () => { it('will add a suffix when there is a selectedViewName and value', () => { mockUseBreakpoints(true); - const wrapper = mountWithApp( - , - ); + const wrapper = mountWithApp(); expect(wrapper).toContainReactText('in:All'); }); @@ -102,11 +100,7 @@ describe('SearchField', () => { it('will not add a suffix when there is no value', () => { const wrapper = mountWithApp( - , + , ); expect(wrapper).not.toContainReactText('in:All'); diff --git a/polaris-react/src/components/IndexFilters/IndexFilters.module.css b/polaris-react/src/components/IndexFilters/IndexFilters.module.css index 335b037ad8e..def55786556 100644 --- a/polaris-react/src/components/IndexFilters/IndexFilters.module.css +++ b/polaris-react/src/components/IndexFilters/IndexFilters.module.css @@ -90,9 +90,8 @@ display: flex; gap: var(--p-space-200); align-items: center; - justify-content: flex-start; - padding: var(--p-space-150) var(--p-space-200); - min-width: 24rem; + justify-content: end; + padding: var(--p-space-150) var(--p-space-300); @media (--p-breakpoints-md-down) { padding: var(--p-space-200); @@ -135,6 +134,29 @@ .ButtonWrap, .ActionWrap { button { + white-space: nowrap; display: flex; } } + +.ButtonWrap .animateSlideIn { + will-change: transform, opacity; + opacity: 1; + transform: translateX(0); + transition: transform var(--p-motion-ease) var(--p-motion-duration-200), + opacity var(--p-motion-ease) var(--p-motion-duration-200); + + /* stylelint-disable selector-max-class -- animation for entrance of save action */ + &.entering, + &.exiting, + &.exited { + opacity: 0; + transform: translateX(-200px); + } + + &.entered { + opacity: 1; + transform: translateX(0); + } + /* stylelint-enable */ +} diff --git a/polaris-react/src/components/IndexFilters/IndexFilters.tsx b/polaris-react/src/components/IndexFilters/IndexFilters.tsx index 4d07bf0b04a..327b6e5a04e 100644 --- a/polaris-react/src/components/IndexFilters/IndexFilters.tsx +++ b/polaris-react/src/components/IndexFilters/IndexFilters.tsx @@ -1,9 +1,6 @@ import React, {useMemo, useCallback, useRef} from 'react'; -import {Transition} from 'react-transition-group'; -import {useI18n} from '../../utilities/i18n'; import {classNames} from '../../utilities/css'; -import {useEventListener} from '../../utilities/use-event-listener'; import {InlineStack} from '../InlineStack'; import {Spinner} from '../Spinner'; import {Filters} from '../Filters'; @@ -15,9 +12,8 @@ import {useBreakpoints} from '../../utilities/breakpoints'; import {useIsSticky} from './hooks'; import { Container, - SortButton, SearchField, - FilterButton, + SortButton, UpdateButtons, EditColumnsButton, } from './components'; @@ -29,23 +25,6 @@ import type { import {IndexFiltersMode} from './types'; import styles from './IndexFilters.module.css'; -const DEFAULT_IGNORED_TAGS = ['INPUT', 'SELECT', 'TEXTAREA']; - -const TRANSITION_DURATION = 150; - -const defaultStyle = { - transition: `opacity ${TRANSITION_DURATION}ms var(--p-motion-ease)`, - opacity: 0, -}; - -const transitionStyles = { - entering: {opacity: 1}, - entered: {opacity: 1}, - exiting: {opacity: 0}, - exited: {opacity: 0}, - unmounted: {opacity: 0}, -}; - type ExecutedCallback = (name: string) => Promise; type ActionableIndexFiltersMode = Exclude< @@ -99,15 +78,8 @@ export interface IndexFiltersProps canCreateNewView?: boolean; /** Callback invoked when a merchant creates a new view */ onCreateNewView?: (name: string) => Promise; - /** Optional override to the default aria-label for the button that toggles the filtering mode */ - filteringAccessibilityLabel?: string; - /** Optional override to the default Tooltip message for the button that toggles the filtering mode */ - filteringAccessibilityTooltip?: string; /** Whether the filter should close when clicking inside another Popover. */ closeOnChildOverlayClick?: boolean; - /** Optional override to the default keyboard shortcuts available. Should be set to true for all instances - * of this component not controlling a root-level index */ - disableKeyboardShortcuts?: boolean; /** Whether to display the edit columns button with the other default mode filter actions */ showEditColumnsButton?: boolean; } @@ -134,50 +106,26 @@ export function IndexFilters({ onQueryFocus, onQueryClear, onEditStart, + mode, + setMode, disabled, disableQueryField, hideFilters, loading, - mode = IndexFiltersMode.Default, - setMode, disclosureZIndexOverride, disableStickyMode, isFlushWhenSticky = false, canCreateNewView = true, onCreateNewView, - filteringAccessibilityLabel, - filteringAccessibilityTooltip, - hideQueryField, closeOnChildOverlayClick, - disableKeyboardShortcuts, showEditColumnsButton, }: IndexFiltersProps) { - const i18n = useI18n(); const {mdDown} = useBreakpoints(); const defaultRef = useRef(null); const filteringRef = useRef(null); - useEventListener('keydown', (event) => { - const hasNoFiltersOrSearch = hideQueryField && hideFilters; - if (disableKeyboardShortcuts || hasNoFiltersOrSearch) return; - - const {key} = event; - const tag = document?.activeElement?.tagName; - if (mode !== IndexFiltersMode.Default && event.key === 'Escape') { - onPressEscape(); - } - - if (key === 'f' && mode === IndexFiltersMode.Default) { - if (tag && DEFAULT_IGNORED_TAGS.includes(tag)) { - return; - } - onPressF(); - event.preventDefault(); - } - }); - const {intersectionRef, measurerRef, indexFilteringHeight, isSticky} = - useIsSticky(mode, Boolean(disableStickyMode), isFlushWhenSticky); + useIsSticky(Boolean(disableStickyMode), isFlushWhenSticky); const viewNames = tabs.map(({content}) => content); @@ -196,7 +144,7 @@ export function IndexFilters({ async (name: string) => { const hasExecuted = await action?.(name); if (hasExecuted) { - setMode(IndexFiltersMode.Default); + // setMode(IndexFiltersMode.Default); afterEffect?.(); } }, @@ -207,13 +155,10 @@ export function IndexFilters({ const onExecutedCancelAction = useCallback(() => { cancelAction?.onAction?.(); - // setSearchOnlyValue(''); - // setSearchFilterValue(''); - setMode(IndexFiltersMode.Default); - }, [cancelAction, setMode]); + }, [cancelAction]); const enhancedPrimaryAction = useMemo(() => { - return primaryAction + return primaryAction && !primaryAction.disabled ? { ...primaryAction, onAction: onExecutedPrimaryAction, @@ -222,7 +167,7 @@ export function IndexFilters({ }, [onExecutedPrimaryAction, primaryAction]); const enhancedCancelAction = useMemo(() => { - return cancelAction + return cancelAction && !cancelAction.disabled ? { ...cancelAction, onAction: onExecutedCancelAction, @@ -241,12 +186,14 @@ export function IndexFilters({ const updateButtonsMarkup = useMemo( () => enhancedCancelAction || enhancedPrimaryAction ? ( - +
+ +
) : null, [enhancedPrimaryAction, enhancedCancelAction, disabled, viewNames], ); @@ -262,16 +209,12 @@ export function IndexFilters({ disabled={disabled} hasUnsavedChanges={sortUnsaved} disclosureZIndexOverride={disclosureZIndexOverride} - onClick={() => { - setMode(IndexFiltersMode.Filtering); - }} onChange={handleChangeSortButton} onChangeKey={onSortKeyChange} onChangeDirection={onSortDirectionChange} /> ); }, [ - setMode, handleChangeSortButton, onSortDirectionChange, onSortKeyChange, @@ -295,49 +238,13 @@ export function IndexFilters({ const isActionLoading = primaryAction?.loading || cancelAction?.loading; - function handleHideFilters() { - cancelAction?.onAction(); - setMode(IndexFiltersMode.Default); - } - - const handleShowFilters = useCallback(() => { - beginEdit(IndexFiltersMode.Filtering); - }, [beginEdit]); - - function handleClickFilterButton() { - if (mode === IndexFiltersMode.Filtering) { - handleHideFilters(); - } else { - handleShowFilters(); - } - } - - const searchFilterTooltipLabelId = disableKeyboardShortcuts - ? 'Polaris.IndexFilters.searchFilterTooltip' - : 'Polaris.IndexFilters.searchFilterTooltipWithShortcut'; - - const searchFilterTooltip = - filteringAccessibilityTooltip || i18n.translate(searchFilterTooltipLabelId); - const searchFilterAriaLabel = - filteringAccessibilityLabel || - i18n.translate('Polaris.IndexFilters.searchFilterAccessibilityLabel'); - const isLoading = loading || isActionLoading; - function onPressEscape() { - cancelAction?.onAction(); - setMode(IndexFiltersMode.Default); - } - const handleQueryChange = useCallback( (value: string) => { - if (mode === IndexFiltersMode.Default) { - setMode(IndexFiltersMode.Filtering); - } - onQueryChange(value); }, - [mode, onQueryChange, setMode], + [onQueryChange], ); const handleQueryClear = useCallback(() => { @@ -348,14 +255,6 @@ export function IndexFilters({ onQueryFocus?.(); } - function onPressF() { - if (mode !== IndexFiltersMode.Default) { - return; - } - - handleShowFilters(); - } - return (
+ {isLoading && !mdDown && ( +
+ {isLoading ? : null} +
+ )} - {isLoading && !mdDown && ( -
- {isLoading ? : null} -
- )} - - {hideFilters || mode === IndexFiltersMode.Filtering ? null : ( - 0 - // } - onClick={handleClickFilterButton} - /> - )} {editColumnsMarkup} {sortMarkup} {mode === IndexFiltersMode.EditingColumns @@ -443,45 +328,27 @@ export function IndexFilters({
- - {(state) => ( -
- {mode === IndexFiltersMode.Filtering ? ( - {}} - onQueryClear={() => {}} - onAddFilterClick={onAddFilterClick} - filters={filters} - appliedFilters={appliedFilters} - onClearAll={onClearAll} - disableFilters={disabled} - hideFilters={hideFilters} - loading={loading || isActionLoading} - mountedState={mdDown ? undefined : state} - closeOnChildOverlayClick={closeOnChildOverlayClick} - > -
- -
- {updateButtonsMarkup} -
-
-
-
- ) : null} -
- )} -
+
+ + {updateButtonsMarkup} + +
); diff --git a/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx b/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx index 6c3dc35a4c8..63e23ea65a3 100644 --- a/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx +++ b/polaris-react/src/components/IndexFilters/components/SearchField/SearchField.tsx @@ -1,6 +1,7 @@ import React, {useId, useState} from 'react'; import {SearchIcon} from '@shopify/polaris-icons'; +import {Bleed} from '../../../Bleed'; import {Box} from '../../../Box'; import {Icon} from '../../../Icon'; import {TextField} from '../../../TextField'; @@ -59,7 +60,7 @@ export function SearchField({ } return ( - + : undefined} + prefix={ + mdUp ? ( + + + + ) : undefined + } focused={focused} label={ placeholder ?? diff --git a/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.module.css b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.module.css index a51f2fa7174..96e583940bf 100644 --- a/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.module.css +++ b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.module.css @@ -5,7 +5,7 @@ .AppliedFilterIndicator { position: absolute; top: calc(var(--p-space-300) - var(--p-border-width-050)); - right: var(--p-space-300); + right: var(--p-space-400); 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); diff --git a/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx index 1a60a6e3df8..ed42fb56920 100644 --- a/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx +++ b/polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx @@ -25,7 +25,6 @@ export interface SortButtonProps { disabled?: boolean; disclosureZIndexOverride?: number; hasUnsavedChanges?: boolean; - onClick(): void; onChange: (selected: string[]) => void; onChangeKey?: (key: string) => void; onChangeDirection?: (direction: string) => void; @@ -37,7 +36,6 @@ export function SortButton({ disabled, disclosureZIndexOverride, hasUnsavedChanges, - onClick, onChange, onChangeKey, onChangeDirection, @@ -56,7 +54,6 @@ export function SortButton({ } function handleChangeChoiceList(sel: string[]) { - onClick(); if (onChangeKey) { const [key] = sel[0].split(' '); onChangeKey(key); @@ -66,7 +63,6 @@ export function SortButton({ } function handleChangeDirection(sel: string[]) { - onClick(); if (onChangeDirection) { const [, direction] = sel[0].split(' '); onChangeDirection(direction); 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 0007b54e076..5b43661e216 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,7 +58,6 @@ 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'], }; @@ -85,7 +84,6 @@ describe('SortButton', () => { const disclosureZIndexOverride = 517; const props: SortButtonProps = { onChange: jest.fn(), - onClick: jest.fn(), choices, selected: ['order-number asc'], disclosureZIndexOverride, @@ -101,7 +99,6 @@ describe('SortButton', () => { const disclosureZIndexOverride = 517; const props: SortButtonProps = { onChange: jest.fn(), - onClick: jest.fn(), choices, selected: ['order-number asc'], disclosureZIndexOverride, @@ -116,7 +113,6 @@ 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'], }; @@ -136,7 +132,6 @@ describe('SortButton', () => { it('selects ascending', () => { const props: SortButtonProps = { onChange: jest.fn(), - onClick: jest.fn(), choices, selected: ['order-number asc'], }; @@ -155,7 +150,6 @@ describe('SortButton', () => { it('selects descending', () => { const props: SortButtonProps = { onChange: jest.fn(), - onClick: jest.fn(), choices, selected: ['order-number desc'], }; @@ -174,7 +168,6 @@ describe('SortButton', () => { it('invokes onChange when clicking the ascending button', () => { const props: SortButtonProps = { onChange: jest.fn(), - onClick: jest.fn(), choices, selected: ['order-number desc'], }; @@ -197,7 +190,6 @@ describe('SortButton', () => { it('invokes onChange when clicking the descending button', () => { const props: SortButtonProps = { onChange: jest.fn(), - onClick: jest.fn(), choices, selected: ['order-number asc'], }; diff --git a/polaris-react/src/components/IndexFilters/components/UpdateButtons/UpdateButtons.tsx b/polaris-react/src/components/IndexFilters/components/UpdateButtons/UpdateButtons.tsx index 624a661656b..53593db3afb 100644 --- a/polaris-react/src/components/IndexFilters/components/UpdateButtons/UpdateButtons.tsx +++ b/polaris-react/src/components/IndexFilters/components/UpdateButtons/UpdateButtons.tsx @@ -117,65 +117,66 @@ export function UpdateButtons({ return cancelButtonMarkup; } - return ( - - {cancelButtonMarkup} - {primaryAction.type === 'save-as' ? ( - {saveButton}} - open={savedViewModalOpen} - title={i18n.translate( - 'Polaris.IndexFilters.UpdateButtons.modal.title', - )} - onClose={handleCloseModal} - primaryAction={{ - onAction: handlePrimaryAction, + const saveButtonMarkup = + primaryAction.type === 'save-as' ? ( + - -
- -
- -
-
-
-
-
- ) : ( - saveButton - )} + }, + ]} + > + +
+ +
+ +
+
+
+
+ + ) : ( + saveButton + ); + + return ( + + {cancelButtonMarkup} + {saveButtonMarkup} ); } diff --git a/polaris-react/src/components/IndexFilters/hooks/useIsSticky/tests/useIsSticky.test.tsx b/polaris-react/src/components/IndexFilters/hooks/useIsSticky/tests/useIsSticky.test.tsx index 3903f388d49..2138a892bfe 100644 --- a/polaris-react/src/components/IndexFilters/hooks/useIsSticky/tests/useIsSticky.test.tsx +++ b/polaris-react/src/components/IndexFilters/hooks/useIsSticky/tests/useIsSticky.test.tsx @@ -2,7 +2,6 @@ import React from 'react'; import {intersectionObserver} from '@shopify/jest-dom-mocks'; import {mountWithApp} from 'tests/utilities'; -import {IndexFiltersMode} from '../../../types'; import {useIsSticky} from '..'; interface Props { @@ -11,7 +10,7 @@ interface Props { function Component({disabled}: Props) { const {intersectionRef, measurerRef, isSticky, indexFilteringHeight} = - useIsSticky(IndexFiltersMode.Default, Boolean(disabled), false); + useIsSticky(Boolean(disabled), false); return (
window.removeEventListener('resize', debouncedComputeDimensions); - }, [measurerRef, mode]); + }, [measurerRef]); useEffect(() => { const observer = observerRef.current; diff --git a/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx b/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx index b9c2c007752..d4b7f1b1d95 100644 --- a/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx +++ b/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx @@ -410,75 +410,6 @@ describe('IndexFilters', () => { }); }); - describe('disableKeyboardShortcuts', () => { - it('renders the FilterButton tooltipContent without the keyboard shortcut', () => { - const wrapper = mountWithApp( - , - ); - - expect(wrapper).toContainReactComponent(FilterButton, { - tooltipContent: 'Search and filter', - }); - }); - - it('does not moves from Default mode to Filtering mode when pressing f', () => { - const onEditStart = jest.fn(); - - mountWithApp( - , - ); - - window.dispatchEvent( - new KeyboardEvent('keydown', { - key: 'f', - }), - ); - - expect(onEditStart).not.toHaveBeenCalledWith(IndexFiltersMode.Filtering); - }); - - it('does not call the cancelAction.onAction method when pressing escape in Filtering mode', () => { - mountWithApp( - , - ); - - window.dispatchEvent( - new KeyboardEvent('keydown', { - key: 'Escape', - }), - ); - - expect(defaultProps.cancelAction!.onAction).not.toHaveBeenCalled(); - }); - - it('does not call the cancelAction.onAction method when pressing escape in EditingColumns mode', () => { - mountWithApp( - , - ); - - window.dispatchEvent( - new KeyboardEvent('keydown', { - key: 'Escape', - }), - ); - - expect(defaultProps.cancelAction!.onAction).not.toHaveBeenCalled(); - }); - }); - describe('disabled', () => { it('renders Filters with the disabled props', () => { const filters: ComponentProps['filters'] = [ diff --git a/polaris-react/src/components/InlineGrid/InlineGrid.tsx b/polaris-react/src/components/InlineGrid/InlineGrid.tsx index 2b15f15df7d..254e6057bcb 100644 --- a/polaris-react/src/components/InlineGrid/InlineGrid.tsx +++ b/polaris-react/src/components/InlineGrid/InlineGrid.tsx @@ -10,7 +10,12 @@ import type {ResponsiveValue, ResponsiveProp} from '../../utilities/css'; import styles from './InlineGrid.module.css'; -type ColumnsAlias = 'oneThird' | 'oneHalf' | 'twoThirds'; +type ColumnsAlias = + | 'oneThird' + | 'oneHalf' + | 'twoThirds' + | 'oneQuarter' + | 'threeQuarters'; type ColumnsType = number | string | ColumnsAlias[]; type Columns = ResponsiveProp; 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(' '); diff --git a/polaris-react/src/components/Tabs/Tabs.module.css b/polaris-react/src/components/Tabs/Tabs.module.css index 92478f243d9..74dca98268b 100644 --- a/polaris-react/src/components/Tabs/Tabs.module.css +++ b/polaris-react/src/components/Tabs/Tabs.module.css @@ -47,7 +47,6 @@ gap: var(--p-space-100); @media (--p-breakpoints-md-up) { - padding: 0 var(--p-space-100); flex-wrap: wrap; align-items: stretch; } diff --git a/polaris-react/src/components/TextField/TextField.module.css b/polaris-react/src/components/TextField/TextField.module.css index f38c340d765..212d1ae60d6 100644 --- a/polaris-react/src/components/TextField/TextField.module.css +++ b/polaris-react/src/components/TextField/TextField.module.css @@ -329,13 +329,17 @@ min-height: var(--p-space-800); border-color: transparent; + ~ .Backdrop { + background-color: var(--p-color-bg-surface-active); + } + &:not(:active):not(:hover) { - border-color: var(--p-color-border-secondary); + border-color: transparent; } - &.Input:hover:not(:focus-visible) { + &.Input:focus-visible { ~ .Backdrop { - background-color: var(--p-color-input-bg-surface-hover); + outline-width: var(--p-border-width-025); } } } @@ -419,7 +423,7 @@ .Prefix { margin-left: var(--p-space-300); - margin-right: var(--p-space-150); + margin-right: var(--p-space-050); } .PrefixIcon { @@ -479,22 +483,50 @@ } .ClearButton { + display: flex; /* stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY */ @mixin focus-ring base, 0, base; /* stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY */ @mixin unstyled-button; /* stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY */ z-index: var(--pc-text-field-contents); + height: 20px; + width: 20px; margin: 0 var(--p-space-300) 0 var(--p-space-100); + border-radius: var(--p-border-radius-200); transition: visibility var(--p-motion-duration-200) var(--p-motion-ease-in-out), opacity var(--p-motion-duration-200) var(--p-motion-ease-in-out); - &:focus-visible:enabled { + &: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; } + &:active { + background: var(--p-color-bg-fill-transparent-active); + } + &:disabled { cursor: default; } diff --git a/polaris-react/src/components/TextField/TextField.tsx b/polaris-react/src/components/TextField/TextField.tsx index 5d0533a4bf0..db775f32a66 100644 --- a/polaris-react/src/components/TextField/TextField.tsx +++ b/polaris-react/src/components/TextField/TextField.tsx @@ -6,7 +6,7 @@ import React, { useCallback, useId, } from 'react'; -import {XCircleIcon} from '@shopify/polaris-icons'; +import {XSmallIcon} from '@shopify/polaris-icons'; import {classNames, variationName} from '../../utilities/css'; import {useI18n} from '../../utilities/i18n'; @@ -17,6 +17,7 @@ import {Connected} from '../Connected'; import {Key} from '../../types'; import type {Error} from '../../types'; import {Icon} from '../Icon'; +import {Bleed} from '../Bleed'; import {Text} from '../Text'; import {Spinner as LoadingSpinner} from '../Spinner'; import {useEventListener} from '../../utilities/use-event-listener'; @@ -390,17 +391,19 @@ export function TextField({ const clearButtonMarkup = clearButton && clearButtonVisible ? ( - + + + ) : 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)" />