diff --git a/static/app/views/settings/organizationIntegrations/addIntegrationButton.spec.tsx b/static/app/views/settings/organizationIntegrations/addIntegrationButton.spec.tsx
index f40c74a190d5..7d27acdf8e26 100644
--- a/static/app/views/settings/organizationIntegrations/addIntegrationButton.spec.tsx
+++ b/static/app/views/settings/organizationIntegrations/addIntegrationButton.spec.tsx
@@ -1,7 +1,7 @@
import {IntegrationProviderFixture} from 'sentry-fixture/integrationProvider';
import {OrganizationFixture} from 'sentry-fixture/organization';
-import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
+import {act, render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
import * as pipelineModal from 'sentry/components/pipeline/modal';
import {AddIntegrationButton} from 'sentry/views/settings/organizationIntegrations/addIntegrationButton';
@@ -36,4 +36,31 @@ describe('AddIntegrationButton', () => {
})
);
});
+
+ it('keeps the button focusable with a tooltip when the provider cannot be added', async () => {
+ const openPipelineModalSpy = jest
+ .spyOn(pipelineModal, 'openPipelineModal')
+ .mockImplementation(() => {});
+
+ render(
+
+ );
+
+ const button = screen.getByRole('button', {name: 'Add integration'});
+ expect(button).toHaveAttribute('aria-disabled', 'true');
+
+ act(() => button.focus());
+ expect(
+ await screen.findByText(
+ `Integration cannot be added on Sentry. Enable this integration via the ${provider.name} instance.`
+ )
+ ).toBeInTheDocument();
+
+ await userEvent.click(button);
+ expect(openPipelineModalSpy).not.toHaveBeenCalled();
+ });
});
diff --git a/static/app/views/settings/organizationIntegrations/addIntegrationButton.tsx b/static/app/views/settings/organizationIntegrations/addIntegrationButton.tsx
index f6a4b70a76e5..a503eb438420 100644
--- a/static/app/views/settings/organizationIntegrations/addIntegrationButton.tsx
+++ b/static/app/views/settings/organizationIntegrations/addIntegrationButton.tsx
@@ -1,6 +1,5 @@
import type {ButtonProps} from '@sentry/scraps/button';
import {Button} from '@sentry/scraps/button';
-import {Tooltip} from '@sentry/scraps/tooltip';
import {t} from 'sentry/locale';
import type {IntegrationWithConfig} from 'sentry/types/integrations';
@@ -55,34 +54,44 @@ export function AddIntegrationButton({
});
return (
-
-
+ }
+ startFlow({
+ provider,
+ organization,
+ onInstall: onAddIntegration,
+ analyticsParams,
+ suppressSuccessMessage,
+ onCancel,
+ onError,
+ });
+ }}
+ >
+ {label}
+
);
}
diff --git a/static/app/views/settings/organizationIntegrations/directEnableButton.spec.tsx b/static/app/views/settings/organizationIntegrations/directEnableButton.spec.tsx
index cf6b89e08779..e131ecef3759 100644
--- a/static/app/views/settings/organizationIntegrations/directEnableButton.spec.tsx
+++ b/static/app/views/settings/organizationIntegrations/directEnableButton.spec.tsx
@@ -1,6 +1,6 @@
import {OrganizationFixture} from 'sentry-fixture/organization';
-import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
+import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
import {DirectEnableButton} from 'sentry/views/settings/organizationIntegrations/directEnableButton';
@@ -42,8 +42,8 @@ describe('DirectEnableButton', () => {
await waitFor(() => expect(mockPost).toHaveBeenCalledTimes(1));
});
- it('disables button when user does not have access', () => {
- MockApiClient.addMockResponse({
+ it('keeps the button focusable with a tooltip when user does not have access', async () => {
+ const mockPost = MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/integrations/direct-enable/github_copilot/`,
method: 'POST',
body: {},
@@ -53,6 +53,15 @@ describe('DirectEnableButton', () => {
organization,
});
- expect(screen.getByRole('button', {name: 'Enable Integration'})).toBeDisabled();
+ const button = screen.getByRole('button', {name: 'Enable Integration'});
+ expect(button).toHaveAttribute('aria-disabled', 'true');
+
+ act(() => button.focus());
+ expect(
+ await screen.findByText('You do not have permission to enable this integration.')
+ ).toBeInTheDocument();
+
+ await userEvent.click(button);
+ expect(mockPost).not.toHaveBeenCalled();
});
});
diff --git a/static/app/views/settings/organizationIntegrations/directEnableButton.tsx b/static/app/views/settings/organizationIntegrations/directEnableButton.tsx
index b95a61cbf060..460a02cb9522 100644
--- a/static/app/views/settings/organizationIntegrations/directEnableButton.tsx
+++ b/static/app/views/settings/organizationIntegrations/directEnableButton.tsx
@@ -1,7 +1,6 @@
import {useMutation, useQueryClient} from '@tanstack/react-query';
import {Button} from '@sentry/scraps/button';
-import {Tooltip} from '@sentry/scraps/tooltip';
import {addErrorMessage} from 'sentry/actionCreators/indicator';
import {t} from 'sentry/locale';
@@ -57,19 +56,29 @@ export function DirectEnableButton({
onError: () => addErrorMessage(t('Failed to enable integration.')),
});
+ const nativelyDisabled = buttonProps.disabled || isPending;
+
return (
- {
+ if (!userHasAccess) {
+ return;
+ }
+ enable();
+ }}
>
- enable()}
- >
- {t('Enable Integration')}
-
-
+ {t('Enable Integration')}
+
);
}