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

feat(autofix): Redesign autofix message box actions (#82086)

For clarity, hanges the segmented control pattern for user-selected
options to a button bar with a second screen with the custom content and
action button.

<img width="665" alt="Screenshot 2024-12-13 at 9 11 21 AM"
src="https://github.com/user-attachments/assets/8452c15c-75bd-40e0-a03b-dfe98e3e4dbd"
/>
<img width="682" alt="Screenshot 2024-12-13 at 9 11 34 AM"
src="https://github.com/user-attachments/assets/d4df96ec-e2be-43e4-bf36-3be74a8982c4"
/>
Rohan Agarwal 2 месяцев назад
Родитель
Сommit
53c1f61291

+ 103 - 0
static/app/components/events/autofix/autofixActionSelector.tsx

@@ -0,0 +1,103 @@
+import styled from '@emotion/styled';
+import {AnimatePresence, motion} from 'framer-motion';
+
+import {Button} from 'sentry/components/button';
+import ButtonBar from 'sentry/components/buttonBar';
+import {IconArrow} from 'sentry/icons';
+import {t} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+import testableTransition from 'sentry/utils/testableTransition';
+
+interface Option<T extends string> {
+  key: T;
+  label: string;
+  active?: boolean;
+}
+
+interface Props<T extends string> {
+  children: (selectedOption: Option<T>) => React.ReactNode;
+  onBack: () => void;
+  onSelect: (value: T) => void;
+  options: Option<T>[];
+  selected: T | null;
+}
+
+function AutofixActionSelector<T extends string>({
+  options,
+  selected,
+  onSelect,
+  onBack,
+  children,
+}: Props<T>) {
+  const selectedOption = options.find(opt => opt.key === selected);
+
+  return (
+    <Container>
+      <AnimatePresence mode="wait">
+        {!selected ? (
+          <motion.div
+            key="options"
+            initial="initial"
+            animate="visible"
+            variants={{
+              initial: {opacity: 0, scale: 1.05},
+              visible: {opacity: 1, scale: 1},
+            }}
+            transition={testableTransition({duration: 0.1})}
+          >
+            <ButtonBar merged>
+              {options.map(option => (
+                <Button
+                  key={option.key}
+                  priority={option.active ? 'primary' : 'default'}
+                  onClick={() => onSelect(option.key)}
+                >
+                  {option.label}
+                </Button>
+              ))}
+            </ButtonBar>
+          </motion.div>
+        ) : (
+          <motion.div
+            key="content"
+            initial={{opacity: 0, scale: 0.95}}
+            animate={{opacity: 1, scale: 1}}
+            transition={testableTransition({duration: 0.1})}
+          >
+            <ContentWrapper>
+              <BackButton
+                size="xs"
+                icon={<IconArrow direction="left" size="xs" />}
+                onClick={onBack}
+                title={t('Back to options')}
+                aria-label={t('Back to options')}
+              />
+              <ContentArea>{selectedOption && children(selectedOption)}</ContentArea>
+            </ContentWrapper>
+          </motion.div>
+        )}
+      </AnimatePresence>
+    </Container>
+  );
+}
+
+const Container = styled('div')`
+  min-height: 40px;
+`;
+
+const ContentWrapper = styled('div')`
+  display: flex;
+  align-items: center;
+  gap: ${space(1)};
+`;
+
+const BackButton = styled(Button)`
+  flex-shrink: 0;
+  height: 40px;
+`;
+
+const ContentArea = styled('div')`
+  flex-grow: 1;
+`;
+
+export default AutofixActionSelector;

+ 7 - 3
static/app/components/events/autofix/autofixMessageBox.analytics.spec.tsx

@@ -45,12 +45,14 @@ describe('AutofixMessageBox Analytics', () => {
     mockButton.mockClear();
   });
 
-  it('passes correct analytics props for suggested root cause without instructions', () => {
+  it('passes correct analytics props for suggested root cause without instructions', async () => {
     const onSendMock = jest.fn();
     render(
       <AutofixMessageBox {...defaultProps} onSend={onSendMock} isRootCauseSelectionStep />
     );
 
+    await userEvent.click(screen.getByRole('button', {name: 'Use suggested root cause'}));
+
     expect(mockButton).toHaveBeenLastCalledWith(
       expect.objectContaining({
         analyticsEventKey: 'autofix.create_fix_clicked',
@@ -70,6 +72,8 @@ describe('AutofixMessageBox Analytics', () => {
       <AutofixMessageBox {...defaultProps} onSend={onSendMock} isRootCauseSelectionStep />
     );
 
+    await userEvent.click(screen.getByRole('button', {name: 'Use suggested root cause'}));
+
     const input = screen.getByPlaceholderText(
       '(Optional) Provide any instructions for the fix...'
     );
@@ -126,7 +130,7 @@ describe('AutofixMessageBox Analytics', () => {
 
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Approve'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Approve'}));
 
     // Find the last call to Button that matches our Create PR button
     const createPRButtonCall = mockButton.mock.calls.find(
@@ -156,7 +160,7 @@ describe('AutofixMessageBox Analytics', () => {
 
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Approve'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Approve'}));
 
     // Find the last call to Button that matches our Setup button
     const setupButtonCall = mockButton.mock.calls.find(

+ 21 - 18
static/app/components/events/autofix/autofixMessageBox.spec.tsx

@@ -90,7 +90,7 @@ describe('AutofixMessageBox', () => {
 
     expect(screen.getByText('Test display text')).toBeInTheDocument();
     expect(
-      screen.getByPlaceholderText('Share helpful context or feedback...')
+      screen.getByPlaceholderText('Share helpful context or directions...')
     ).toBeInTheDocument();
     expect(screen.getByRole('button', {name: 'Send'})).toBeInTheDocument();
   });
@@ -99,7 +99,7 @@ describe('AutofixMessageBox', () => {
     const onSendMock = jest.fn();
     render(<AutofixMessageBox {...defaultProps} onSend={onSendMock} />);
 
-    const input = screen.getByPlaceholderText('Share helpful context or feedback...');
+    const input = screen.getByPlaceholderText('Share helpful context or directions...');
     await userEvent.type(input, 'Test message');
     await userEvent.click(screen.getByRole('button', {name: 'Send'}));
 
@@ -115,7 +115,7 @@ describe('AutofixMessageBox', () => {
 
     render(<AutofixMessageBox {...defaultProps} />);
 
-    const input = screen.getByPlaceholderText('Share helpful context or feedback...');
+    const input = screen.getByPlaceholderText('Share helpful context or directions...');
     await userEvent.type(input, 'Test message');
     await userEvent.click(screen.getByRole('button', {name: 'Send'}));
 
@@ -136,7 +136,7 @@ describe('AutofixMessageBox', () => {
 
     render(<AutofixMessageBox {...defaultProps} />);
 
-    const input = screen.getByPlaceholderText('Share helpful context or feedback...');
+    const input = screen.getByPlaceholderText('Share helpful context or directions...');
     await userEvent.type(input, 'Test message');
     await userEvent.click(screen.getByRole('button', {name: 'Send'}));
 
@@ -173,6 +173,7 @@ describe('AutofixMessageBox', () => {
     );
 
     // Test suggested root cause
+    await userEvent.click(screen.getByRole('button', {name: 'Use suggested root cause'}));
     const input = screen.getByPlaceholderText(
       '(Optional) Provide any instructions for the fix...'
     );
@@ -189,7 +190,9 @@ describe('AutofixMessageBox', () => {
     );
 
     // Test custom root cause
-    await userEvent.click(screen.getAllByText('Propose your own root cause')[0]);
+    await userEvent.click(
+      screen.getByRole('button', {name: 'Propose your own root cause'})
+    );
     const customInput = screen.getByPlaceholderText('Propose your own root cause...');
     await userEvent.type(customInput, 'Custom root cause');
     await userEvent.click(screen.getByRole('button', {name: 'Send'}));
@@ -200,18 +203,18 @@ describe('AutofixMessageBox', () => {
   it('renders segmented control for changes step', () => {
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    expect(screen.getByRole('radio', {name: 'Iterate'})).toBeInTheDocument();
-    expect(screen.getByRole('radio', {name: 'Approve'})).toBeInTheDocument();
-    expect(screen.getByRole('radio', {name: 'Test'})).toBeInTheDocument();
+    expect(screen.getByRole('button', {name: 'Iterate'})).toBeInTheDocument();
+    expect(screen.getByRole('button', {name: 'Approve'})).toBeInTheDocument();
+    expect(screen.getByRole('button', {name: 'Add tests'})).toBeInTheDocument();
   });
 
   it('shows feedback input when "Iterate" is selected', async () => {
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Iterate'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Iterate'}));
 
     expect(
-      screen.getByPlaceholderText('Share helpful context or feedback...')
+      screen.getByPlaceholderText('Share helpful context or directions...')
     ).toBeInTheDocument();
     expect(screen.getByRole('button', {name: 'Send'})).toBeInTheDocument();
   });
@@ -231,7 +234,7 @@ describe('AutofixMessageBox', () => {
 
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Approve'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Approve'}));
 
     expect(
       screen.getByText('Draft 1 pull request for the above changes?')
@@ -262,7 +265,7 @@ describe('AutofixMessageBox', () => {
 
     render(<AutofixMessageBox {...multipleChangesProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Approve'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Approve'}));
 
     expect(
       screen.getByText('Draft 2 pull requests for the above changes?')
@@ -320,7 +323,7 @@ describe('AutofixMessageBox', () => {
 
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Approve'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Approve'}));
 
     expect(
       screen.getByText('Draft 1 pull request for the above changes?')
@@ -341,15 +344,15 @@ describe('AutofixMessageBox', () => {
   it('shows segmented control options for changes step', () => {
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    expect(screen.getByRole('radio', {name: 'Approve'})).toBeInTheDocument();
-    expect(screen.getByRole('radio', {name: 'Iterate'})).toBeInTheDocument();
-    expect(screen.getByRole('radio', {name: 'Test'})).toBeInTheDocument();
+    expect(screen.getByRole('button', {name: 'Approve'})).toBeInTheDocument();
+    expect(screen.getByRole('button', {name: 'Iterate'})).toBeInTheDocument();
+    expect(screen.getByRole('button', {name: 'Add tests'})).toBeInTheDocument();
   });
 
   it('shows "Test" button and static message when "Test" is selected', async () => {
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Test'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Add tests'}));
 
     expect(
       screen.getByText('Write unit tests to make sure the issue is fixed?')
@@ -366,7 +369,7 @@ describe('AutofixMessageBox', () => {
 
     render(<AutofixMessageBox {...changesStepProps} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Test'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Add tests'}));
     await userEvent.click(screen.getByRole('button', {name: 'Add Tests'}));
 
     await waitFor(() => {

+ 135 - 127
static/app/components/events/autofix/autofixMessageBox.tsx

@@ -5,6 +5,7 @@ import {AnimatePresence, type AnimationProps, motion} from 'framer-motion';
 import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
 import {openModal} from 'sentry/actionCreators/modal';
 import {Button, LinkButton} from 'sentry/components/button';
+import AutofixActionSelector from 'sentry/components/events/autofix/autofixActionSelector';
 import AutofixFeedback from 'sentry/components/events/autofix/autofixFeedback';
 import {AutofixSetupWriteAccessModal} from 'sentry/components/events/autofix/autofixSetupWriteAccessModal';
 import {
@@ -21,13 +22,12 @@ import {useAutofixSetup} from 'sentry/components/events/autofix/useAutofixSetup'
 import Input from 'sentry/components/input';
 import LoadingIndicator from 'sentry/components/loadingIndicator';
 import {ScrollCarousel} from 'sentry/components/scrollCarousel';
-import {SegmentedControl} from 'sentry/components/segmentedControl';
 import {
+  IconChat,
   IconCheckmark,
   IconChevron,
   IconClose,
   IconFatal,
-  IconFocus,
   IconOpen,
   IconSad,
 } from 'sentry/icons';
@@ -179,14 +179,14 @@ function SetupAndCreatePRsButton({
 
 interface RootCauseAndFeedbackInputAreaProps {
   actionText: string;
-  changesMode: 'give_feedback' | 'add_tests' | 'create_prs';
+  changesMode: 'give_feedback' | 'add_tests' | 'create_prs' | null;
   groupId: string;
   handleSend: (e: FormEvent<HTMLFormElement>) => void;
   isRootCauseSelectionStep: boolean;
   message: string;
   primaryAction: boolean;
   responseRequired: boolean;
-  rootCauseMode: 'suggested_root_cause' | 'custom_root_cause';
+  rootCauseMode: 'suggested_root_cause' | 'custom_root_cause' | null;
   setMessage: (message: string) => void;
 }
 
@@ -213,7 +213,7 @@ function RootCauseAndFeedbackInputArea({
               onChange={e => setMessage(e.target.value)}
               placeholder={
                 !isRootCauseSelectionStep
-                  ? 'Share helpful context or feedback...'
+                  ? 'Share helpful context or directions...'
                   : rootCauseMode === 'suggested_root_cause'
                     ? '(Optional) Provide any instructions for the fix...'
                     : 'Propose your own root cause...'
@@ -281,23 +281,19 @@ function StepIcon({step}: {step: AutofixStep}) {
     if (step.changes.every(change => change.pull_request)) {
       return <IconCheckmark size="sm" color="green300" isCircled />;
     }
-    return <IconFocus size="sm" color="gray300" />;
+    return null;
   }
 
   if (step.type === AutofixStepType.ROOT_CAUSE_ANALYSIS) {
     if (step.causes?.length === 0) {
       return <IconSad size="sm" color="gray300" />;
     }
-    return step.selection ? (
-      <IconCheckmark size="sm" color="green300" isCircled />
-    ) : (
-      <IconFocus size="sm" color="gray300" />
-    );
+    return step.selection ? <IconCheckmark size="sm" color="green300" isCircled /> : null;
   }
 
   switch (step.status) {
     case AutofixStatus.WAITING_FOR_USER_RESPONSE:
-      return <IconFocus size="sm" color="gray300" />;
+      return <IconChat size="sm" color="gray300" />;
     case AutofixStatus.PROCESSING:
       return <ProcessingStatusIndicator size={14} mini hideMessage />;
     case AutofixStatus.CANCELLED:
@@ -339,12 +335,12 @@ function AutofixMessageBox({
   const contentRef = useRef<HTMLDivElement>(null);
 
   const [rootCauseMode, setRootCauseMode] = useState<
-    'suggested_root_cause' | 'custom_root_cause'
-  >('suggested_root_cause');
+    'suggested_root_cause' | 'custom_root_cause' | null
+  >(null);
 
   const [changesMode, setChangesMode] = useState<
-    'give_feedback' | 'add_tests' | 'create_prs'
-  >('create_prs');
+    'give_feedback' | 'add_tests' | 'create_prs' | null
+  >(null);
 
   const changes =
     isChangesStep && step?.type === AutofixStepType.CHANGES ? step.changes : [];
@@ -403,14 +399,14 @@ function AutofixMessageBox({
           <ContentArea>
             {step && (
               <StepHeader>
-                <StepIconContainer>
-                  <StepIcon step={step} />
-                </StepIconContainer>
                 <StepTitle
                   dangerouslySetInnerHTML={{
                     __html: singleLineRenderer(step.title),
                   }}
                 />
+                <StepIconContainer>
+                  <StepIcon step={step} />
+                </StepIconContainer>
                 <StepHeaderRightSection>
                   {scrollIntoView !== null && (
                     <ScrollIntoViewButtonWrapper>
@@ -440,114 +436,132 @@ function AutofixMessageBox({
             />
           </ContentArea>
           {!isDisabled && (
-            <ActionBar>
-              {isRootCauseSelectionStep && (
-                <Fragment>
-                  <SegmentedControl
-                    size="xs"
-                    value={rootCauseMode}
-                    onChange={setRootCauseMode}
-                    aria-label={t('Root cause selection')}
-                  >
-                    <SegmentedControl.Item key="suggested_root_cause">
-                      {t('Use suggested root cause')}
-                    </SegmentedControl.Item>
-                    <SegmentedControl.Item key="custom_root_cause">
-                      {t('Propose your own root cause')}
-                    </SegmentedControl.Item>
-                  </SegmentedControl>
-                </Fragment>
-              )}
-              {isChangesStep && !prsMade && (
-                <Fragment>
-                  <SegmentedControl
-                    size="xs"
-                    value={changesMode}
-                    onChange={setChangesMode}
-                    aria-label={t('Changes selection')}
-                  >
-                    <SegmentedControl.Item key="create_prs">
-                      {t('Approve')}
-                    </SegmentedControl.Item>
-                    <SegmentedControl.Item key="give_feedback">
-                      {t('Iterate')}
-                    </SegmentedControl.Item>
-                    <SegmentedControl.Item key="add_tests">
-                      {t('Test')}
-                    </SegmentedControl.Item>
-                  </SegmentedControl>
-                </Fragment>
+            <InputSection>
+              {isRootCauseSelectionStep ? (
+                <AutofixActionSelector
+                  options={[
+                    {key: 'custom_root_cause', label: t('Propose your own root cause')},
+                    {
+                      key: 'suggested_root_cause',
+                      label: t('Use suggested root cause'),
+                      active: true,
+                    },
+                  ]}
+                  selected={rootCauseMode}
+                  onSelect={value => setRootCauseMode(value)}
+                  onBack={() => setRootCauseMode(null)}
+                >
+                  {option => (
+                    <RootCauseAndFeedbackInputArea
+                      handleSend={handleSend}
+                      isRootCauseSelectionStep={isRootCauseSelectionStep}
+                      message={message}
+                      rootCauseMode={option.key}
+                      responseRequired={responseRequired}
+                      setMessage={setMessage}
+                      actionText={actionText}
+                      primaryAction={primaryAction}
+                      changesMode={changesMode}
+                      groupId={groupId}
+                    />
+                  )}
+                </AutofixActionSelector>
+              ) : isChangesStep && !prsMade ? (
+                <AutofixActionSelector
+                  options={[
+                    {key: 'add_tests', label: t('Add tests')},
+                    {key: 'give_feedback', label: t('Iterate')},
+                    {key: 'create_prs', label: t('Approve'), active: true},
+                  ]}
+                  selected={changesMode}
+                  onSelect={value => setChangesMode(value)}
+                  onBack={() => setChangesMode(null)}
+                >
+                  {option => (
+                    <Fragment>
+                      {option.key === 'give_feedback' && (
+                        <RootCauseAndFeedbackInputArea
+                          handleSend={handleSend}
+                          isRootCauseSelectionStep={isRootCauseSelectionStep}
+                          message={message}
+                          rootCauseMode={rootCauseMode}
+                          responseRequired={responseRequired}
+                          setMessage={setMessage}
+                          actionText={actionText}
+                          primaryAction
+                          changesMode={option.key}
+                          groupId={groupId}
+                        />
+                      )}
+                      {option.key === 'add_tests' && (
+                        <form onSubmit={handleSend}>
+                          <InputArea>
+                            <StaticMessage>
+                              Write unit tests to make sure the issue is fixed?
+                            </StaticMessage>
+                            <Button type="submit" priority="primary">
+                              Add Tests
+                            </Button>
+                          </InputArea>
+                        </form>
+                      )}
+                      {option.key === 'create_prs' && (
+                        <InputArea>
+                          <StaticMessage>
+                            Draft {changes.length} pull request
+                            {changes.length > 1 ? 's' : ''} for the above changes?
+                          </StaticMessage>
+                          <SetupAndCreatePRsButton changes={changes} groupId={groupId} />
+                        </InputArea>
+                      )}
+                    </Fragment>
+                  )}
+                </AutofixActionSelector>
+              ) : isChangesStep && prsMade ? (
+                <ViewPRButtons aria-label={t('View pull requests')}>
+                  {changes.map(
+                    change =>
+                      change.pull_request?.pr_url && (
+                        <LinkButton
+                          key={`${change.repo_external_id}-${Math.random()}`}
+                          size="xs"
+                          priority="primary"
+                          icon={<IconOpen size="xs" />}
+                          href={change.pull_request.pr_url}
+                          external
+                        >
+                          View PR in {change.repo_name}
+                        </LinkButton>
+                      )
+                  )}
+                </ViewPRButtons>
+              ) : (
+                <RootCauseAndFeedbackInputArea
+                  handleSend={handleSend}
+                  isRootCauseSelectionStep={isRootCauseSelectionStep}
+                  message={message}
+                  rootCauseMode={rootCauseMode}
+                  responseRequired={responseRequired}
+                  setMessage={setMessage}
+                  actionText={actionText}
+                  primaryAction={primaryAction}
+                  changesMode={changesMode}
+                  groupId={groupId}
+                />
               )}
-            </ActionBar>
+            </InputSection>
           )}
+          {isDisabled && <Placeholder />}
         </ContentWrapper>
       </AnimatedContent>
-      <InputSection>
-        {(!isChangesStep || changesMode === 'give_feedback') &&
-          !prsMade &&
-          !isDisabled && (
-            <RootCauseAndFeedbackInputArea
-              handleSend={handleSend}
-              isRootCauseSelectionStep={isRootCauseSelectionStep}
-              message={message}
-              rootCauseMode={rootCauseMode}
-              responseRequired={responseRequired}
-              setMessage={setMessage}
-              actionText={actionText}
-              primaryAction={primaryAction}
-              changesMode={changesMode}
-              groupId={groupId}
-            />
-          )}
-        {isChangesStep && changesMode === 'add_tests' && !prsMade && (
-          <form onSubmit={handleSend}>
-            <InputArea>
-              <Fragment>
-                <StaticMessage>
-                  Write unit tests to make sure the issue is fixed?
-                </StaticMessage>
-                <Button type="submit" priority="primary">
-                  Add Tests
-                </Button>
-              </Fragment>
-            </InputArea>
-          </form>
-        )}
-        {isChangesStep && changesMode === 'create_prs' && !prsMade && (
-          <InputArea>
-            <Fragment>
-              <StaticMessage>
-                Draft {changes.length} pull request{changes.length > 1 ? 's' : ''} for the
-                above changes?
-              </StaticMessage>
-              <SetupAndCreatePRsButton changes={changes} groupId={groupId} />
-            </Fragment>
-          </InputArea>
-        )}
-        {isChangesStep && prsMade && (
-          <ViewPRButtons aria-label={t('View pull requests')}>
-            {changes.map(
-              change =>
-                change.pull_request?.pr_url && (
-                  <LinkButton
-                    key={`${change.repo_external_id}-${Math.random()}`}
-                    size="xs"
-                    priority="primary"
-                    icon={<IconOpen size="xs" />}
-                    href={change.pull_request.pr_url}
-                    external
-                  >
-                    View PR in {change.repo_name}
-                  </LinkButton>
-                )
-            )}
-          </ViewPRButtons>
-        )}
-      </InputSection>
     </Container>
   );
 }
 
+const Placeholder = styled('div')`
+  padding: ${space(1)};
+`;
+
 const ViewPRButtons = styled(ScrollCarousel)`
   width: 100%;
   padding: 0 ${space(1)};
@@ -595,7 +609,6 @@ const StepTitle = styled('div')`
   white-space: nowrap;
   display: flex;
   align-items: center;
-  flex-grow: 1;
 
   span {
     margin-right: ${space(1)};
@@ -611,6 +624,7 @@ const StepHeaderRightSection = styled('div')`
 const StepIconContainer = styled('div')`
   display: flex;
   align-items: center;
+  margin-right: auto;
 `;
 
 const StepHeader = styled('div')`
@@ -633,7 +647,6 @@ const StaticMessage = styled('p')`
   padding-top: ${space(1)};
   padding-left: ${space(1)};
   margin-bottom: 0;
-  color: ${p => p.theme.subText};
   border-top: 1px solid ${p => p.theme.border};
 `;
 
@@ -657,13 +670,8 @@ const ProcessingStatusIndicator = styled(LoadingIndicator)`
   }
 `;
 
-const ActionBar = styled('div')`
-  padding-bottom: ${space(1)};
-  padding-left: ${space(2)};
-`;
-
 const InputSection = styled('div')`
-  padding: 0 ${space(2)} ${space(2)} ${space(2)};
+  padding: ${space(0.5)} ${space(2)} ${space(2)};
 `;
 
 export default AutofixMessageBox;

+ 9 - 6
static/app/components/events/autofix/autofixSteps.spec.tsx

@@ -62,9 +62,7 @@ describe('AutofixSteps', () => {
     render(<AutofixSteps {...defaultProps} />);
 
     expect(screen.getByText('Root cause 1')).toBeInTheDocument();
-    expect(
-      screen.getByPlaceholderText('(Optional) Provide any instructions for the fix...')
-    ).toBeInTheDocument();
+    expect(screen.getByText('Use suggested root cause')).toBeInTheDocument();
   });
 
   it('handles root cause selection', async () => {
@@ -76,6 +74,8 @@ describe('AutofixSteps', () => {
 
     render(<AutofixSteps {...defaultProps} />);
 
+    await userEvent.click(screen.getByRole('button', {name: 'Use suggested root cause'}));
+
     const input = screen.getByPlaceholderText(
       '(Optional) Provide any instructions for the fix...'
     );
@@ -98,6 +98,7 @@ describe('AutofixSteps', () => {
 
     render(<AutofixSteps {...defaultProps} />);
 
+    await userEvent.click(screen.getByRole('button', {name: 'Use suggested root cause'}));
     await userEvent.click(screen.getByRole('button', {name: 'Find a Fix'}));
 
     await waitFor(() => {
@@ -107,9 +108,11 @@ describe('AutofixSteps', () => {
     });
   });
 
-  it('renders AutofixMessageBox with correct props', () => {
+  it('renders AutofixMessageBox with correct props', async () => {
     render(<AutofixSteps {...defaultProps} />);
 
+    await userEvent.click(screen.getByRole('button', {name: 'Use suggested root cause'}));
+
     const messageBox = screen.getByPlaceholderText(
       '(Optional) Provide any instructions for the fix...'
     );
@@ -195,9 +198,9 @@ describe('AutofixSteps', () => {
 
     render(<AutofixSteps {...propsWithChanges} />);
 
-    await userEvent.click(screen.getByRole('radio', {name: 'Iterate'}));
+    await userEvent.click(screen.getByRole('button', {name: 'Iterate'}));
 
-    const input = screen.getByPlaceholderText('Share helpful context or feedback...');
+    const input = screen.getByPlaceholderText('Share helpful context or directions...');
     await userEvent.type(input, 'Feedback on changes');
     await userEvent.click(screen.getByRole('button', {name: 'Send'}));