Skip to content

Commit 1054087

Browse files
feat: Filters for Failover History Table (#1079)
Add filters for Failover History table, but only for active-active domains Create filter for Cluster Attribute Scope, which depends on domain description to suggest possible scopes Create filter for Cluster Attribute Value, which depends on selected scope to suggest possible values/names Remove (now) unused styles file from DomainPageFailovers Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
1 parent 68394a8 commit 1054087

File tree

6 files changed

+450
-15
lines changed

6 files changed

+450
-15
lines changed
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import { render, screen, userEvent } from '@/test-utils/rtl';
2+
3+
import { type PageQueryParamValues } from '@/hooks/use-page-query-params/use-page-query-params.types';
4+
import { mockDomainPageQueryParamsValues } from '@/views/domain-page/__fixtures__/domain-page-query-params';
5+
import type domainPageQueryParamsConfig from '@/views/domain-page/config/domain-page-query-params.config';
6+
import { mockActiveActiveDomain } from '@/views/shared/active-active/__fixtures__/active-active-domain';
7+
import { type ActiveActiveDomain } from '@/views/shared/active-active/active-active.types';
8+
9+
import { PRIMARY_CLUSTER_SCOPE } from '../../domain-page-failovers/domain-page-failovers.constants';
10+
import DomainPageFailoversFilters from '../domain-page-failovers-filters';
11+
12+
describe(DomainPageFailoversFilters.name, () => {
13+
it('renders both filter comboboxes and reset button', () => {
14+
setup({});
15+
16+
expect(screen.getByText('Cluster Attribute Scope')).toBeInTheDocument();
17+
expect(screen.getByText('Cluster Attribute Value')).toBeInTheDocument();
18+
expect(screen.getByText('Reset filters')).toBeInTheDocument();
19+
});
20+
21+
it('displays cluster attribute scope options including primary and domain scopes', async () => {
22+
const { user } = setup({});
23+
24+
const scopeCombobox = screen.getByPlaceholderText(
25+
'Scope of cluster attribute'
26+
);
27+
await user.click(scopeCombobox);
28+
29+
expect(screen.getByText(PRIMARY_CLUSTER_SCOPE)).toBeInTheDocument();
30+
expect(screen.getByText('region')).toBeInTheDocument();
31+
});
32+
33+
it('disables cluster attribute value combobox when scope is primary', () => {
34+
setup({
35+
queryParamsOverrides: {
36+
clusterAttributeScope: PRIMARY_CLUSTER_SCOPE,
37+
},
38+
});
39+
40+
expect(
41+
screen.getByPlaceholderText('Value/name of cluster attribute')
42+
).toBeDisabled();
43+
});
44+
45+
it('enables cluster attribute value combobox when scope is not primary', () => {
46+
setup({
47+
queryParamsOverrides: {
48+
clusterAttributeScope: 'region',
49+
},
50+
});
51+
52+
expect(
53+
screen.getByPlaceholderText('Value/name of cluster attribute')
54+
).not.toBeDisabled();
55+
});
56+
57+
it('displays cluster attribute values for selected scope', async () => {
58+
const { user } = setup({
59+
queryParamsOverrides: {
60+
clusterAttributeScope: 'region',
61+
},
62+
});
63+
64+
const valueCombobox = screen.getByPlaceholderText(
65+
'Value/name of cluster attribute'
66+
);
67+
await user.click(valueCombobox);
68+
69+
expect(screen.getByText('region0')).toBeInTheDocument();
70+
expect(screen.getByText('region1')).toBeInTheDocument();
71+
});
72+
73+
it('calls setQueryParams with new scope and resets value when scope changes', async () => {
74+
const { user, mockSetQueryParams } = setup({});
75+
76+
const scopeCombobox = screen.getByPlaceholderText(
77+
'Scope of cluster attribute'
78+
);
79+
await user.click(scopeCombobox);
80+
await user.click(screen.getByText('region'));
81+
82+
expect(mockSetQueryParams).toHaveBeenCalledWith({
83+
clusterAttributeScope: 'region',
84+
clusterAttributeValue: undefined,
85+
});
86+
});
87+
88+
it('calls setQueryParams with new value when cluster attribute value changes', async () => {
89+
const { user, mockSetQueryParams } = setup({
90+
queryParamsOverrides: {
91+
clusterAttributeScope: 'region',
92+
},
93+
});
94+
95+
const valueCombobox = screen.getByPlaceholderText(
96+
'Value/name of cluster attribute'
97+
);
98+
await user.click(valueCombobox);
99+
await user.click(screen.getByText('region0'));
100+
101+
expect(mockSetQueryParams).toHaveBeenCalledWith({
102+
clusterAttributeValue: 'region0',
103+
});
104+
});
105+
106+
it('calls setQueryParams with undefined when clearing scope', async () => {
107+
const { user, mockSetQueryParams } = setup({
108+
queryParamsOverrides: {
109+
clusterAttributeScope: 'region',
110+
clusterAttributeValue: 'region0',
111+
},
112+
});
113+
114+
const scopeCombobox = screen.getByPlaceholderText(
115+
'Scope of cluster attribute'
116+
);
117+
await user.click(scopeCombobox);
118+
119+
// Find and click the clear button (BaseUI Combobox clearable)
120+
const clearButtons = screen.getAllByLabelText('Clear value');
121+
await user.click(clearButtons[0]);
122+
123+
expect(mockSetQueryParams).toHaveBeenCalledWith({
124+
clusterAttributeScope: undefined,
125+
clusterAttributeValue: undefined,
126+
});
127+
});
128+
129+
it('calls setQueryParams with undefined when clearing value', async () => {
130+
const { user, mockSetQueryParams } = setup({
131+
queryParamsOverrides: {
132+
clusterAttributeScope: 'region',
133+
clusterAttributeValue: 'region0',
134+
},
135+
});
136+
137+
const valueCombobox = screen.getByPlaceholderText(
138+
'Value/name of cluster attribute'
139+
);
140+
await user.click(valueCombobox);
141+
142+
// Find and click the clear button
143+
const clearButtons = screen.getAllByLabelText('Clear value');
144+
await user.click(clearButtons[1]);
145+
146+
expect(mockSetQueryParams).toHaveBeenCalledWith({
147+
clusterAttributeValue: undefined,
148+
});
149+
});
150+
151+
it('resets both filters when reset filters button is clicked', async () => {
152+
const { user, mockSetQueryParams } = setup({
153+
queryParamsOverrides: {
154+
clusterAttributeScope: 'region',
155+
clusterAttributeValue: 'region0',
156+
},
157+
});
158+
159+
const resetButton = screen.getByText('Reset filters');
160+
await user.click(resetButton);
161+
162+
expect(mockSetQueryParams).toHaveBeenCalledWith({
163+
clusterAttributeScope: undefined,
164+
clusterAttributeValue: undefined,
165+
});
166+
});
167+
168+
it('displays current scope value in combobox', () => {
169+
setup({
170+
queryParamsOverrides: {
171+
clusterAttributeScope: 'region',
172+
},
173+
});
174+
175+
expect(
176+
screen.getByPlaceholderText('Scope of cluster attribute')
177+
).toHaveValue('region');
178+
});
179+
180+
it('displays current value in cluster attribute value combobox', () => {
181+
setup({
182+
queryParamsOverrides: {
183+
clusterAttributeScope: 'region',
184+
clusterAttributeValue: 'region0',
185+
},
186+
});
187+
188+
expect(
189+
screen.getByPlaceholderText('Value/name of cluster attribute')
190+
).toHaveValue('region0');
191+
});
192+
});
193+
194+
function setup({
195+
queryParamsOverrides,
196+
domainDescriptionOverrides,
197+
}: {
198+
queryParamsOverrides?: Partial<
199+
PageQueryParamValues<typeof domainPageQueryParamsConfig>
200+
>;
201+
domainDescriptionOverrides?: Partial<ActiveActiveDomain>;
202+
} = {}) {
203+
const mockSetQueryParams = jest.fn();
204+
205+
const domainDescription = {
206+
...mockActiveActiveDomain,
207+
...domainDescriptionOverrides,
208+
};
209+
210+
const queryParams = {
211+
...mockDomainPageQueryParamsValues,
212+
...queryParamsOverrides,
213+
};
214+
215+
render(
216+
<DomainPageFailoversFilters
217+
domainDescription={domainDescription}
218+
queryParams={queryParams}
219+
setQueryParams={mockSetQueryParams}
220+
/>
221+
);
222+
223+
const user = userEvent.setup();
224+
225+
return { mockSetQueryParams, user };
226+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { type Theme } from 'baseui';
2+
import { styled as createStyled } from 'baseui';
3+
import { type ButtonOverrides } from 'baseui/button';
4+
import type { FormControlOverrides } from 'baseui/form-control/types';
5+
import { type StyleObject } from 'styletron-react';
6+
7+
export const styled = {
8+
FiltersContainer: createStyled('div', ({ $theme }) => ({
9+
display: 'flex',
10+
flexDirection: 'column',
11+
flexWrap: 'wrap',
12+
gap: $theme.sizing.scale500,
13+
[$theme.mediaQuery.medium]: {
14+
flexDirection: 'row',
15+
alignItems: 'flex-end',
16+
},
17+
marginTop: $theme.sizing.scale950,
18+
marginBottom: $theme.sizing.scale900,
19+
})),
20+
FilterContainer: createStyled('div', ({ $theme }) => ({
21+
flexGrow: 1,
22+
flexShrink: 1,
23+
flexBasis: '0',
24+
[$theme.mediaQuery.medium]: {
25+
alignSelf: 'flex-start',
26+
},
27+
})),
28+
};
29+
30+
export const overrides = {
31+
comboboxFormControl: {
32+
Label: {
33+
style: ({ $theme }: { $theme: Theme }): StyleObject => ({
34+
...$theme.typography.LabelXSmall,
35+
}),
36+
},
37+
ControlContainer: {
38+
style: (): StyleObject => ({
39+
margin: '0px',
40+
}),
41+
},
42+
} satisfies FormControlOverrides,
43+
clearFiltersButton: {
44+
Root: {
45+
style: ({ $theme }: { $theme: Theme }): StyleObject => ({
46+
whiteSpace: 'nowrap',
47+
flexGrow: 2,
48+
height: $theme.sizing.scale950,
49+
[$theme.mediaQuery.medium]: {
50+
flexGrow: 0,
51+
alignSelf: 'flex-end',
52+
},
53+
}),
54+
},
55+
} satisfies ButtonOverrides,
56+
};

0 commit comments

Comments
 (0)