@parca/profile 0.19.45 → 0.19.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.19.47](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.46...@parca/profile@0.19.47) (2025-09-02)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.19.46](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.45...@parca/profile@0.19.46) (2025-08-28)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.19.45](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.44...@parca/profile@0.19.45) (2025-08-28)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -25,7 +25,7 @@ export function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGra
25
25
  const to = range.getToMs();
26
26
  let mergedProfileParams = {};
27
27
  if (query.profileType().delta) {
28
- mergedProfileParams = { mergeFrom: from, mergeTo: to };
28
+ mergedProfileParams = { mergeFrom: from * 1000000, mergeTo: to * 1000000 };
29
29
  }
30
30
  setTimeRangeSelection(range);
31
31
  selectQuery({
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/GroupByLabelsDropdown/index.tsx"],"names":[],"mappings":"AAsBA,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC9C;AAED,QAAA,MAAM,qBAAqB,GAAI,uCAAqC,KAAK,KAAG,GAAG,CAAC,OA0E/E,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/GroupByLabelsDropdown/index.tsx"],"names":[],"mappings":"AAwBA,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC9C;AAED,QAAA,MAAM,qBAAqB,GAAI,uCAAqC,KAAK,KAAG,GAAG,CAAC,OAoF/E,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -12,9 +12,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
  import Select from 'react-select';
15
+ import { testId } from '@parca/test-utils';
15
16
  import { FIELD_LABELS } from '../../../ProfileFlameGraph/FlameGraphArrow';
16
17
  const GroupByLabelsDropdown = ({ labels, groupBy, setGroupByLabels }) => {
17
- return (_jsxs("div", { className: "flex flex-col", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("label", { className: "text-sm", children: "Group by" }) }), _jsx(Select, { id: "h-group-by-labels-selector", isMulti: true, defaultMenuIsOpen: false, defaultValue: undefined, name: "labels", options: labels.map(label => ({ label, value: `${FIELD_LABELS}.${label}` })), className: "parca-select-container text-sm rounded-md bg-white", classNamePrefix: "parca-select", value: groupBy
18
+ return (_jsxs("div", { className: "flex flex-col", ...testId('GROUP_BY_CONTAINER'), children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("label", { className: "text-sm", ...testId('GROUP_BY_LABEL'), children: "Group by" }) }), _jsx(Select, { isMulti: true, defaultMenuIsOpen: false, defaultValue: undefined, name: "labels", options: labels.map(label => ({ label, value: `${FIELD_LABELS}.${label}` })), className: "parca-select-container text-sm rounded-md bg-white", classNamePrefix: "parca-select", menuPortalTarget: document.body, components: {
19
+ // eslint-disable-next-line react/prop-types
20
+ MenuList: ({ children, innerProps }) => (_jsx("div", { ...testId('GROUP_BY_SELECT_FLYOUT'), ...innerProps, children: children })),
21
+ }, value: groupBy
18
22
  .filter(l => l.startsWith(FIELD_LABELS))
19
23
  .map(l => ({ value: l, label: l.slice(FIELD_LABELS.length + 1) })), onChange: newValue => {
20
24
  setGroupByLabels(newValue.map(option => option.value));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/index.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAoB,KAAK,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,aAAa,KAAG,OASxD,CAAC;AAyIF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,cAAc,GAAI,eAAoB,mBAAwB,KAAG,GAAG,CAAC,OAsM1E,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,EAAoB,KAAK,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,aAAa,KAAG,OASxD,CAAC;AAyIF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,cAAc,GAAI,eAAoB,mBAAwB,KAAG,GAAG,CAAC,OAyN1E,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -15,6 +15,7 @@ import { useCallback } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import cx from 'classnames';
17
17
  import { Button, Input, Select } from '@parca/components';
18
+ import { testId } from '@parca/test-utils';
18
19
  import { useProfileViewContext } from '../../context/ProfileViewContext';
19
20
  import { getPresetByKey, getPresetsForProfileType, isPresetKey } from './filterPresets';
20
21
  import { useProfileFilters } from './useProfileFilters';
@@ -154,11 +155,11 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
154
155
  }
155
156
  }, [onApplyFilters]);
156
157
  const filtersToRender = localFilters.length > 0 ? localFilters : appliedFilters ?? [];
157
- return (_jsxs("div", { className: "flex gap-2 w-full items-start", children: [_jsxs("div", { className: "flex-1 flex flex-wrap gap-2", children: [filtersToRender.map(filter => {
158
+ return (_jsxs("div", { className: "flex gap-2 w-full items-start", ...testId('PROFILE_FILTERS_CONTAINER'), children: [_jsxs("div", { className: "flex-1 flex flex-wrap gap-2", children: [filtersToRender.map(filter => {
158
159
  const isNumberField = filter.field === 'address' || filter.field === 'line_number';
159
160
  const matchTypeItems = isNumberField ? numberMatchTypeItems : stringMatchTypeItems;
160
161
  const isPresetFilter = filter.type != null && isPresetKey(filter.type);
161
- return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", disabled: readOnly, onSelection: key => {
162
+ return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", disabled: readOnly, ...testId('FILTER_TYPE_SELECT'), flyoutTestId: "filter-type-select-flyout", onSelection: key => {
162
163
  // Check if this is a preset selection
163
164
  if (isPresetKey(key)) {
164
165
  const preset = getPresetByKey(key);
@@ -190,7 +191,7 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
190
191
  });
191
192
  }
192
193
  }
193
- }, className: cx('gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1', readOnly && isPresetFilter ? 'rounded-md' : 'rounded-l-md rounded-r-none', !readOnly && (isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none'), readOnly ? 'w-auto' : filter.type != null ? 'border-r-0 w-auto' : 'w-32'), hideCaretDropdown: readOnly }), filter.type != null && !isPresetFilter && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', disabled: readOnly, onSelection: key => {
194
+ }, className: cx('gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1', readOnly && isPresetFilter ? 'rounded-md' : 'rounded-l-md rounded-r-none', !readOnly && (isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none'), readOnly ? 'w-auto' : filter.type != null ? 'border-r-0 w-auto' : 'w-32'), hideCaretDropdown: readOnly }), filter.type != null && !isPresetFilter && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', disabled: readOnly, ...testId('FILTER_FIELD_SELECT'), flyoutTestId: "filter-field-select-flyout", onSelection: key => {
194
195
  const newField = key;
195
196
  const isNewFieldNumber = newField === 'address' || newField === 'line_number';
196
197
  const isCurrentFieldNumber = filter.field === 'address' || filter.field === 'line_number';
@@ -203,7 +204,7 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
203
204
  else {
204
205
  updateFilter(filter.id, { field: newField });
205
206
  }
206
- }, className: cx('rounded-none border-r-0 w-32 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Select, { items: matchTypeItems, selectedKey: filter.matchType ?? '', disabled: readOnly, onSelection: key => updateFilter(filter.id, { matchType: key }), className: cx('rounded-none border-r-0 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Input, { placeholder: "Value", value: filter.value, disabled: readOnly, onChange: e => updateFilter(filter.id, { value: e.target.value }), onKeyDown: handleKeyDown, className: "rounded-none w-36 text-sm focus:outline-1" })] })), !readOnly && (_jsx(Button, { variant: "neutral", onClick: () => {
207
+ }, className: cx('rounded-none border-r-0 w-32 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Select, { items: matchTypeItems, selectedKey: filter.matchType ?? '', disabled: readOnly, ...testId('FILTER_MATCH_TYPE_SELECT'), flyoutTestId: "filter-match-type-select-flyout", onSelection: key => updateFilter(filter.id, { matchType: key }), className: cx('rounded-none border-r-0 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Input, { placeholder: "Value", value: filter.value, disabled: readOnly, onChange: e => updateFilter(filter.id, { value: e.target.value }), onKeyDown: handleKeyDown, className: "rounded-none w-36 text-sm focus:outline-1", ...testId('FILTER_VALUE_INPUT') })] })), !readOnly && (_jsx(Button, { variant: "neutral", ...testId('FILTER_REMOVE_BUTTON'), onClick: () => {
207
208
  // If we're displaying local filters and this is the last one, reset everything
208
209
  if (localFilters.length > 0 && localFilters.length === 1) {
209
210
  resetFilters();
@@ -219,6 +220,6 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
219
220
  }, className: cx('h-[38px] p-3', filter.type != null
220
221
  ? 'rounded-none rounded-r-md'
221
222
  : 'rounded-l-none rounded-r-md'), children: _jsx(Icon, { icon: "mdi:close", className: "h-4 w-4" }) }))] }, filter.id));
222
- }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "neutral", onClick: addFilter, className: "p-3 h-[38px]", children: _jsx(Icon, { icon: "mdi:filter-plus-outline", className: "h-4 w-4" }) })), !readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (_jsxs(Button, { variant: "neutral", onClick: addFilter, className: "flex items-center gap-2", children: [_jsx(Icon, { icon: "mdi:filter-outline", className: "h-4 w-4" }), _jsx("span", { children: "Filter" })] }))] }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, disabled: !hasUnsavedChanges || !localFilters.some(isFilterComplete), className: cx('flex items-center gap-2 sticky top-0'), children: _jsx("span", { children: "Apply" }) }))] }));
223
+ }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "neutral", onClick: addFilter, className: "p-3 h-[38px]", ...testId('ADD_FILTER_BUTTON'), children: _jsx(Icon, { icon: "mdi:filter-plus-outline", className: "h-4 w-4" }) })), !readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (_jsxs(Button, { variant: "neutral", onClick: addFilter, className: "flex items-center gap-2", ...testId('ADD_FILTER_BUTTON'), children: [_jsx(Icon, { icon: "mdi:filter-outline", className: "h-4 w-4" }), _jsx("span", { children: "Filter" })] }))] }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, disabled: !hasUnsavedChanges || !localFilters.some(isFilterComplete), className: cx('flex items-center gap-2 sticky top-0'), ...testId('APPLY_FILTERS_BUTTON'), children: _jsx("span", { children: "Apply" }) }))] }));
223
224
  };
224
225
  export default ProfileFilters;
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.19.45",
3
+ "version": "0.19.47",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@floating-ui/react": "^0.27.12",
7
7
  "@headlessui/react": "^1.7.19",
8
8
  "@iconify/react": "^4.0.0",
9
9
  "@parca/client": "0.17.3",
10
- "@parca/components": "0.16.365",
10
+ "@parca/components": "0.16.366",
11
11
  "@parca/dynamicsize": "0.16.65",
12
12
  "@parca/hooks": "0.0.100",
13
13
  "@parca/icons": "0.16.72",
14
14
  "@parca/parser": "0.16.79",
15
15
  "@parca/store": "0.16.184",
16
- "@parca/test-utils": "0.0.7",
16
+ "@parca/test-utils": "0.0.8",
17
17
  "@parca/utilities": "0.0.107",
18
18
  "@popperjs/core": "^2.11.8",
19
19
  "@protobuf-ts/runtime-rpc": "^2.5.0",
@@ -79,5 +79,5 @@
79
79
  "access": "public",
80
80
  "registry": "https://registry.npmjs.org/"
81
81
  },
82
- "gitHead": "c427f3d04301d39138d77344d49a6bd44ab9d3d4"
82
+ "gitHead": "bb4ac9df3124432f6733cec5d84e74a68ad30338"
83
83
  }
@@ -76,7 +76,7 @@ export function MetricsGraphSection({
76
76
  const to = range.getToMs();
77
77
  let mergedProfileParams = {};
78
78
  if (query.profileType().delta) {
79
- mergedProfileParams = {mergeFrom: from, mergeTo: to};
79
+ mergedProfileParams = {mergeFrom: from * 1_000_000, mergeTo: to * 1_000_000};
80
80
  }
81
81
  setTimeRangeSelection(range);
82
82
  selectQuery({
@@ -13,6 +13,8 @@
13
13
 
14
14
  import Select from 'react-select';
15
15
 
16
+ import {testId} from '@parca/test-utils';
17
+
16
18
  import {FIELD_LABELS} from '../../../ProfileFlameGraph/FlameGraphArrow';
17
19
 
18
20
  interface LabelOption {
@@ -28,13 +30,14 @@ interface Props {
28
30
 
29
31
  const GroupByLabelsDropdown = ({labels, groupBy, setGroupByLabels}: Props): JSX.Element => {
30
32
  return (
31
- <div className="flex flex-col">
33
+ <div className="flex flex-col" {...testId('GROUP_BY_CONTAINER')}>
32
34
  <div className="flex items-center justify-between">
33
- <label className="text-sm">Group by</label>
35
+ <label className="text-sm" {...testId('GROUP_BY_LABEL')}>
36
+ Group by
37
+ </label>
34
38
  </div>
35
39
 
36
40
  <Select<LabelOption, true>
37
- id="h-group-by-labels-selector"
38
41
  isMulti
39
42
  defaultMenuIsOpen={false}
40
43
  defaultValue={undefined}
@@ -42,6 +45,15 @@ const GroupByLabelsDropdown = ({labels, groupBy, setGroupByLabels}: Props): JSX.
42
45
  options={labels.map(label => ({label, value: `${FIELD_LABELS}.${label}`}))}
43
46
  className="parca-select-container text-sm rounded-md bg-white"
44
47
  classNamePrefix="parca-select"
48
+ menuPortalTarget={document.body}
49
+ components={{
50
+ // eslint-disable-next-line react/prop-types
51
+ MenuList: ({children, innerProps}) => (
52
+ <div {...testId('GROUP_BY_SELECT_FLYOUT')} {...innerProps}>
53
+ {children}
54
+ </div>
55
+ ),
56
+ }}
45
57
  value={groupBy
46
58
  .filter(l => l.startsWith(FIELD_LABELS))
47
59
  .map(l => ({value: l, label: l.slice(FIELD_LABELS.length + 1)}))}
@@ -17,6 +17,7 @@ import {Icon} from '@iconify/react';
17
17
  import cx from 'classnames';
18
18
 
19
19
  import {Button, Input, Select, type SelectItem} from '@parca/components';
20
+ import {testId} from '@parca/test-utils';
20
21
 
21
22
  import {useProfileViewContext} from '../../context/ProfileViewContext';
22
23
  import {getPresetByKey, getPresetsForProfileType, isPresetKey} from './filterPresets';
@@ -204,7 +205,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
204
205
  const filtersToRender = localFilters.length > 0 ? localFilters : appliedFilters ?? [];
205
206
 
206
207
  return (
207
- <div className="flex gap-2 w-full items-start">
208
+ <div className="flex gap-2 w-full items-start" {...testId('PROFILE_FILTERS_CONTAINER')}>
208
209
  <div className="flex-1 flex flex-wrap gap-2">
209
210
  {filtersToRender.map(filter => {
210
211
  const isNumberField = filter.field === 'address' || filter.field === 'line_number';
@@ -218,6 +219,8 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
218
219
  selectedKey={filter.type}
219
220
  placeholder="Select Filter"
220
221
  disabled={readOnly}
222
+ {...testId('FILTER_TYPE_SELECT')}
223
+ flyoutTestId="filter-type-select-flyout"
221
224
  onSelection={key => {
222
225
  // Check if this is a preset selection
223
226
  if (isPresetKey(key)) {
@@ -266,6 +269,8 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
266
269
  items={fieldItems}
267
270
  selectedKey={filter.field ?? ''}
268
271
  disabled={readOnly}
272
+ {...testId('FILTER_FIELD_SELECT')}
273
+ flyoutTestId="filter-field-select-flyout"
269
274
  onSelection={key => {
270
275
  const newField = key as ProfileFilter['field'];
271
276
  const isNewFieldNumber = newField === 'address' || newField === 'line_number';
@@ -292,6 +297,8 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
292
297
  items={matchTypeItems}
293
298
  selectedKey={filter.matchType ?? ''}
294
299
  disabled={readOnly}
300
+ {...testId('FILTER_MATCH_TYPE_SELECT')}
301
+ flyoutTestId="filter-match-type-select-flyout"
295
302
  onSelection={key =>
296
303
  updateFilter(filter.id, {matchType: key as ProfileFilter['matchType']})
297
304
  }
@@ -309,6 +316,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
309
316
  onChange={e => updateFilter(filter.id, {value: e.target.value})}
310
317
  onKeyDown={handleKeyDown}
311
318
  className="rounded-none w-36 text-sm focus:outline-1"
319
+ {...testId('FILTER_VALUE_INPUT')}
312
320
  />
313
321
  </>
314
322
  )}
@@ -316,6 +324,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
316
324
  {!readOnly && (
317
325
  <Button
318
326
  variant="neutral"
327
+ {...testId('FILTER_REMOVE_BUTTON')}
319
328
  onClick={() => {
320
329
  // If we're displaying local filters and this is the last one, reset everything
321
330
  if (localFilters.length > 0 && localFilters.length === 1) {
@@ -345,13 +354,23 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
345
354
  })}
346
355
 
347
356
  {!readOnly && localFilters.length > 0 && (
348
- <Button variant="neutral" onClick={addFilter} className="p-3 h-[38px]">
357
+ <Button
358
+ variant="neutral"
359
+ onClick={addFilter}
360
+ className="p-3 h-[38px]"
361
+ {...testId('ADD_FILTER_BUTTON')}
362
+ >
349
363
  <Icon icon="mdi:filter-plus-outline" className="h-4 w-4" />
350
364
  </Button>
351
365
  )}
352
366
 
353
367
  {!readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (
354
- <Button variant="neutral" onClick={addFilter} className="flex items-center gap-2">
368
+ <Button
369
+ variant="neutral"
370
+ onClick={addFilter}
371
+ className="flex items-center gap-2"
372
+ {...testId('ADD_FILTER_BUTTON')}
373
+ >
355
374
  <Icon icon="mdi:filter-outline" className="h-4 w-4" />
356
375
  <span>Filter</span>
357
376
  </Button>
@@ -364,6 +383,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
364
383
  onClick={onApplyFilters}
365
384
  disabled={!hasUnsavedChanges || !localFilters.some(isFilterComplete)}
366
385
  className={cx('flex items-center gap-2 sticky top-0')}
386
+ {...testId('APPLY_FILTERS_BUTTON')}
367
387
  >
368
388
  <span>Apply</span>
369
389
  </Button>