Repository: Sage/carbon (ts)

Issue:
Item remains highlighted in Select dropdown after clearing
### Description

With a simple controlled select (similar to one in the documentation), after clicking Clear, the input is cleared but the previous item remains highlighted. The expectation is that no item should be highlighted.

### Reproduction

https://stackblitz.com/edit/parsium-carbon-starter-df8xvy?file=src%2FApp.tsx

### Steps to reproduce

Click to open the dropdown - no item is highlighted.
Select an item (for example, Black).
The value in the input now corresponds to the selection
Open the dropdown again - the selected item is highlighted too.
Click Clear button.
The input is cleared.
Open the dropdown again - the previous value is still highlighted 😞.

### JIRA ticket numbers (Sage only)

_No response_

### Suggested solution

_No response_

### Carbon version

131.1.0

### Design tokens version

_No response_

### Relevant browsers

Chrome

### Relevant OSs

MacOS

### Additional context

_No response_

### Confidentiality

- [X] I confirm there is no confidential or commercially sensitive information included.

Pull request:
fix(select): option remaining highlighted after clearing value
fix #6669

### Proposed behaviour

<!--
A clear and concise description of what changes this PR makes. If applicable, include any UI screenshots to help explain your request. If you are a Sage contributor, please DO NOT share any commercially sensitive information, such as UI screenshots or code from Sage products.
-->

A selected option no longer remains highlighted after the value is cleared.

### Current behaviour

<!--
A clear and concise description of the behaviour before this change. If applicable, include any UI screenshots to help explain your request. If you are a Sage contributor, please DO NOT share any commercially sensitive information, such as UI screenshots or code from Sage products.
-->

A selected option remains highlighted when the value has been cleared. 

### Checklist

<!-- Each PR should include the following -->

- [x] Commits follow our style guide
- [x] Related issues linked in commit messages if required
- [ ] Screenshots are included in the PR if useful
- [ ] All themes are supported if required
- [ ] Unit tests added or updated if required
- [x] Playwright automation tests added or updated if required
- [ ] Storybook added or updated if required
- [ ] Translations added or updated (including creating or amending translation keys table in storybook) if required
- [ ] Typescript `d.ts` file added or updated if required
- [ ] Related docs have been updated if required

#### QA

- [x] Tested in provided StackBlitz sandbox/Storybook
- [ ] Add new Playwright test coverage if required
- [ ] Carbon implementation matches Design System/designs
- [ ] UI Tests GitHub check reviewed if required

### Additional context

<!-- Add any other context or links about the pull request here. -->

### Testing instructions

<!--
How can a reviewer test this PR?

If this PR addresses a pre-existing bug, please include a link to a sandbox that reproduces the original bug. A starter template has been provided to help you do this:
<https://stackblitz.com/fork/github/Parsium/carbon-starter>
-->

**Issue:**
- Go to [controlled](https://carbon.sage.com/?path=/story/select--controlled) story for simple select or [controlled](https://carbon.sage.com/?path=/story/select-filterable--controlled) story for filterable select. 
- Select an option. 
- Open dropdown - see option is highlighted. 
- Click button to clear value.
- Open dropdown - see option is still highlighted. 

**Fix**:
- Go to controlled stories on branch and follow the same steps. 
- See option is no longer highlighted when value is cleared. 

Patch:
diff --git a/src/components/select/filterable-select/filterable-select.pw.tsx b/src/components/select/filterable-select/filterable-select.pw.tsx
index 23453794a..f0a901714 100644
--- a/src/components/select/filterable-select/filterable-select.pw.tsx
+++ b/src/components/select/filterable-select/filterable-select.pw.tsx
@@ -16,6 +16,8 @@ import {
   FilterableSelectNestedInDialog,
   SelectionConfirmed,
   FilterableSelectWithDisabledOption,
+  FilterableSelectControlled,
+  WithObjectAsValue,
 } from "../../../../src/components/select/filterable-select/components.test-pw";
 import {
   commonDataElementInputPreview,
@@ -767,6 +769,50 @@ test.describe("FilterableSelect component", () => {
     );
   });
 
+  test("should not highlight previously selected option when value is cleared", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FilterableSelectControlled />);
+
+    const inputElement = getDataElementByValue(page, "input");
+    await expect(inputElement).toHaveValue("Green");
+
+    const clearValueButton = page.getByRole("button");
+    await clearValueButton.click();
+
+    await expect(inputElement).toHaveValue("");
+    await dropdownButton(page).click();
+
+    const optionElement = selectOptionByText(page, "Green");
+    await expect(optionElement).toHaveCSS(
+      "background-color",
+      "rgba(0, 0, 0, 0)"
+    );
+  });
+
+  test("should not highlight previously selected option when object as value is cleared", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<WithObjectAsValue />);
+
+    const inputElement = getDataElementByValue(page, "input");
+    await expect(inputElement).toHaveValue("Green");
+
+    const clearValueButton = page.getByRole("button");
+    await clearValueButton.click();
+
+    await expect(inputElement).toHaveValue("");
+    await dropdownButton(page).click();
+
+    const optionElement = selectOptionByText(page, "Green");
+    await expect(optionElement).toHaveCSS(
+      "background-color",
+      "rgba(0, 0, 0, 0)"
+    );
+  });
+
   test("should select another option when one is already selected and the value is an object", async ({
     mount,
     page,
diff --git a/src/components/select/select-list/select-list.component.tsx b/src/components/select/select-list/select-list.component.tsx
index 1baf97c0c..068d87d9d 100644
--- a/src/components/select/select-list/select-list.component.tsx
+++ b/src/components/select/select-list/select-list.component.tsx
@@ -562,7 +562,13 @@ const SelectList = React.forwardRef(
     }, [childrenList, filterText, getIndexOfMatch, virtualizer]);
 
     useEffect(() => {
-      if (!highlightedValue) {
+      // remove the current selected option if the value is cleared
+      // this prevents it from remaining highlighted when the list is re-opened
+      if (
+        (!highlightedValue || Object.keys(highlightedValue).length === 0) &&
+        !isOpen
+      ) {
+        setCurrentOptionsListIndex(-1);
         return;
       }
       const indexOfMatch = getIndexOfMatch(highlightedValue);
@@ -572,7 +578,7 @@ const SelectList = React.forwardRef(
       }
 
       setCurrentOptionsListIndex(indexOfMatch);
-    }, [getIndexOfMatch, highlightedValue]);
+    }, [getIndexOfMatch, highlightedValue, isOpen]);
 
     // ensure that the currently-selected option is always visible immediately after
     // it has been changed
diff --git a/src/components/select/simple-select/simple-select.pw.tsx b/src/components/select/simple-select/simple-select.pw.tsx
index 23c09cf19..981a30910 100644
--- a/src/components/select/simple-select/simple-select.pw.tsx
+++ b/src/components/select/simple-select/simple-select.pw.tsx
@@ -16,6 +16,8 @@ import {
   SelectWithOptionGroupHeader,
   SelectionConfirmed,
   SelectWithDynamicallyAddedOption,
+  SimpleSelectControlled,
+  WithObjectAsValue,
 } from "./components.test-pw";
 import {
   commonDataElementInputPreview,
@@ -604,6 +606,50 @@ test.describe("SimpleSelect component", () => {
     );
   });
 
+  test("should not highlight previously selected option when value is cleared", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<SimpleSelectControlled />);
+
+    const inputElement = getDataElementByValue(page, "input");
+    await expect(inputElement).toHaveValue("Green");
+
+    const clearValueButton = page.getByRole("button");
+    await clearValueButton.click();
+
+    await expect(inputElement).toHaveValue("");
+    await selectText(page).click();
+
+    const optionElement = selectOptionByText(page, "Green");
+    await expect(optionElement).toHaveCSS(
+      "background-color",
+      "rgba(0, 0, 0, 0)"
+    );
+  });
+
+  test("should not highlight previously selected option when object as value is cleared", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<WithObjectAsValue />);
+
+    const inputElement = getDataElementByValue(page, "input");
+    await expect(inputElement).toHaveValue("Green");
+
+    const clearValueButton = page.getByRole("button");
+    await clearValueButton.click();
+
+    await expect(inputElement).toHaveValue("");
+    await selectText(page).click();
+
+    const optionElement = selectOptionByText(page, "Green");
+    await expect(optionElement).toHaveCSS(
+      "background-color",
+      "rgba(0, 0, 0, 0)"
+    );
+  });
+
   test("should select another option when one is already selected and the value is an object", async ({
     mount,
     page,


Test patch:
diff --git a/src/components/select/filterable-select/components.test-pw.tsx b/src/components/select/filterable-select/components.test-pw.tsx
index e8a5bd4ce..91f74de1b 100644
--- a/src/components/select/filterable-select/components.test-pw.tsx
+++ b/src/components/select/filterable-select/components.test-pw.tsx
@@ -585,3 +585,86 @@ export const FilterableSelectWithDisabledOption = () => {
     </>
   );
 };
+
+export const FilterableSelectControlled = () => {
+  const [value, setValue] = useState("5");
+  function onChangeHandler(event: React.ChangeEvent<HTMLInputElement>) {
+    setValue(event.target.value);
+  }
+  function clearValue() {
+    setValue("");
+  }
+  return (
+    <>
+      <Button onClick={clearValue} mb={2}>
+        clear
+      </Button>
+      <FilterableSelect
+        id="controlled"
+        name="controlled"
+        label="color"
+        value={value}
+        onChange={onChangeHandler}
+      >
+        <Option text="Amber" value="1" />
+        <Option text="Black" value="2" />
+        <Option text="Blue" value="3" />
+        <Option text="Brown" value="4" />
+        <Option text="Green" value="5" />
+        <Option text="Orange" value="6" />
+        <Option text="Pink" value="7" />
+        <Option text="Purple" value="8" />
+        <Option text="Red" value="9" />
+        <Option text="White" value="10" />
+        <Option text="Yellow" value="11" />
+      </FilterableSelect>
+    </>
+  );
+};
+
+export const WithObjectAsValue = () => {
+  const optionListValues = useRef([
+    { id: "Amber", value: 1, text: "Amber" },
+    { id: "Black", value: 2, text: "Black" },
+    { id: "Blue", value: 3, text: "Blue" },
+    { id: "Brown", value: 4, text: "Brown" },
+    { id: "Green", value: 5, text: "Green" },
+    { id: "Orange", value: 6, text: "Orange" },
+    { id: "Pink", value: 7, text: "Pink" },
+    { id: "Purple", value: 8, text: "Purple" },
+    { id: "Red", value: 9, text: "Red" },
+    { id: "White", value: 10, text: "White" },
+    { id: "Yellow", value: 11, text: "Yellow" },
+  ]);
+
+  const [value, setValue] = useState<Record<string, unknown>>(
+    optionListValues.current[4]
+  );
+
+  function onChangeHandler(event: React.ChangeEvent<HTMLInputElement>) {
+    if (typeof event.target.value === "object") {
+      setValue(event.target.value);
+    }
+  }
+  function clearValue() {
+    setValue({});
+  }
+  return (
+    <>
+      <Button onClick={clearValue} mb={2}>
+        clear
+      </Button>
+      <FilterableSelect
+        id="with-object"
+        name="with-object"
+        label="color"
+        value={value}
+        onChange={onChangeHandler}
+      >
+        {optionListValues.current.map((option) => (
+          <Option key={option.id} text={option.text} value={option} />
+        ))}
+      </FilterableSelect>
+    </>
+  );
+};
diff --git a/src/components/select/simple-select/components.test-pw.tsx b/src/components/select/simple-select/components.test-pw.tsx
index 04c2c7dc5..1a00c4900 100644
--- a/src/components/select/simple-select/components.test-pw.tsx
+++ b/src/components/select/simple-select/components.test-pw.tsx
@@ -10,6 +10,7 @@ import OptionGroupHeader from "../option-group-header/option-group-header.compon
 import Box from "../../box";
 import Icon from "../../icon";
 import Dialog from "../../dialog";
+import Button from "../../button";
 import { Select, Option, SimpleSelectProps } from "..";
 
 export const SimpleSelectComponent = (props: Partial<SimpleSelectProps>) => {
@@ -519,3 +520,86 @@ export const SelectWithDynamicallyAddedOption = () => {
     </Select>
   );
 };
+
+export const SimpleSelectControlled = () => {
+  const [value, setValue] = useState("5");
+  function onChangeHandler(event: React.ChangeEvent<HTMLInputElement>) {
+    setValue(event.target.value);
+  }
+  function clearValue() {
+    setValue("");
+  }
+  return (
+    <>
+      <Button onClick={clearValue} mb={2}>
+        clear
+      </Button>
+      <Select
+        id="controlled"
+        name="controlled"
+        value={value}
+        onChange={onChangeHandler}
+        label="color"
+      >
+        <Option text="Amber" value="1" />
+        <Option text="Black" value="2" />
+        <Option text="Blue" value="3" />
+        <Option text="Brown" value="4" />
+        <Option text="Green" value="5" />
+        <Option text="Orange" value="6" />
+        <Option text="Pink" value="7" />
+        <Option text="Purple" value="8" />
+        <Option text="Red" value="9" />
+        <Option text="White" value="10" />
+        <Option text="Yellow" value="11" />
+      </Select>
+    </>
+  );
+};
+
+export const WithObjectAsValue = () => {
+  const optionListValues = [
+    { id: "Amber", value: 1, text: "Amber" },
+    { id: "Black", value: 2, text: "Black" },
+    { id: "Blue", value: 3, text: "Blue" },
+    { id: "Brown", value: 4, text: "Brown" },
+    { id: "Green", value: 5, text: "Green" },
+    { id: "Orange", value: 6, text: "Orange" },
+    { id: "Pink", value: 7, text: "Pink" },
+    { id: "Purple", value: 8, text: "Purple" },
+    { id: "Red", value: 9, text: "Red" },
+    { id: "White", value: 10, text: "White" },
+    { id: "Yellow", value: 11, text: "Yellow" },
+  ];
+
+  const [value, setValue] = useState<Record<string, unknown>>(
+    optionListValues[4]
+  );
+
+  function onChangeHandler(event: React.ChangeEvent<HTMLInputElement>) {
+    if (typeof event.target.value === "object") {
+      setValue(event.target.value);
+    }
+  }
+  function clearValue() {
+    setValue({});
+  }
+  return (
+    <>
+      <Button onClick={clearValue} mb={2}>
+        clear
+      </Button>
+      <Select
+        id="with-object"
+        name="with-object"
+        value={value}
+        onChange={onChangeHandler}
+        label="color"
+      >
+        {optionListValues.map((option) => (
+          <Option key={option.id} text={option.text} value={option} />
+        ))}
+      </Select>
+    </>
+  );
+};