Skip to content

Commit a60d8aa

Browse files
authored
[IndexFilters] Add support for passing zIndexOverride to disclosures (#11883)
### WHY are these changes introduced? `Filters`, `IndexFilters`, and `Tabs` all render `Popovers` and `Tooltips`, which support a `zIndexOverride` when needed by consuming apps. For example, the SettingsModal has a `z-index` of 516, which is higher than `Popover` and `Tooltip`. When rendered in a /settings view, these need to have an increased `z-index` from `--p-z-index-2` (400) to `--p-z-index-9` (517). ### WHAT is this pull request doing? This PR simply adds support for passing through a `z-index` override to the `Popover` and `Tooltip` components composed by `Filters`, `IndexFilters`, and `Tabs` through a new `disclosureZIndexOverride` prop. ### How to 🎩 🖥 [Local development instructions](https://github.com/Shopify/polaris/blob/main/README.md#install-dependencies-and-build-workspaces) 🗒 [General tophatting guidelines](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting.md) 📄 [Changelog guidelines](https://github.com/Shopify/polaris/blob/main/.github/CONTRIBUTING.md#changelog) ### 🎩 checklist - [ ] Tested a [snapshot](https://github.com/Shopify/polaris/blob/main/documentation/Releasing.md#-snapshot-releases) (pending) - [ ] Tested on [mobile](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting.md#cross-browser-testing) - [ ] Tested on [multiple browsers](https://help.shopify.com/en/manual/shopify-admin/supported-browsers) - [ ] Tested for [accessibility](https://github.com/Shopify/polaris/blob/main/documentation/Accessibility%20testing.md) - [ ] Updated the component's `README.md` with documentation changes - [ ] [Tophatted documentation](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting%20documentation.md) changes in the style guide
1 parent 3740304 commit a60d8aa

File tree

13 files changed

+229
-19
lines changed

13 files changed

+229
-19
lines changed

.changeset/all-humans-cry.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/polaris': minor
3+
---
4+
5+
Added a `disclosureZIndexOverride` prop to `Filters`, `IndexFilters`, and `Tabs` that is passed to `Popover` and `Tooltip` when provided

polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ export interface FilterPillProps extends FilterInterface {
2525
selected?: boolean;
2626
/** Whether the Popover will be initially open or not */
2727
initialActive: boolean;
28-
/** Callback invoked when the filter is removed */
29-
onRemove?(key: string): void;
30-
/** Callback invoked when the filter is clicked */
31-
onClick?(key: string): void;
3228
/** Whether filtering is disabled */
3329
disabled?: boolean;
30+
/** Override z-index of popovers and tooltips */
31+
disclosureZIndexOverride?: number;
3432
/** Whether the filter should close when clicking inside another Popover. */
3533
closeOnChildOverlayClick?: boolean;
34+
/** Callback invoked when the filter is removed */
35+
onRemove?(key: string): void;
36+
/** Callback invoked when the filter is clicked */
37+
onClick?(key: string): void;
3638
}
3739

3840
export function FilterPill({
@@ -44,6 +46,7 @@ export function FilterPill({
4446
hideClearButton,
4547
selected,
4648
initialActive,
49+
disclosureZIndexOverride,
4750
closeOnChildOverlayClick,
4851
onRemove,
4952
onClick,
@@ -207,6 +210,7 @@ export function FilterPill({
207210
key={filterKey}
208211
onClose={handlePopoverClose}
209212
preferredAlignment="left"
213+
zIndexOverride={disclosureZIndexOverride}
210214
preventCloseOnChildOverlayClick={!closeOnChildOverlayClick}
211215
>
212216
<div className={styles.PopoverWrapper}>

polaris-react/src/components/Filters/components/FilterPill/tests/FilterPill.test.tsx

+15
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,21 @@ describe('<Filters />', () => {
157157
});
158158
});
159159

160+
it('passes the disclosureZIndexOverride to Popover when provided', () => {
161+
const disclosureZIndexOverride = 517;
162+
const wrapper = mountWithApp(
163+
<FilterPill
164+
{...defaultProps}
165+
disclosureZIndexOverride={disclosureZIndexOverride}
166+
/>,
167+
);
168+
const activator = wrapper.find(UnstyledButton);
169+
activator?.trigger('onClick');
170+
expect(wrapper).toContainReactComponent(Popover, {
171+
zIndexOverride: disclosureZIndexOverride,
172+
});
173+
});
174+
160175
it('invokes the onRemove callback when clicking the clear button inside the Popover', () => {
161176
const spy = jest.fn();
162177
const wrapper = mountWithApp(

polaris-react/src/components/IndexFilters/IndexFilters.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ export interface IndexFiltersProps
8080
onEditStart?: (mode: ActionableIndexFiltersMode) => void;
8181
/** The current mode of the IndexFilters component. Used to determine which view to show */
8282
mode: IndexFiltersMode;
83+
/** Override z-index of popovers and tooltips */
84+
disclosureZIndexOverride?: number;
8385
/** Callback to set the mode of the IndexFilters component */
8486
setMode: (mode: IndexFiltersMode) => void;
8587
/** Will disable all the elements within the IndexFilters component */
@@ -136,6 +138,7 @@ export function IndexFilters({
136138
loading,
137139
mode,
138140
setMode,
141+
disclosureZIndexOverride,
139142
disableStickyMode,
140143
isFlushWhenSticky = false,
141144
canCreateNewView = true,
@@ -280,6 +283,7 @@ export function IndexFilters({
280283
onChangeKey={onSortKeyChange}
281284
onChangeDirection={onSortDirectionChange}
282285
disabled={disabled}
286+
disclosureZIndexOverride={disclosureZIndexOverride}
283287
/>
284288
);
285289
}, [
@@ -289,6 +293,7 @@ export function IndexFilters({
289293
sortOptions,
290294
sortSelected,
291295
disabled,
296+
disclosureZIndexOverride,
292297
]);
293298

294299
function handleClickEditColumnsButton() {
@@ -398,6 +403,7 @@ export function IndexFilters({
398403
disabled={Boolean(
399404
mode !== IndexFiltersMode.Default || disabled,
400405
)}
406+
disclosureZIndexOverride={disclosureZIndexOverride}
401407
canCreateNewView={canCreateNewView}
402408
onCreateNewView={onCreateNewView}
403409
/>
@@ -428,6 +434,9 @@ export function IndexFilters({
428434
...defaultStyle,
429435
...transitionStyles[state],
430436
}}
437+
disclosureZIndexOverride={
438+
disclosureZIndexOverride
439+
}
431440
/>
432441
)}
433442
{editColumnsMarkup}

polaris-react/src/components/IndexFilters/components/SearchFilterButton/SearchFilterButton.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface SearchFilterButtonProps {
1313
label: string;
1414
disabled?: boolean;
1515
tooltipContent: string;
16+
disclosureZIndexOverride?: number;
1617
hideFilters?: boolean;
1718
hideQueryField?: boolean;
1819
style: CSSProperties;
@@ -23,6 +24,7 @@ export function SearchFilterButton({
2324
label,
2425
disabled,
2526
tooltipContent,
27+
disclosureZIndexOverride,
2628
style,
2729
hideFilters,
2830
hideQueryField,
@@ -53,7 +55,12 @@ export function SearchFilterButton({
5355
);
5456

5557
return (
56-
<Tooltip content={content} preferredPosition="above" hoverDelay={400}>
58+
<Tooltip
59+
content={content}
60+
preferredPosition="above"
61+
hoverDelay={400}
62+
zIndexOverride={disclosureZIndexOverride}
63+
>
5764
{activator}
5865
</Tooltip>
5966
);

polaris-react/src/components/IndexFilters/components/SortButton/SortButton.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,19 @@ export enum SortButtonDirection {
2020
export interface SortButtonProps {
2121
choices: SortButtonChoice[];
2222
selected: ChoiceListProps['selected'];
23-
onChange: (selected: string[]) => void;
2423
disabled?: boolean;
24+
disclosureZIndexOverride?: number;
25+
onChange: (selected: string[]) => void;
2526
onChangeKey?: (key: string) => void;
2627
onChangeDirection?: (direction: string) => void;
2728
}
2829

2930
export function SortButton({
3031
choices,
3132
selected,
32-
onChange,
3333
disabled,
34+
disclosureZIndexOverride,
35+
onChange,
3436
onChangeKey,
3537
onChangeDirection,
3638
}: SortButtonProps) {
@@ -99,6 +101,7 @@ export function SortButton({
99101
content={i18n.translate('Polaris.IndexFilters.SortButton.tooltip')}
100102
preferredPosition="above"
101103
hoverDelay={400}
104+
zIndexOverride={disclosureZIndexOverride}
102105
>
103106
<Button
104107
size="slim"
@@ -114,12 +117,13 @@ export function SortButton({
114117

115118
return (
116119
<Popover
120+
fluidContent
117121
active={active && !disabled}
118122
activator={sortButton}
119123
autofocusTarget="first-node"
120124
onClose={handleClose}
121125
preferredAlignment="right"
122-
fluidContent
126+
zIndexOverride={disclosureZIndexOverride}
123127
>
124128
<Box
125129
minWidth="148px"

polaris-react/src/components/IndexFilters/components/SortButton/tests/SortButton.test.tsx

+31
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {mountWithApp} from 'tests/utilities';
33

44
import {ChoiceList} from '../../../../ChoiceList';
55
import {Popover} from '../../../../Popover';
6+
import {Tooltip} from '../../../../Tooltip';
67
import {UnstyledButton} from '../../../../UnstyledButton';
78
import type {SortButtonChoice} from '../../../types';
89
import {SortButton} from '..';
@@ -79,6 +80,36 @@ describe('SortButton', () => {
7980
});
8081
});
8182

83+
it('passes the disclosureZIndexOverride to the popover when provided', () => {
84+
const disclosureZIndexOverride = 517;
85+
const props: SortButtonProps = {
86+
onChange: jest.fn(),
87+
choices,
88+
selected: ['order-number asc'],
89+
disclosureZIndexOverride,
90+
};
91+
const wrapper = mountWithApp(<SortButton {...props} />);
92+
93+
expect(wrapper).toContainReactComponent(Popover, {
94+
zIndexOverride: disclosureZIndexOverride,
95+
});
96+
});
97+
98+
it('passes the disclosureZIndexOverride to the tooltip when provided', () => {
99+
const disclosureZIndexOverride = 517;
100+
const props: SortButtonProps = {
101+
onChange: jest.fn(),
102+
choices,
103+
selected: ['order-number asc'],
104+
disclosureZIndexOverride,
105+
};
106+
const wrapper = mountWithApp(<SortButton {...props} />);
107+
108+
expect(wrapper).toContainReactComponent(Tooltip, {
109+
zIndexOverride: disclosureZIndexOverride,
110+
});
111+
});
112+
82113
it('fires the onChange handler when the ChoiceList changes', () => {
83114
const props: SortButtonProps = {
84115
onChange: jest.fn(),

polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx

+62-5
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,20 @@ describe('IndexFilters', () => {
141141
});
142142
});
143143

144+
it('passes the disclosureZIndexOverride to the Tabs when provided', () => {
145+
const disclosureZIndexOverride = 517;
146+
const wrapper = mountWithApp(
147+
<IndexFilters
148+
{...defaultProps}
149+
disclosureZIndexOverride={disclosureZIndexOverride}
150+
/>,
151+
);
152+
153+
expect(wrapper).toContainReactComponent(Tabs, {
154+
disclosureZIndexOverride,
155+
});
156+
});
157+
144158
it('renders the SearchFilterButton tooltipContent with keyboard shortcut by default', () => {
145159
const wrapper = mountWithApp(<IndexFilters {...defaultProps} />);
146160

@@ -149,6 +163,20 @@ describe('IndexFilters', () => {
149163
});
150164
});
151165

166+
it('passes the disclosureZIndexOverride to the SearchFilterButton when provided', () => {
167+
const disclosureZIndexOverride = 517;
168+
const wrapper = mountWithApp(
169+
<IndexFilters
170+
{...defaultProps}
171+
disclosureZIndexOverride={disclosureZIndexOverride}
172+
/>,
173+
);
174+
175+
expect(wrapper).toContainReactComponent(SearchFilterButton, {
176+
disclosureZIndexOverride,
177+
});
178+
});
179+
152180
it('onQueryChange gets called correctly', () => {
153181
const wrapper = mountWithApp(
154182
<IndexFilters {...defaultProps} mode={IndexFiltersMode.Filtering} />,
@@ -161,13 +189,42 @@ describe('IndexFilters', () => {
161189
expect(defaultProps.onQueryChange).toHaveBeenCalledWith('bar');
162190
});
163191

164-
it('onSort gets called correctly', () => {
165-
const wrapper = mountWithApp(<IndexFilters {...defaultProps} />);
166-
wrapper.act(() => {
167-
wrapper.find(SortButton)!.trigger('onChange', ['customer-name asc']);
192+
describe('sortOptions', () => {
193+
it('does not render the SortButton component when sortOptions is not provided', () => {
194+
const wrapper = mountWithApp(
195+
<IndexFilters {...defaultProps} sortOptions={undefined} />,
196+
);
197+
198+
expect(wrapper).not.toContainReactComponent(SortButton);
199+
});
200+
201+
it('renders the SortButton component when sortOptions is provided', () => {
202+
const wrapper = mountWithApp(<IndexFilters {...defaultProps} />);
203+
expect(wrapper).toContainReactComponent(SortButton);
204+
});
205+
206+
it('passes zIndexOverride to the SortButton component when provided', () => {
207+
const disclosureZIndexOverride = 517;
208+
const wrapper = mountWithApp(
209+
<IndexFilters
210+
{...defaultProps}
211+
disclosureZIndexOverride={disclosureZIndexOverride}
212+
/>,
213+
);
214+
215+
expect(wrapper).toContainReactComponent(SortButton, {
216+
disclosureZIndexOverride,
217+
});
168218
});
169219

170-
expect(defaultProps.onSort).toHaveBeenCalledWith(['customer-name asc']);
220+
it('onSort gets called correctly', () => {
221+
const wrapper = mountWithApp(<IndexFilters {...defaultProps} />);
222+
wrapper.act(() => {
223+
wrapper.find(SortButton)!.trigger('onChange', ['customer-name asc']);
224+
});
225+
226+
expect(defaultProps.onSort).toHaveBeenCalledWith(['customer-name asc']);
227+
});
171228
});
172229

173230
describe('filters', () => {

polaris-react/src/components/Tabs/Tabs.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,20 @@ export interface TabsProps {
4242
selected: number;
4343
/** Whether the Tabs are disabled or not. */
4444
disabled?: boolean;
45-
/** Optional callback invoked when a Tab becomes selected. */
46-
onSelect?: (selectedTabIndex: number) => void;
4745
/** Whether to show the add new view Tab. */
4846
canCreateNewView?: boolean;
4947
/** Label for the new view Tab. Will override the default of "Create new view" */
5048
newViewAccessibilityLabel?: string;
51-
/** Optional callback invoked when a merchant saves a new view from the Modal */
52-
onCreateNewView?: (value: string) => Promise<boolean>;
5349
/** Fit tabs to container */
5450
fitted?: boolean;
5551
/** Text to replace disclosures horizontal dots */
5652
disclosureText?: string;
53+
/** Override z-index of popovers and tooltips */
54+
disclosureZIndexOverride?: number;
55+
/** Optional callback invoked when a Tab becomes selected. */
56+
onSelect?: (selectedTabIndex: number) => void;
57+
/** Optional callback invoked when a merchant saves a new view from the Modal */
58+
onCreateNewView?: (value: string) => Promise<boolean>;
5759
}
5860
const CREATE_NEW_VIEW_ID = 'create-new-view';
5961

@@ -68,6 +70,7 @@ export const Tabs = ({
6870
onSelect,
6971
fitted,
7072
disclosureText,
73+
disclosureZIndexOverride,
7174
}: TabsProps) => {
7275
const i18n = useI18n();
7376
const {mdDown} = useBreakpoints();
@@ -205,17 +208,19 @@ export const Tabs = ({
205208
onToggleModal={handleToggleModal}
206209
onTogglePopover={handleTogglePopover}
207210
viewNames={viewNames}
211+
disclosureZIndexOverride={disclosureZIndexOverride}
208212
ref={index === selected ? selectedTabRef : null}
209213
/>
210214
);
211215
},
212216
[
213217
disabled,
214-
handleTabClick,
215218
tabs,
216219
children,
217220
selected,
218221
tabToFocus,
222+
disclosureZIndexOverride,
223+
handleTabClick,
219224
handleToggleModal,
220225
handleTogglePopover,
221226
],
@@ -578,6 +583,7 @@ export const Tabs = ({
578583
active={disclosureActivatorVisible && showDisclosure}
579584
onClose={handleClose}
580585
autofocusTarget="first-node"
586+
zIndexOverride={disclosureZIndexOverride}
581587
>
582588
<List
583589
focusIndex={hiddenTabs.indexOf(tabToFocus)}
@@ -608,6 +614,7 @@ export const Tabs = ({
608614
)}
609615
preferredPosition="above"
610616
hoverDelay={400}
617+
zIndexOverride={disclosureZIndexOverride}
611618
>
612619
{newTab}
613620
</Tooltip>

0 commit comments

Comments
 (0)