From 664ee9e3629ba36885ba362968b21f6806ab5d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 27 Feb 2026 10:00:19 -0500 Subject: [PATCH 1/2] ref(settings): convert ProjectFiltersSettings from class to function component --- .../projectFilters/projectFiltersSettings.tsx | 204 ++++++++---------- 1 file changed, 92 insertions(+), 112 deletions(-) diff --git a/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx b/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx index c883c16bae4256..5b5d5a23364e51 100644 --- a/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx +++ b/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx @@ -1,4 +1,4 @@ -import {Component, Fragment, useCallback} from 'react'; +import {Fragment, useCallback, useState} from 'react'; import {css} from '@emotion/react'; import styled from '@emotion/styled'; import iconAndroid from 'sentry-logos/logo-android.svg'; @@ -185,6 +185,8 @@ const LEGACY_BROWSER_SUBFILTERS = { }, }; +type LegacyBrowserSubfilterKeys = Array; + type FormFieldProps = React.ComponentProps; type RowProps = { @@ -205,130 +207,108 @@ type RowState = { subfilters: Set; }; -class LegacyBrowserFilterRow extends Component { - constructor(props: RowProps) { - super(props); - - let initialSubfilters: any; - if (props.data.active === true) { - initialSubfilters = new Set( - Object.keys(LEGACY_BROWSER_SUBFILTERS).filter( - key => - !LEGACY_BROWSER_SUBFILTERS[key as keyof typeof LEGACY_BROWSER_SUBFILTERS] - .legacy - ) - ); - } else if (props.data.active === false) { - initialSubfilters = new Set(); - } else { - initialSubfilters = new Set(props.data.active); - } +function getActiveSubfilters() { + return new Set( + Object.keys(LEGACY_BROWSER_SUBFILTERS).filter( + key => + !LEGACY_BROWSER_SUBFILTERS[key as keyof typeof LEGACY_BROWSER_SUBFILTERS].legacy + ) + ); +} - this.state = { - loading: false, - error: false, - subfilters: initialSubfilters, - }; +function getInitialSubfilters(active: boolean | string[]): Set { + switch (active) { + case true: + return getActiveSubfilters(); + case false: + return new Set(); + default: + return new Set(active); } +} + +function LegacyBrowserFilterRow({data, disabled, onToggle}: RowProps) { + const [subfilters, setSubfilters] = useState(getInitialSubfilters(data.active)); - handleToggleSubfilters = (subfilter: boolean, e: React.MouseEvent) => { - let {subfilters} = this.state; + const handleToggleSubfilters = (subfilter: boolean, e: React.MouseEvent) => { + let newSubfilters = new Set(subfilters); if (subfilter === true) { - subfilters = new Set( - Object.keys(LEGACY_BROWSER_SUBFILTERS).filter( - key => - !LEGACY_BROWSER_SUBFILTERS[key as keyof typeof LEGACY_BROWSER_SUBFILTERS] - .legacy - ) - ); + newSubfilters = getActiveSubfilters(); } else if (subfilter === false) { - subfilters = new Set(); - } else if (subfilters.has(subfilter)) { - subfilters.delete(subfilter); + newSubfilters = new Set(); + } else if (newSubfilters.has(subfilter)) { + newSubfilters.delete(subfilter); } else { - subfilters.add(subfilter); + newSubfilters.add(subfilter); } - this.setState( - { - subfilters: new Set(subfilters), - }, - () => { - this.props.onToggle(this.props.data, subfilters, e); - } - ); + setSubfilters(newSubfilters); + onToggle(data, newSubfilters, e); }; - render() { - const {disabled} = this.props; - return ( + return ( +
-
- - - {t('Filter out legacy browsers')}: - - - - - - - - {t( - 'The browser versions filtered out will be periodically evaluated and updated.' - )} - -
- - {Object.keys(LEGACY_BROWSER_SUBFILTERS) - .filter(key => { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - if (!LEGACY_BROWSER_SUBFILTERS[key].legacy) { - return true; - } - return this.state.subfilters.has(key); - }) - .map(key => { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - const subfilter = LEGACY_BROWSER_SUBFILTERS[key]; - return ( - - -
- {subfilter.title} - {subfilter.helpText} -
- -
- ); - })} -
+ + {t('Filter out legacy browsers')}: + + + + + + + {t( + 'The browser versions filtered out will be periodically evaluated and updated.' + )} +
- ); - } + + {(Object.keys(LEGACY_BROWSER_SUBFILTERS) as LegacyBrowserSubfilterKeys) + .filter(key => { + if (!LEGACY_BROWSER_SUBFILTERS[key].legacy) { + return true; + } + return subfilters.has(key); + }) + .map(key => { + const subfilter = LEGACY_BROWSER_SUBFILTERS[key]; + return ( + + +
+ {subfilter.title} + {subfilter.helpText} +
+ +
+ ); + })} +
+
+ ); } function CustomFilters({project, disabled}: {disabled: boolean; project: Project}) { From ee7c30dfaa0bbf9e48ccc7beb222b2ac06d0415d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 9 Mar 2026 10:32:50 -0400 Subject: [PATCH 2/2] ref: remove RowState altogether --- .../project/projectFilters/projectFiltersSettings.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx b/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx index 5b5d5a23364e51..0f732f82a45642 100644 --- a/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx +++ b/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx @@ -195,18 +195,12 @@ type RowProps = { }; onToggle: ( data: RowProps['data'], - filters: RowState['subfilters'], + filters: Set, event: React.MouseEvent ) => void; disabled?: boolean; }; -type RowState = { - error: boolean | Error; - loading: boolean; - subfilters: Set; -}; - function getActiveSubfilters() { return new Set( Object.keys(LEGACY_BROWSER_SUBFILTERS).filter( @@ -428,7 +422,7 @@ export function ProjectFiltersSettings({project, params, features}: Props) { event: React.MouseEvent; onBlur: FormFieldProps['onBlur']; onChange: FormFieldProps['onChange']; - subfilters: RowState['subfilters']; + subfilters: Set; }) => { onChange?.(subfilters, event); onBlur?.(subfilters, event);