Skip to content

Commit f51d12c

Browse files
authored
Add read-only mode to ProfileFilters component (#5872)
Introduces a readOnly prop to the ProfileFilters component, disabling filter editing and hiding interactive controls when enabled.
1 parent 518eebb commit f51d12c

3 files changed

Lines changed: 66 additions & 36 deletions

File tree

ui/packages/shared/components/src/Select/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const Select = ({
4949
disabled = false,
5050
icon,
5151
id,
52+
hideCaretDropdown,
5253
}: {
5354
items: SelectItem[];
5455
selectedKey: string | undefined;
@@ -61,6 +62,7 @@ const Select = ({
6162
disabled?: boolean;
6263
icon?: JSX.Element;
6364
id?: string;
65+
hideCaretDropdown?: boolean;
6466
}): JSX.Element => {
6567
const selection = items.find(v => v.key === selectedKey) ?? {
6668
key: selectedKey,
@@ -83,7 +85,7 @@ const Select = ({
8385
className={cx(
8486
styles,
8587
width !== undefined ? `w-${width}` : 'w-full',
86-
disabled ? 'cursor-not-allowed opacity-50 pointer-events-none' : '',
88+
disabled ? 'cursor-not-allowed pointer-events-none' : '',
8789
primary ? primaryStyles : defaultStyles,
8890
{[className]: className.length > 0}
8991
)}
@@ -97,9 +99,11 @@ const Select = ({
9799
? selection.element.active
98100
: placeholder}
99101
</div>
100-
<div className={cx(icon != null ? '' : 'pointer-events-none text-gray-400')}>
101-
{icon ?? <Icon icon="heroicons:chevron-up-down-20-solid" aria-hidden="true" />}
102-
</div>
102+
{hideCaretDropdown !== true && (
103+
<div className={cx(icon != null ? '' : 'pointer-events-none text-gray-400')}>
104+
{icon ?? <Icon icon="heroicons:chevron-up-down-20-solid" aria-hidden="true" />}
105+
</div>
106+
)}
103107
</Listbox.Button>
104108
</div>
105109

ui/packages/shared/profile/src/ProfileView/components/ProfileFilters/index.tsx

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ const numberMatchTypeItems: SelectItem[] = [
168168
},
169169
];
170170

171-
const ProfileFilters = (): JSX.Element => {
171+
export interface ProfileFiltersProps {
172+
readOnly?: boolean;
173+
}
174+
175+
const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Element => {
172176
const {profileSource} = useProfileViewContext();
173177
const currentProfileType = profileSource?.ProfileType()?.toString();
174178
const filterTypeItems = getFilterTypeItems(currentProfileType);
@@ -213,6 +217,7 @@ const ProfileFilters = (): JSX.Element => {
213217
items={filterTypeItems}
214218
selectedKey={filter.type}
215219
placeholder="Select Filter"
220+
disabled={readOnly}
216221
onSelection={key => {
217222
// Check if this is a preset selection
218223
if (isPresetKey(key)) {
@@ -246,17 +251,21 @@ const ProfileFilters = (): JSX.Element => {
246251
}
247252
}}
248253
className={cx(
249-
'rounded-l-md pr-1 gap-0 focus:z-50 focus:relative focus:outline-1',
250-
isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none',
251-
filter.type != null ? 'border-r-0 w-auto' : 'w-32'
254+
'gap-0 focus:z-50 focus:relative focus:outline-1',
255+
readOnly ? '' : 'pr-1',
256+
readOnly && isPresetFilter ? 'rounded-md' : 'rounded-l-md rounded-r-none',
257+
!readOnly && (isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none'),
258+
readOnly ? 'w-auto' : filter.type != null ? 'border-r-0 w-auto' : 'w-32'
252259
)}
260+
hideCaretDropdown={readOnly}
253261
/>
254262

255263
{filter.type != null && !isPresetFilter && (
256264
<>
257265
<Select
258266
items={fieldItems}
259267
selectedKey={filter.field ?? ''}
268+
disabled={readOnly}
260269
onSelection={key => {
261270
const newField = key as ProfileFilter['field'];
262271
const isNewFieldNumber = newField === 'address' || newField === 'line_number';
@@ -272,70 +281,84 @@ const ProfileFilters = (): JSX.Element => {
272281
updateFilter(filter.id, {field: newField});
273282
}
274283
}}
275-
className="rounded-none border-r-0 w-32 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1"
284+
className={cx(
285+
'rounded-none border-r-0 w-32 gap-0 focus:z-50 focus:relative focus:outline-1',
286+
readOnly ? '' : 'pr-1'
287+
)}
288+
hideCaretDropdown={readOnly}
276289
/>
277290

278291
<Select
279292
items={matchTypeItems}
280293
selectedKey={filter.matchType ?? ''}
294+
disabled={readOnly}
281295
onSelection={key =>
282296
updateFilter(filter.id, {matchType: key as ProfileFilter['matchType']})
283297
}
284-
className="rounded-none border-r-0 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1"
298+
className={cx(
299+
'rounded-none border-r-0 gap-0 focus:z-50 focus:relative focus:outline-1',
300+
readOnly ? '' : 'pr-1'
301+
)}
302+
hideCaretDropdown={readOnly}
285303
/>
286304

287305
<Input
288306
placeholder="Value"
289307
value={filter.value}
308+
disabled={readOnly}
290309
onChange={e => updateFilter(filter.id, {value: e.target.value})}
291310
onKeyDown={handleKeyDown}
292311
className="rounded-none w-36 text-sm focus:outline-1"
293312
/>
294313
</>
295314
)}
296315

297-
<Button
298-
variant="neutral"
299-
onClick={() => {
300-
// If we're displaying local filters and this is the last one, reset everything
301-
if (localFilters.length > 0 && localFilters.length === 1) {
302-
resetFilters();
303-
}
304-
// If we're displaying applied filters and this is the last one, reset everything
305-
else if (localFilters.length === 0 && filtersToRender.length === 1) {
306-
resetFilters();
307-
}
308-
// Otherwise, just remove this specific filter
309-
else {
310-
removeFilter(filter.id);
311-
}
312-
}}
313-
className={cx(
314-
'h-[38px] p-3',
315-
filter.type != null ? 'rounded-none rounded-r-md' : 'rounded-l-none rounded-r-md'
316-
)}
317-
>
318-
<Icon icon="mdi:close" className="h-4 w-4" />
319-
</Button>
316+
{!readOnly && (
317+
<Button
318+
variant="neutral"
319+
onClick={() => {
320+
// If we're displaying local filters and this is the last one, reset everything
321+
if (localFilters.length > 0 && localFilters.length === 1) {
322+
resetFilters();
323+
}
324+
// If we're displaying applied filters and this is the last one, reset everything
325+
else if (localFilters.length === 0 && filtersToRender.length === 1) {
326+
resetFilters();
327+
}
328+
// Otherwise, just remove this specific filter
329+
else {
330+
removeFilter(filter.id);
331+
}
332+
}}
333+
className={cx(
334+
'h-[38px] p-3',
335+
filter.type != null
336+
? 'rounded-none rounded-r-md'
337+
: 'rounded-l-none rounded-r-md'
338+
)}
339+
>
340+
<Icon icon="mdi:close" className="h-4 w-4" />
341+
</Button>
342+
)}
320343
</div>
321344
);
322345
})}
323346

324-
{localFilters.length > 0 && (
347+
{!readOnly && localFilters.length > 0 && (
325348
<Button variant="neutral" onClick={addFilter} className="p-3 h-[38px]">
326349
<Icon icon="mdi:filter-plus-outline" className="h-4 w-4" />
327350
</Button>
328351
)}
329352

330-
{localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (
353+
{!readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (
331354
<Button variant="neutral" onClick={addFilter} className="flex items-center gap-2">
332355
<Icon icon="mdi:filter-outline" className="h-4 w-4" />
333356
<span>Filter</span>
334357
</Button>
335358
)}
336359
</div>
337360

338-
{localFilters.length > 0 && (
361+
{!readOnly && localFilters.length > 0 && (
339362
<Button
340363
variant="primary"
341364
onClick={onApplyFilters}

ui/packages/shared/profile/src/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export * from './ProfileTypeSelector';
2626
export * from './SourceView';
2727
export * from './ProfileMetricsGraph';
2828

29+
export {default as ProfileFilters} from './ProfileView/components/ProfileFilters';
30+
export {useProfileFiltersUrlState} from './ProfileView/components/ProfileFilters/useProfileFiltersUrlState';
31+
2932
export const DEFAULT_PROFILE_EXPLORER_PARAM_VALUES = {
3033
dashboard_items: 'flamegraph',
3134
};

0 commit comments

Comments
 (0)