Skip to content

Commit 67b95df

Browse files
dashedmarkstory
andauthored
feat(hybrid-cloud): Update create new organization button (#38717)
Co-authored-by: Mark Story <[email protected]>
1 parent 36e3a53 commit 67b95df

File tree

4 files changed

+151
-25
lines changed

4 files changed

+151
-25
lines changed

static/app/components/sidebar/sidebarDropdown/switchOrganization.tsx

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,48 @@ import {IconAdd, IconChevron} from 'sentry/icons';
1010
import {t} from 'sentry/locale';
1111
import space from 'sentry/styles/space';
1212
import {OrganizationSummary} from 'sentry/types';
13+
import useOrganization from 'sentry/utils/useOrganization';
14+
import useResolveRoute from 'sentry/utils/useResolveRoute';
1315
import withOrganizations from 'sentry/utils/withOrganizations';
1416

1517
import Divider from './divider.styled';
1618

19+
function CreateOrganization({canCreateOrganization}: {canCreateOrganization: boolean}) {
20+
const currentOrganization = useOrganization();
21+
const route = useResolveRoute('/organizations/new/');
22+
23+
if (!canCreateOrganization) {
24+
return null;
25+
}
26+
27+
const menuItemProps: Partial<React.ComponentProps<typeof SidebarMenuItem>> = {};
28+
29+
if (currentOrganization.features.includes('customer-domains')) {
30+
menuItemProps.href = route;
31+
menuItemProps.openInNewTab = false;
32+
} else {
33+
menuItemProps.to = route;
34+
}
35+
36+
return (
37+
<SidebarMenuItem
38+
data-test-id="sidebar-create-org"
39+
style={{alignItems: 'center'}}
40+
{...menuItemProps}
41+
>
42+
<MenuItemLabelWithIcon>
43+
<StyledIconAdd />
44+
<span>{t('Create a new organization')}</span>
45+
</MenuItemLabelWithIcon>
46+
</SidebarMenuItem>
47+
);
48+
}
49+
1750
type Props = {
1851
canCreateOrganization: boolean;
1952
organizations: OrganizationSummary[];
2053
};
54+
2155
/**
2256
* Switch Organization Menu Label + Sub Menu
2357
*/
@@ -64,18 +98,7 @@ const SwitchOrganization = ({organizations, canCreateOrganization}: Props) => (
6498
{organizations && !!organizations.length && canCreateOrganization && (
6599
<Divider css={{marginTop: 0}} />
66100
)}
67-
{canCreateOrganization && (
68-
<SidebarMenuItem
69-
data-test-id="sidebar-create-org"
70-
to="/organizations/new/"
71-
style={{alignItems: 'center'}}
72-
>
73-
<MenuItemLabelWithIcon>
74-
<StyledIconAdd />
75-
<span>{t('Create a new organization')}</span>
76-
</MenuItemLabelWithIcon>
77-
</SidebarMenuItem>
78-
)}
101+
{<CreateOrganization canCreateOrganization={canCreateOrganization} />}
79102
</SwitchOrganizationMenu>
80103
)}
81104
</Fragment>

static/app/components/sidebar/switchOrganization.spec.tsx

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
import {act, render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
22

33
import {SwitchOrganization} from 'sentry/components/sidebar/sidebarDropdown/switchOrganization';
4+
import {Organization} from 'sentry/types';
5+
import {OrganizationContext} from 'sentry/views/organizationContext';
46

57
describe('SwitchOrganization', function () {
8+
function mountWithOrg(children, organization?: Organization) {
9+
if (!organization) {
10+
organization = TestStubs.Organization() as Organization;
11+
}
12+
return (
13+
<OrganizationContext.Provider value={organization}>
14+
{children}
15+
</OrganizationContext.Provider>
16+
);
17+
}
18+
619
it('can list organizations', function () {
720
jest.useFakeTimers();
821
render(
9-
<SwitchOrganization
10-
canCreateOrganization={false}
11-
organizations={[
12-
TestStubs.Organization({name: 'Organization 1'}),
13-
TestStubs.Organization({name: 'Organization 2', slug: 'org2'}),
14-
]}
15-
/>
22+
mountWithOrg(
23+
<SwitchOrganization
24+
canCreateOrganization={false}
25+
organizations={[
26+
TestStubs.Organization({name: 'Organization 1'}),
27+
TestStubs.Organization({name: 'Organization 2', slug: 'org2'}),
28+
]}
29+
/>
30+
)
1631
);
1732

1833
userEvent.hover(screen.getByTestId('sidebar-switch-org'));
@@ -27,18 +42,26 @@ describe('SwitchOrganization', function () {
2742

2843
it('shows "Create an Org" if they have permission', function () {
2944
jest.useFakeTimers();
30-
render(<SwitchOrganization canCreateOrganization organizations={[]} />);
45+
render(mountWithOrg(<SwitchOrganization canCreateOrganization organizations={[]} />));
3146

3247
userEvent.hover(screen.getByTestId('sidebar-switch-org'));
3348
act(() => void jest.advanceTimersByTime(500));
3449

3550
expect(screen.getByTestId('sidebar-create-org')).toBeInTheDocument();
51+
52+
const createOrgLink = screen.getByRole('link', {name: 'Create a new organization'});
53+
expect(createOrgLink).toBeInTheDocument();
54+
expect(createOrgLink).toHaveAttribute('href', '/organizations/new/');
3655
jest.useRealTimers();
3756
});
3857

3958
it('does not have "Create an Org" if they do not have permission', function () {
4059
jest.useFakeTimers();
41-
render(<SwitchOrganization canCreateOrganization={false} organizations={[]} />);
60+
render(
61+
mountWithOrg(
62+
<SwitchOrganization canCreateOrganization={false} organizations={[]} />
63+
)
64+
);
4265

4366
userEvent.hover(screen.getByTestId('sidebar-switch-org'));
4467
act(() => void jest.advanceTimersByTime(500));
@@ -47,6 +70,36 @@ describe('SwitchOrganization', function () {
4770
jest.useRealTimers();
4871
});
4972

73+
it('uses sentry URL for "Create an Org"', function () {
74+
const currentOrg = TestStubs.Organization({
75+
name: 'Organization',
76+
slug: 'org',
77+
links: {
78+
organizationUrl: 'http://org.sentry.io',
79+
regionUrl: 'http://eu.sentry.io',
80+
},
81+
features: ['customer-domains'],
82+
});
83+
84+
jest.useFakeTimers();
85+
render(
86+
mountWithOrg(
87+
<SwitchOrganization canCreateOrganization organizations={[]} />,
88+
currentOrg
89+
)
90+
);
91+
92+
userEvent.hover(screen.getByTestId('sidebar-switch-org'));
93+
act(() => void jest.advanceTimersByTime(500));
94+
95+
expect(screen.getByTestId('sidebar-create-org')).toBeInTheDocument();
96+
97+
const createOrgLink = screen.getByRole('link', {name: 'Create a new organization'});
98+
expect(createOrgLink).toBeInTheDocument();
99+
expect(createOrgLink).toHaveAttribute('href', 'https://sentry.io/organizations/new/');
100+
jest.useRealTimers();
101+
});
102+
50103
it('shows orgs pending deletion with a special icon', function () {
51104
const orgPendingDeletion = TestStubs.Organization({
52105
slug: 'org-2',
@@ -55,10 +108,12 @@ describe('SwitchOrganization', function () {
55108

56109
jest.useFakeTimers();
57110
render(
58-
<SwitchOrganization
59-
canCreateOrganization
60-
organizations={[TestStubs.Organization(), orgPendingDeletion]}
61-
/>
111+
mountWithOrg(
112+
<SwitchOrganization
113+
canCreateOrganization
114+
organizations={[TestStubs.Organization(), orgPendingDeletion]}
115+
/>
116+
)
62117
);
63118

64119
userEvent.hover(screen.getByTestId('sidebar-switch-org'));
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {OrganizationSummary} from 'sentry/types';
2+
3+
function shouldUseLegacyRoute(organization: OrganizationSummary) {
4+
const {organizationUrl} = organization.links;
5+
return !organizationUrl || !organization.features.includes('customer-domains');
6+
}
7+
8+
export default shouldUseLegacyRoute;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import ConfigStore from 'sentry/stores/configStore';
2+
import {OrganizationSummary} from 'sentry/types';
3+
import useOrganization from 'sentry/utils/useOrganization';
4+
5+
import shouldUseLegacyRoute from './shouldUseLegacyRoute';
6+
7+
/**
8+
* If organization is passed, then a URL with the route will be returned with the customer domain prefix attached if the
9+
* organization has customer domain feature enabled.
10+
* Otherwise, if the organization is not given, then if the current organization has customer domain enabled, then we
11+
* use the sentry URL as the prefix.
12+
*/
13+
function useResolveRoute(route: string, organization?: OrganizationSummary) {
14+
const {sentryUrl} = ConfigStore.get('links');
15+
const currentOrganization = useOrganization();
16+
const hasCustomerDomain = currentOrganization.features.includes('customer-domains');
17+
18+
if (!organization) {
19+
if (hasCustomerDomain) {
20+
return `${sentryUrl}${route}`;
21+
}
22+
return route;
23+
}
24+
25+
const {organizationUrl} = organization.links;
26+
27+
const useLegacyRoute = shouldUseLegacyRoute(organization);
28+
if (useLegacyRoute) {
29+
if (hasCustomerDomain) {
30+
// If the current org is a customer domain, then we need to change the hostname in addition to
31+
// updating the path.
32+
33+
return `${sentryUrl}${route}`;
34+
}
35+
return route;
36+
}
37+
return `${organizationUrl}${route}`;
38+
}
39+
40+
export default useResolveRoute;

0 commit comments

Comments
 (0)