Browse Source

ref(teambadge): remove class component and use rtl (#31479)

* ref(teambadge): remove class component and use rtl

* fix(teambadge): update query

* ref(badge): code review

* ref(badge): sort type keys

* fix(teambadge): remove index import
Jonas 3 years ago
parent
commit
c4a77a47af

+ 1 - 0
static/app/components/idBadge/baseBadge.tsx

@@ -42,6 +42,7 @@ const BaseBadge = React.memo(
           team={team}
           organization={organization}
           project={project}
+          data-test-id="badge-styled-avatar"
         />
       )}
 

+ 9 - 2
static/app/components/idBadge/getBadge.tsx

@@ -4,7 +4,7 @@ import BaseBadge from 'sentry/components/idBadge/baseBadge';
 import MemberBadge from 'sentry/components/idBadge/memberBadge';
 import OrganizationBadge from 'sentry/components/idBadge/organizationBadge';
 import ProjectBadge from 'sentry/components/idBadge/projectBadge';
-import TeamBadge from 'sentry/components/idBadge/teamBadge/badge';
+import {TeamBadge} from 'sentry/components/idBadge/teamBadge';
 import UserBadge from 'sentry/components/idBadge/userBadge';
 import {Member, User} from 'sentry/types';
 
@@ -17,7 +17,14 @@ type Props = Omit<BaseBadgeProps, 'displayName'> & {
   user?: User;
 };
 
-function getBadge({organization, team, project, user, member, ...props}: Props) {
+function getBadge({
+  organization,
+  team,
+  project,
+  user,
+  member,
+  ...props
+}: Props): React.ReactElement | null {
   if (organization) {
     return <OrganizationBadge organization={organization} {...props} />;
   }

+ 4 - 3
static/app/components/idBadge/teamBadge/badge.tsx

@@ -6,13 +6,14 @@ import BaseBadge from 'sentry/components/idBadge/baseBadge';
 type BaseBadgeProps = React.ComponentProps<typeof BaseBadge>;
 type Team = NonNullable<BaseBadgeProps['team']>;
 
-type Props = Partial<Omit<BaseBadgeProps, 'project' | 'organization' | 'team'>> & {
+export interface BadgeProps
+  extends Partial<Omit<BaseBadgeProps, 'project' | 'organization' | 'team'>> {
   team: Team;
   // If true, will use default max-width, or specify one as a string
   hideOverflow?: boolean | string;
-};
+}
 
-const Badge = ({hideOverflow = true, team, ...props}: Props) => (
+const Badge = ({hideOverflow = true, team, ...props}: BadgeProps): React.ReactElement => (
   <BaseBadge
     data-test-id="team-badge"
     displayName={

+ 24 - 41
static/app/components/idBadge/teamBadge/index.tsx

@@ -1,57 +1,40 @@
 import * as React from 'react';
-import isEqual from 'lodash/isEqual';
 
 import TeamStore from 'sentry/stores/teamStore';
 import {Team} from 'sentry/types';
 
-import Badge from './badge';
+import Badge, {BadgeProps} from './badge';
 
-type Props = React.ComponentProps<typeof Badge>;
+function TeamBadge(props: BadgeProps) {
+  const [team, setTeam] = React.useState<Team>(props.team);
 
-type State = {
-  team: Team;
-};
+  const onTeamStoreUpdate = React.useCallback(
+    (updatedTeam: Set<string>) => {
+      if (!updatedTeam.has(team.id)) {
+        return;
+      }
 
-class TeamBadgeContainer extends React.Component<Props, State> {
-  state: State = {team: this.props.team};
+      const newTeam = TeamStore.getById(team.id);
 
-  UNSAFE_componentWillReceiveProps(nextProps: Props) {
-    if (this.state.team === nextProps.team) {
-      return;
-    }
+      if (!newTeam) {
+        return;
+      }
 
-    if (isEqual(this.state.team, nextProps.team)) {
-      return;
-    }
-
-    this.setState({team: nextProps.team});
-  }
-
-  componentWillUnmount() {
-    this.unlistener?.();
-  }
-
-  unlistener = TeamStore.listen(
-    (team: Set<string>) => this.onTeamStoreUpdate(team),
-    undefined
+      setTeam(newTeam);
+    },
+    [team, TeamStore]
   );
 
-  onTeamStoreUpdate(updatedTeam: Set<string>) {
-    if (!updatedTeam.has(this.state.team.id)) {
-      return;
-    }
-
-    const team = TeamStore.getById(this.state.team.id);
-    if (!team || isEqual(team.avatar, this.state.team.avatar)) {
-      return;
-    }
+  React.useEffect(() => {
+    const unsubscribeTeam = TeamStore.listen(
+      (teamSet: Set<string>) => onTeamStoreUpdate(teamSet),
+      undefined
+    );
 
-    this.setState({team});
-  }
+    return () => unsubscribeTeam();
+  }, [onTeamStoreUpdate]);
 
-  render() {
-    return <Badge {...this.props} team={this.state.team} />;
-  }
+  return <Badge {...props} team={team} />;
 }
 
-export default TeamBadgeContainer;
+export {TeamBadge};

+ 1 - 0
static/app/stores/teamStore.tsx

@@ -18,6 +18,7 @@ type TeamStoreInterface = CommonStoreInterface<State> & {
   getAll(): Team[];
   getById(id: string): Team | null;
   getBySlug(slug: string): Team | null;
+  init(): void;
   initialized: boolean;
   loadInitialData(items: Team[], hasMore?: boolean | null, cursor?: string | null): void;
   onCreateSuccess(team: Team): void;

+ 1 - 1
static/app/views/settings/components/teamSelect.tsx

@@ -7,7 +7,7 @@ import Confirm from 'sentry/components/confirm';
 import DropdownAutoComplete from 'sentry/components/dropdownAutoComplete';
 import {Item} from 'sentry/components/dropdownAutoComplete/types';
 import DropdownButton from 'sentry/components/dropdownButton';
-import TeamBadge from 'sentry/components/idBadge/teamBadge';
+import {TeamBadge} from 'sentry/components/idBadge/teamBadge';
 import Link from 'sentry/components/links/link';
 import LoadingIndicator from 'sentry/components/loadingIndicator';
 import {Panel, PanelBody, PanelHeader, PanelItem} from 'sentry/components/panels';

+ 0 - 44
tests/js/spec/components/idBadge/teamBadge.spec.jsx

@@ -1,44 +0,0 @@
-import {mountWithTheme} from 'sentry-test/enzyme';
-
-import TeamBadge from 'sentry/components/idBadge/teamBadge';
-import TeamStore from 'sentry/stores/teamStore';
-
-describe('TeamBadge', function () {
-  beforeEach(() => {
-    TeamStore.init();
-  });
-
-  it('renders with Avatar and team name', function () {
-    const wrapper = mountWithTheme(
-      <TeamBadge team={TestStubs.Team()} />,
-      TestStubs.routerContext()
-    );
-    expect(wrapper.find('StyledAvatar')).toHaveLength(1);
-    expect(wrapper.find('BadgeDisplayName').text()).toEqual('#team-slug');
-  });
-
-  it('listens for avatar changes from TeamStore', function () {
-    const team = TestStubs.Team();
-    const wrapper = mountWithTheme(<TeamBadge team={team} />, TestStubs.routerContext());
-
-    TeamStore.onUpdateSuccess(team.id, {
-      ...team,
-      avatar: 'better_avatar.jpg',
-    });
-
-    expect(wrapper.state('team').avatar).toBe('better_avatar.jpg');
-  });
-
-  it('updates state from props', function () {
-    const team = TestStubs.Team();
-    const wrapper = mountWithTheme(<TeamBadge team={team} />, TestStubs.routerContext());
-    wrapper.setProps({
-      team: {
-        ...team,
-        avatar: 'better_avatar.jpg',
-      },
-    });
-
-    expect(wrapper.state('team').avatar).toBe('better_avatar.jpg');
-  });
-});

+ 45 - 0
tests/js/spec/components/idBadge/teamBadge.spec.tsx

@@ -0,0 +1,45 @@
+import {act, mountWithTheme, screen} from 'sentry-test/reactTestingLibrary';
+
+import {TeamBadge} from 'sentry/components/idBadge/teamBadge';
+import TeamStore from 'sentry/stores/teamStore';
+
+describe('TeamBadge', function () {
+  beforeEach(() => {
+    TeamStore.init();
+  });
+
+  it('renders with Avatar and team name', function () {
+    mountWithTheme(<TeamBadge team={TestStubs.Team()} />, {
+      context: TestStubs.routerContext(),
+    });
+    expect(screen.getByTestId('badge-styled-avatar')).toBeInTheDocument();
+    expect(screen.getByText(/#team-slug/)).toBeInTheDocument();
+  });
+
+  it('listens for avatar changes from TeamStore', async function () {
+    const team = TestStubs.Team();
+    mountWithTheme(<TeamBadge team={team} />, {
+      context: TestStubs.routerContext(),
+    });
+
+    act(() => {
+      TeamStore.onUpdateSuccess(team.id, {
+        ...team,
+        slug: 'new-team-slug',
+      });
+    });
+
+    expect(await screen.findByText(/#new-team-slug/)).toBeInTheDocument();
+  });
+
+  it('updates state from props', async function () {
+    const team = TestStubs.Team();
+    const {rerender} = mountWithTheme(<TeamBadge team={team} />, {
+      context: TestStubs.routerContext(),
+    });
+
+    rerender(<TeamBadge team={TestStubs.Team({slug: 'new-team-slug'})} />);
+
+    expect(await screen.findByText(/#new-team-slug/)).toBeInTheDocument();
+  });
+});

+ 1 - 1
tests/js/spec/views/settings/organizationMembers/organizationMemberDetail.spec.jsx

@@ -142,7 +142,7 @@ describe('OrganizationMemberDetail', function () {
       wrapper.find('TeamSelect DropdownButton').simulate('click');
 
       // Click the first item
-      wrapper.find('TeamSelect DropdownTeamBadge').first().simulate('click');
+      wrapper.find('TeamSelect [title="new team"]').at(0).simulate('click');
 
       // Save Member
       wrapper.find('Button[priority="primary"]').simulate('click');