Browse Source

feat(page-filters): Visual indicator for desynced filters on page filter dropdowns (#32144)

Now dropdowns will be colored differently when they are filled from URL params
David Wang 3 years ago
parent
commit
a7e5e21575

+ 9 - 10
static/app/components/datePageFilter.tsx

@@ -2,7 +2,7 @@ import {withRouter, WithRouterProps} from 'react-router';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
 
 
 import {updateDateTime} from 'sentry/actionCreators/pageFilters';
 import {updateDateTime} from 'sentry/actionCreators/pageFilters';
-import DropdownButton from 'sentry/components/dropdownButton';
+import PageFilterDropdownButton from 'sentry/components/organizations/pageFilters/pageFilterDropdownButton';
 import PageFilterPinButton from 'sentry/components/organizations/pageFilters/pageFilterPinButton';
 import PageFilterPinButton from 'sentry/components/organizations/pageFilters/pageFilterPinButton';
 import TimeRangeSelector, {
 import TimeRangeSelector, {
   ChangeData,
   ChangeData,
@@ -26,7 +26,7 @@ type Props = Omit<
 };
 };
 
 
 function DatePageFilter({router, resetParamsOnChange, ...props}: Props) {
 function DatePageFilter({router, resetParamsOnChange, ...props}: Props) {
-  const {selection} = useLegacyStore(PageFiltersStore);
+  const {selection, desyncedFilters} = useLegacyStore(PageFiltersStore);
   const organization = useOrganization();
   const organization = useOrganization();
   const {start, end, period, utc} = selection.datetime;
   const {start, end, period, utc} = selection.datetime;
 
 
@@ -54,11 +54,16 @@ function DatePageFilter({router, resetParamsOnChange, ...props}: Props) {
     }
     }
 
 
     return (
     return (
-      <StyledDropdownButton isOpen={isOpen} icon={<IconCalendar />} {...getActorProps()}>
+      <PageFilterDropdownButton
+        isOpen={isOpen}
+        icon={<IconCalendar />}
+        highlighted={desyncedFilters.has('datetime')}
+        {...getActorProps()}
+      >
         <DropdownTitle>
         <DropdownTitle>
           <TitleContainer>{label}</TitleContainer>
           <TitleContainer>{label}</TitleContainer>
         </DropdownTitle>
         </DropdownTitle>
-      </StyledDropdownButton>
+      </PageFilterDropdownButton>
     );
     );
   };
   };
 
 
@@ -96,12 +101,6 @@ const StyledPageTimeRangeSelector = styled(PageTimeRangeSelector)`
   box-shadow: none;
   box-shadow: none;
 `;
 `;
 
 
-const StyledDropdownButton = styled(DropdownButton)`
-  width: 100%;
-  height: 40px;
-  text-overflow: ellipsis;
-`;
-
 const TitleContainer = styled('div')`
 const TitleContainer = styled('div')`
   overflow: hidden;
   overflow: hidden;
   white-space: nowrap;
   white-space: nowrap;

+ 10 - 11
static/app/components/environmentPageFilter.tsx

@@ -3,8 +3,8 @@ import {withRouter, WithRouterProps} from 'react-router';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
 
 
 import {updateEnvironments} from 'sentry/actionCreators/pageFilters';
 import {updateEnvironments} from 'sentry/actionCreators/pageFilters';
-import DropdownButton from 'sentry/components/dropdownButton';
 import MultipleEnvironmentSelector from 'sentry/components/organizations/multipleEnvironmentSelector';
 import MultipleEnvironmentSelector from 'sentry/components/organizations/multipleEnvironmentSelector';
+import PageFilterDropdownButton from 'sentry/components/organizations/pageFilters/pageFilterDropdownButton';
 import {IconWindow} from 'sentry/icons';
 import {IconWindow} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import {t} from 'sentry/locale';
 import PageFiltersStore from 'sentry/stores/pageFiltersStore';
 import PageFiltersStore from 'sentry/stores/pageFiltersStore';
@@ -24,7 +24,7 @@ type Props = {
 function EnvironmentPageFilter({router, resetParamsOnChange = []}: Props) {
 function EnvironmentPageFilter({router, resetParamsOnChange = []}: Props) {
   const {projects, initiallyLoaded: projectsLoaded} = useProjects();
   const {projects, initiallyLoaded: projectsLoaded} = useProjects();
   const organization = useOrganization();
   const organization = useOrganization();
-  const {selection, isReady} = useLegacyStore(PageFiltersStore);
+  const {selection, isReady, desyncedFilters} = useLegacyStore(PageFiltersStore);
 
 
   const [selectedEnvironments, setSelectedEnvironments] = useState<string[] | null>(null);
   const [selectedEnvironments, setSelectedEnvironments] = useState<string[] | null>(null);
 
 
@@ -41,22 +41,26 @@ function EnvironmentPageFilter({router, resetParamsOnChange = []}: Props) {
 
 
   const customDropdownButton = ({isOpen, getActorProps, summary}) => {
   const customDropdownButton = ({isOpen, getActorProps, summary}) => {
     return (
     return (
-      <StyledDropdownButton isOpen={isOpen} {...getActorProps()}>
+      <PageFilterDropdownButton
+        isOpen={isOpen}
+        {...getActorProps()}
+        highlighted={desyncedFilters.has('environments')}
+      >
         <DropdownTitle>
         <DropdownTitle>
           <IconWindow />
           <IconWindow />
           <TitleContainer>{summary}</TitleContainer>
           <TitleContainer>{summary}</TitleContainer>
         </DropdownTitle>
         </DropdownTitle>
-      </StyledDropdownButton>
+      </PageFilterDropdownButton>
     );
     );
   };
   };
 
 
   const customLoadingIndicator = (
   const customLoadingIndicator = (
-    <StyledDropdownButton showChevron={false} disabled>
+    <PageFilterDropdownButton showChevron={false} disabled>
       <DropdownTitle>
       <DropdownTitle>
         <IconWindow />
         <IconWindow />
         {t('Loading\u2026')}
         {t('Loading\u2026')}
       </DropdownTitle>
       </DropdownTitle>
-    </StyledDropdownButton>
+    </PageFilterDropdownButton>
   );
   );
 
 
   return (
   return (
@@ -74,11 +78,6 @@ function EnvironmentPageFilter({router, resetParamsOnChange = []}: Props) {
   );
   );
 }
 }
 
 
-const StyledDropdownButton = styled(DropdownButton)`
-  width: 100%;
-  height: 40px;
-`;
-
 const TitleContainer = styled('div')`
 const TitleContainer = styled('div')`
   overflow: hidden;
   overflow: hidden;
   white-space: nowrap;
   white-space: nowrap;

+ 28 - 0
static/app/components/organizations/pageFilters/pageFilterDropdownButton.tsx

@@ -0,0 +1,28 @@
+import styled from '@emotion/styled';
+
+import DropdownButton from 'sentry/components/dropdownButton';
+
+type Props = {
+  /**
+   * Highlights the button blue. For page filters this indicates the filter
+   * has been desynced from the URL.
+   */
+  highlighted?: boolean;
+};
+
+export default styled(DropdownButton)<Props>`
+  width: 100%;
+  height: 40px;
+  text-overflow: ellipsis;
+  ${p =>
+    p.highlighted &&
+    `
+    &,
+    &:active,
+    &:hover,
+    &:focus {
+      background-color: ${p.theme.purple100};
+      border-color: ${p.theme.purple200};
+    }
+  `}
+`;

+ 10 - 12
static/app/components/projectPageFilter.tsx

@@ -5,8 +5,8 @@ import isEqual from 'lodash/isEqual';
 import partition from 'lodash/partition';
 import partition from 'lodash/partition';
 
 
 import {updateProjects} from 'sentry/actionCreators/pageFilters';
 import {updateProjects} from 'sentry/actionCreators/pageFilters';
-import DropdownButton from 'sentry/components/dropdownButton';
 import MultipleProjectSelector from 'sentry/components/organizations/multipleProjectSelector';
 import MultipleProjectSelector from 'sentry/components/organizations/multipleProjectSelector';
+import PageFilterDropdownButton from 'sentry/components/organizations/pageFilters/pageFilterDropdownButton';
 import PlatformList from 'sentry/components/platformList';
 import PlatformList from 'sentry/components/platformList';
 import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
 import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
 import {IconProject} from 'sentry/icons';
 import {IconProject} from 'sentry/icons';
@@ -69,7 +69,7 @@ export function ProjectPageFilter({router, specificProjectSlugs, ...otherProps}:
   );
   );
   const {projects, initiallyLoaded: projectsLoaded} = useProjects();
   const {projects, initiallyLoaded: projectsLoaded} = useProjects();
   const organization = useOrganization();
   const organization = useOrganization();
-  const {selection, isReady} = useLegacyStore(PageFiltersStore);
+  const {selection, isReady, desyncedFilters} = useLegacyStore(PageFiltersStore);
 
 
   useEffect(() => {
   useEffect(() => {
     if (!isEqual(selection.projects, currentSelectedProjects)) {
     if (!isEqual(selection.projects, currentSelectedProjects)) {
@@ -117,22 +117,26 @@ export function ProjectPageFilter({router, specificProjectSlugs, ...otherProps}:
       <IconProject />
       <IconProject />
     );
     );
     return (
     return (
-      <StyledDropdownButton isOpen={isOpen} {...getActorProps()}>
+      <PageFilterDropdownButton
+        isOpen={isOpen}
+        highlighted={desyncedFilters.has('projects')}
+        {...getActorProps()}
+      >
         <DropdownTitle>
         <DropdownTitle>
           {icon}
           {icon}
           <TitleContainer>{title}</TitleContainer>
           <TitleContainer>{title}</TitleContainer>
         </DropdownTitle>
         </DropdownTitle>
-      </StyledDropdownButton>
+      </PageFilterDropdownButton>
     );
     );
   };
   };
 
 
   const customLoadingIndicator = (
   const customLoadingIndicator = (
-    <StyledDropdownButton showChevron={false} disabled>
+    <PageFilterDropdownButton showChevron={false} disabled>
       <DropdownTitle>
       <DropdownTitle>
         <IconProject />
         <IconProject />
         {t('Loading\u2026')}
         {t('Loading\u2026')}
       </DropdownTitle>
       </DropdownTitle>
-    </StyledDropdownButton>
+    </PageFilterDropdownButton>
   );
   );
 
 
   return (
   return (
@@ -151,12 +155,6 @@ export function ProjectPageFilter({router, specificProjectSlugs, ...otherProps}:
   );
   );
 }
 }
 
 
-const StyledDropdownButton = styled(DropdownButton)`
-  width: 100%;
-  height: 40px;
-  text-overflow: ellipsis;
-`;
-
 const TitleContainer = styled('div')`
 const TitleContainer = styled('div')`
   overflow: hidden;
   overflow: hidden;
   white-space: nowrap;
   white-space: nowrap;