diff --git a/site/gatsby-site/playwright/e2e-full/submit.spec.ts b/site/gatsby-site/playwright/e2e-full/submit.spec.ts
index 309b4c65ab..acd3c0d899 100644
--- a/site/gatsby-site/playwright/e2e-full/submit.spec.ts
+++ b/site/gatsby-site/playwright/e2e-full/submit.spec.ts
@@ -1497,4 +1497,54 @@ test.describe('The Submit form', () => {
await expect(page.locator('.tw-toast:has-text("Please verify all information programmatically pulled from the report")')).toBeVisible();
await expect(page.locator('.tw-toast:has-text("Error fetching news.")')).not.toBeVisible();
});
+
+ test('Should autocomplete new entities', async ({ page, skipOnEmptyEnvironment }) => {
+
+ await conditionalIntercept(
+ page,
+ '**/parseNews**',
+ () => true,
+ parseNews,
+ 'parseNews'
+ );
+
+ await trackRequest(
+ page,
+ '**/graphql',
+ (req) => req.postDataJSON().operationName == 'FindSubmissions',
+ 'findSubmissions'
+ );
+
+ await page.goto(url);
+
+ await waitForRequest('findSubmissions');
+
+ await page.locator('input[name="url"]').fill(
+ `https://www.arstechnica.com/gadgets/2017/11/youtube-to-crack-down-on-inappropriate-content-masked-as-kids-cartoons/`
+ );
+
+ await page.locator('button:has-text("Fetch info")').click();
+
+ await waitForRequest('parseNews');
+
+ await page.locator('[name="incident_date"]').fill('2020-01-01');
+
+ await expect(page.locator('.form-has-errors')).not.toBeVisible();
+
+ await page.locator('[data-cy="to-step-2"]').click();
+
+ await page.locator('[data-cy="to-step-3"]').click();
+
+ await page.locator('input[name="developers"]').fill('New entity');
+ await page.keyboard.press('Enter');
+
+ await page.locator('input[name="deployers"]').fill('New entity');
+
+ await page.locator('#deployers-tags .dropdown-item[aria-label="New entity"]').click();
+
+ await page.locator('button[type="submit"]').click();
+
+ await expect(page.locator('.tw-toast:has-text("Report successfully added to review queue. You can see your submission")')).toBeVisible();
+ await expect(page.locator(':text("Please review. Some data is missing.")')).not.toBeVisible();
+ });
});
\ No newline at end of file
diff --git a/site/gatsby-site/playwright/e2e-full/signup.spec.ts b/site/gatsby-site/playwright/e2e/signup.spec.ts
similarity index 100%
rename from site/gatsby-site/playwright/e2e-full/signup.spec.ts
rename to site/gatsby-site/playwright/e2e/signup.spec.ts
diff --git a/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js b/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js
index 66ce40eddc..85cd649f7b 100644
--- a/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js
+++ b/site/gatsby-site/src/components/forms/SubmissionWizard/StepThree.js
@@ -160,6 +160,8 @@ const FormDetails = ({
const [submitCount, setSubmitCount] = useState(0);
+ const [entityNamesList, setEntityNamesList] = useState(entityNames);
+
const { isRole } = useUserContext();
const {
@@ -191,6 +193,26 @@ const FormDetails = ({
}
}, [submissionFailed, submissionComplete, submissionReset]);
+ const handleEntityChange = (values) => {
+ // Update entityNamesList with adding values that are not in entityNamesList
+ const newEntityNamesList = values
+ .filter((value) => {
+ if (!value.label) {
+ return !entityNamesList.includes(value);
+ }
+ return !entityNamesList.includes(value?.label);
+ })
+ .map((entity) => {
+ if (entity.label) {
+ return entity.label;
+ } else {
+ return entity;
+ }
+ });
+
+ setEntityNamesList([...entityNamesList, ...newEntityNamesList]);
+ };
+
const saveInLocalStorage = useRef(
debounce((values) => {
localStorage.setItem('formValues', JSON.stringify(values));
@@ -265,8 +287,8 @@ const FormDetails = ({
placeholder={t('Who employed or was responsible for the technology?')}
className="mt-3"
schema={schema}
- options={entityNames}
- handleChange={handleChange}
+ options={entityNamesList}
+ handleChange={handleEntityChange}
handleBlur={handleBlur}
touched={touched}
values={values}
@@ -282,8 +304,8 @@ const FormDetails = ({
placeholder={t('Who created or built the technology involved in the incident?')}
className="mt-3"
schema={schema}
- options={entityNames}
- handleChange={handleChange}
+ options={entityNamesList}
+ handleChange={handleEntityChange}
handleBlur={handleBlur}
touched={touched}
values={values}
@@ -299,8 +321,8 @@ const FormDetails = ({
placeholder={t('Who experienced negative impacts?')}
className="mt-3"
schema={schema}
- options={entityNames}
- handleChange={handleChange}
+ options={entityNamesList}
+ handleChange={handleEntityChange}
handleBlur={handleBlur}
touched={touched}
values={values}
diff --git a/site/gatsby-site/src/components/forms/TagsControl.js b/site/gatsby-site/src/components/forms/TagsControl.js
index 18893712ae..45746b4a83 100644
--- a/site/gatsby-site/src/components/forms/TagsControl.js
+++ b/site/gatsby-site/src/components/forms/TagsControl.js
@@ -3,7 +3,14 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import Tags from './Tags';
-const TagsControl = ({ name, placeholder, className, disabled = false, options = undefined }) => {
+const TagsControl = ({
+ name,
+ placeholder,
+ className,
+ disabled = false,
+ options = undefined,
+ handleChange = undefined,
+}) => {
const {
0: { value },
2: { setTouched, setValue },
@@ -20,6 +27,9 @@ const TagsControl = ({ name, placeholder, className, disabled = false, options =
onChange={(value) => {
setTouched(true);
setValue(value);
+ if (handleChange && handleChange.length > 0) {
+ handleChange(value);
+ }
}}
{...{
name,
@@ -29,6 +39,6 @@ const TagsControl = ({ name, placeholder, className, disabled = false, options =
}}
/>
);
-}
+};
export default TagsControl;
diff --git a/site/gatsby-site/src/components/forms/TagsInputGroup.js b/site/gatsby-site/src/components/forms/TagsInputGroup.js
index 4b11381a6b..d6b47b6875 100644
--- a/site/gatsby-site/src/components/forms/TagsInputGroup.js
+++ b/site/gatsby-site/src/components/forms/TagsInputGroup.js
@@ -41,7 +41,9 @@ const TagsInputGroup = ({
}
data-cy={props['data-cy']}
>
-