Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 71 additions & 7 deletions static/gsAdmin/components/addToStartupProgramAction.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('AddToStartupProgramAction', () => {
expect(await screen.findByRole('spinbutton', {name: 'Credit Amount'})).toHaveValue(
5000
);
expect(screen.getByRole('textbox', {name: 'Notes'})).toHaveValue('sentryforstartups');
expect(screen.getByText('sentryforstartups')).toBeInTheDocument();
});

it('can submit with default values', async () => {
Expand Down Expand Up @@ -97,7 +97,69 @@ describe('AddToStartupProgramAction', () => {
expect(onSuccess).toHaveBeenCalled();
});

it('can submit with custom values', async () => {
it('can submit with a different option selected', async () => {
const updateMock = MockApiClient.addMockResponse({
url: `/_admin/customers/${organization.slug}/balance-changes/`,
method: 'POST',
body: {},
});

triggerAddToStartupProgramModal(modalProps);

const {waitForModalToHide} = renderGlobalModal();

await userEvent.click(await screen.findByText('sentryforstartups'));
await userEvent.click(screen.getByText('ycombinator'));

await userEvent.click(screen.getByRole('button', {name: 'Submit'}));

await waitForModalToHide();

await waitFor(() => {
expect(updateMock).toHaveBeenCalledWith(
`/_admin/customers/${organization.slug}/balance-changes/`,
expect.objectContaining({
method: 'POST',
data: {
creditAmount: 500000,
ticketUrl: '',
notes: 'ycombinator',
},
})
);
});
});

it('shows custom notes field when "Enter custom notes" is selected', async () => {
triggerAddToStartupProgramModal(modalProps);

renderGlobalModal();

expect(screen.queryByRole('textbox', {name: 'Custom Notes'})).not.toBeInTheDocument();

await userEvent.click(await screen.findByText('sentryforstartups'));
await userEvent.click(screen.getByText('Enter custom notes'));

expect(screen.getByRole('textbox', {name: 'Custom Notes'})).toBeInTheDocument();
});

it('hides custom notes field when switching back to a preset option', async () => {
triggerAddToStartupProgramModal(modalProps);

renderGlobalModal();

// Select "Enter custom notes"
await userEvent.click(await screen.findByText('sentryforstartups'));
await userEvent.click(screen.getByText('Enter custom notes'));
expect(screen.getByRole('textbox', {name: 'Custom Notes'})).toBeInTheDocument();

// Switch back to a preset option
await userEvent.click(screen.getByText('Enter custom notes'));
await userEvent.click(screen.getByText('a16z'));
expect(screen.queryByRole('textbox', {name: 'Custom Notes'})).not.toBeInTheDocument();
});

it('can submit with custom notes', async () => {
const updateMock = MockApiClient.addMockResponse({
url: `/_admin/customers/${organization.slug}/balance-changes/`,
method: 'POST',
Expand All @@ -115,9 +177,13 @@ describe('AddToStartupProgramAction', () => {

await userEvent.type(screen.getByRole('textbox', {name: 'Ticket URL'}), url);

const notesInput = screen.getByRole('textbox', {name: 'Notes'});
await userEvent.clear(notesInput);
await userEvent.type(notesInput, 'custom note');
await userEvent.click(screen.getByText('sentryforstartups'));
await userEvent.click(screen.getByText('Enter custom notes'));

await userEvent.type(
screen.getByRole('textbox', {name: 'Custom Notes'}),
'custom note'
);

await userEvent.click(screen.getByRole('button', {name: 'Submit'}));

Expand Down Expand Up @@ -156,7 +222,6 @@ describe('AddToStartupProgramAction', () => {
expect(submitButton).toBeDisabled();
expect(screen.getByRole('spinbutton', {name: 'Credit Amount'})).toBeDisabled();
expect(screen.getByRole('textbox', {name: 'Ticket URL'})).toBeDisabled();
expect(screen.getByRole('textbox', {name: 'Notes'})).toBeDisabled();
await waitForModalToHide();
});

Expand Down Expand Up @@ -205,7 +270,6 @@ describe('AddToStartupProgramAction', () => {
{timeout: 5_000}
);
expect(screen.getByRole('textbox', {name: 'Ticket URL'})).toBeEnabled();
expect(screen.getByRole('textbox', {name: 'Notes'})).toBeEnabled();
expect(screen.getByRole('button', {name: /submit/i})).toBeEnabled();
}, 25_000);

Expand Down
44 changes: 40 additions & 4 deletions static/gsAdmin/components/addToStartupProgramAction.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Fragment} from 'react';
import {Fragment, useState} from 'react';

import {Flex} from '@sentry/scraps/layout';
import {Heading, Text} from '@sentry/scraps/text';
Expand All @@ -8,6 +8,7 @@ import type {ModalRenderProps} from 'sentry/actionCreators/modal';
import {openModal} from 'sentry/actionCreators/modal';
import {InputField} from 'sentry/components/forms/fields/inputField';
import {NumberField} from 'sentry/components/forms/fields/numberField';
import {SelectField} from 'sentry/components/forms/fields/selectField';
import {TextField} from 'sentry/components/forms/fields/textField';
import {Form, type FormProps} from 'sentry/components/forms/form';
import {fetchMutation, useMutation} from 'sentry/utils/queryClient';
Expand All @@ -16,6 +17,20 @@ import type {RequestError} from 'sentry/utils/requestError/requestError';
import type {Subscription} from 'getsentry/types';
import {formatBalance} from 'getsentry/utils/billing';

const STARTUP_PROGRAM_OPTIONS = [
{value: 'ycombinator', label: 'ycombinator'},
{value: 'sentryforstartups', label: 'sentryforstartups'},
{value: 'a16z', label: 'a16z'},
{value: 'accelatoms', label: 'accelatoms'},
{value: 'accelfam', label: 'accelfam'},
{value: 'renderstack', label: 'renderstack'},
{value: 'finpack', label: 'finpack'},
{value: 'betaworks', label: 'betaworks'},
{value: 'alchemist', label: 'alchemist'},
{value: 'antler', label: 'antler'},
{value: 'other', label: 'Enter custom notes'},
];

function coerceValue(value: number) {
if (isNaN(value)) {
return undefined;
Expand Down Expand Up @@ -46,6 +61,8 @@ function AddToStartupProgramModal({
Header,
Body,
}: AddToStartupProgramModalProps) {
const [showCustomNotes, setShowCustomNotes] = useState(false);

const {mutate, isPending} = useMutation<
Record<string, any>,
RequestError,
Expand Down Expand Up @@ -82,7 +99,13 @@ function AddToStartupProgramModal({
const creditAmountInput = Number(data.creditAmount);
const creditAmount = coerceValue(creditAmountInput);
const ticketUrl = typeof data.ticketUrl === 'string' ? data.ticketUrl : '';
const notes = typeof data.notes === 'string' ? data.notes : '';
const rawNotes = typeof data.notes === 'string' ? data.notes : '';
const notes =
rawNotes === 'other'
? typeof data.customNotes === 'string'
? data.customNotes
: ''
: rawNotes;

if (!creditAmount || isPending) {
return;
Expand Down Expand Up @@ -139,14 +162,27 @@ function AddToStartupProgramModal({
stacked
disabled={isPending}
/>
<TextField
<SelectField
name="notes"
label="Notes"
options={STARTUP_PROGRAM_OPTIONS}
inline={false}
stacked
maxLength={500}
disabled={isPending}
onChange={value => {
setShowCustomNotes(value === 'other');
}}
/>
{showCustomNotes && (
<TextField
name="customNotes"
label="Custom Notes"
inline={false}
stacked
maxLength={500}
disabled={isPending}
/>
)}
</div>
</Flex>
</Form>
Expand Down
Loading