Browse Source

feat(widget-builder): Move top level filters to Filter step (#36663)

These are also disabled since we're pinning the
filters to the dashboard and don't want to allow
changes in the widget.
Shruthi 2 years ago
parent
commit
26c21649fe

+ 3 - 1
static/app/components/datePageFilter.tsx

@@ -31,7 +31,7 @@ type Props = Omit<
     resetParamsOnChange?: string[];
   };
 
-function DatePageFilter({router, resetParamsOnChange, ...props}: Props) {
+function DatePageFilter({router, resetParamsOnChange, disabled, ...props}: Props) {
   const {selection, desyncedFilters} = usePageFilters();
   const organization = useOrganization();
   const {start, end, period, utc} = selection.datetime;
@@ -70,6 +70,7 @@ function DatePageFilter({router, resetParamsOnChange, ...props}: Props) {
     return (
       <PageFilterDropdownButton
         detached
+        disabled={disabled}
         hideBottomBorder={false}
         isOpen={isOpen}
         highlighted={desyncedFilters.has('datetime')}
@@ -95,6 +96,7 @@ function DatePageFilter({router, resetParamsOnChange, ...props}: Props) {
       utc={utc}
       onUpdate={handleUpdate}
       customDropdownButton={customDropdownButton}
+      disabled={disabled}
       showPin
       detached
       {...props}

+ 4 - 0
static/app/components/environmentPageFilter.tsx

@@ -20,6 +20,7 @@ type EnvironmentSelectorProps = React.ComponentProps<typeof EnvironmentSelector>
 type Props = {
   router: WithRouterProps['router'];
   alignDropdown?: EnvironmentSelectorProps['alignDropdown'];
+  disabled?: EnvironmentSelectorProps['disabled'];
   /**
    * Max character length for the dropdown title. Default is 20. This number
    * is used to determine how many projects to show, and how much to truncate.
@@ -35,6 +36,7 @@ function EnvironmentPageFilter({
   router,
   resetParamsOnChange = [],
   alignDropdown,
+  disabled,
   maxTitleLength = 20,
 }: Props) {
   const {projects, initiallyLoaded: projectsLoaded} = useProjects();
@@ -67,6 +69,7 @@ function EnvironmentPageFilter({
         isOpen={isOpen}
         highlighted={desyncedFilters.has('environments')}
         data-test-id="page-filter-environment-selector"
+        disabled={disabled}
       >
         <DropdownTitle>
           <PageFilterPinIndicator filter="environments">
@@ -107,6 +110,7 @@ function EnvironmentPageFilter({
       customDropdownButton={customDropdownButton}
       customLoadingIndicator={customLoadingIndicator}
       alignDropdown={alignDropdown}
+      disabled={disabled}
       detached
       showPin
     />

+ 3 - 0
static/app/components/organizations/environmentSelector.tsx

@@ -49,6 +49,7 @@ type Props = WithRouterProps & {
   }) => React.ReactElement;
   customLoadingIndicator?: React.ReactNode;
   detached?: boolean;
+  disabled?: boolean;
   forceEnvironment?: string;
   /**
    * Show the pin button in the dropdown's header actions
@@ -72,6 +73,7 @@ function EnvironmentSelector({
   customDropdownButton,
   customLoadingIndicator,
   detached,
+  disabled,
   forceEnvironment,
   router,
   showPin,
@@ -241,6 +243,7 @@ function EnvironmentSelector({
           closeOnSelect
           blendCorner={false}
           detached={detached}
+          disabled={disabled}
           searchPlaceholder={t('Filter environments')}
           onSelect={handleQuickSelect}
           onClose={handleMenuClose}

+ 4 - 0
static/app/components/organizations/projectSelector/index.tsx

@@ -64,6 +64,10 @@ type Props = WithRouterProps & {
    * Only allow a single project to be selected at once
    */
   disableMultipleProjectSelection?: boolean;
+  /**
+   * Disable the dropdown
+   */
+  disabled?: boolean;
   /**
    * Message to show in the footer
    */

+ 10 - 0
static/app/components/organizations/timeRangeSelector/index.tsx

@@ -132,6 +132,11 @@ type Props = WithRouterProps & {
    */
   detached?: boolean;
 
+  /**
+   * Disable the dropdown
+   */
+  disabled?: boolean;
+
   /**
    * Small info icon with tooltip hint text
    */
@@ -386,6 +391,9 @@ class TimeRangeSelector extends PureComponent<Props, State> {
   };
 
   handleOpen = () => {
+    if (this.props.disabled) {
+      return;
+    }
     this.setState({isOpen: true});
     // Start loading react-date-picker
     import('../timeRangeSelector/dateRange/index');
@@ -407,6 +415,7 @@ class TimeRangeSelector extends PureComponent<Props, State> {
       maxPickableDays,
       customDropdownButton,
       detached,
+      disabled,
       alignDropdown,
       showPin,
     } = this.props;
@@ -450,6 +459,7 @@ class TimeRangeSelector extends PureComponent<Props, State> {
                 blendCorner={false}
                 maxHeight={400}
                 detached={detached}
+                disabled={disabled}
                 items={items}
                 searchPlaceholder={t('Provide a time range')}
                 rootClassName={css`

+ 4 - 0
static/app/components/projectPageFilter.tsx

@@ -26,6 +26,7 @@ import useProjects from 'sentry/utils/useProjects';
 type ProjectSelectorProps = React.ComponentProps<typeof ProjectSelector>;
 
 type Props = WithRouterProps & {
+  disabled?: ProjectSelectorProps['disabled'];
   /**
    * Message to display at the bottom of project list
    */
@@ -83,6 +84,7 @@ function ProjectPageFilter({
   specificProjectSlugs,
   maxTitleLength = 30,
   resetParamsOnChange = [],
+  disabled,
   ...otherProps
 }: Props) {
   const [currentSelectedProjects, setCurrentSelectedProjects] = useState<number[] | null>(
@@ -171,6 +173,7 @@ function ProjectPageFilter({
           isOpen={isOpen}
           highlighted={desyncedFilters.has('projects')}
           data-test-id="page-filter-project-selector"
+          disabled={disabled}
         >
           <DropdownTitle>
             <PageFilterPinIndicator filter="projects">{icon}</PageFilterPinIndicator>
@@ -208,6 +211,7 @@ function ProjectPageFilter({
       onApplyChange={handleApplyChange}
       customDropdownButton={customProjectDropdown}
       customLoadingIndicator={customLoadingIndicator}
+      disabled={disabled}
       detached
       showPin
       {...otherProps}

+ 21 - 6
static/app/views/dashboardsV2/releasesSelectControl.tsx

@@ -1,4 +1,4 @@
-import {Fragment, useState} from 'react';
+import {useState} from 'react';
 import styled from '@emotion/styled';
 
 import Badge from 'sentry/components/badge';
@@ -9,12 +9,17 @@ import {t} from 'sentry/locale';
 import {Release} from 'sentry/types';
 import {useReleases} from 'sentry/utils/releases/releasesProvider';
 
-function ReleasesSelectControl() {
+type Props = {
+  className?: string;
+  isDisabled?: boolean;
+};
+
+function ReleasesSelectControl({className, isDisabled}: Props) {
   const {releases, loading} = useReleases();
   const [selectedReleases, setSelectedReleases] = useState<Release[]>([]);
 
   const triggerLabel = selectedReleases.length ? (
-    <TextOverflow>{selectedReleases[0]}</TextOverflow>
+    <TextOverflow>{selectedReleases[0]} </TextOverflow>
   ) : (
     t('All Releases')
   );
@@ -24,8 +29,10 @@ function ReleasesSelectControl() {
       multiple
       isClearable
       isSearchable
+      isDisabled={isDisabled}
       isLoading={loading}
       menuTitle={t('Filter Releases')}
+      className={className}
       options={
         releases.length
           ? releases.map(release => {
@@ -39,12 +46,12 @@ function ReleasesSelectControl() {
       onChange={opts => setSelectedReleases(opts.map(opt => opt.value))}
       value={selectedReleases}
       triggerLabel={
-        <Fragment>
-          {triggerLabel}
+        <ButtonLabelWrapper>
+          {triggerLabel}{' '}
           {selectedReleases.length > 1 && (
             <StyledBadge text={`+${selectedReleases.length - 1}`} />
           )}
-        </Fragment>
+        </ButtonLabelWrapper>
       }
       triggerProps={{icon: <IconReleases />}}
     />
@@ -56,3 +63,11 @@ export default ReleasesSelectControl;
 const StyledBadge = styled(Badge)`
   flex-shrink: 0;
 `;
+
+const ButtonLabelWrapper = styled('span')`
+  width: 100%;
+  text-align: left;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+`;

+ 40 - 0
static/app/views/dashboardsV2/widgetBuilder/buildSteps/filterResultsStep/index.tsx

@@ -1,14 +1,22 @@
 import {useCallback, useEffect, useRef} from 'react';
 import styled from '@emotion/styled';
 
+import Feature from 'sentry/components/acl/feature';
 import Button from 'sentry/components/button';
+import ButtonBar from 'sentry/components/buttonBar';
+import DatePageFilter from 'sentry/components/datePageFilter';
+import EnvironmentPageFilter from 'sentry/components/environmentPageFilter';
 import Input from 'sentry/components/forms/controls/input';
 import Field from 'sentry/components/forms/field';
+import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
+import ProjectPageFilter from 'sentry/components/projectPageFilter';
 import {IconAdd, IconDelete} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
 import {Organization, PageFilters} from 'sentry/types';
+import {ReleasesProvider} from 'sentry/utils/releases/releasesProvider';
 import {getDatasetConfig} from 'sentry/views/dashboardsV2/datasetConfig/base';
+import ReleasesSelectControl from 'sentry/views/dashboardsV2/releasesSelectControl';
 import {WidgetQuery, WidgetType} from 'sentry/views/dashboardsV2/types';
 
 import {BuildStep} from '../buildStep';
@@ -100,6 +108,18 @@ export function FilterResultsStep({
           : t('This is how you filter down your search.')
       }
     >
+      <Feature features={['dashboards-top-level-filter']}>
+        <StyledPageFilterBar>
+          <ProjectPageFilter disabled />
+          <EnvironmentPageFilter disabled />
+          <DatePageFilter alignDropdown="left" disabled />
+        </StyledPageFilterBar>
+        <FilterButtons>
+          <ReleasesProvider organization={organization} selection={selection}>
+            <StyledReleasesSelectControl isDisabled className="widget-release-select" />
+          </ReleasesProvider>
+        </FilterButtons>
+      </Feature>
       <div>
         {queries.map((query, queryIndex) => {
           return (
@@ -165,6 +185,26 @@ const QueryField = styled(Field)`
   padding-bottom: ${space(1)};
 `;
 
+const StyledPageFilterBar = styled(PageFilterBar)`
+  margin-bottom: ${space(1)};
+  margin-right: ${space(2)};
+`;
+
+const FilterButtons = styled(ButtonBar)`
+  grid-template-columns: 1fr;
+
+  margin-bottom: ${space(1)};
+  margin-right: ${space(2)};
+
+  justify-content: space-between;
+`;
+
+const StyledReleasesSelectControl = styled(ReleasesSelectControl)`
+  button {
+    width: 100%;
+  }
+`;
+
 const SearchConditionsWrapper = styled('div')`
   display: flex;
   align-items: center;

+ 7 - 5
static/app/views/dashboardsV2/widgetBuilder/widgetBuilder.tsx

@@ -1047,11 +1047,13 @@ function WidgetBuilder({
               <Body>
                 <MainWrapper>
                   <Main>
-                    <StyledPageFilterBar condensed>
-                      <ProjectPageFilter />
-                      <EnvironmentPageFilter />
-                      <DatePageFilter alignDropdown="left" />
-                    </StyledPageFilterBar>
+                    {!!!organization.features.includes('dashboards-top-level-filter') && (
+                      <StyledPageFilterBar condensed>
+                        <ProjectPageFilter />
+                        <EnvironmentPageFilter />
+                        <DatePageFilter alignDropdown="left" />
+                      </StyledPageFilterBar>
+                    )}
                     <BuildSteps symbol="colored-numeric">
                       <VisualizationStep
                         widget={currentWidget}

+ 8 - 0
tests/js/spec/components/organizations/environmentSelector.spec.tsx

@@ -134,6 +134,14 @@ describe('EnvironmentSelector', function () {
     expect(screen.getByLabelText('dev')).toBeInTheDocument();
   });
 
+  it('does not open selector menu when disabled', async function () {
+    renderSelector({disabled: true});
+    await clickMenu();
+
+    // Dropdown not open
+    expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
+  });
+
   describe('Superuser My Projects / all environments', function () {
     it('shows env when no team belonging', async function () {
       ConfigStore.set('user', {...ConfigStore.get('user'), isSuperuser: true});

Some files were not shown because too many files changed in this diff