Просмотр исходного кода

feat(page-filters): Add top level alert for desynced filters (#32141)

* top level alert for when filters are desynced

* state now inside alert

* address comments

* fix test
David Wang 3 лет назад
Родитель
Сommit
d5e0ab6b95

+ 2 - 0
static/app/components/organizations/pageFilters/container.tsx

@@ -9,6 +9,7 @@ import {
   updateEnvironments,
   updateProjects,
 } from 'sentry/actionCreators/pageFilters';
+import DesyncedFilterAlert from 'sentry/components/organizations/pageFilters/desyncedFiltersAlert';
 import ConfigStore from 'sentry/stores/configStore';
 import PageFiltersStore from 'sentry/stores/pageFiltersStore';
 import {useLegacyStore} from 'sentry/stores/useLegacyStore';
@@ -170,6 +171,7 @@ function Container({skipLoadLastUsed, children, ...props}: Props) {
   return (
     <Fragment>
       {!hideGlobalHeader && <GlobalSelectionHeader {...props} {...additionalProps} />}
+      {hideGlobalHeader && <DesyncedFilterAlert router={router} />}
       {children}
     </Fragment>
   );

+ 78 - 0
static/app/components/organizations/pageFilters/desyncedFiltersAlert.tsx

@@ -0,0 +1,78 @@
+import {useState} from 'react';
+import {InjectedRouter} from 'react-router';
+import styled from '@emotion/styled';
+
+import {revertToPinnedFilters} from 'sentry/actionCreators/pageFilters';
+import Alert from 'sentry/components/alert';
+import Button from 'sentry/components/button';
+import ButtonBar from 'sentry/components/buttonBar';
+import {IconClose, IconInfo} from 'sentry/icons';
+import {t} from 'sentry/locale';
+import PageFiltersStore from 'sentry/stores/pageFiltersStore';
+import {useLegacyStore} from 'sentry/stores/useLegacyStore';
+import space from 'sentry/styles/space';
+import useOrganization from 'sentry/utils/useOrganization';
+
+type Props = {
+  router: InjectedRouter;
+};
+
+export default function DesyncedFilterAlert({router}: Props) {
+  const {desyncedFilters} = useLegacyStore(PageFiltersStore);
+  const organization = useOrganization();
+  const [hideAlert, setHideAlert] = useState(false);
+
+  const onRevertClick = () => {
+    revertToPinnedFilters(organization.slug, router);
+  };
+
+  if (desyncedFilters.size === 0 || hideAlert) {
+    return null;
+  }
+
+  return (
+    <Alert type="info" icon={<IconInfo size="md" />} system>
+      <AlertWrapper>
+        <AlertText>
+          {t(
+            "You're viewing a shared link. Certain queries and filters have been automatically filled from URL parameters."
+          )}
+        </AlertText>
+        <ButtonBar gap={1.5}>
+          <RevertButton priority="link" size="zero" onClick={onRevertClick} borderless>
+            {t('Revert')}
+          </RevertButton>
+          <Button
+            priority="link"
+            size="zero"
+            icon={<IconClose color="purple300" />}
+            aria-label={t('Close Alert')}
+            onClick={() => setHideAlert(true)}
+          />
+        </ButtonBar>
+      </AlertWrapper>
+    </Alert>
+  );
+}
+
+const AlertWrapper = styled('div')`
+  display: flex;
+`;
+
+const AlertText = styled('div')`
+  flex: 1;
+  line-height: 22px;
+`;
+
+const RevertButton = styled(Button)`
+  display: flex;
+  font-weight: bold;
+  font-size: ${p => p.theme.fontSizeMedium};
+  color: ${p => p.theme.purple300};
+
+  &:after {
+    content: '|';
+    margin-left: ${space(2)};
+    color: ${p => p.theme.purple200};
+  }
+`;

+ 11 - 1
tests/js/spec/components/organizations/globalSelectionHeader.spec.jsx

@@ -11,6 +11,7 @@ import OrganizationsStore from 'sentry/stores/organizationsStore';
 import PageFiltersStore from 'sentry/stores/pageFiltersStore';
 import ProjectsStore from 'sentry/stores/projectsStore';
 import {getItem} from 'sentry/utils/localStorage';
+import {OrganizationContext} from 'sentry/views/organizationContext';
 
 const changeQuery = (routerContext, query) => ({
   ...routerContext,
@@ -571,7 +572,13 @@ describe('GlobalSelectionHeader', function () {
     OrganizationActions.update(initializationObj.organization);
 
     wrapper = mountWithTheme(
-      <PageFiltersContainer organization={initializationObj.organization} />,
+      <OrganizationContext.Provider value={initializationObj.organization}>
+        <PageFiltersContainer
+          organization={initializationObj.organization}
+          hideGlobalHeader
+        />
+      </OrganizationContext.Provider>,
+
       initializationObj.routerContext
     );
 
@@ -582,6 +589,9 @@ describe('GlobalSelectionHeader', function () {
     // Wait for desynced filters to update
     await tick();
     expect(PageFiltersStore.getState().desyncedFilters).toEqual(new Set(['projects']));
+
+    wrapper.update();
+    expect(wrapper.find('DesyncedFilterAlert')).toHaveLength(1);
   });
 
   /**