Merge branch 'main' into avm99963-monorail

Merged commit 4137ed7879acadbf891e8c471108acb874dae886.

GitOrigin-RevId: b6100ffc5b1da355a35f37b13fcaaf746ee8b307
diff --git a/static_src/react/issue-wizard/AttachmentUploader.css b/static_src/react/issue-wizard/AttachmentUploader.css
new file mode 100644
index 0000000..1a4883e
--- /dev/null
+++ b/static_src/react/issue-wizard/AttachmentUploader.css
@@ -0,0 +1,52 @@
+.materialIcons {
+  font-family: 'Material Icons';
+  font-weight: normal;
+  font-style: normal;
+  font-size: 20px;
+  line-height: 1;
+  letter-spacing: normal;
+  text-transform: none;
+  display: inline-block;
+  white-space: nowrap;
+  word-wrap: normal;
+  direction: ltr;
+  -webkit-font-feature-settings: 'liga';
+  -webkit-font-smoothing: 'antialiased';
+
+}
+
+.button {
+  margin-right: 8px;
+  padding: 0.1em 4px;
+  display: inline-flex;
+  width: auto;
+  cursor: pointer;
+  border: var(--chops-normal-border);
+  margin-left: 0;
+}
+
+.controls:focus-within > label{
+  border: 2px solid #1976d2;
+}
+
+.controls {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  width: 100%;
+  font-size: 12px;
+}
+
+.inputUpload {
+  opacity: 0;
+  width: 0;
+  height: 0;
+  position: absolute;
+  top: -9999;
+  left: -9999;
+}
+
+.error {
+  color: red;
+}
diff --git a/static_src/react/issue-wizard/AttachmentUploader.tsx b/static_src/react/issue-wizard/AttachmentUploader.tsx
new file mode 100644
index 0000000..a207ef5
--- /dev/null
+++ b/static_src/react/issue-wizard/AttachmentUploader.tsx
@@ -0,0 +1,87 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import React from 'react';
+import Button from '@material-ui/core/Button';
+import styles from './AttachmentUploader.css';
+
+type Props = {
+  files: Array<File>,
+  setFiles: Function,
+  setSubmitEnable: Function,
+}
+
+const isSameFile = (a: File, b: File) => {
+  // NOTE: This function could return a false positive if two files have the
+  // exact same name, lastModified time, size, and type but different
+  // content. This is extremely unlikely, however.
+  return a.name === b.name && a.lastModified === b.lastModified &&
+    a.size === b.size && a.type === b.type;
+}
+
+const getTotalSize = (files: Array<File>) => {
+  let size = 0;
+  files.forEach((f) => {
+    size += f.size;
+  });
+  return size;
+}
+
+const MAX_SIZE = 10 * 1000 * 1000;
+export default function AttachmentUploader(props: Props): React.ReactElement {
+
+  const {files, setFiles, setSubmitEnable} = props;
+  const [isOverSize, setIsOverSize] = React.useState(false);
+
+  const onSelectFile = (event: {currentTarget: any;}) => {
+    const input = event.currentTarget;
+    if (!input.files || input.files.length === 0) {
+      return;
+    }
+
+    const newFiles = [...input.files].filter((f1) => {
+      const fileExist = files.some((f2) => isSameFile(f1, f2));
+      return !fileExist;
+    })
+
+    const expendFiles = [...files].concat(newFiles);
+    const filesSize = getTotalSize(expendFiles);
+    setIsOverSize(filesSize > MAX_SIZE);
+    setSubmitEnable(filesSize <= MAX_SIZE);
+    setFiles(expendFiles);
+  }
+
+  const onRemoveFile = (index: number) => () => {
+    let remainingFiles = [...files];
+    remainingFiles.splice(index, 1);
+    const filesSize = getTotalSize(remainingFiles);
+    setIsOverSize(filesSize > MAX_SIZE);
+    setSubmitEnable(filesSize <= MAX_SIZE);
+    setFiles(remainingFiles);
+  }
+  return (
+    <>
+      <div className={styles.controls}>
+        <input className={styles.inputUpload} id="file-uploader" type="file" multiple onChange={onSelectFile}/>
+        <label className={styles.button} for="file-uploader">
+          <i className={styles.materialIcons} role="presentation">attach_file</i>Add attachments
+        </label>
+        You can include multiple attachments (Max: 10.0 MB per issue)
+      </div>
+      {files.length === 0 ? null :
+        (<ul>
+          {
+            files?.map((f, i) => (
+              <li>
+                {f.name}
+                <Button onClick={onRemoveFile(i)}> X</Button>
+              </li>
+            ))
+          }
+        </ul>)
+      }
+      {isOverSize ? <div className={styles.error}>Warning: Attachments are too big !</div> : null}
+    </>
+  );
+}
diff --git a/static_src/react/issue-wizard/ConfirmBackModal.tsx b/static_src/react/issue-wizard/ConfirmBackModal.tsx
new file mode 100644
index 0000000..0d07b83
--- /dev/null
+++ b/static_src/react/issue-wizard/ConfirmBackModal.tsx
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import * as React from 'react';
+import {makeStyles} from '@material-ui/styles';
+import Dialog from '@material-ui/core/Dialog';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogContentText from '@material-ui/core/DialogContentText';
+import DialogActions from '@material-ui/core/DialogActions';
+import Button from '@material-ui/core/Button';
+
+const userStyles = makeStyles({
+  actionsButtons: {
+    paddingTop: '0',
+  },
+  primaryButton: {
+    backgroundColor: 'rgb(25, 118, 210)',
+    color: 'white',
+  }
+});
+
+type Props = {
+  enable: boolean,
+  setEnable: Function,
+  confirmBack: Function,
+}
+
+export function ConfirmBackModal(props: Props): React.ReactElement {
+  const {enable, setEnable, confirmBack} = props;
+  const classes = userStyles();
+
+  return (
+    <Dialog open={enable}>
+        <DialogTitle>Warning!</DialogTitle>
+        <DialogContent>
+          <DialogContentText>
+            Changes you made on this page won't be saved.
+          </DialogContentText>
+        </DialogContent>
+        <DialogActions className={classes.actionsButtons}>
+          <Button onClick={() => setEnable(false)}>Cancel</Button>
+          <Button onClick={() => {
+            confirmBack();
+            setEnable(false);
+          }} className={classes.primaryButton}>Ok</Button>
+        </DialogActions>
+    </Dialog>
+  )
+}
diff --git a/static_src/react/issue-wizard/CustomQuestions/CustomQuestionInput.tsx b/static_src/react/issue-wizard/CustomQuestions/CustomQuestionInput.tsx
new file mode 100644
index 0000000..aa7fdd0
--- /dev/null
+++ b/static_src/react/issue-wizard/CustomQuestions/CustomQuestionInput.tsx
@@ -0,0 +1,48 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import React from 'react'
+import OutlinedInput from "@material-ui/core/OutlinedInput";
+import {makeStyles} from '@material-ui/styles';
+
+const userStyles = makeStyles({
+  head: {
+    marginTop: '1.5rem',
+    fontSize: '1rem'
+  },
+  inputArea: {
+    width: '100%',
+  },
+});
+
+type Props = {
+  question: string,
+  updateAnswers: Function,
+}
+
+export default function CustomQuestionInput(props: Props): React.ReactElement {
+
+  const classes = userStyles();
+
+  const {question, updateAnswers} = props;
+  const [answer, setAnswer] = React.useState('');
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setAnswer(e.target.value);
+    updateAnswers(e.target.value);
+  };
+  const getInnerHtml = ()=> {
+    return {__html: question};
+  }
+  return (
+    <>
+      <h3 dangerouslySetInnerHTML={getInnerHtml()} className={classes.head}/>
+      <OutlinedInput
+        value={answer}
+        onChange={handleChange}
+        className={classes.inputArea}
+        inputProps={{ maxLength: 1000 }}
+      />
+    </>
+  );
+}
diff --git a/static_src/react/issue-wizard/CustomQuestions/CustomQuestionSelector.tsx b/static_src/react/issue-wizard/CustomQuestions/CustomQuestionSelector.tsx
new file mode 100644
index 0000000..bb855a3
--- /dev/null
+++ b/static_src/react/issue-wizard/CustomQuestions/CustomQuestionSelector.tsx
@@ -0,0 +1,105 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import React from 'react';
+import {makeStyles} from '@material-ui/styles';
+import SelectMenu from '../SelectMenu.tsx';
+import {CustomQuestion, CustomQuestionType} from '../IssueWizardTypes.tsx';
+import CustomQuestionInput from './CustomQuestionInput.tsx';
+import CustomQuestionTextarea from './CustomQuestionTextarea.tsx';
+import {GetSelectMenuOptions} from '../IssueWizardUtils.tsx';
+
+const userStyles = makeStyles({
+  head: {
+    marginTop: '1.5rem',
+    fontSize: '1rem'
+  },
+  inputArea: {
+    width: '100%',
+  },
+  tip: {
+    margin: '0.5rem 0',
+  },
+});
+
+type Props = {
+  question: string,
+  tip?: string,
+  options: string[],
+  subQuestions: CustomQuestion[] | null,
+  updateAnswers: Function,
+}
+
+export default function CustomQuestionSelector(props: Props): React.ReactElement {
+
+  const classes = userStyles();
+
+  const {question, updateAnswers, options, subQuestions, tip} = props;
+  const [selectedOption, setSelectedOption] = React.useState(options[0]);
+
+  const [subQuestion, setSubQuestion] = React.useState(subQuestions? subQuestions[0] : null);
+
+  React.useEffect(() => {
+    updateAnswers(options[0]);
+  },[]);
+
+  const handleOptionChange = (option: string) => {
+    setSelectedOption(option);
+    updateAnswers(option);
+    const index = options.indexOf(option);
+    if (subQuestions !== null) {
+      setSubQuestion(subQuestions[index]);
+    }
+  };
+
+  const updateSubQuestionAnswer = (answer:string) => {
+    const updatedAnswer = selectedOption + ' ' + answer;
+    updateAnswers(updatedAnswer);
+  }
+  const optionList = GetSelectMenuOptions(options);
+
+  let renderSubQuestion = null;
+
+  if (subQuestion != null) {
+    switch(subQuestion.type) {
+      case CustomQuestionType.Input:
+        renderSubQuestion =
+          <CustomQuestionInput
+            question={subQuestion.question}
+            updateAnswers={updateSubQuestionAnswer}
+          />
+        break;
+      case CustomQuestionType.Text:
+        renderSubQuestion =
+            <CustomQuestionTextarea
+              question={subQuestion.question}
+              tip={subQuestion.tip}
+              updateAnswers={updateSubQuestionAnswer}
+            />;
+        break;
+      default:
+        break;
+    }
+  }
+
+  const getQuestionInnerHtml = () => {
+    return {__html: question};
+  }
+
+  const getTipInnerHtml = () => {
+    return {__html: tip};
+  }
+  return (
+    <>
+      <h3 dangerouslySetInnerHTML={getQuestionInnerHtml()} className={classes.head}/>
+      {tip? <div dangerouslySetInnerHTML={getTipInnerHtml()} className={classes.tip}/> : null}
+      <SelectMenu
+        optionsList={optionList}
+        selectedOption={selectedOption}
+        setOption={handleOptionChange}
+      />
+      {renderSubQuestion}
+    </>
+  );
+}
diff --git a/static_src/react/issue-wizard/CustomQuestions/CustomQuestionTextarea.tsx b/static_src/react/issue-wizard/CustomQuestions/CustomQuestionTextarea.tsx
new file mode 100644
index 0000000..fdbdf1f
--- /dev/null
+++ b/static_src/react/issue-wizard/CustomQuestions/CustomQuestionTextarea.tsx
@@ -0,0 +1,59 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import React from 'react';
+import OutlinedInput from "@material-ui/core/OutlinedInput";
+import {makeStyles} from '@material-ui/styles';
+
+const userStyles = makeStyles({
+  head: {
+    marginTop: '1.5rem',
+    fontSize: '1rem'
+  },
+  inputArea: {
+    width: '100%',
+  },
+  tip: {
+    margin: '0.5rem 0'
+  },
+});
+
+type Props = {
+  question: string,
+  tip?: string,
+  updateAnswers: Function,
+}
+
+export default function CustomQuestionTextarea(props: Props): React.ReactElement {
+
+  const classes = userStyles();
+
+  const {question, updateAnswers, tip} = props;
+  const [answer, setAnswer] = React.useState('');
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setAnswer(e.target.value);
+    updateAnswers(e.target.value);
+  };
+
+  const getQuestionInnerHtml = ()=> {
+    return {__html: question};
+  }
+
+  const getTipInnerHtml = ()=> {
+    return {__html: tip};
+  }
+  return (
+    <>
+      <h3 dangerouslySetInnerHTML={getQuestionInnerHtml()} className={classes.head}/>
+      {tip? <div dangerouslySetInnerHTML={getTipInnerHtml()} className={classes.tip}/> : null}
+      <OutlinedInput
+        multiline={true}
+        rows={3}
+        value={answer}
+        onChange={handleChange}
+        className={classes.inputArea}
+      />
+    </>
+  );
+}
diff --git a/static_src/react/issue-wizard/CustomQuestionsStep.tsx b/static_src/react/issue-wizard/CustomQuestionsStep.tsx
new file mode 100644
index 0000000..9e1ef72
--- /dev/null
+++ b/static_src/react/issue-wizard/CustomQuestionsStep.tsx
@@ -0,0 +1,186 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import React from 'react';
+import {makeStyles} from '@material-ui/styles';
+import {grey} from '@material-ui/core/colors';
+import DotMobileStepper from './DotMobileStepper.tsx';
+import {CustomQuestion, CustomQuestionType} from './IssueWizardTypes.tsx';
+import CustomQuestionInput from './CustomQuestions/CustomQuestionInput.tsx';
+import CustomQuestionTextarea from './CustomQuestions/CustomQuestionTextarea.tsx';
+import CustomQuestionSelector from './CustomQuestions/CustomQuestionSelector.tsx';
+import Alert from '@material-ui/core/Alert';
+import AttachmentUploader from './AttachmentUploader.tsx';
+import Modal from '@material-ui/core/Modal';
+import Box from '@material-ui/core/Box';
+import {LABELS_PREFIX} from './IssueWizardConfig.ts';
+
+const userStyles = makeStyles({
+  greyText: {
+    color: grey[600],
+  },
+  root: {
+    width: '100%',
+  },
+  modalBox: {
+    position: 'absolute',
+    top: '40%',
+    left: '50%',
+    transform: 'translate(-50%, -50%)',
+    width: 400,
+    backgroundColor: 'white',
+    borderRadius: '10px',
+    padding: '10px',
+  },
+  modalTitle: {
+    fontSize: '20px',
+    margin: '5px 0px',
+  },
+  modalContext: {
+    fontSize: '15px',
+  },
+});
+
+type Props = {
+  setActiveStep: Function,
+  questions: CustomQuestion[],
+  onSubmit: Function,
+  setnewIssueID: Function,
+};
+
+export default function CustomQuestionsStep(props: Props): React.ReactElement {
+
+  const {setActiveStep, questions, onSubmit, setnewIssueID} = props;
+  const classes = userStyles();
+
+  const customQuestions = new Array();
+
+  const [additionalComments, setAdditionalComments] = React.useState('');
+  const [attachments, setAttachments] = React.useState([]);
+  const [answers, setAnswers] = React.useState(Array(questions.length).fill(''));
+  const [hasError, setHasError] = React.useState(false);
+  const [submitEnable, setSubmitEnable] = React.useState(true);
+  const [isSubmitting, setIsSubmitting] = React.useState(false);
+
+  const updateAnswer = (answer: string, index: number) => {
+    const updatedAnswers = answers;
+    const answerPrefix = questions[index].answerPrefix !== LABELS_PREFIX ?
+      '<b>' + questions[index].answerPrefix + '</b> ' : LABELS_PREFIX;
+    updatedAnswers[index] = answerPrefix + answer;
+    setAnswers(updatedAnswers);
+  }
+
+  questions.forEach((q, i) => {
+    switch(q.type) {
+      case CustomQuestionType.Input:
+        customQuestions.push(
+          <CustomQuestionInput
+            question={q.question}
+            updateAnswers={(answer: string) => updateAnswer(answer, i)}
+          />
+        );
+        return;
+      case CustomQuestionType.Text:
+          customQuestions.push(
+            <CustomQuestionTextarea
+              question={q.question}
+              tip={q.tip}
+              updateAnswers={(answer: string) => updateAnswer(answer, i)}
+            />
+          );
+          return;
+      case CustomQuestionType.Select:
+        customQuestions.push(
+          <CustomQuestionSelector
+            question={q.question}
+            tip={q.tip}
+            options={q.options}
+            subQuestions={q.subQuestions}
+            updateAnswers={(answer: string) => updateAnswer(answer, i)}
+          />
+        );
+        return;
+      default:
+        return;
+    }
+  });
+
+  const loadFiles = () => {
+    if (!attachments || attachments.length === 0) {
+      return Promise.resolve([]);
+    }
+    const loads = attachments.map(loadLocalFile);
+    return Promise.all(loads);
+  }
+
+  const loadLocalFile = (f: File) => {
+    return new Promise((resolve, reject) => {
+      const r = new FileReader();
+      r.onloadend = () => {
+        resolve({filename: f.name, content: btoa(r.result)});
+      };
+      r.onerror = () => {
+        reject(r.error);
+      };
+
+      r.readAsBinaryString(f);
+    });
+  }
+
+  const onSuccess = (response: Issue) => {
+    //redirect to issue
+    setIsSubmitting(false);
+    const issueId = response.name.split('/')[3];
+    setnewIssueID(issueId);
+    setActiveStep(3);
+  };
+
+  const onFailure = () => {
+    setIsSubmitting(false);
+    setHasError(true);
+  }
+
+  const onMakeIssue = () => {
+    setHasError(false);
+    setIsSubmitting(true);
+    try {
+      const uploads = loadFiles();
+      uploads.then((files) => {
+        // TODO: add attachments to request
+        onSubmit(additionalComments, answers, files, onSuccess, onFailure);
+      }, onFailure)
+    } catch (e) {
+      onFailure();
+    }
+  }
+
+  return (
+    <>
+      <h2 className={classes.greyText}>Extra Information about the Issue</h2>
+      {hasError
+        ? <Alert severity="error" onClose={() => {setHasError(false)}}>Something went wrong, please try again later.</Alert>
+        : null
+      }
+      <div className={classes.root}>
+        {customQuestions}
+
+        <CustomQuestionTextarea
+          question="Additional comments"
+          updateAnswers={(answer: string) => setAdditionalComments(answer)}
+        />
+
+        <h3>Upload any relevant screenshots</h3>
+        <AttachmentUploader files={attachments} setFiles={setAttachments} setSubmitEnable={setSubmitEnable}/>
+
+      </div>
+      <DotMobileStepper nextEnabled={submitEnable} activeStep={2} setActiveStep={setActiveStep} onSubmit={onMakeIssue}/>
+      <Modal open={isSubmitting} >
+        <Box className={classes.modalBox}>
+          <p className={classes.modalTitle}>Thanks for contributing to Chromium!</p>
+          <p>Stay put, we're filing your issue!</p>
+        </Box>
+      </Modal>
+    </>
+  );
+}
diff --git a/static_src/react/issue-wizard/DetailsStep.test.tsx b/static_src/react/issue-wizard/DetailsStep.test.tsx
index eaef0e7..e53c3a9 100644
--- a/static_src/react/issue-wizard/DetailsStep.test.tsx
+++ b/static_src/react/issue-wizard/DetailsStep.test.tsx
@@ -12,7 +12,12 @@
   afterEach(cleanup);
 
   it('renders', async () => {
-    const {container} = render(<DetailsStep />);
+    const textFiled = {
+      oneLineSummary: '',
+      stepsToReproduce: '',
+      describeProblem: '',
+    };
+    const {container} = render(<DetailsStep textValues={textFiled} setIsRegression={() => {}}/>);
 
     // this is checking for the first question
     const input = container.querySelector('input');
@@ -20,15 +25,21 @@
 
     // this is checking for the rest
     const count = document.querySelectorAll('textarea').length;
-    assert.equal(count, 3)
+    assert.equal(count, 4)
   });
 
   it('renders category in title', async () => {
-    const {container} = render(<DetailsStep category='UI'/>);
+    const textFiled = {
+      oneLineSummary: '',
+      stepsToReproduce: '',
+      describeProblem: '',
+    };
+
+    const {container} = render(<DetailsStep category='UI' textValues={textFiled} setIsRegression={() => {}}/>);
 
     // this is checking the title contains our category
     const title = container.querySelector('h2');
     assert.include(title?.innerText, 'Details for problems with UI');
   });
 
-});
\ No newline at end of file
+});
diff --git a/static_src/react/issue-wizard/DetailsStep.tsx b/static_src/react/issue-wizard/DetailsStep.tsx
index 1a69cc1..ae968c1 100644
--- a/static_src/react/issue-wizard/DetailsStep.tsx
+++ b/static_src/react/issue-wizard/DetailsStep.tsx
@@ -5,8 +5,14 @@
 import React from 'react';
 import {createStyles, createTheme} from '@material-ui/core/styles';
 import {makeStyles} from '@material-ui/styles';
+import { TextareaAutosize } from '@material-ui/core';
 import TextField from '@material-ui/core/TextField';
 import {red, grey} from '@material-ui/core/colors';
+import DotMobileStepper from './DotMobileStepper.tsx';
+import SelectMenu from './SelectMenu.tsx';
+import {OS_LIST, ISSUE_WIZARD_QUESTIONS, ISSUE_REPRODUCE_PLACEHOLDER, OS_CHANNEL_LIST} from './IssueWizardConfig.ts'
+import {getTipByCategory} from './IssueWizardUtils.tsx';
+import CustomQuestionSelector from './CustomQuestions/CustomQuestionSelector.tsx';
 
 /**
  * The detail step is the second step on the dot
@@ -19,47 +25,120 @@
   createStyles({
     root: {
       '& > *': {
-        margin: theme.spacing(1),
         width: '100%',
       },
     },
     head: {
-        marginTop: '25px',
+      marginTop: '1.5rem',
+      fontSize: '1rem'
     },
     red: {
         color: red[600],
     },
-    grey: {
-        color: grey[600],
+    pageHeader: {
+      color: grey[600],
+      fontSize: '1.5rem',
+      margin: '1rem 0',
     },
+    inlineStyle: {
+      display: 'inline-flex',
+      alignItems: 'center',
+      marginTop: '1.5rem',
+    },
+    inlineTitle: {
+      marginRight: '10px',
+      fontSize: '1rem',
+    }
   }), {defaultTheme: theme}
 );
 
-export default function DetailsStep({textValues, setTextValues, category}:
-  {textValues: Object, setTextValues: Function, category: string}): React.ReactElement {
+type Props = {
+  textValues: Object,
+  setTextValues: Function,
+  category: string,
+  setActiveStep: Function,
+  osName: string,
+  setOsName: Function,
+  setIsRegression: Function,
+};
+
+export default function DetailsStep(props: Props): React.ReactElement {
   const classes = useStyles();
 
+  const {
+    textValues,
+    setTextValues,
+    category,
+    setActiveStep,
+    setIsRegression
+  } = props;
+
   const handleChange = (valueName: string) => (e: React.ChangeEvent<HTMLInputElement>) => {
     const textInput = e.target.value;
     setTextValues({...textValues, [valueName]: textInput});
   };
 
+  const selectOs = (os: string) => {
+    setTextValues({...textValues, 'osName': os});
+  }
+
+  const selectChannel = (channel: string) => {
+    setTextValues({...textValues, 'channel': channel});
+  }
+
+  const tipByCategory = getTipByCategory(ISSUE_WIZARD_QUESTIONS);
+
+  const nextEnabled =
+    (textValues.oneLineSummary.trim() !== '') &&
+    (textValues.stepsToReproduce.trim() !== ISSUE_REPRODUCE_PLACEHOLDER) &&
+    (textValues.stepsToReproduce.trim() !== '') &&
+    (textValues.describeProblem.trim() !== '');
+
+  const getTipInnerHtml = () => {
+    return {__html: tipByCategory.get(category)};
+  }
   return (
     <>
-        <h2 className={classes.grey}>Details for problems with {category}</h2>
+        <h2 className={classes.pageHeader}>Details for problems with {category}</h2>
+
         <form className={classes.root} noValidate autoComplete="off">
-            <h3 className={classes.head}>Please enter a one line summary <span className={classes.red}>*</span></h3>
-            <TextField id="outlined-basic-1" variant="outlined" onChange={handleChange('oneLineSummary')}/>
+          <div dangerouslySetInnerHTML={getTipInnerHtml()}/>
 
-            <h3 className={classes.head}>Steps to reproduce problem <span className={classes.red}>*</span></h3>
-            <TextField multiline rows={4} id="outlined-basic-2" variant="outlined" onChange={handleChange('stepsToReproduce')}/>
+          <h3 className={classes.head}>Please confirm that the following version information is correct. <span className={classes.red}>*</span></h3>
+          <div className={classes.inlineStyle}>
+            <h3 className={classes.inlineTitle}>Operating System:</h3>
+            <SelectMenu optionsList={OS_LIST} selectedOption={textValues.osName} setOption={selectOs} />
+            <h3 className={classes.inlineTitle}>Channel:</h3>
+            <SelectMenu optionsList={OS_CHANNEL_LIST} selectedOption={textValues.channel} setOption={selectChannel} />
+          </div>
+          <div className={classes.inlineStyle}>
+            <h3 className={classes.inlineTitle}>Chrome version: </h3>
+            <TextField variant="outlined" onChange={handleChange('chromeVersion')} value={textValues.chromeVersion}/>
+          </div>
 
-            <h3 className={classes.head}>Please describe the problem <span className={classes.red}>*</span></h3>
-            <TextField multiline rows={3} id="outlined-basic-3" variant="outlined" onChange={handleChange('describeProblem')}/>
+          <h3 className={classes.head}>Please enter a one line summary (100 character limit) <span className={classes.red}>*</span></h3>
+          <TextField id="outlined-basic-1" variant="outlined" inputProps={{maxLength: 100}} onChange={handleChange('oneLineSummary')} value={textValues.oneLineSummary}/>
 
-            <h3 className={classes.head}>Additional Comments</h3>
-            <TextField multiline rows={3} id="outlined-basic-4" variant="outlined" onChange={handleChange('additionalComments')}/>
+          <h3 className={classes.head}>Steps to reproduce problem (5000 character limit) <span className={classes.red}>*</span></h3>
+          <TextareaAutosize minRows={4} id="outlined-basic-2" maxLength={5000} onChange={handleChange('stepsToReproduce')} value={textValues.stepsToReproduce}/>
+
+          <h3 className={classes.head}>Please describe the problem (5000 character limit)<span className={classes.red}>*</span></h3>
+          <TextareaAutosize minRows={3} id="outlined-basic-3" maxLength={5000} onChange={handleChange('describeProblem')} value={textValues.describeProblem}/>
+
+          <CustomQuestionSelector
+            question="Did this work before?"
+            options={["Not applicable or don't know", "Yes - This is a regression", "No - I think it never worked"]}
+            subQuestions={null}
+            updateAnswers={(answer: string) => {
+              if (answer === "Yes - This is a regression") {
+                setIsRegression(true);
+              } else {
+                setIsRegression(false);
+              }
+            }}
+          />
         </form>
+        <DotMobileStepper nextEnabled={nextEnabled} activeStep={1} setActiveStep={setActiveStep}/>
     </>
   );
 }
diff --git a/static_src/react/issue-wizard/DotMobileStepper.test.tsx b/static_src/react/issue-wizard/DotMobileStepper.test.tsx
index 5203110..b7c9aa4 100644
--- a/static_src/react/issue-wizard/DotMobileStepper.test.tsx
+++ b/static_src/react/issue-wizard/DotMobileStepper.test.tsx
@@ -18,17 +18,17 @@
 
     // this is checking the buttons for the stepper rendered
       const count = document.querySelectorAll('button').length;
-      assert.equal(count, 2)
+      assert.equal(count, 1)
   });
 
-  it('back button disabled on first step', () => {
+  it('back button not avlialbe on first step', () => {
     render(<DotMobileStepper activeStep={0} nextEnabled={true}/>).container;
 
     // Finds a button on the page with "back" as text using React testing library.
-    const backButton = screen.getByRole('button', {name: /backButton/i}) as HTMLButtonElement;
+    const backButton = document.querySelector('[aria-label="backButton"]');
 
-    // Back button is disabled on the first step.
-    assert.isTrue(backButton.disabled);
+    // Back button is not avliable on the first step.
+    assert.notExists(backButton);
   });
 
   it('both buttons enabled on second step', () => {
@@ -46,14 +46,4 @@
     // Next button is not disabled on the second step.
     assert.isFalse(nextButton.disabled);
   });
-
-  it('next button disabled on last step', () => {
-    render(<DotMobileStepper activeStep={2}/>).container;
-
-    // Finds a button on the page with "next" as text using React testing library.
-    const nextButton = screen.getByRole('button', {name: /nextButton/i}) as HTMLButtonElement;
-
-    // Next button is disabled on the second step.
-    assert.isTrue(nextButton.disabled);
-  });
-});
\ No newline at end of file
+});
diff --git a/static_src/react/issue-wizard/DotMobileStepper.tsx b/static_src/react/issue-wizard/DotMobileStepper.tsx
index 9870f03..9aa3fa8 100644
--- a/static_src/react/issue-wizard/DotMobileStepper.tsx
+++ b/static_src/react/issue-wizard/DotMobileStepper.tsx
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import React from 'react';
+import React, {useEffect} from 'react';
 import {createTheme} from '@material-ui/core/styles';
 import {makeStyles} from '@material-ui/styles';
 import MobileStepper from '@material-ui/core/MobileStepper';
 import Button from '@material-ui/core/Button';
+import Box from '@material-ui/core/Box';
 import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
 import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
+import {ConfirmBackModal} from './ConfirmBackModal.tsx';
 
 const theme: Theme = createTheme();
 
@@ -16,9 +18,19 @@
   root: {
     width: '100%',
     flexGrow: 1,
+    padding: '8px 0px',
+  },
+  back: {
+    padding: '6px 0px',
   },
 }, {defaultTheme: theme});
 
+type Props = {
+  nextEnabled: boolean,
+  activeStep: number,
+  setActiveStep: Function,
+  onSubmit?: Function,
+}
 /**
  * `<DotMobileStepper />`
  *
@@ -26,47 +38,82 @@
  *
  *  @return ReactElement.
  */
-export default function DotsMobileStepper({nextEnabled, activeStep, setActiveStep} : {nextEnabled: boolean, activeStep: number, setActiveStep: Function}) : React.ReactElement {
+export default function DotsMobileStepper(props: Props) : React.ReactElement {
+
+  const {nextEnabled, activeStep, setActiveStep, onSubmit}  = props;
   const classes = useStyles();
 
+  const [showConfirmModal, setShowConfirmModal] = React.useState(false);
+
   const handleNext = () => {
-    setActiveStep((prevActiveStep: number) => prevActiveStep + 1);
+    setActiveStep(activeStep + 1);
   };
 
   const handleBack = () => {
-    setActiveStep((prevActiveStep: number) => prevActiveStep - 1);
+    if (activeStep === 2) {
+      setShowConfirmModal(true);
+    } else {
+      setActiveStep(activeStep - 1);
+    }
   };
 
-  let label;
-  let icon;
-
-  if (activeStep === 2){
-    label = 'Submit';
-    icon = '';
-  } else {
-    label = 'Next';
-    icon = <KeyboardArrowRight />;
+  const onSubmitIssue = () => {
+    if (onSubmit) {
+      onSubmit();
+    }
   }
+
+  const onBrowserBackButtonEvent = (e: Event) => {
+    e.preventDefault();
+    if (activeStep === 0) {
+      window.history.back();
+    } else {
+      setActiveStep(activeStep-1);
+    }
+  }
+
+  useEffect(() => {
+    window.history.pushState(null, '', window.location.pathname);
+    window.addEventListener('popstate', onBrowserBackButtonEvent);
+    return () => {
+      window.removeEventListener('popstate', onBrowserBackButtonEvent);
+    };
+  }, [activeStep]);
+
+  let nextButton;
+  if (activeStep === 2){
+    nextButton = (<Button aria-label="nextButton" size="medium" onClick={onSubmitIssue} disabled={!nextEnabled}>{'Submit'}</Button>);
+  } else {
+    nextButton =
+      (<Button aria-label="nextButton" size="medium" onClick={handleNext} disabled={!nextEnabled}>
+        {'Next'}
+        <KeyboardArrowRight />
+      </Button>);
+  }
+
+  const backButton = activeStep === 0 ? <Box></Box> :
+    (<Button aria-label="backButton" size="medium" onClick={handleBack} disabled={activeStep === 0} className={classes.back}>
+      <KeyboardArrowLeft />
+      Back
+    </Button>);
+
   return (
-    <MobileStepper
-      id="mobile-stepper"
-      variant="dots"
-      steps={3}
-      position="static"
-      activeStep={activeStep}
-      className={classes.root}
-      nextButton={
-        <Button aria-label="nextButton" size="medium" onClick={handleNext} disabled={activeStep === 2 || !nextEnabled}>
-          {label}
-          {icon}
-        </Button>
-      }
-      backButton={
-        <Button aria-label="backButton" size="medium" onClick={handleBack} disabled={activeStep === 0}>
-          <KeyboardArrowLeft />
-          Back
-        </Button>
-      }
-    />
+    <>
+      <MobileStepper
+        id="mobile-stepper"
+        variant="dots"
+        steps={3}
+        position="static"
+        activeStep={activeStep}
+        className={classes.root}
+        nextButton={nextButton}
+        backButton={backButton}
+      />
+      <ConfirmBackModal
+        enable={showConfirmModal}
+        setEnable={setShowConfirmModal}
+        confirmBack={()=>{setActiveStep(activeStep-1);}}
+      />
+    </>
   );
-}
\ No newline at end of file
+}
diff --git a/static_src/react/issue-wizard/Header.tsx b/static_src/react/issue-wizard/Header.tsx
new file mode 100644
index 0000000..e8dfdd9
--- /dev/null
+++ b/static_src/react/issue-wizard/Header.tsx
@@ -0,0 +1,26 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import React from 'react';
+import AppBar from '@material-ui/core/AppBar';
+import Toolbar from '@material-ui/core/Toolbar';
+import Typography from '@material-ui/core/Typography'
+
+
+
+export default function Header() {
+  return (
+    <>
+    <AppBar sx={{bgcolor: "white"}}>
+      <Toolbar>
+        <img src='/static/images/chromium.svg' width='=40' height='40'/>
+        <Typography variant="h5" component="div" color="black"> Bugs</Typography>
+      </Toolbar>
+    </AppBar>
+    <Toolbar />
+    </>
+  );
+
+
+}
\ No newline at end of file
diff --git a/static_src/react/issue-wizard/IssueWizardConfig.ts b/static_src/react/issue-wizard/IssueWizardConfig.ts
new file mode 100644
index 0000000..b7f1c17
--- /dev/null
+++ b/static_src/react/issue-wizard/IssueWizardConfig.ts
@@ -0,0 +1,470 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO: create a `monorail/frontend/config/` folder to store all the feature config file
+import {IssueCategory, IssueWizardPersona, CustomQuestionType} from "./IssueWizardTypes.tsx";
+
+// Customer Question convert to related labels
+export const LABELS_PREFIX = 'LABELS: ';
+
+export const ISSUE_WIZARD_QUESTIONS: IssueCategory[] = [
+  {
+    name: 'UI',
+    description: 'Problems with the user interface (e.g. tabs, context menus, etc...)',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-UI',
+    customQuestions: [],
+  },
+  {
+    name: 'Network / Downloading',
+    description: 'Problems with accessing remote content',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Internals-Network',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Input,
+        question: "What specific URL can reproduce the problem?",
+        answerPrefix: "Example URL: ",
+      },
+    ],
+  },
+  {
+    name: 'Audio / Video',
+    description: 'Problems playing back sound or movies',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Internals-Media',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Input,
+        question: "What specific URL can reproduce the problem?",
+        answerPrefix: "Example URL: ",
+      },
+      {
+        type: CustomQuestionType.Select,
+        question: "Does this feature work correctly in other browsers?",
+        answerPrefix: "Does this work in other browsers?\n",
+        options: ["Not sure - I don't know", "Yes - This is just a Chromium problem", "No - I can reproduce the problem in another browser"],
+        subQuestions: [
+          null,
+          null,
+          {
+            type:CustomQuestionType.Input,
+            question: "Which other browsers (including versions) also have the problem?",
+          }],
+      },
+      {
+        type: CustomQuestionType.Text,
+        question: "Please open chrome://gpu in a new Chrome tab and paste the report here.",
+        answerPrefix: "Contents of chrome://gpu: \n",
+      }
+    ],
+  },
+  {
+    name: 'Content',
+    description: "Problems with webpages not working correctly",
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: '',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Input,
+        question: "What specific URL has a problem?",
+        answerPrefix: "Example URL: ",
+      },
+      {
+        type: CustomQuestionType.Select,
+        question: "Does the problem occur on multiple sites?",
+        answerPrefix: LABELS_PREFIX,
+        options: ["Not sure - I don't know", "Yes - Please describe below", "No - Just that one URL"],
+        subQuestions: [null,null,null],
+      },
+      {
+        type: CustomQuestionType.Select,
+        question: "Is it a problem with a plugin?",
+        answerPrefix: "Is it a problem with a plugin? ",
+        options: ["Not sure - I don't know", "Yes - Those darn plugins", "No - It's the browser itself"],
+        subQuestions: [
+          null,
+          {
+            type:CustomQuestionType.Input,
+            question: "Which plugin?",
+          },
+          null],
+      },
+      {
+        type: CustomQuestionType.Select,
+        question: "Does this feature work correctly in other browsers?",
+        answerPrefix: "Does this work in other browsers? ",
+        options: ["Not sure - I don't know", "Yes - This is just a Chromium problem", "No - I can reproduce the problem in another browser"],
+        subQuestions: [
+          null,
+          null,
+          {
+            type:CustomQuestionType.Input,
+            question: "Which other browsers (including versions) also have the problem?",
+          }],
+      },
+    ],
+  },
+  {
+    name: 'Apps',
+    description: 'Issues with Webstore apps',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Platform-Apps',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Input,
+        question: "What is the link to that software in <a href='https://chrome.google.com/webstore' target='_blank'>the Chrome Webstore </a>?",
+        answerPrefix: "Webstore page: ",
+      }
+    ],
+  },
+  {
+    name: 'Extensions / Themes',
+    description: 'Issues with Webstore extensions and themes',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Platform-Extensions',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Select,
+        question: "What kind of software had the problem?",
+        answerPrefix: LABELS_PREFIX,
+        options: ["Chrome Extension - Adds new browser functionality", "Chrome Theme - Makes Chrome look awesome"],
+        subQuestions: [
+          null,
+          {
+            type:CustomQuestionType.Input,
+            question: "Do you know the latest version where it worked?",
+          },
+          null],
+      },
+      {
+        type: CustomQuestionType.Input,
+        question: "What is the link to that software in <a href='https://chrome.google.com/webstore' target='_blank'>the Chrome Webstore</a>?",
+        answerPrefix: "WebStore page: ",
+      },
+    ],
+  },
+  {
+    name: 'Webstore',
+    description: 'Problems with the Chrome Webstore itself',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Webstore',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Input,
+        question: "What is the URL of the Chrome WesStore page that had the problem?",
+        answerPrefix: "Webstore page: ",
+      },
+    ],
+  },
+  {
+    name: 'Sync',
+    description: 'Problems syncing data to a Google account',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Services-Sync',
+    customQuestions: [],
+  },
+  {
+    name: 'Enterprise',
+    description: 'Policy configuration and deployment issues',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Enterprise',
+    customQuestions: [],
+  },
+  {
+    name: 'Installation',
+    description: 'Problem installing Chrome',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: 'Cr-Internals-Installer',
+    customQuestions: [],
+  },
+  {
+    name: 'Crashes',
+    description: 'The browser closes abruptly or I see "Aw, Snap!" pages',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    tip: 'Please read the instructions on <a href="https://sites.google.com/a/chromium.org/dev/for-testers/bug-reporting-guidelines/reporting-crash-bug" target="_blank">reporting a crash issue</a>',
+    component: '',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Input,
+        question: "Do you have a Report ID from chrome://crashes?",
+        answerPrefix: "Crashed report ID: ",
+      },
+      {
+        type: CustomQuestionType.Select,
+        question: "How severe is the crash?",
+        options: ["Just one tab", "Just one plugin", "The whole browser"],
+        answerPrefix: "How much crashed? ",
+        subQuestions: null,
+      },
+      {
+        type: CustomQuestionType.Select,
+        question: "Is it a problem with a plugin?",
+        answerPrefix: "Is it a problem with a plugin? ",
+        options: ["Not sure - I don't know", "Yes - Those darn plugins", "No - It's the browser itself"],
+        subQuestions: [
+          null,
+          {
+            type:CustomQuestionType.Input,
+            question: "Which plugin?",
+          },
+          null],
+      },
+    ],
+    labels: ['Stability-Crash'],
+  },
+  {
+    name: 'Security',
+    description: 'Problems with the browser security',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    tip: 'Please follow the instructions for <a href="https://www.chromium.org/Home/chromium-security/reporting-security-bugs" target="_blank">reporting security issues</a>.',
+    component: '',
+    customQuestions: [],
+    labels: ['Restrict-View-SecurityTeam'],
+  },
+  {
+    name: 'Other',
+    description: 'Something not listed here',
+    persona: IssueWizardPersona.EndUser,
+    enabled: true,
+    component: '',
+    customQuestions: [
+      {
+        type: CustomQuestionType.Select,
+        question: "Please select a label to classify your issue:",
+        answerPrefix: LABELS_PREFIX,
+        options: [
+          "Not sure - I don't know",
+          "Type-Feature - Request for new or improved features",
+          "Type-Bug-Regression - Used to work, now broken",
+          "Type-Bug - Software not working correctly",
+          "Cr-UI-I18N - Issue in translating UI to other languages"
+        ],
+        subQuestions: null,
+      },
+    ],
+  },
+  {
+    name: 'API',
+    description: 'Problems with a browser API',
+    persona: IssueWizardPersona.Developer,
+    enabled: true,
+    component: '',
+    customQuestions: [
+      {
+        type:CustomQuestionType.Select,
+        question:"Which <a href='https://bugs.chromium.org/p/chromium/adminComponents' target='_blank'>component</a> does this fall under?",
+        answerPrefix: LABELS_PREFIX,
+        options: [
+          "Not sure - I don't know",
+          "Blink>Animation",
+          "Blink>BackgroundSync",
+          "Blink>Bindings",
+          "Blink>Bluetooth",
+          "Blink>Canvas",
+          "Blink>Compositing",
+          "Blink>CSS",
+          "Blink>DataTransfer",
+          "Blink>DOM",
+          "Blink>Editing",
+          "Blink>FileAPI",
+          "Blink>Focus",
+          "Blink>Fonts",
+          "Blink>Forms",
+          "Blink>Fullscreen",
+          "Blink>GamepadAPI",
+          "Blink>GetUserMedia",
+          "Blink>HitTesting",
+          "Blink>HTML",
+          "Blink>Image",
+          "Blink>Input",
+          "Blink>Internals",
+          "Blink>Javascript",
+          "Blink>Layout",
+          "Blink>Loader",
+          "Blink>Location",
+          "Blink>Media",
+          "Blink>MediaStream",
+          "Blink>MemoryAllocator",
+          "Blink>Messaging",
+          "Blink>Network",
+          "Blink>Paint",
+          "Blink>Payments",
+          "Blink>PerformanceAPIs",
+          "Blink>PermissionsAPI",
+          "Blink>PresentationAPI",
+          "Blink>PushAPI",
+          "Blink>SavePage",
+          "Blink>Scheduling",
+          "Blink>Scroll",
+          "Blink>SecurityFeature",
+          "Blink>ServiceWorker",
+          "Blink>Speech",
+          "Blink>Storage",
+          "Blink>SVG",
+          "Blink>TextAutosize",
+          "Blink>TextEncoding",
+          "Blink>TextSelection",
+          "Blink>USB",
+          "Blink>Vibration",
+          "Blink>ViewSource",
+          "Blink>WebAudio",
+          "Blink>WebComponents",
+          "Blink>WebCrypto",
+          "Blink>WebFonts",
+          "Blink>WebGL",
+          "Blink>WebGPU",
+          "Blink>WebMIDI",
+          "Blink>WebRTC",
+          "Blink>WebShare",
+          "Blink>WebVR",
+          "Blink>WindowDialog",
+          "Blink>Workers",
+          "Blink>XML",
+        ],
+        subQuestions: null,
+      },
+      {
+        type: CustomQuestionType.Select,
+        question: "Does this feature work correctly in other browsers?",
+        answerPrefix: "Does this work in other browsers? ",
+        tip: "Tip: Use <a href='https://www.browserstack.com/' target='_blank'>browserstack.com</a> to compare behavior on different browser versions.",
+        options: ["Not sure - I don't know", "Yes - This is just a Chrome problem", "No - I can reproduce the problem in another browser"],
+        subQuestions: [
+          null,
+          null,
+          {
+            type:CustomQuestionType.Text,
+            question: "Details of interop issue",
+            tip: "Please describe what the behavior is on other browsers and link to any <a href='https://browser-issue-tracker-search.appspot.com/' target='_blank'>existing bugs.</a>",
+          }
+        ],
+      },
+    ]
+  },
+  {
+    name: 'JavaScript',
+    description: 'Problems with the JavaScript interpreter',
+    persona: IssueWizardPersona.Developer,
+    enabled: true,
+    component: 'Cr-Blink',
+    customQuestions: [],
+  },
+  {
+    name: 'Developer Tools',
+    description: 'Problems with the Developer tool chain/inspector',
+    persona: IssueWizardPersona.Developer,
+    enabled: true,
+    component: 'Cr-Platform-DevTools',
+    customQuestions: [],
+  },
+];
+
+export const OS_LIST = [
+  {
+    name: 'Android',
+    description: '',
+  },
+  {
+  name: 'Chrome OS',
+  description: '',
+  },
+  {
+    name: 'iOS',
+    description: '',
+  },
+  {
+    name: 'Linux',
+    description: '',
+  },
+  {
+    name: 'Mac OS',
+    description: '',
+  },
+  {
+    name: 'Windows',
+    description: '',
+  },
+  {
+    name: 'Unknown/Other',
+    description: '',
+  },
+]
+
+// possible user os channel
+export const OS_CHANNEL_LIST = [
+  {
+    name: 'Not sure',
+    description: '',
+  },
+  {
+    name: 'Stable',
+    description: '',
+  },
+  {
+    name: 'Beta',
+    description: '',
+  },
+  {
+    name: 'Dev',
+    description: '',
+  },
+  {
+    name: 'Canary',
+    description: '',
+  },
+]
+
+export const BROWSER_LIST = [
+  {
+    name: 'Apple Safari',
+    description: '',
+  },
+  {
+    name: 'Google Chrome or Chromium',
+    description: '',
+  },
+  {
+    name: 'Mozilla Firefox',
+    description: '',
+  },
+  {
+    name: 'Microsoft Edge (Chromium)',
+    description: '',
+  },
+  {
+    name: 'Microsoft Edge (Legacy)',
+    description: '',
+  },
+  {
+    name: 'Microsoft Internet Explorer',
+    description: '',
+  },
+  {
+    name: 'Opera',
+    description: '',
+  },
+  {
+    name: 'Samsung Internet',
+    description: '',
+  },
+  {
+    name: 'Unknown / Other',
+    description: '',
+  },
+]
+
+export const ISSUE_REPRODUCE_PLACEHOLDER = '1.\n2.\n3.';
diff --git a/static_src/react/issue-wizard/IssueWizardDescriptionsUtils.tsx b/static_src/react/issue-wizard/IssueWizardDescriptionsUtils.tsx
new file mode 100644
index 0000000..e32d2d5
--- /dev/null
+++ b/static_src/react/issue-wizard/IssueWizardDescriptionsUtils.tsx
@@ -0,0 +1,85 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {LABELS_PREFIX} from "./IssueWizardConfig.ts";
+
+// Chromium project component prefix
+const CR_PREFIX = 'Cr-';
+
+// customized function for add additoinal data base on different categories.
+export function expandDescriptions(
+  category: string,
+  customQuestionsAnswers: Array<string>,
+  isRegression: boolean,
+  description: string,
+  labels: Array<any>,
+  component?: string,
+  ): {expandDescription:string, expandLabels:Array<any>, compVal:string} {
+    let expandDescription = "";
+    let expandLabels = labels;
+    let compVal = component || '';
+    let typeLabel = isRegression ? 'Type-Bug-Regression' : 'Type-Bug';
+
+    customQuestionsAnswers.forEach((ans) => {
+      if (ans.startsWith(LABELS_PREFIX)) {
+        const currentAnswer = ans.substring(LABELS_PREFIX.length);
+        switch (category) {
+          case 'Content':
+            if (currentAnswer.split(' - ')[0] === 'Yes') {
+              compVal = 'Cr-Blink';
+              typeLabel = 'Type-Bug';
+            } else {
+              compVal = '';
+              typeLabel = 'Type-Compat';
+            }
+            break;
+          case 'Extensions / Themes':
+            if (currentAnswer.split(' - ')[0] === 'Chrome Extension') {
+              compVal = 'Cr-Platform-Extensions';
+            } else {
+              compVal = 'Cr-UI-Browser-Themes';
+            }
+            break;
+          case 'Security':
+            if (typeLabel === '') {
+              typeLabel = 'Type-Bug-Security';
+            }
+          case 'Other':
+            typeLabel = "Type-Bug";
+            const issueType = currentAnswer.split(' - ')[0];
+            if (issueType !== 'Not sure'){
+              typeLabel = issueType;
+            }
+            if (issueType === 'Cr-UI-I18N') {
+              compVal = 'Cr-UI-I18N';
+            }
+            break;
+          case 'API':
+            compVal = currentAnswer;
+            if (compVal === "Not sure - I don't know") {
+              compVal = '';
+            }
+            break;
+        }
+      } else {
+        expandDescription = expandDescription + ans + "\n\n";
+      }
+    });
+
+    expandDescription = expandDescription + description;
+
+    if (typeLabel.length > 0) {
+      expandLabels.push({
+        label: typeLabel
+      });
+    }
+
+    if (compVal.length > 0) {
+      if (compVal.startsWith(CR_PREFIX)) {
+        compVal = compVal.substring(CR_PREFIX.length);
+        compVal = compVal.replace(/-/g, '>');
+      }
+    }
+    return {expandDescription, expandLabels, compVal};
+  }
diff --git a/static_src/react/issue-wizard/IssueWizardFeedback.tsx b/static_src/react/issue-wizard/IssueWizardFeedback.tsx
new file mode 100644
index 0000000..0dff09b
--- /dev/null
+++ b/static_src/react/issue-wizard/IssueWizardFeedback.tsx
@@ -0,0 +1,98 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import * as React from 'react';
+import {makeStyles} from '@material-ui/styles';
+import Dialog from '@material-ui/core/Dialog';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogContentText from '@material-ui/core/DialogContentText';
+import DialogActions from '@material-ui/core/DialogActions';
+import Button from '@material-ui/core/Button';
+import Input from '@material-ui/core/Input';
+
+const userStyles = makeStyles({
+  title: {
+    backgroundColor: 'rgb(84, 110, 122)',
+    color: 'white',
+    font: '300 20px / 24px Roboto, RobotoDraft, Helvetica, Arial, sans-serif'
+  },
+  inputArea: {
+    padding: '10px',
+  },
+  content: {
+    backgroundColor: 'rgb(250, 250, 250)',
+    padding: '12px 16px',
+  },
+  contentText: {
+    fontSize: '12px',
+  },
+  actionsButton: {
+    backgroundColor: 'rgb(250, 250, 250)',
+    borderTop: '1px solid rgb(224, 224, 224)',
+  }
+});
+
+type Props = {
+  enable: boolean,
+  setEnable: Function,
+}
+
+export function IssueWizardFeedback(props: Props): React.ReactElement {
+  React.useEffect(() => {
+    const script = document.createElement("script");
+    script.src = 'https://support.google.com/inapp/api.js';
+    script.async = true;
+    document.body.appendChild(script);
+  }, []);
+
+  const classes = userStyles();
+  const {enable, setEnable} = props;
+  const [feedback, setFeedback] = React.useState('');
+
+  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const textInput = e.target.value;
+    setFeedback(textInput);
+  };
+
+  const issueWizardFeedbackSend = () => {
+    window.userfeedback.api.startFeedback({
+      'productId': '5208992',  // Required.
+      'bucket': 'IssueWizard',  // Optional.
+      'report': {
+        'description': feedback
+      }
+    });
+    setEnable(false);
+  }
+
+  return (
+      <Dialog open={enable}>
+        <DialogTitle className={classes.title}>Send Feedback</DialogTitle>
+          <Input
+            placeholder="Have Feedback? We'd love to hear it, but please don't share sensitive informations. Have questions? Try help or support."
+            disableUnderline={true}
+            multiline={true}
+            rows={3}
+            className={classes.inputArea}
+            inputProps={{maxLength: 5000}}
+            onChange={handleInputChange}
+          />
+        <DialogContent className={classes.content}>
+          <DialogContentText className={classes.contentText}>
+          Some account and system information may be sent to Google. We will use it to fix problems and improve our services, subject to our
+           <a href="https://myaccount.google.com/privacypolicy?hl=en&amp;authuser=0" target="_blank"> Privacy Policy </a>
+           and <a href="https://www.google.com/intl/en/policies/terms?authuser=0" target="_blank"> Terms of Service </a>
+           . We may email you for more information or updates.
+          Go to <a href="https://support.google.com/legal/answer/3110420?hl=en&amp;authuser=0" target="_blank"> Legal Help </a>
+          to ask for content changes for legal reasons.
+          </DialogContentText>
+        </DialogContent>
+        <DialogActions className={classes.actionsButton}>
+          <Button onClick={()=>{setEnable(false);}}>Cancel</Button>
+          <Button onClick={issueWizardFeedbackSend}>Send</Button>
+        </DialogActions>
+    </Dialog>
+  );
+}
diff --git a/static_src/react/issue-wizard/IssueWizardTypes.tsx b/static_src/react/issue-wizard/IssueWizardTypes.tsx
new file mode 100644
index 0000000..3f43bce
--- /dev/null
+++ b/static_src/react/issue-wizard/IssueWizardTypes.tsx
@@ -0,0 +1,57 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// this const is used on issue wizard lading page for render user role  options
+export enum IssueWizardPersona {
+  EndUser = "EndUser",
+  Developer = "Developer",
+  Contributor = "Contributor",
+};
+
+
+export const ISSUE_WIZARD_PERSONAS_DETAIL  = Object.freeze({
+  [IssueWizardPersona.EndUser]: {
+    name: 'End User',
+    description: 'I am trying to use a website.',
+  },
+  [IssueWizardPersona.Developer]: {
+    name: 'Web Developer',
+    description: 'I am trying to build something on a website.',
+  },
+  [IssueWizardPersona.Contributor]: {
+    name: 'Chromium Contributor',
+    description: 'I know about a problem in specific tests or code.',
+  }
+});
+
+export enum CustomQuestionType {
+  EMPTY, // this is used to define there is no subquestions
+  Text,
+  Input,
+  Select,
+}
+export type CustomQuestion = {
+  type: CustomQuestionType,
+  question: string,
+  answerPrefix?: string,
+  tip?: string,
+  options?: string[],
+  subQuestions?: CustomQuestion[] | null,
+};
+
+export type IssueCategory = {
+  name: string,
+  description: string,
+  persona: IssueWizardPersona,
+  enabled: boolean,
+  tip?: string,
+  component?: string,
+  customQuestions?: CustomQuestion[],
+  labels?: Array<string>,
+};
+
+export type SelectMenuOption = {
+  name: string,
+  description?: string,
+};
diff --git a/static_src/react/issue-wizard/IssueWizardUtils.tsx b/static_src/react/issue-wizard/IssueWizardUtils.tsx
new file mode 100644
index 0000000..e709115
--- /dev/null
+++ b/static_src/react/issue-wizard/IssueWizardUtils.tsx
@@ -0,0 +1,162 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {CustomQuestion, IssueCategory, SelectMenuOption, IssueWizardPersona} from "./IssueWizardTypes";
+
+
+const CHROME_VERSION_REX = /chrome\/(\d|\.)+/i;
+// this function is used to get the issue list belong to different persona
+// when a user group is selected a list of related issue categories will show up
+export function GetCategoriesByPersona (categories: IssueCategory[]): Map<IssueWizardPersona, SelectMenuOption[]> {
+  const categoriesByPersona = new Map<IssueWizardPersona, SelectMenuOption[]>();
+
+  categories.forEach((category) => {
+    if (category.enabled) {
+      const currentIssuePersona = category.persona;
+      const currentCategories = categoriesByPersona.get(currentIssuePersona) ?? [];
+      currentCategories.push({
+        name: category.name,
+        description: category.description,
+      });
+      categoriesByPersona.set(currentIssuePersona, currentCategories);
+    }
+  });
+
+  return categoriesByPersona;
+}
+
+// this function is used to get the customer questions belong to different issue category
+// the customer question page will render base on these data
+export function GetQuestionsByCategory(categories: IssueCategory[]): Map<string, CustomQuestion[] | null> {
+  const questionsByCategory = new Map<string, CustomQuestion[] | null>();
+  categories.forEach((category) => {
+    questionsByCategory.set(category.name, category.customQuestions ?? null);
+  })
+  return questionsByCategory;
+}
+
+// this function is used to convert the options list fit for render use SelectMenu
+export function GetSelectMenuOptions(optionsList: string[]): SelectMenuOption[] {
+  const selectMenuOptionList = new Array<SelectMenuOption>();
+  optionsList.forEach((option) => {
+    selectMenuOptionList.push({name: option});
+  });
+  return selectMenuOptionList;
+}
+
+/**
+ * Detects the user's operating system.
+ */
+ export function getOs() {
+  const userAgent = window.navigator.userAgent,
+    platform = window.navigator.platform,
+    macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
+    windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
+    iosPlatforms = ['iPhone', 'iPad', 'iPod'];
+
+    if (macosPlatforms.indexOf(platform) !== -1) {
+      return'Mac OS';
+    } else if (iosPlatforms.indexOf(platform) !== -1) {
+      return 'iOS';
+    } else if (windowsPlatforms.indexOf(platform) !== -1) {
+      return 'Windows';
+    } else if (/Android/.test(userAgent)) {
+      return 'Android';
+    } else if (/Linux/.test(platform)) {
+      return 'Linux';
+    } else if (/\bCrOS\b/.test(userAgent)) {
+      return 'Chrome OS';
+    }
+
+    return 'Unknown / Other';
+
+}
+
+// this function is used to get the tip belong to different issue category
+// used for render detail page
+export function getTipByCategory(categories: IssueCategory[]): Map<string, string> {
+  const tipByCategory = new Map<string, string>();
+  categories.forEach((category) => {
+    if (category.tip) {
+      tipByCategory.set(category.name, category.tip);
+    }
+  })
+  return tipByCategory;
+}
+
+// this function is used to get the component value for each issue category used for make issue
+export function getCompValByCategory(categories: IssueCategory[]): Map<string, string> {
+  const compValByCategory = new Map<string, string>();
+  categories.forEach((category) => {
+    if (category.component) {
+      compValByCategory.set(category.name, category.component);
+    }
+  })
+  return compValByCategory;
+}
+
+export function getLabelsByCategory(categories: IssueCategory[]): Map<string, Array<string>> {
+  const labelsByCategory = new Map<string, Array<string>>();
+  categories.forEach((category) => {
+    if (category.labels) {
+      labelsByCategory.set(category.name, category.labels);
+    }
+  })
+  return labelsByCategory;
+}
+
+
+export function buildIssueDescription(
+  reproduceStep: string,
+  description: string,
+  comments: string,
+  os: string,
+  chromeVersion: string,
+  channel: string,
+  ): string {
+  const issueDescription =
+    "<b>Steps to reproduce the problem:</b>\n" + reproduceStep.trim() + "\n\n"
+    + "<b>Problem Description:</b>\n" + description.trim() + "\n\n"
+    + "<b>Additional Comments:</b>\n" + comments.trim() + "\n\n"
+    + "<b>Chrome version: </b>" + chromeVersion.trim() + " <b>Channel: </b>" + channel + "\n\n"
+    + "<b>OS:</b>" + os.trim();
+  return issueDescription;
+}
+
+export function buildIssueLabels(category: string, osName: string, chromeVersion: string, configLabels: Array<string> | null | undefined): Array<any> {
+  const labels = [
+    {label:'via-wizard-'+category},
+    {label:'Pri-2'},
+  ];
+
+  const os = osName.split(' ')[0];
+  if (os !== 'Unknown/Other') {
+    labels.push({
+      label: 'OS-'+os
+    })
+  }
+  const mainChromeVersion = chromeVersion.split('.').length > 0 ? chromeVersion.split('.')[0] : null;
+  if (mainChromeVersion !== null) {
+    labels.push({
+      label:'Needs-Triage-M'+mainChromeVersion
+    });
+  }
+
+  if (configLabels) {
+    configLabels.forEach((v) => {
+      labels.push({label: v});
+    })
+  }
+  return labels;
+}
+
+
+export function getChromeVersion() {
+  const userAgent = window.navigator.userAgent;
+  var browser= userAgent.match(CHROME_VERSION_REX) || [];
+  if (browser.length > 0) {
+    return browser[0].split('/')[1];
+  }
+  return "<Copy from:'about:version'>";
+}
diff --git a/static_src/react/issue-wizard/LandingStep.tsx b/static_src/react/issue-wizard/LandingStep.tsx
index 2925e87..3a83b2c 100644
--- a/static_src/react/issue-wizard/LandingStep.tsx
+++ b/static_src/react/issue-wizard/LandingStep.tsx
@@ -9,6 +9,10 @@
 import Checkbox, { CheckboxProps } from '@material-ui/core/Checkbox';
 import SelectMenu from './SelectMenu.tsx';
 import { RadioDescription } from './RadioDescription/RadioDescription.tsx';
+import {GetCategoriesByPersona} from './IssueWizardUtils.tsx';
+import {ISSUE_WIZARD_QUESTIONS} from './IssueWizardConfig.ts';
+import DotMobileStepper from './DotMobileStepper.tsx';
+import {IssueWizardPersona} from './IssueWizardTypes.tsx';
 
 const CustomCheckbox = withStyles({
   root: {
@@ -28,9 +32,6 @@
   flex: {
     display: 'flex',
   },
-  inlineBlock: {
-    display: 'inline-block',
-  },
   warningBox: {
     minHeight: '10vh',
     borderStyle: 'solid',
@@ -39,7 +40,7 @@
     borderRadius: '8px',
     background: yellow[50],
     padding: '0px 20px 1em',
-    margin: '30px 0px'
+    margin: '1rem 0'
   },
   warningHeader: {
     color: yellow[800],
@@ -54,27 +55,79 @@
   },
   header: {
     color: grey[900],
-    fontSize: '28px',
-    marginTop: '6vh',
+    fontSize: '1.5rem',
+    margin: '1rem 0',
   },
   subheader: {
     color: grey[700],
-    fontSize: '18px',
-    lineHeight: '32px',
+    fontSize: '1.125rem',
+    margin: '1rem 0',
+  },
+  alertDetail: {
+    fontSize: '16px',
+  },
+  link: {
+    fontSize: '20px',
+    fontWeight: 'bolder',
+    textDecoration: 'underline',
   },
   red: {
     color: red[600],
   },
+  line: {
+    color: grey[200],
+    marginTop: '1.5rem',
+    minWidth: '360px',
+  }
 });
 
-export default function LandingStep({ checkExisting, setCheckExisting, userType, setUserType, category, setCategory }:
-  { checkExisting: boolean, setCheckExisting: Function, userType: string, setUserType: Function, category: string, setCategory: Function }) {
+type Props = {
+  userPersona: IssueWizardPersona,
+  setUserPersona: Function,
+  category: string,
+  setCategory: Function,
+  setActiveStep: Function,
+};
+
+export default function LandingStep(props: Props) {
+
+  const {userPersona, setUserPersona, category, setCategory, setActiveStep} = props;
   const classes = useStyles();
 
+  const categoriesByPersonaMap = GetCategoriesByPersona(ISSUE_WIZARD_QUESTIONS);
+
+  const [categoryList, setCategoryList] = React.useState(categoriesByPersonaMap.get(userPersona));
+  const [checkExisting, setCheckExisting] = React.useState(false);
+
   const handleCheckChange = (event: React.ChangeEvent<HTMLInputElement>) => {
     setCheckExisting(event.target.checked);
   };
 
+  const onSelectUserPersona = (userPersona: string) => {
+    setUserPersona(userPersona);
+    setCategoryList(categoriesByPersonaMap.get(userPersona));
+    setCategory('');
+  }
+
+  const contributorAlert = () => {
+    return (
+      <div>
+        <div className={classes.subheader}>
+          Prefer to file an issue manually?
+        </div>
+        <div className={classes.alertDetail}>
+          It's usually best to work through this short wizard so that your issue is given the labels needed for the right team to see it.
+          Otherwise it might take longer for your issue to be triaged and resolved.
+        </div>
+        <div className={classes.alertDetail}>
+          However, if you are a Chromium contributor and none of the other options apply, you may use the
+          <a className={classes.link} href="entry"> regular issue entry form</a>.
+        </div>
+      </div>
+    );
+  }
+
+  const nextEnabled = (userPersona != IssueWizardPersona.Contributor) && checkExisting && (category != '');
   return (
     <>
       <p className={classes.header}>Report an issue with Chromium</p>
@@ -86,27 +139,35 @@
       <p className={classes.subheader}>
         Please select your following role: <span className={classes.red}>*</span>
       </p>
-      <RadioDescription value={userType} setValue={setUserType} />
-      <div className={classes.subheader}>
-        Which of the following best describes the issue that you are reporting? <span className={classes.red}>*</span>
-      </div>
-      <SelectMenu option={category} setOption={setCategory} />
-      <div className={classes.warningBox}>
-        <p className={classes.warningHeader}> Avoid duplicate issue reports:</p>
+      <RadioDescription selectedRadio={userPersona} onClickRadio={onSelectUserPersona} />
+      { userPersona === IssueWizardPersona.Contributor ? contributorAlert() :
         <div>
-          <div className={classes.star}>*</div>
-          <FormControlLabel className={classes.pad}
-            control={
-              <CustomCheckbox
-                checked={checkExisting}
-                onChange={handleCheckChange}
-                name="warningCheck"
+          <div className={classes.subheader}>
+            Which of the following best describes the issue that you are reporting? <span className={classes.red}>*</span>
+          </div>
+          <SelectMenu optionsList={categoryList} selectedOption={category} setOption={setCategory} />
+          <div className={classes.warningBox}>
+            <p className={classes.warningHeader}> <span className={classes.star}>*</span>Avoid duplicate issue reports:</p>
+            <div>
+              <FormControlLabel className={classes.pad}
+                control={
+                  <CustomCheckbox
+                    checked={checkExisting}
+                    onChange={handleCheckChange}
+                    name="warningCheck"
+                  />
+                }
+                label={
+                  <span>By checking this box, I'm acknowledging that I have searched for <a href="/p/chromium/issues/list" target="_blank">existing issues</a> that already report this problem.</span>
+                }
               />
-            }
-            label="By checking this box, I'm acknowledging that I have searched for existing issues that already report this problem."
-          />
+            </div>
+          </div>
         </div>
-      </div>
+      }
+      { userPersona === IssueWizardPersona.Contributor ? null :
+        <DotMobileStepper nextEnabled={nextEnabled} activeStep={0} setActiveStep={setActiveStep}/>
+      }
     </>
   );
-}
\ No newline at end of file
+}
diff --git a/static_src/react/issue-wizard/RadioDescription/RadioDescription.test.tsx b/static_src/react/issue-wizard/RadioDescription/RadioDescription.test.tsx
index 296e449..6d1398f 100644
--- a/static_src/react/issue-wizard/RadioDescription/RadioDescription.test.tsx
+++ b/static_src/react/issue-wizard/RadioDescription/RadioDescription.test.tsx
@@ -3,12 +3,12 @@
 // found in the LICENSE file.
 
 import React from 'react';
-import { render, screen, cleanup } from '@testing-library/react';
-import userEvent from '@testing-library/user-event'
+import { render, screen, cleanup, fireEvent } from '@testing-library/react';
 import { assert } from 'chai';
 import sinon from 'sinon';
 
 import { RadioDescription } from './RadioDescription.tsx';
+import {IssueWizardPersona} from '../IssueWizardTypes.tsx';
 
 describe('RadioDescription', () => {
   afterEach(cleanup);
@@ -29,7 +29,7 @@
   it('checks selected radio value', () => {
     // We're passing in the "Web Developer" value here manually
     // to tell our code that that radio button is selected.
-    render(<RadioDescription value={'Web Developer'} />);
+    render(<RadioDescription selectedRadio={IssueWizardPersona.Developer} />);
 
     const checkedRadio = screen.getByRole('radio', { name: /Web Developer/i });
     assert.isTrue(checkedRadio.checked);
@@ -43,25 +43,25 @@
     // Using the sinon.js testing library to create a function for testing.
     const setValue = sinon.stub();
 
-    render(<RadioDescription setValue={setValue} />);
+    render(<RadioDescription onClickRadio={setValue} />);
 
     const radio = screen.getByRole('radio', { name: /Web Developer/i });
-    userEvent.click(radio);
+    fireEvent.click(radio);
 
     // Asserts that "Web Developer" was passed into our "setValue" function.
-    sinon.assert.calledWith(setValue, 'Web Developer');
+    sinon.assert.calledWith(setValue, IssueWizardPersona.Developer);
   });
 
   it('sets radio value when any part of the parent RoleSelection is clicked', () => {
     const setValue = sinon.stub();
 
-    render(<RadioDescription setValue={setValue} />);
+    render(<RadioDescription onClickRadio={setValue} />);
 
     // Click text in the RoleSelection component
     const p = screen.getByText('End User');
-    userEvent.click(p);
+    fireEvent.click(p);
 
     // Asserts that "End User" was passed into our "setValue" function.
-    sinon.assert.calledWith(setValue, 'End User');
+    sinon.assert.calledWith(setValue, IssueWizardPersona.EndUser);
   });
-});
\ No newline at end of file
+});
diff --git a/static_src/react/issue-wizard/RadioDescription/RadioDescription.tsx b/static_src/react/issue-wizard/RadioDescription/RadioDescription.tsx
index 9a5a7d2..d371ef7 100644
--- a/static_src/react/issue-wizard/RadioDescription/RadioDescription.tsx
+++ b/static_src/react/issue-wizard/RadioDescription/RadioDescription.tsx
@@ -5,12 +5,7 @@
 import React from 'react';
 import { makeStyles } from '@material-ui/styles';
 import { RoleSelection } from './RoleSelection/RoleSelection.tsx';
-
-const userGroups = Object.freeze({
-  END_USER: 'End User',
-  WEB_DEVELOPER: 'Web Developer',
-  CONTRIBUTOR: 'Chromium Contributor',
-});
+import {ISSUE_WIZARD_PERSONAS_DETAIL, IssueWizardPersona} from '../IssueWizardTypes.tsx';
 
 const useStyles = makeStyles({
   flex: {
@@ -19,41 +14,48 @@
   }
 });
 
+const getUserGroupSelectors = (
+  value: IssueWizardPersona,
+  onSelectorClick:
+    (selector: string) =>
+      (event: React.MouseEvent<HTMLElement>) => any) => {
+  const selectors = new Array();
+  Object.entries(ISSUE_WIZARD_PERSONAS_DETAIL).forEach(([key, persona]) => {
+    selectors.push(
+        <RoleSelection
+          checked={IssueWizardPersona[value] === key}
+          handleOnClick={onSelectorClick(key)}
+          value={persona.name}
+          description={persona.description}
+          inputProps={{ 'aria-label': persona.name }}
+        />
+      );
+  });
+  return selectors;
+}
 /**
  * RadioDescription contains a set of radio buttons and descriptions (RoleSelection)
  * to be chosen from in the landing step of the Issue Wizard.
  *
  * @returns React.ReactElement
  */
-export const RadioDescription = ({ value, setValue }: { value: string, setValue: Function }): React.ReactElement => {
+type Props = {
+  selectedRadio: IssueWizardPersona,
+  onClickRadio: Function,
+}
+
+export const RadioDescription = (props: Props): React.ReactElement => {
+  const { selectedRadio, onClickRadio } = props;
   const classes = useStyles();
 
   const handleRoleSelectionClick = (userGroup: string) =>
-    (event: React.MouseEvent<HTMLElement>) => setValue(userGroup)
+     (event: React.MouseEvent<HTMLElement>) => onClickRadio(userGroup);
+
+  const userGroupsSelectors = getUserGroupSelectors(selectedRadio, handleRoleSelectionClick);
 
   return (
     <div className={classes.flex}>
-      <RoleSelection
-        checked={value === userGroups.END_USER}
-        handleOnClick={handleRoleSelectionClick(userGroups.END_USER)}
-        value={userGroups.END_USER}
-        description="I am a user trying to do something on a website."
-        inputProps={{ 'aria-label': userGroups.END_USER }}
-      />
-      <RoleSelection
-        checked={value === userGroups.WEB_DEVELOPER}
-        handleOnClick={handleRoleSelectionClick(userGroups.WEB_DEVELOPER)}
-        value={userGroups.WEB_DEVELOPER}
-        description="I am a web developer trying to build something."
-        inputProps={{ 'aria-label': userGroups.WEB_DEVELOPER }}
-      />
-      <RoleSelection
-        checked={value === userGroups.CONTRIBUTOR}
-        handleOnClick={handleRoleSelectionClick(userGroups.CONTRIBUTOR)}
-        value={userGroups.CONTRIBUTOR}
-        description="I know about a problem in specific tests or code."
-        inputProps={{ 'aria-label': userGroups.CONTRIBUTOR }}
-      />
+      {userGroupsSelectors}
     </div>
   );
-}
\ No newline at end of file
+}
diff --git a/static_src/react/issue-wizard/RadioDescription/RoleSelection/RoleSelection.tsx b/static_src/react/issue-wizard/RadioDescription/RoleSelection/RoleSelection.tsx
index 803a7b7..f1f1933 100644
--- a/static_src/react/issue-wizard/RadioDescription/RoleSelection/RoleSelection.tsx
+++ b/static_src/react/issue-wizard/RadioDescription/RoleSelection/RoleSelection.tsx
@@ -10,7 +10,8 @@
 const useStyles = makeStyles({
   container: {
     width: '320px',
-    height: '150px',
+    minWidth: '140px',
+    height: '160px',
     position: 'relative',
     display: 'inline-block',
     cursor: 'pointer',
@@ -18,15 +19,14 @@
   text: {
     position: 'absolute',
     display: 'inline-block',
-    left: '55px',
   },
   title: {
-    marginTop: '7px',
-    fontSize: '20px',
+    margin: '0.5rem 0',
+    fontSize: '1.125rem',
     color: grey[900],
   },
   subheader: {
-    fontSize: '16px',
+    fontSize: '0.875rem',
     color: grey[800],
   },
   line: {
diff --git a/static_src/react/issue-wizard/SelectMenu.test.tsx b/static_src/react/issue-wizard/SelectMenu.test.tsx
index 13efef6..b25baea 100644
--- a/static_src/react/issue-wizard/SelectMenu.test.tsx
+++ b/static_src/react/issue-wizard/SelectMenu.test.tsx
@@ -14,7 +14,7 @@
   let container: React.RenderResult;
 
   beforeEach(() => {
-    container = render(<SelectMenu />).container;
+    container = render(<SelectMenu optionsList = {['op1', 'op2']} />).container;
   });
 
   it('renders', () => {
@@ -22,7 +22,7 @@
     assert.isNotNull(form)
   });
 
-  it('renders options on click', () => {
+  it('renders options on click', async () => {
     const input = document.getElementById('outlined-select-category');
     if (!input) {
       throw new Error('Input is undefined');
@@ -31,8 +31,8 @@
     userEvent.click(input)
 
     // 14 is the current number of options in the select menu
-    const count = screen.getAllByTestId('select-menu-item').length;
+    const count = (await screen.findAllByTestId('select-menu-item')).length;
 
-    assert.equal(count, 14);
+    assert.equal(count, 2);
   });
-});
\ No newline at end of file
+});
diff --git a/static_src/react/issue-wizard/SelectMenu.tsx b/static_src/react/issue-wizard/SelectMenu.tsx
index 3b0b96d..f440d55 100644
--- a/static_src/react/issue-wizard/SelectMenu.tsx
+++ b/static_src/react/issue-wizard/SelectMenu.tsx
@@ -7,65 +7,7 @@
 import {makeStyles} from '@material-ui/styles';
 import MenuItem from '@material-ui/core/MenuItem';
 import TextField from '@material-ui/core/TextField';
-
-const CATEGORIES = [
-  {
-    value: 'UI',
-    label: 'UI',
-  },
-  {
-    value: 'Accessibility',
-    label: 'Accessibility',
-  },
-  {
-    value: 'Network/Downloading',
-    label: 'Network/Downloading',
-  },
-  {
-    value: 'Audio/Video',
-    label: 'Audio/Video',
-  },
-  {
-    value: 'Content',
-    label: 'Content',
-  },
-  {
-    value: 'Apps',
-    label: 'Apps',
-  },
-  {
-    value: 'Extensions/Themes',
-    label: 'Extensions/Themes',
-  },
-  {
-    value: 'Webstore',
-    label: 'Webstore',
-  },
-  {
-    value: 'Sync',
-    label: 'Sync',
-  },
-  {
-    value: 'Enterprise',
-    label: 'Enterprise',
-  },
-  {
-    value: 'Installation',
-    label: 'Installation',
-  },
-  {
-    value: 'Crashes',
-    label: 'Crashes',
-  },
-  {
-    value: 'Security',
-    label: 'Security',
-  },
-  {
-    value: 'Other',
-    label: 'Other',
-  },
-];
+import {SelectMenuOption} from './IssueWizardTypes.tsx';
 
 const theme: Theme = createTheme();
 
@@ -74,15 +16,19 @@
     display: 'flex',
     flexWrap: 'wrap',
     maxWidth: '65%',
+    marginRight: '1rem',
   },
   textField: {
-    marginLeft: theme.spacing(1),
-    marginRight: theme.spacing(1),
+    margin: '0',
   },
   menu: {
     width: '100%',
     minWidth: '300px',
   },
+  description: {
+    fontSize: 'small',
+    color: 'gray',
+  },
 }), {defaultTheme: theme});
 
 /**
@@ -92,8 +38,17 @@
  *
  * @return ReactElement.
  */
-export default function SelectMenu({option, setOption}: {option: string, setOption: Function}) {
+type Props = {
+  optionsList: SelectMenuOption[] | null,
+  selectedOption: SelectMenuOption | null,
+  setOption: Function,
+};
+
+export default function SelectMenu(props: Props) {
   const classes = useStyles();
+
+  const {optionsList, selectedOption, setOption} = props;
+
   const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
     setOption(event.target.value as string);
   };
@@ -105,7 +60,7 @@
         select
         label=''
         className={classes.textField}
-        value={option}
+        value={selectedOption}
         onChange={handleChange}
         InputLabelProps={{shrink: false}}
         SelectProps={{
@@ -117,17 +72,25 @@
         variant="outlined"
         fullWidth={true}
       >
-      {CATEGORIES.map(option => (
-        <MenuItem
-          className={classes.menu}
-          key={option.value}
-          value={option.value}
-          data-testid="select-menu-item"
-        >
-           {option.label}
-        </MenuItem>
-       ))}
+      {
+        optionsList?.map(option => (
+          <MenuItem
+            className={classes.menu}
+            key={option.name}
+            value={option.name}
+            data-testid="select-menu-item"
+          >
+            <div>
+              <div>{option.name}</div>
+              {
+                option.description ?
+                  <div className={classes.description}>{option.description}</div>
+                  : null
+              }
+            </div>
+          </MenuItem>))
+      }
       </TextField>
     </form>
   );
-}
\ No newline at end of file
+}
diff --git a/static_src/react/issue-wizard/SubmitSuccessStep.tsx b/static_src/react/issue-wizard/SubmitSuccessStep.tsx
new file mode 100644
index 0000000..7654b7c
--- /dev/null
+++ b/static_src/react/issue-wizard/SubmitSuccessStep.tsx
@@ -0,0 +1,31 @@
+ // Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import React from 'react';
+import {makeStyles} from '@material-ui/styles';
+
+const userStyles = makeStyles({
+  content: {
+    fontSize: '15px',
+    marginBottom: '15px',
+  }
+});
+
+type Props = {
+  issueID: string
+}
+export default function SubmitSuccessStep({issueID} : Props): React.ReactElement {
+  const classes = userStyles();
+  const issueLink = '/p/chromium/issues/detail?id=' + issueID;
+  return (
+    <>
+      <h1>Well done!</h1>
+      <div className={classes.content}>
+        <div>Your issue has successfully submitted! Thank you for your contribution to maintaining Chromium.</div>
+        <div>Click <a href={issueLink}>here</a> to see your filed bug.</div>
+      </div>
+      <img src='/static/images/dog.png'/>
+    </>
+  );
+}