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

feat(releases): Disable "date adopted" on multiple environments (#27950)

Scott Cooper 3 лет назад
Родитель
Сommit
f124a3016c

+ 12 - 19
static/app/views/releases/list/index.tsx

@@ -169,28 +169,20 @@ class ReleasesList extends AsyncView<Props, State> {
   }
 
   getSort(): SortOption {
+    const {environments} = this.props.selection;
     const {sort} = this.props.location.query;
 
-    switch (sort) {
-      case SortOption.CRASH_FREE_USERS:
-        return SortOption.CRASH_FREE_USERS;
-      case SortOption.CRASH_FREE_SESSIONS:
-        return SortOption.CRASH_FREE_SESSIONS;
-      case SortOption.SESSIONS:
-        return SortOption.SESSIONS;
-      case SortOption.USERS_24_HOURS:
-        return SortOption.USERS_24_HOURS;
-      case SortOption.SESSIONS_24_HOURS:
-        return SortOption.SESSIONS_24_HOURS;
-      case SortOption.BUILD:
-        return SortOption.BUILD;
-      case SortOption.SEMVER:
-        return SortOption.SEMVER;
-      case SortOption.ADOPTION:
-        return SortOption.ADOPTION;
-      default:
-        return SortOption.DATE;
+    // Require 1 environment for date adopted
+    if (sort === SortOption.ADOPTION && environments.length !== 1) {
+      return SortOption.DATE;
+    }
+
+    const sortExists = Object.values(SortOption).includes(sort);
+    if (sortExists) {
+      return sort;
     }
+
+    return SortOption.DATE;
   }
 
   getDisplay(): DisplayOption {
@@ -594,6 +586,7 @@ class ReleasesList extends AsyncView<Props, State> {
                   selected={activeSort}
                   selectedDisplay={activeDisplay}
                   onSelect={this.handleSortBy}
+                  environments={selection.environments}
                   organization={organization}
                 />
                 <ReleaseDisplayOptions

+ 2 - 2
static/app/views/releases/list/releaseDisplayOptions.tsx

@@ -6,8 +6,8 @@ import ReleaseListDropdown from './releaseListDropdown';
 import {DisplayOption} from './utils';
 
 const displayOptions = {
-  [DisplayOption.SESSIONS]: t('Sessions'),
-  [DisplayOption.USERS]: t('Users'),
+  [DisplayOption.SESSIONS]: {label: t('Sessions')},
+  [DisplayOption.USERS]: {label: t('Users')},
 };
 
 type Props = {

+ 32 - 13
static/app/views/releases/list/releaseListDropdown.tsx

@@ -1,13 +1,19 @@
+import {ComponentProps} from 'react';
+
 import DropdownControl, {DropdownItem} from 'app/components/dropdownControl';
+import Tooltip from 'app/components/tooltip';
 
-import {DisplayOption, SortOption, StatusOption} from './utils';
+type DropdownItemProps = Pick<
+  ComponentProps<typeof DropdownItem>,
+  'disabled' | 'title'
+> & {
+  label: string;
+  tooltip?: string;
+};
 
 type Props = {
   label: string;
-  options:
-    | Record<DisplayOption, string>
-    | Record<SortOption, string>
-    | Record<StatusOption, string>;
+  options: Record<string, DropdownItemProps>;
   selected: string;
   onSelect: (key: string) => void;
   className?: string;
@@ -24,16 +30,29 @@ const ReleaseListDropdown = ({
   const selectedLabel = optionEntries.find(([key, _value]) => key === selected)?.[1];
 
   return (
-    <DropdownControl buttonProps={{prefix}} label={selectedLabel} className={className}>
-      {optionEntries.map(([key, label]) => (
-        <DropdownItem
+    <DropdownControl
+      alwaysRenderMenu={false}
+      buttonProps={{prefix}}
+      label={selectedLabel?.label}
+      className={className}
+    >
+      {optionEntries.map(([key, {label, tooltip, ...props}]) => (
+        <Tooltip
           key={key}
-          onSelect={onSelect}
-          eventKey={key}
-          isActive={selected === key}
+          containerDisplayMode="block"
+          title={tooltip}
+          delay={500}
+          disabled={!tooltip}
         >
-          {label}
-        </DropdownItem>
+          <DropdownItem
+            onSelect={onSelect}
+            eventKey={key}
+            isActive={selected === key}
+            {...props}
+          >
+            {label}
+          </DropdownItem>
+        </Tooltip>
       ))}
     </DropdownControl>
   );

+ 20 - 10
static/app/views/releases/list/releaseListSortOptions.tsx

@@ -1,3 +1,4 @@
+import {ComponentProps} from 'react';
 import styled from '@emotion/styled';
 
 import {t} from 'app/locale';
@@ -11,6 +12,7 @@ type Props = {
   selectedDisplay: DisplayOption;
   onSelect: (key: string) => void;
   organization: Organization;
+  environments: string[];
 };
 
 function ReleaseListSortOptions({
@@ -18,28 +20,36 @@ function ReleaseListSortOptions({
   selectedDisplay,
   onSelect,
   organization,
+  environments,
 }: Props) {
   const sortOptions = {
-    [SortOption.DATE]: t('Date Created'),
-    [SortOption.SESSIONS]: t('Total Sessions'),
+    [SortOption.DATE]: {label: t('Date Created')},
+    [SortOption.SESSIONS]: {label: t('Total Sessions')},
     ...(selectedDisplay === DisplayOption.USERS
       ? {
-          [SortOption.USERS_24_HOURS]: t('Active Users'),
-          [SortOption.CRASH_FREE_USERS]: t('Crash Free Users'),
+          [SortOption.USERS_24_HOURS]: {label: t('Active Users')},
+          [SortOption.CRASH_FREE_USERS]: {label: t('Crash Free Users')},
         }
       : {
-          [SortOption.SESSIONS_24_HOURS]: t('Active Sessions'),
-          [SortOption.CRASH_FREE_SESSIONS]: t('Crash Free Sessions'),
+          [SortOption.SESSIONS_24_HOURS]: {label: t('Active Sessions')},
+          [SortOption.CRASH_FREE_SESSIONS]: {label: t('Crash Free Sessions')},
         }),
-  } as Record<SortOption, string>;
+  } as ComponentProps<typeof ReleaseListDropdown>['options'];
 
   if (organization.features.includes('semver')) {
-    sortOptions[SortOption.BUILD] = t('Build Number');
-    sortOptions[SortOption.SEMVER] = t('Semantic Version');
+    sortOptions[SortOption.BUILD] = {label: t('Build Number')};
+    sortOptions[SortOption.SEMVER] = {label: t('Semantic Version')};
   }
 
   if (organization.features.includes('release-adoption-stage')) {
-    sortOptions[SortOption.ADOPTION] = t('Date Adopted');
+    const isDisabled = environments.length !== 1;
+    sortOptions[SortOption.ADOPTION] = {
+      label: t('Date Adopted'),
+      disabled: isDisabled,
+      tooltip: isDisabled
+        ? t('Select one environment to use this sort option.')
+        : undefined,
+    };
   }
 
   return (

+ 2 - 2
static/app/views/releases/list/releaseListStatusOptions.tsx

@@ -6,8 +6,8 @@ import ReleaseListDropdown from './releaseListDropdown';
 import {StatusOption} from './utils';
 
 const options = {
-  [StatusOption.ACTIVE]: t('Active'),
-  [StatusOption.ARCHIVED]: t('Archived'),
+  [StatusOption.ACTIVE]: {label: t('Active')},
+  [StatusOption.ARCHIVED]: {label: t('Archived')},
 };
 
 type Props = {

+ 18 - 0
tests/js/spec/views/releases/list/index.spec.jsx

@@ -219,6 +219,24 @@ describe('ReleasesList', function () {
     });
   });
 
+  it('disables adoption sort when more than one environment is selected', function () {
+    wrapper.unmount();
+    const adoptionProps = {
+      ...props,
+      organization: {...organization, features: ['release-adoption-stage']},
+    };
+    wrapper = mountWithTheme(
+      <ReleasesList
+        {...adoptionProps}
+        location={{query: {sort: SortOption.ADOPTION}}}
+        selection={{...props.selection, environments: ['a', 'b']}}
+      />,
+      routerContext
+    );
+    const sortDropdown = wrapper.find('ReleaseListSortOptions');
+    expect(sortDropdown.find('ButtonLabel').text()).toBe('Sort ByDate Created');
+  });
+
   it('display the right Crash Free column', async function () {
     const displayDropdown = wrapper.find('ReleaseListDisplayOptions');